PageSourceSearch

http://doska.guwi.ru:5010/board/PeaktcazdYA

html guwi.ru collected 2026-10-05 00:16:37 UTC 8,351 bytes, 197 lines download raw bytes

1<!DOCTYPE html>
2<html lang="ru">
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Тест 01</title>
7    <link rel="stylesheet" href="/static/style.css">
8    
9<style>
10    #whiteboard-container { position: relative; width: 100%; max-width: 1200px; margin: 0 auto; background: #fff; border: 1px solid #ddd; }
11    #drawing-canvas { display: block; width: 100%; height: 600px; cursor: crosshair; background: white; }
12    .toolbar { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; margin-bottom: 10px; padding: 10px; background: #f0f0f0; border-radius: 4px; }
13    .toolbar .color-picker { width: 40px; height: 40px; border: none; padding: 0; cursor: pointer; }
14    .users-online { font-size: 14px; color: #555; margin-left: auto; }
15</style>
16
17</head>
18<body>
19    <header>
20        <div class="header-title">
21            <h1>Совместная доска</h1>
22        </div>
23        <nav class="header-nav">
24            <ul class="nav-menu">
25                <li><a href="/">Главная</a></li>
26                
27                    <li><a href="/login">Вход</a></li>
28                    
29                        <li><a href="/register">Регистрация</a></li>
30                    
31                
32            </ul>
33        </nav>
34    </header>
35    <main>
36        
37            
38        
39        
40<div class="whiteboard-page">
41    <h2>Тест 01 <small>(ID: 1, открытая)</small></h2>
42    <div class="toolbar">
43        <button id="pen-tool" class="btn active">🖊️ Кисть</button>
44        <button id="eraser-tool" class="btn">🧹 Ластик</button>
45        <input type="color" id="color-picker" value="#000000" class="color-picker" title="Цвет кисти">
46        <label>Размер: <input type="range" id="size-slider" min="1" max="50" value="3"></label>
47        <button id="clear-board" class="btn secondary">🗑️ Очистить</button>
48        <button id="export-png" class="btn secondary">📸 Скачать PNG</button>
49        <button id="copy-link-btn" class="btn secondary">📋 Копировать ссылку</button>
50        <span class="users-online">Пользователей онлайн: <span id="online-count">1</span></span>
51    </div>
52    <div id="whiteboard-container">
53        <canvas id="drawing-canvas" width="1200" height="600"></canvas>
54    </div>
55</div>
56
57    </main>
58    
59<script src="https://cdn.socket.io/4.5.4/socket.io.min.js"></script>
vendor: 1 bytes, line 59
59
60<script>
61    const canvas = document.getElementById('drawing-canvas');
62    const ctx = canvas.getContext('2d');
63    const boardId = 1;
64    const boardLink = "http://doska.guwi.ru:5010/board/PeaktcazdYA";
65
66    let currentTool = 'pen';
67    let currentColor = '#000000';
68    let currentSize = 3;
69    let drawing = false;
70    let currentPoints = [];
71
72    const socket = io();
73    socket.emit('join_board', { board_id: boardId });
74
75    socket.on('init_canvas', (data) => { redrawAll(data.actions); });
76    socket.on('draw', (action) => { drawAction(action); });
77    socket.on('clear', () => { clearCanvas(); });
78    socket.on('online_count', (data) => { document.getElementById('online-count').textContent = data.count; });
79
80    function redrawAll(actions) { clearCanvas(); actions.forEach(a => drawAction(a)); }
81    function drawAction(action) {
82        if (action.type === 'draw') {
83            ctx.save();
84            ctx.globalCompositeOperation = (action.tool === 'eraser') ? 'destination-out' : 'source-over';
85            ctx.strokeStyle = (action.tool === 'eraser') ? 'rgba(0,0,0,1)' : action.color;
86            ctx.lineWidth = action.size;
87            ctx.lineCap = 'round';
88            ctx.lineJoin = 'round';
89            const pts = action.points;
90            if (pts.length < 2) {
91                ctx.beginPath();
92                ctx.arc(pts[0].x, pts[0].y, ctx.lineWidth/2, 0, 2*Math.PI);
93                ctx.fillStyle = ctx.strokeStyle;
94                ctx.fill();
95            } else {
96                ctx.beginPath();
97                ctx.moveTo(pts[0].x, pts[0].y);
98                for (let i=1; i<pts.length; i++) ctx.lineTo(pts[i].x, pts[i].y);
99                ctx.stroke();
100            }
101            ctx.restore();
102        }
103    }
104    function clearCanvas() { ctx.clearRect(0, 0, canvas.width, canvas.height); }
105
106    function getPos(e) {
107        const rect = canvas.getBoundingClientRect();
108        const scaleX = canvas.width / rect.width;
109        const scaleY = canvas.height / rect.height;
110        let cx, cy;
111        if (e.touches) { cx = e.touches[0].clientX; cy = e.touches[0].clientY; }
112        else { cx = e.clientX; cy = e.clientY; }
113        return { x: (cx - rect.left) * scaleX, y: (cy - rect.top) * scaleY };
114    }
115
116    function startDrawing(e) {
117        drawing = true;
118        currentPoints = [];
119        const pos = getPos(e);
120        currentPoints.push(pos);
121        drawAction({ type:'draw', tool:currentTool, color:currentTool==='eraser'?'#000':currentColor, size:currentSize, points:[pos] });
122        e.preventDefault();
123    }
124    function draw(e) {
125        if (!drawing) return;
126        const pos = getPos(e);
127        currentPoints.push(pos);
128        drawAction({ type:'draw', tool:currentTool, color:currentTool==='eraser'?'#000':currentColor, size:currentSize, points:currentPoints.slice(-2) });
129        e.preventDefault();
130    }
131    function stopDrawing(e) {
132        if (!drawing) return;
133        drawing = false;
134        if (currentPoints.length > 0) {
135            socket.emit('draw', { board_id: boardId, action: {
136                type:'draw', tool:currentTool, color:currentTool==='eraser'?'#000':currentColor,
137                size:currentSize, points: currentPoints
138            }});
139        }
140        currentPoints = [];
141        e.preventDefault();
142    }
143
144    canvas.addEventListener('mousedown', startDrawing);
145    canvas.addEventListener('mousemove', draw);
146    canvas.addEventListener('mouseup', stopDrawing);
147    canvas.addEventListener('mouseleave', stopDrawing);
148    canvas.addEventListener('touchstart', startDrawing, {passive:false});
149    canvas.addEventListener('touchmove', draw, {passive:false});
150    canvas.addEventListener('touchend', stopDrawing);
151
152    document.getElementById('pen-tool').addEventListener('click', ()=>{
153        currentTool='pen';
154        document.getElementById('pen-tool').classList.add('active');
155        document.getElementById('eraser-tool').classList.remove('active');
156    });
157    document.getElementById('eraser-tool').addEventListener('click', ()=>{
158        currentTool='eraser';
159        document.getElementById('eraser-tool').classList.add('active');
160        document.getElementById('pen-tool').classList.remove('active');
161    });
162    document.getElementById('color-picker').addEventListener('input', e=>{ currentColor=e.target.value; });
163    document.getElementById('size-slider').addEventListener('input', e=>{ currentSize=parseInt(e.target.value); });
164    document.getElementById('clear-board').addEventListener('click', ()=>{
165        if (confirm('Очистить доску?')) {
166            socket.emit('clear', { board_id: boardId });
167            clearCanvas();
168        }
169    });
170    document.getElementById('export-png').addEventListener('click', ()=>{
171        const link = document.createElement('a');
172        link.download = 'whiteboard.png';
173        link.href = canvas.toDataURL('image/png');
174        link.click();
175    });
176
177    // Копирование ссылки
178    function copyToClipboard(text) {
179        if (navigator.clipboard && window.isSecureContext) {
180            navigator.clipboard.writeText(text).then(()=>alert('Ссылка скопирована!')).catch(()=>fallbackCopy(text));
181        } else { fallbackCopy(text); }
182    }
183    function fallbackCopy(text) {
184        const ta = document.createElement('textarea');
185        ta.value = text;
186        document.body.appendChild(ta);
187        ta.select();
188        try { document.execCommand('copy'); alert('Ссылка скопирована!'); } catch(e){ alert('Не удалось скопировать'); }
189        document.body.removeChild(ta);
190    }
191    document.getElementById('copy-link-btn').addEventListener('click', ()=>{ copyToClipboard(boardLink); });
192
193    window.addEventListener('beforeunload', ()=>{ socket.emit('leave_board', { board_id: boardId }); });
194</script>
194
195
196</body>
197</html>

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.