onenote-clone / static /script.js
tiffank1802
Fix canvas scrolling - 2000x2000 scrollable canvas
d76695a
const API = "/api";
let nbId = null, secId = null, pageId = null;
let tool = "pen", paths = [], drawing = false;
let canvas, ctx;
window.onload = function() {
canvas = document.getElementById("drawing-canvas");
ctx = canvas.getContext("2d");
resizeCanvas();
initDrawing();
loadNotebooks();
setInterval(savePage, 30000);
};
function showRibbonTab(tab) {
document.querySelectorAll(".tab-btn").forEach(b => b.classList.remove("active"));
document.querySelectorAll(".ribbon-content").forEach(c => c.classList.remove("active"));
document.querySelector('[data-tab="'+tab+'"]').classList.add("active");
document.getElementById(tab+"-tab").classList.add("active");
}
function showEditor(type) {
document.querySelectorAll(".toggle-btn").forEach(b => b.classList.remove("active"));
document.querySelectorAll(".editor-panel").forEach(p => p.classList.remove("active"));
document.getElementById("btn-"+type).classList.add("active");
document.getElementById(type+"-editor").classList.add("active");
if(type === "draw") setTimeout(resizeCanvas, 100);
}
function formatText(cmd, val) { document.execCommand(cmd, false, val); }
// Insert Table
function insertTable() {
let html = '<table border="1" style="border-collapse:collapse;width:100%"><tr>';
for(let i=0; i<3; i++) html += '<td style="padding:8px;border:1px solid #ccc">&nbsp;</td>';
html += '</tr></table><br>';
document.execCommand("insertHTML", false, html);
}
// Insert Link
function insertLink() {
let url = prompt("URL du lien:");
if(url) document.execCommand("insertHTML", false, '<a href="'+url+'" style="color:#7719AA">'+url+'</a>');
}
// Handle Image
function handleImage(input) {
if(input.files[0]) {
let reader = new FileReader();
reader.onload = e => document.execCommand("insertHTML", false, '<img src="'+e.target.result+'" style="max-width:100%">');
reader.readAsDataURL(input.files[0]);
}
}
// Drawing
function setDrawTool(t) {
tool = t;
document.querySelectorAll(".tool-btn").forEach(b => b.classList.remove("active"));
document.getElementById("tool-"+t).classList.add("active");
}
function initDrawing() {
canvas.onpointerdown = e => { e.preventDefault(); drawing = true; startDraw(e); };
canvas.onpointermove = e => { if(drawing) moveDraw(e); };
canvas.onpointerup = () => { drawing = false; savePage(); };
canvas.ontouchstart = e => { e.preventDefault(); drawing = true; startDraw(e.touches[0]); };
canvas.ontouchmove = e => { e.preventDefault(); if(drawing) moveDraw(e.touches[0]); };
canvas.ontouchend = () => { drawing = false; };
canvas.onmousedown = e => { drawing = true; startDraw(e); };
canvas.onmousemove = e => { if(drawing) moveDraw(e); };
canvas.onmouseup = () => { drawing = false; };
}
function resizeCanvas() {
canvas.width = 2000;
canvas.height = 2000;
redraw();
}
function getPos(e) {
let rect = canvas.getBoundingClientRect();
return { x: e.clientX - rect.left, y: e.clientY - rect.top, p: e.pressure || 0.5 };
}
function startDraw(e) {
let pos = getPos(e);
if(tool === "eraser") erase(pos);
else {
let color = document.getElementById("pen-color").value;
let w = parseInt(document.getElementById("pen-width").value);
paths.push({ points:[pos], color:tool==="highlighter"?rgba(color,.3):color, width:tool==="highlighter"?w*3:w });
redraw();
}
}
function moveDraw(e) {
let pos = getPos(e);
if(tool === "eraser") erase(pos);
else if(paths.length) {
paths[paths.length-1].points.push(pos);
redraw();
}
}
function erase(pos) {
paths = paths.filter(p => !p.points.some(pt => dist(pt,pos)<20));
redraw();
}
function dist(a,b) { return Math.sqrt((a.x-b.x)**2+(a.y-b.y)**2); }
function rgba(hex,a) {
let r=parseInt(hex.slice(1,3),16),g=parseInt(hex.slice(3,5),16),b=parseInt(hex.slice(5,7),16);
return `rgba(${r},${g},${b},${a})`;
}
function redraw() {
ctx.clearRect(0,0,canvas.width,canvas.height);
paths.forEach(p => {
if(p.points.length<2) return;
ctx.beginPath(); ctx.lineCap="round"; ctx.strokeStyle=p.color; ctx.lineWidth=p.width;
ctx.moveTo(p.points[0].x,p.points[0].y);
p.points.forEach(pt => ctx.lineTo(pt.x,pt.y));
ctx.stroke();
});
}
function clearDrawing() { if(confirm("Effacer?")){ paths=[]; redraw(); savePage(); }}
// Zoom
function changeZoom(d) {
let z = document.getElementById("zoom-level");
let v = parseInt(z.textContent)+d;
z.textContent = Math.max(50,Math.min(200,v))+"%";
}
// API
async function loadNotebooks() {
let res = await fetch(API+"/notebooks");
let list = document.getElementById("notebook-list");
list.innerHTML = "";
(await res.json()).forEach(nb => {
let li = document.createElement("li");
li.textContent = nb.name;
li.style.borderLeft = "3px solid "+nb.color;
li.onclick = () => selectNotebook(nb.id, li);
list.appendChild(li);
});
}
async function selectNotebook(id, el) {
nbId = id;
document.querySelectorAll("#notebook-list li").forEach(l => l.classList.remove("selected"));
el.classList.add("selected");
let res = await fetch(API+"/notebooks/"+id+"/sections");
let list = document.getElementById("section-list");
list.innerHTML = "";
(await res.json()).forEach(s => {
let li = document.createElement("li");
li.textContent = s.name;
li.onclick = () => selectSection(s.id, li);
list.appendChild(li);
});
}
async function selectSection(id, el) {
secId = id;
document.querySelectorAll("#section-list li").forEach(l => l.classList.remove("selected"));
el.classList.add("selected");
let res = await fetch(API+"/sections/"+id+"/pages");
let list = document.getElementById("page-list");
list.innerHTML = "";
(await res.json()).forEach(p => {
let li = document.createElement("li");
li.textContent = p.title;
li.onclick = () => loadPage(p.id, li);
list.appendChild(li);
});
}
async function loadPage(id, el) {
pageId = id;
document.querySelectorAll("#page-list li").forEach(l => l.classList.remove("selected"));
el.classList.add("selected");
let p = await fetch(API+"/pages/"+id).then(r=>r.json());
document.getElementById("page-title").value = p.title;
document.getElementById("rich-editor").innerHTML = p.content || "";
try { paths = JSON.parse(p.drawing_data||"[]"); } catch(e){ paths=[]; }
redraw();
}
async function savePage() {
if(!pageId) return;
await fetch(API+"/pages/"+pageId, {
method:"PUT", headers:{"Content-Type":"application/json"},
body:JSON.stringify({
title:document.getElementById("page-title").value,
content:document.getElementById("rich-editor").innerHTML,
drawing_data:JSON.stringify(paths)
})
});
document.getElementById("status-text").textContent = "Sauvegardé";
}
async function addNotebook() {
let name = prompt("Nom:");
if(name) { await fetch(API+"/notebooks", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({name,color:"#7719AA"})}); loadNotebooks(); }
}
async function addSection() {
if(!nbId) return alert("Sélectionnez un notebook");
let name = prompt("Nom:");
if(name) { await fetch(API+"/notebooks/"+nbId+"/sections", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({name})}); selectNotebook(nbId, document.querySelector("#notebook-list li.selected")); }
}
async function addPage() {
if(!secId) return alert("Sélectionnez une section");
let title = prompt("Titre:");
if(title) { await fetch(API+"/sections/"+secId+"/pages", {method:"POST", headers:{"Content-Type":"application/json"}, body:JSON.stringify({title})}); selectSection(secId, document.querySelector("#section-list li.selected")); }
}