PageSourceSearch

https://buffagni.it/js/ui-scale.js?cache=2

js buffagni.it collected 2026-10-05 04:16:17 UTC 7,289 bytes, 180 lines download raw bytes

1/*
2 * ui-scale.js — UI scale compensation for the Tesla in-car browser.
3 *
4 * The browser's effective page zoom changed, so the same CSS px now covers more of
5 * the panel and the whole UI reads oversized. We counter it by shrinking the root,
6 * which hands the layout back the CSS width it used to have.
7 *
8 *     scale k  ->  effective layout size = innerWidth / k  x  innerHeight / k
9 *
10 * Modes:
11 *   zoom       CSS zoom on <html>. Layout-correct, position:fixed unaffected. Spec-compliant
12 *              zoom (Chrome 128+) multiplies computed font-size, so if a minimum font size
13 *              floor is in play, small text stops scaling with everything else.
14 *   transform  scale() on <body>. Computed font sizes untouched, so immune to that clamping,
15 *              at the cost of reparenting fixed positioning to <body>.
16 *
17 * Anything that sizes against window.innerWidth/innerHeight must divide by the active
18 * scale, because those report the OUTER viewport while the layout now lives in the
19 * inner (scaled) coordinate space. Use CCFIX.viewportWidth()/viewportHeight().
20 *
21 * Overrides: ?uiscale=0.8  ?uimode=transform   (persisted to cookies)
22 *
23 * No dependencies — this loads before utils.js/cookies.js so it reads params and
24 * cookies itself.
25 */
26var CCFIX = (function () {
27    'use strict';
28
29    var STEP = 0.05, MIN = 0.4, MAX = 2.0;
30    // Auto is a hypothesis (devicePixelRatio moves with zoom but also with genuine hiDPI),
31    // not a measurement, so it gets a tighter floor than a manual setting.
32    var AUTO_MIN = 0.65;
33
34    var scale = 1, mode = 'zoom', auto = true;
35
36    function param(name) {
37        var m = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)').exec(window.location.href);
38        if (!m) return null;
39        return m[2] ? decodeURIComponent(m[2].replace(/\+/g, ' ')) : '';
40    }
41
42    function readCookie(name) {
43        var m = document.cookie.match(new RegExp('(?:^|; )' + name + '=([^;]*)'));
44        return m ? decodeURIComponent(m[1]) : null;
45    }
46
47    function writeCookie(name, value) {
48        try {
49            document.cookie = name + '=' + encodeURIComponent(value) + '; path=/; max-age=' + (60 * 60 * 24 * 365);
50        } catch (e) { /* private mode */ }
51    }
52
53    function clamp(k, lo) {
54        return Math.min(MAX, Math.max(lo === undefined ? MIN : lo, k));
55    }
56
57    function suggested() {
58        var dpr = window.devicePixelRatio || 1;
59        return clamp(Math.round((1 / dpr) / STEP) * STEP, AUTO_MIN);
60    }
61
62    function injectStyle() {
63        if (document.getElementById('ccfix-style')) return;
64        var css =
65            'html{-webkit-text-size-adjust:none !important;text-size-adjust:none !important;}' +
66            'html.ccfix-zoom{zoom:var(--ccfix-scale,1);}
66' +
67            'html.ccfix-transform{overflow:hidden !important;width:100% !important;height:100% !important;}' +
68            'html.ccfix-transform > body{' +
69                'transform:scale(var(--ccfix-scale,1)) !important;transform-origin:0 0 !important;' +
70                'width:calc(100% / var(--ccfix-scale,1)) !important;' +
71                'height:calc(100% / var(--ccfix-scale,1)) !important;}' +
72            /* styles.less uses viewport units in three places. Viewport units do not rescale
73               with zoom or transform, so without these the full-screen overlay and the pairing
74               pane would be left at the wrong size. Percentage-based rules self-correct. */
75            'html.ccfix-active body .overlay{' +
76                'width:calc(100vw / var(--ccfix-scale,1)) !important;' +
77                'height:calc(100vh / var(--ccfix-scale,1)) !important;}' +
78            'html.ccfix-active body article.pairing,' +
79            'html.ccfix-active body article.paired,' +
80            'html.ccfix-active body article.journey,' +
81            'html.ccfix-active body article.disclaimer{' +
82                'height:calc((100vh / var(--ccfix-scale,1)) - var(--footer-h, 84px)) !important;}';
83
84        var style = document.createElement('style');
85        style.id = 'ccfix-style';
86        style.appendChild(document.createTextNode(css));
87        (document.head || document.documentElement).appendChild(style);
88    }
89
90    function apply() {
91        var html = document.documentElement;
92        var active = Math.abs(scale - 1) > 0.0005;
93
94        html.style.setProperty('--ccfix-scale', String(scale));
95        html.classList.toggle('ccfix-active', active);
96        html.classList.toggle('ccfix-zoom', active && mode === 'zoom');
97        html.classList.toggle('ccfix-transform', active && mode === 'transform');
98
99        writeCookie('ccfixScale', String(scale));
100        writeCookie('ccfixMode', mode);
101        writeCookie('ccfixAuto', auto ? '1' : '0');
102
103        if (typeof window.onCCFixChange === 'function') {
104            try { window.onCCFixChange(scale, mode); } catch (e) { }
105        }
106    }
107
108    function relayout() {
109        // Anything sized off the viewport has to be recomputed in the new coordinate space.
110        if (typeof setDeviceMode === 'function' && typeof currentDisplayMode !== 'undefined' && currentDisplayMode) {
111            try { setDeviceMode(currentDisplayMode); } catch (e) { }
112        }
113    }
114
115    function set(k, isAuto) {
116        auto = !!isAuto;
117        scale = clamp(Math.round(k / 0.001) * 0.001);
118        apply();
119        relayout();
120        return scale;
121    }
122
123    function init() {
124        var qMode = param('uimode');
125        if (qMode === 'zoom' || qMode === 'transform') mode = qMode;
126        else if (readCookie('ccfixMode') === 'transform') mode = 'transform';
127
128        var qScale = param('uiscale');
129        if (qScale !== null && qScale !== '' && isFinite(parseFloat(qScale))) {
130            scale = clamp(parseFloat(qScale));
131            auto = false;
132        } else {
133            var ck = readCookie('ccfixScale');
134            if (ck !== null && isFinite(parseFloat(ck)) && readCookie('ccfixAuto') === '0') {
135                scale = clamp(parseFloat(ck));
136                auto = false;
137            } else {
138                scale = suggested();
139                auto = true;
140            }
141        }
142
143        injectStyle();
144        apply();
145
146        window.addEventListener('resize', function () {
147            if (auto) {
148                var next = suggested();
149                if (Math.abs(next - scale) > 0.0005) {
150                    scale = next;
151                    apply();
152                }
153            }
154            relayout();
155        });
156    }
157
158    init();
159
160    return {
161        // effective layout viewport — use these instead of window.innerWidth/innerHeight
162        viewportWidth: function () { return window.innerWidth / scale; },
163        viewportHeight: function () { return window.innerHeight / scale; },
164        get scale() { return scale; },
165        get mode() { return mode; },
166        get auto() { return auto; },
167        get active() { return Math.abs(scale - 1) > 0.0005; },
168        suggested: suggested,
169        step: STEP,
170        setScale: function (k) { return set(k, false); },
171        setAuto: function () { return set(suggested(), true); },
172        setMode: function (m) {
173            if (m !== 'zoom' && m !== 'transform') return mode;
174            mode = m;
175            apply();
176            relayout();
177            return mode;
178        }
179    };
180})();

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.