PageSourceSearch

https://iampho.ru/assets/common/qr/qr-svg.js

js iampho.ru collected 2026-10-07 18:10:44 UTC 5,318 bytes, 101 lines download raw bytes

1/**
2 * Дословная копия panel/assets/common/qr/qr-svg.js — зачем код рисует браузер и откуда взялась
3 * геометрия, написано в шапке оригинала.
4 *
5 * Общего каталога ассетов у приложений нет, поэтому файл именно копируют. Геометрия обязана
6 * совпадать с панельной: один и тот же код заведения гость видит и в панели, и в баннере сайта,
7 * и здесь, а по панельной ещё и напечатаны наклейки. Совпадение тел стережёт
8 * {@see panel/src/Application/Qr/Test/QrSvgCopyTest.php} — правишь здесь, правь и там.
9 *
10 * Нужен глобальный qrcode (vendor/qrcode/qrcode.min.js). Ветка badges тут не срабатывает:
11 * app.qrStoreBadges не определён, — и оставлена только ради дословности копии.
12 */
13(() => {
14
15    const FRAME_WIDTH = 185;
16    const FRAME_HEIGHT = 184;
17    const FRAME_HEIGHT_WITH_BADGES = 275;
18
19    // Коробка под сам код: левый верхний угол и сторона в единицах рамки. Масштаб считается
20    // от неё, а не задан числом, — тогда код с длинной ссылкой станет мельче, а не вылезет
21    // за рамку. На коротком адресе выходит ровно тот же scale(.555), что рисовал сервер.
22    const QR_LEFT = 21.5;
23    const QR_TOP = 22;
24    const QR_SIZE = 138.75;
25
26    // Сторона модуля в пикселях у голого кода — как у серверной картинки, чтобы вёрстка не поехала.
27    const PLAIN_MODULE_PX = 10;
28
29    /**
30     * Сам код: контур в единицах модуля (одна клетка = единица) и сторона в клетках.
31     * @returns {{path: string, count: number}}
32     */
33    const drawPath = (text) => {
34        // 0 — версия кода подбирается по длине данных, 'L' — уровень коррекции, как на сервере.
35        const code = qrcode(0, 'L');
36
37        code.addData(text);
38        code.make();
39
40        const count = code.getModuleCount();
41        let path = '';
42
43        for (let row = 0; row < count; row++) {
44            for (let col = 0; col < count; col++) {
45                if (code.isDark(row, col)) {
46                    path += `M${col} ${row}h1v1h-1z`;
47                }
48            }
49        }
50
51        // Все тёмные модули одним контуром. Сервер рисовал каждый отдельной фигурой, и на стыках
52        // вылезали белые волоски от сглаживания — здесь их нет, а файл вдвое легче.
53        return {path, count};
54    };
55
56    /**
57     * @param {string} text - что зашито в код, обычно адрес мультиссылки foodpicasso.com/x/{id}
58     * @param {{badges?: boolean}} [options] - badges: дорисовать кнопки App Store и Google Play
59     * @returns {string} разметка svg целиком, годная и для показа, и для скачивания файлом
60     */
61    const qrSvg = (text, options) => {
62        const withBadges = Boolean(options && options.badges);
63        const height = withBadges ? FRAME_HEIGHT_WITH_BADGES : FRAME_HEIGHT;
64        const {path, count}
64 = drawPath(text);
65
66        return `<svg xmlns="http://www.w3.org/2000/svg" width="${FRAME_WIDTH}px" height="${height}px"`
67            + ` viewBox="0 0 ${FRAME_WIDTH} ${height}">`
68            + `<rect x="0" y="0.5" width="${FRAME_WIDTH}" height="${height - 1}" rx="5" fill="white" stroke="#D0D0D0"/>`
69            + `<g transform="translate(${QR_LEFT}, ${QR_TOP}) scale(${QR_SIZE / count})">`
70            + `<path d="${path}" fill="#000000"/>`
71            + `</g>`
72            + (withBadges ? (window.app && window.app.qrStoreBadges) || '' : '')
73            + `</svg>`;
74    };
75
76    /**
77     * Голый код без рамки и кнопок — тем показывают, например, код приглашения.
78     * Поля вокруг кода не рисуем: их даёт отступ блока, в котором картинка стоит.
79     *
80     * @param {string} text - что зашито в код
81     * @returns {string} разметка svg целиком
82     */
83    const qrSvgPlain = (text) => {
84        const {path, count}
84 = drawPath(text);
85        const size = count * PLAIN_MODULE_PX;
86
87        return `<svg xmlns="http://www.w3.org/2000/svg" width="${size}px" height="${size}px"`
88            + ` viewBox="0 0 ${count} ${count}">`
89            + `<rect width="${count}" height="${count}" fill="white"/>`
90            + `<path d="${path}" fill="#000000"/>`
91            + `</svg>`;
92    };
93
94    window.fpQrSvg = {svg: qrSvg, plain: qrSvgPlain};
95
96    if (window.app) {
97        window.app.qrSvg = qrSvg;
98        window.app.qrSvgPlain = qrSvgPlain;
99    }
100
101})();

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.