PageSourceSearch

https://senat.bi/wp-content/plugins/senat-burundi-qrcode-fixed/assets/js/sbqr-renderer.js?ver=4.0.0

js senat.bi collected 2026-10-02 11:51:28 UTC 15,028 bytes, 353 lines download raw bytes

1/**
2 * SBQR Custom QR Code Renderer v4.0
3 * Responsive HiDPI — modules: square/dot/rounded/diamond
4 * Gradient, coins stylisés, logo centré, inversion, coins QR arrondis.
5 */
6(function (win) {
7    'use strict';
8
9    function SBQRRenderer(opts) {
10        this.opts = Object.assign({
11            text: 'https://senat.bi/',
12            maxSize: 300,
13            margin: 2,
14            correctLevel: 'H',
15            moduleStyle: 'square',
16            moduleGap: 8,
17            colorDark: '#000000',
18            colorLight: '#ffffff',
19            transparentBg: false,
20            invert: false,
21            gradientEnabled: false,
22            gradientColor2: '#006633',
23            gradientDir: 'diagonal',
24            eyeOuterStyle: 'rounded',
25            eyeInnerStyle: 'square',
26            eyeColorEnabled: false,
27            eyeColor: '#000000',
28            logoInQR: false,
29            logoUrl: '',
30            logoSizeRatio: 0.22,
31            logoBgColor: '#ffffff',
32            logoBgRadius: 12,
33            logoPadding: 6,
34            logoBgTransparent: false,
35            shadowEnabled: false,
36            shadowColor: 'rgba(0,0,0,0.25)',
37            qrCornerRadius: 0,
38        }, opts);
39        this._matrix = null;
40        this._count  = 0;
41        this._canvas = null;
42        this._container = null;
43        this._ro = null;
44        this._logoImg = null;
45        this._currentSize = 0;
46    }
47
48    /* ─── Public ────────────────────────────────── */
49    SBQRRenderer.prototype.render = function (container) {
50        this._container = container;
51        this._buildMatrix();
52        this._paint();
53        this._watchResize();
54    };
55
56    SBQRRenderer.prototype.destroy = function () {
57        if (this._ro) { this._ro.disconnect(); this._ro = null; }
58    };
59
60    /* ─── Matrix ────────────────────────────────── */
61    SBQRRenderer.prototype._buildMatrix = function () {
62        var opts = this.opts;
63        var tmp = document.createElement('div');
64        tmp.style.cssText = 'position:absolute;top:-9999px;left:-9999px;width:1px;height:1px;overflow:hidden;';
65        document.body.appendChild(tmp);
66        try {
67            var qr = new QRCode(tmp, {
68                text: opts.text, width: 10, height: 10,
69                correctLevel: QRCode.CorrectLevel[opts.correctLevel] || QRCode.CorrectLevel.H
70            });
71            var d = qr._oQRCode, n = d.getModuleCount(), mat = [];
72            for (var r = 0; r < n; r++) { mat[r] = []; for (var c = 0; c < n; c++) mat[r][c] = d.isDark(r, c); }
73            this._matrix = mat; this._count = n;
74        } catch (e) { console.error('[SBQR]', e); }
75        document.body.removeChild(tmp);
76    };
77
78    /* ─── Paint ─────────────────────────────────── */
79    SBQRRenderer.prototype._paint = function () {
80        if (!this._matrix) return;
81        var container = this._container, opts = this.opts;
82        var rawW = container.clientWidth || container.offsetWidth || opts.maxSize;
83
84        // If explicit width/height are provided, use them; otherwise use maxSize logic
85        var size;
86        if (opts.width && opts.width > 0) {
87            size = opts.width;
88        } else {
89            size = Math.min(rawW, opts.maxSize);
90            if (size < 60) size = 60;
91        }
92        this._currentSize = size;
93
94        var canvasH = (opts.height && opts.height > 0) ? opts.height : size;
95
96        var dpr = win.devicePixelRatio || 1;
97        var canvas = this._canvas;
98        if (!canvas) {
99            canvas = document.createElement('canvas');
100            canvas.style.cssText = 'display:block;width:100%;height:auto;';
101            this._canvas = canvas;
102            container.innerHTML = '';
103            container.appendChild(canvas);
104        }
105        canvas.width  = size * dpr;
106        canvas.height = canvasH * dpr;
107
108        var ctx = canvas.getContext('2d');
109        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
110        this._draw(ctx, size);
111    };
112
113    SBQRRenderer.prototype._watchResize = function () {
114        var self = this;
115        if (typeof ResizeObserver !== 'undefined') {
116            this._ro = new ResizeObserver(function () { self._paint(); });
117            this._ro.observe(this._container);
118        }
119    };
120
121    /* ─── Draw ──────────────────────────────────── */
122    SBQRRenderer.prototype._draw = function (ctx, size) {
123        var opts = this.opts, n = this._count, mat = this._matrix;
124        var mgn  = opts.margin;
125        var ms   = (size - mgn * 2) / n;
126
127        // Couleurs effectives (avec inversion)
128        var cDark  = opts.invert ? opts.colorLight : opts.colorDark;
129        var cLight = opts.invert ? opts.colorDark  : opts.colorLight;
130
131        // Fond
132        if (opts.qrCornerRadius > 0) {
133            ctx.save();
134            this._clip(ctx, 0, 0, size, size, opts.qrCornerRadius * (size / 100));
135        }
136        if (!opts.transparentBg) { ctx.fillStyle = cLight; ctx.fillRect(0, 0, size, size); }
137        else { ctx.clearRect(0, 0, size, size); }
138
139        // Gradient
140        var darkFill = this._buildFill(ctx, opts, size, cDark);
141
142        // Calcul de la zone logo pour exclure les modules en dessous
143        var logoZone = null;
144        if (opts.logoInQR && opts.logoUrl) {
145            var maxL  = size * opts.logoSizeRatio;
146            // On ne connaît pas le ratio avant le chargement de l'image,
147            // on utilise le carré maxL × maxL comme zone de sécurité conservative
148            var lSize = maxL;
149            var pad   = opts.logoPadding;
150            var bgW   = lSize + pad * 2;
151            var bgX   = (size - bgW) / 2;
152            var bgY   = (size - bgW) / 2;
153            logoZone  = { x: bgX, y: bgY, w: bgW };
154        }
155
156        // Modules données (on saute les modules dans la zone logo)
157        ctx.fillStyle = darkFill;
158        for (var r = 0; r < n; r++) {
159            for (var c = 0; c < n; c++) {
160                if (!mat[r][c]) continue;
161                if (this._isFinder(r, c, n)) continue;
162                // Exclure les modules qui chevauchent la zone logo
163                if (logoZone) {
164                    var mx = mgn + c * ms, my = mgn + r * ms;
165                    if (mx + ms > logoZone.x && mx < logoZone.x + logoZone.w &&
166                        my + ms > logoZone.y && my < logoZone.y + logoZone.w) {
167                        continue;
168                    }
169                }
170                this._drawModule(ctx, mgn + c * ms, mgn + r * ms, ms, opts.moduleStyle, opts.moduleGap);
171            }
172        }
173
174        // Coins stylisés
175        var eyeColor = opts.eyeColorEnabled ? (opts.invert ? this._invertHex(opts.eyeColor) : opts.eyeColor) : cDark;
176        this._drawAllEyes(ctx, n, ms, mgn, eyeColor, cLight, opts.transparentBg);
177
178        if (opts.qrCornerRadius > 0) ctx.restore();
179
180        // Logo — toujours dessiné en dernier, par-dessus les modules
181        var self = this;
182        if (opts.logoInQR && opts.logoUrl) {
183            if (this._logoImg) {
184                this._drawLogo(ctx, this._logoImg, size);
185            } else {
186                var img = new Image(); img.crossOrigin = 'anonymous';
187                img.onload = function () {
188                    self._logoImg = img;
189                    // Redessiner entièrement pour garantir que le logo est par-dessus tout
190                    var c2 = self._canvas.getContext('2d');
191                    c2.setTransform(win.devicePixelRatio || 1, 0, 0, win.devicePixelRatio || 1, 0, 0);
192                    self._draw(c2, self._currentSize);
193                };
194                img.onerror = function () { console.warn('[SBQR] Logo non chargé'); };
195                img.src = opts.logoUrl;
196            }
197        }
198    };
199
200    SBQRRenderer.prototype._clip = function (ctx, x, y, w, h, r) {
201        ctx.beginPath();
202        ctx.moveTo(x + r, y); ctx.lineTo(x + w - r, y); ctx.arcTo(x + w, y, x + w, y + r, r);
203        ctx.lineTo(x + w, y + h - r); ctx.arcTo(x + w, y + h, x + w - r, y + h, r);
204        ctx.lineTo(x + r, y + h); ctx.arcTo(x, y + h, x, y + h - r, r);
205        ctx.lineTo(x, y + r); ctx.arcTo(x, y, x + r, y, r);
206        ctx.closePath(); ctx.clip();
207    };
208
209    SBQRRenderer.prototype._buildFill = function (ctx, opts, size, cDark) {
210        if (!opts.gradientEnabled) return cDark;
211        var g, c2 = opts.invert ? this._invertHex(opts.gradientColor2) : opts.gradientColor2;
212        switch (opts.gradientDir) {
213            case 'horizontal': g = ctx.createLinearGradient(0, 0, size, 0); break;
214            case 'vertical':   g = ctx.createLinearGradient(0, 0, 0, size); break;
215            case 'radial':     g = ctx.createRadialGradient(size/2,size/2,0,size/2,size/2,size/2); break;
216            default:           g = ctx.createLinearGradient(0, 0, size, size);
217        }
218        g.addColorStop(0, cDark); g.addColorStop(1, c2);
219        return g;
220    };
221
222    SBQRRenderer.prototype._isFinder = function (r, c, n) {
223        return (r < 7 && c < 7) || (r < 7 && c >= n - 7) || (r >= n - 7 && c < 7);
224    };
225
226    /* ─── Module individuel ─────────────────────── */
227    SBQRRenderer.prototype._drawModule = function (ctx, x, y, s, style, gap) {
228        var g = s * (gap / 100);
229        var x2 = x + g / 2, y2 = y + g / 2, s2 = s - g;
230        switch (style) {
231            case 'dot':
232                ctx.beginPath();
233                ctx.arc(x + s / 2, y + s / 2, s2 / 2, 0, Math.PI * 2);
234                ctx.fill();
235                break;
236            case 'rounded':
237                this._rrFill(ctx, x2, y2, s2, s2, s2 * 0.3);
238                break;
239            case 'diamond':
240                ctx.save();
241                ctx.translate(x + s / 2, y + s / 2);
242                ctx.rotate(Math.PI / 4);
243                ctx.fillRect(-s2 * 0.38, -s2 * 0.38, s2 * 0.76, s2 * 0.76);
244                ctx.restore();
245                break;
246            default:
247                ctx.fillRect(x2, y2, s2, s2);
248        }
249    };
250
251    /* ─── Finder Patterns ───────────────────────── */
252    SBQRRenderer.prototype._drawAllEyes = function (ctx, n, ms, margin, color, light, transparent) {
253        var pos = [{r:0,c:0},{r:0,c:n-7},{r:n-7,c:0}];
254        for (var i = 0; i < 3; i++) {
255            this._drawEye(ctx, margin + pos[i].c * ms, margin + pos[i].r * ms, ms, color, light, transparent);
256        }
257    };
258
259    SBQRRenderer.prototype._drawEye = function (ctx, x, y, ms, color, light, transparent) {
260        var opts = this.opts, outer = 7 * ms, inner = 3 * ms;
261        var ix = x + 2 * ms, iy = y + 2 * ms;
262        ctx.fillStyle = color;
263
264        switch (opts.eyeOuterStyle) {
265            case 'circle':
266                this._ring(ctx, x + outer/2, y + outer/2, outer/2, outer/2 - ms, color, light, transparent);
267                break;
268            case 'rounded': {
269                var ro = ms * 2;
270                this._rrFill(ctx, x, y, outer, outer, ro);
271                if (transparent) { ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = 'rgba(255,255,255,1)'; }
272                else { ctx.fillStyle = light; }
273                this._rrFill(ctx, x + ms, y + ms, outer - 2*ms, outer - 2*ms, ro * 0.4);
274                ctx.globalCompositeOperation = 'source-over';
275                ctx.fillStyle = color;
276                break;
277            }
278            case 'pointed':
279                ctx.fillRect(x, y, outer, outer);
280                ctx.fillStyle = transparent ? 'rgba(0,0,0,0)' : light;
281                ctx.fillRect(x + ms, y + ms, outer - 2*ms, outer - 2*ms);
282                ctx.fillStyle = color;
283                break;
284            default:
285                ctx.fillRect(x, y, outer, outer);
286                ctx.fillStyle = transparent ? 'rgba(0,0,0,0)' : light;
287                ctx.fillRect(x + ms, y + ms, outer - 2*ms, outer - 2*ms);
288                ctx.fillStyle = color;
289        }
290
291        switch (opts.eyeInnerStyle) {
292            case 'dot':
293                ctx.beginPath(); ctx.arc(ix + inner/2, iy + inner/2, inner/2, 0, Math.PI*2); ctx.fill(); break;
294            case 'rounded':
295                this._rrFill(ctx, ix, iy, inner, inner, ms * 0.8); break;
296            default:
297                ctx.fillRect(ix, iy, inner, inner);
298        }
299    };
300
301    SBQRRenderer.prototype._ring = function (ctx, cx, cy, r1, r2, fill, bg, transparent) {
302        ctx.fillStyle = fill;
303        ctx.beginPath(); ctx.arc(cx, cy, r1, 0, Math.PI*2); ctx.fill();
304        if (transparent) { ctx.globalCompositeOperation = 'destination-out'; ctx.fillStyle = 'rgba(255,255,255,1)'; }
305        else { ctx.fillStyle = bg; }
306        ctx.beginPath(); ctx.arc(cx, cy, r2, 0, Math.PI*2); ctx.fill();
307        ctx.globalCompositeOperation = 'source-over';
308    };
309
310    /* ─── Logo ──────────────────────────────────── */
311    SBQRRenderer.prototype._drawLogo = function (ctx, img, size) {
312        var opts = this.opts;
313        // Preserve image aspect ratio
314        var maxL = size * opts.logoSizeRatio;
315        var ratio = (img.naturalWidth && img.naturalHeight) ? img.naturalWidth / img.naturalHeight : 1;
316        var lW, lH;
317        if (ratio >= 1) { lW = maxL; lH = maxL / ratio; }
318        else            { lH = maxL; lW = maxL * ratio; }
319
320        var lx = (size - lW) / 2, ly = (size - lH) / 2;
321        var pad = opts.logoPadding;
322        var bgW = lW + pad * 2, bgH = lH + pad * 2;
323        var bgX = lx - pad,     bgY = ly - pad;
324
325        if (!opts.logoBgTransparent) {
326            ctx.fillStyle = opts.logoBgColor || '#ffffff';
327            this._rrFill(ctx, bgX, bgY, bgW, bgH, opts.logoBgRadius);
328        }
329        ctx.drawImage(img, lx, ly, lW, lH);
330    };
331
332    /* ─── Helpers ───────────────────────────────── */
333    SBQRRenderer.prototype._rrFill = function (ctx, x, y, w, h, r) {
334        r = Math.max(0, Math.min(r, w/2, h/2));
335        ctx.beginPath();
336        ctx.moveTo(x+r, y); ctx.lineTo(x+w-r, y); ctx.arcTo(x+w, y, x+w, y+r, r);
337        ctx.lineTo(x+w, y+h-r); ctx.arcTo(x+w, y+h, x+w-r, y+h, r);
338        ctx.lineTo(x+r, y+h); ctx.arcTo(x, y+h, x, y+h-r, r);
339        ctx.lineTo(x, y+r); ctx.arcTo(x, y, x+r, y, r);
340        ctx.closePath(); ctx.fill();
341    };
342
343    SBQRRenderer.prototype._invertHex = function (hex) {
344        hex = hex.replace('#', '');
345        if (hex.length === 3) hex = hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];
346        var r = 255 - parseInt(hex.slice(0,2),16);
347        var g = 255 - parseInt(hex.slice(2,4),16);
348        var b = 255 - parseInt(hex.slice(4,6),16);
349        return '#' + [r,g,b].map(function(v){return ('0'+v.toString(16)).slice(-2);}).join('');
350    };
351
352    win.SBQRRenderer = SBQRRenderer;
353})(window);

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.