PageSourceSearch

https://pupwife.com/scripts/desktop/citrusBirthdayAssets.js

js pupwife.com collected 2026-09-30 15:04:44 UTC 3,947 bytes, 110 lines download raw bytes

1/**
2 * Birthday art preload + tinted balloon texture cache.
3 */
4(function (global) {
5    'use strict';
6
7    var cache = {
8        baseBalloon: null,
9        goldBalloon: null,
10        cakeSheet: null,
11        tinted: {}
12    };
13
14    function loadImage(src) {
15        return new Promise(function (resolve, reject) {
16            var img = new Image();
17            img.crossOrigin = 'anonymous';
18            img.onload = function () { resolve(img); };
19            img.onerror = function () { reject(new Error('Failed to load ' + src)); };
20            img.src = src;
21        });
22    }
23
24    function hexToRgb(hex) {
25        var h = String(hex || '').replace('#', '');
26        if (h.length === 3) {
27            h = h[0] + h[0] + h[1] + h[1] + h[2] + h[2];
28        }
29        return {
30            r: parseInt(h.slice(0, 2), 16) || 0,
31            g: parseInt(h.slice(2, 4), 16) || 0,
32            b: parseInt(h.slice(4, 6), 16) || 0
33        };
34    }
35
36    function bakeTintedBalloon(baseImg, targetHex) {
37        var key = String(targetHex).toLowerCase();
38        if (cache.tinted[key]) return cache.tinted[key];
39
40        var w = baseImg.naturalWidth || baseImg.width;
41        var h = baseImg.naturalHeight || baseImg.height;
42        var canvas = document.createElement('canvas');
43        canvas.width = w;
44        canvas.height = h;
45        var ctx = canvas.getContext('2d');
46        if (!ctx) return baseImg.src || null;
47
48        ctx.drawImage(baseImg, 0, 0, w, h);
49        var imageData = ctx.getImageData(0, 0, w, h);
50        var target = hexToRgb(targetHex);
51        var data = imageData.data;
52
53        for (var i = 0; i < data.length; i += 4) {
54            var a = data[i + 3];
55            if (a < 8) continue;
56            var r = data[i];
57            var g = data[i + 1];
58            var b = data[i + 2];
59            var lum = (0.299 * r + 0.587 * g + 0.114 * b) / 255;
60            var shade = 0.35 + lum * 0.75;
61            data[i] = Math.min(255, target.r * shade);
62            data[i + 1] = Math.min(255, target.g * shade);
63            data[i + 2] = Math.min(255, target.b * shade);
64        }
65
66        ctx.putImageData(imageData, 0, 0);
67        cache.tinted[key] = canvas.toDataURL('image/png');
68        return cache.tinted[key];
69    }
70
71    function preload(entry) {
72        if (!entry || !entry.assets) return Promise.reject(new Error('No birthday assets'));
73        return Promise.all([
74            loadImage(entry.assets.balloon).then(function (img) { cache.baseBalloon = img; }),
75            loadImage(entry.assets.balloonGold).then(function (img) { cache.goldBalloon = img; }),
76            loadImage(entry.assets.cakeSheet).then(function (img) { cache.cakeSheet = img; })
77        ]);
78    }
79
80    function getTintedSpriteSource(colorHex) {
81        if (!cache.baseBalloon) return null;
82        return bakeTintedBalloon(cache.baseBalloon, colorHex);
83    }
84
85    function getGoldSpriteSource() {
86        if (cache.goldBalloon && cache.goldBalloon.src) return cache.goldBalloon.src;
87        return '/images/birthday/balloon_gold.png';
88    }
89
90    function pickRandomColor(entry) {
91        var colors = entry && entry.balloonColors ? entry.balloonColors : ['#F46607'];
92        return colors[Math.floor(Math.random() * colors.length)];
93    }
94
95    function pickDisplaySize(entry) {
96        var min = (entry && entry.balloonDisplayPx && entry.balloonDisplayPx.min) || 80;
97        var max = (entry && entry.balloonDisplayPx && entry.balloonDisplayPx.max) || 100;
98        return min + Math.random() * (max - min);
99    }
100
101    global.CitrusBirthdayAssets = {
102        preload: preload,
103        getTintedSpriteSource: getTintedSpriteSource,
104        getGoldSpriteSource: getGoldSpriteSource,
105        pickRandomColor: pickRandomColor,
106        pickDisplaySize: pickDisplaySize,
107        getCakeSheet: function () { return cache.cakeSheet; },
108        getBaseBalloon: function () { return cache.baseBalloon; }
109    };
110})(typeof window !== 'undefined' ? window : this);

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.