<!DOCTYPE html>
<!--
	┌───┬───┬───┬───┬───┬───┬───┬───┬───┐
	│ F │ R │ A │ N │ C │ I │ S │ C │ O │
	└───┴───┴───┴───┴───┴───┴───┴───┴───┘
	-->
<html xmlns="http://www.w3.org/1999/xhtml" lang="pt">
	<!-- Box Drawing https://www.unicode.org/charts/PDF/U2500.pdf -->
	<head>
		<title>✕</title>
		<!--
			┌────────────┐
			│   M E T A  │
			└────────────┘
			-->
		<meta charset="utf-8">
		<!--<meta name="viewport" content="width=device-width, initial-scale=1.0">-->
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
		<meta name="author" content="Francisco">
		<meta name="description" content="Palavra-a-palavra">
		<meta name="locality" content="portugal">
		<meta name="lang" content="pt">
		<meta name="keywords" content="desenho, arte, criatividade, pensamento criativo, draw, drawing, art, creativity, creative thinking, ttct">
		<meta name="ROBOTS" content="INDEX,FOLLOW">
		<meta name="revisit" content="4 days">
		<meta name="lightning" content="lnurlp:zap@francisco.pt">
		<meta name="google-site-verification" content="8Pt6Z943DPOWZZk6ssm3MYfXiXLQQwwyeZZYGsWE2To">
		<meta property="og:title" content="Prof. Francisco">
		<meta property="og:image" content="https://francisco.pt/fabric/palavra_tutti.png">
		<meta property="og:description" content="Palavra-a-palavra">
		<meta property="og:url" content="https://francisco.pt/fabric/palavra-v2.html">
		<meta property="og:image:width" content="640">
		<meta property="og:image:height" content="480">
		<meta property="og:type" content="website">
		<!--
			┌──────────────────┐
			│   F A V I C O N  │
			└──────────────────┘
			-->
		<link rel="apple-touch-icon-precomposed" sizes="57x57" href="https://francisco.pt/favicon/apple-touch-icon-57x57.png">
		<link rel="apple-touch-icon-precomposed" sizes="114x114" href="https://francisco.pt/favicon/apple-touch-icon-114x114.png">
		<link rel="apple-touch-icon-precomposed" sizes="72x72" href="https://francisco.pt/favicon/apple-touch-icon-72x72.png">
		<link rel="apple-touch-icon-precomposed" sizes="144x144" href="https://francisco.pt/favicon/apple-touch-icon-144x144.png">
		<link rel="apple-touch-icon-precomposed" sizes="120x120" href="https://francisco.pt/favicon/apple-touch-icon-120x120.png">
		<link rel="apple-touch-icon-precomposed" sizes="152x152" href="https://francisco.pt/favicon/apple-touch-icon-152x152.png">
		<link rel="icon" type="image/png" href="https://francisco.pt/favicon/favicon-32x32.png" sizes="32x32">
		<link rel="icon" type="image/png" href="https://francisco.pt/favicon/favicon-16x16.png" sizes="16x16">
		<meta name="application-name" content="Francisco">
		<meta name="msapplication-TileColor" content="#FFFFFF">
		<meta name="msapplication-TileImage" content="https://francisco.pt/favicon/mstile-144x144.png">
		<!--
			┌───────────┐
			│   C S S   │
			└───────────┘
			-->
		<!-- Bootstrap styles -->
		<link rel="stylesheet" type="text/css" href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" integrity="sha384-GLhlTQ8iRABdZLl6O3oVMWSktQOp6b7In1Zl3/Jr59b6EGGoI1aFkw7cmDA6j6gD" crossorigin="anonymous">
		<!-- Font styles -->
		<link rel="stylesheet" type="text/css" href="../bootstrap/css/fonts.css">
		<!-- Footer info -->
		<link rel="stylesheet" type="text/css" href="../bootstrap/css/footer.css">
		<!-- Main styles -->
		<link rel="stylesheet" type="text/css" href="../bootstrap/css/graph.css">
		<!--
			┌─────────┐
			│   J S   │
			└─────────┘
			-->
		<!-- Bootstrap scripts -->
		<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/js/bootstrap.bundle.min.js" integrity="sha384-w76AqPfDkMBDXo30jS1Sgez6pr3x5MlQ1ZAGC+nuZB+EYdgRZgiwxhTBTkF7CXvN" crossorigin="anonymous"></script>
		<!-- Fabric.js -->
		<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/5.2.4/fabric.min.js"></script>
		<!-- JS PDF -->
		<script src="https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js"></script>
		<!-- svg2pdf.js -->
		<!--<script src="https://cdn.jsdelivr.net/npm/svg2pdf.js@1.5.0/dist/svg2pdf.min.js"></script>-->
		<script src="https://cdn.jsdelivr.net/npm/svg2pdf.js@latest/dist/svg2pdf.min.js"></script>
		<!--
			<script src="https://francisco.pt/graph/jspdf.js"></script>
			<script src="https://francisco.pt/graph/standard_fonts_metrics.js"></script>
			   <script src="https://francisco.pt/graph/split_text_to_size.js"></script>
			   -->
		<!-- Carimbo ttf > code64 -->
		<!-- https://rawgit.com/MrRio/jsPDF/master/fontconverter/fontconverter.html -->
		<script src="https://francisco.pt/fabric/Carimbo-V3.2-normal.js"></script>
		<script src="https://francisco.pt/fabric/dinishcondensed-italic.js"></script>
		<script src="https://francisco.pt/fabric/dinishcondensed-bold.js"></script>
		<script src="https://francisco.pt/fabric/dinishcondensed-regular.js"></script>
		<script src="https://francisco.pt/fabric/CourierPrime-Regular.js"></script>
		<style>
			
			:root {
			touch-action: none;
			height: 100%;
			}
			
			html, body {
			margin: 0;
			padding: 0;
			height: 100%;
			overflow: hidden;
			}
			.canvas-container {
			position: relative;
			width: 100vw; /* Largura de 100% do viewport */
			height: 100vh; /* Altura de 100% do viewport */
			display: flex;
			justify-content: center;
			align-items: center;
			z-index: 1;
			}
			#canvas {
			/* border: 1px solid black; /* Limite do Canvas */
			display: block; /* Remove espaços ao redor do canvas */
			max-width: 100%; /* Faz com que o canvas não ultrapasse a largura do viewport */
			max-height: 100%; /* Faz com que o canvas não ultrapasse a altura do viewport */
			/* background-color: white; /* Fundo branco para melhor visibilidade */
			width: 100vw;
			height: 100vh;
			margin: 0;
			padding: 0;
			/* background-color: #f0f0f0; /* Cor de fundo do canvas */
			}
			#color-menu {
			position: fixed;
			bottom: 10px;
			left: 10px;
			display: flex;
			flex-direction: row;
			gap: 10px;
			}
			.color-circle {
			width: 14px;
			height: 14px; 
			border-radius: 50%;
			cursor: pointer;
			border: 2px solid #fff;
			}
			.top-left {
			position: fixed;
			top: 0;
			left: 0;
			z-index: 10;
			}
			.top-right {
			position: fixed;
			top: 0;
			right: 0;
			z-index: 10;
			}
			.bottom-left {
			position: fixed;
			bottom: 0;
			left: 0;
			z-index: 10;
			}
			.bottom-right {
			position: fixed;
			bottom: 0;
			right: 0;
			z-index: 10;
			}
			.bg-85 {
				background-color: rgba(255, 255, 255, 0.85);
			}
			.miras {
			display: block;
			z-index: 20;
			}
			.truncate {
			width: 100%;
			white-space: nowrap;
			overflow: hidden;
			text-overflow: ellipsis;
			}
			.display-none {
			display: none;
			}
			#textInput:focus {
			outline: none; /* Remove o contorno no foco */
			}
			/* Hide default radio input */
			.bullet-input {
			display: none;
			}
			/* Base style for the label */
			.bullet-label {
			display: flex;
			align-items: center;
			cursor: pointer;
			font-size: 14px; /* Adjust text size */
			margin: 0;
			}
			/* Style for the custom bullet */
			.snap-bullet {
			width: 10px;
			height: 10px;
			margin-right: 6px;
			border-radius: 50%;
			background-color: #cdcdcd; /* Default color */
			transition: background-color 0.3s ease;
			}
			/* Change bullet color when the input is checked */
			.bullet-input:checked + .bullet-label .snap-bullet {
			background-color: #e619e5; /* Highlighted color */
			}
					
		    /* Remove o estilo de hover após clique em dispositivos touch */	
			.btn-no-touch-hover:active {
				outline: none;
			}
			
		    /* Aplica o hover apenas em dispositivos com mouse */
			@media (any-hover: hover) and (pointer: fine) {
				.btn-no-touch-hover:hover {
					background-color: #000;
					color: #fff;
				}
			}
			
		    /* Remove o hover em dispositivos touch */
			@media (any-hover: none) and (pointer: coarse) {
				.btn-no-touch-hover:hover {
					background-color: initial;
					color: initial;
				}
			}
			
		</style>
	</head>
	<body>

		<!--
			┌───────────────────┐
			│   D E S E N H O   │
			└───────────────────┘
			-->
		<div class="canvas-container">
			<canvas class="" id="canvas"></canvas>
		</div>
		
				<!--
					┌─────────────┐
					│   B A C K   │
					└─────────────┘
					-->
		<div 
		    id="floating-menu-1" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1050; top: 20px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-1" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
	            type="button" 
	            class="btn btn-outline-dark rounded-0 text-uppercase btn-no-touch-hover" 
	            onclick="history.back()">Voltar</button>
			</div>
		</div>
		
		
		
				<!--
					┌─────────────────┐
					│   A P A G A R   │
					└─────────────────┘
					-->
		<div 
		    id="floating-menu-8" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; top: 55px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-8" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
	            type="button" 
	            id="clearCanvas" 
	            class="btn btn-outline-dark rounded-0 text-uppercase btn-no-touch-hover">Limpar tudo</button>
			</div>
		</div>
		
		
				<!--
					┌───────────────────┐
					│   A U T O R E S   │
					└───────────────────┘
					-->
		
		<div 
		    id="floating-menu-6" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; top: 20px; right: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-6" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<div class="text-editor btn btn-group-sm truncate " style="background-color: rgba(255, 255, 255, 0); border-radius: 0; border: 1px solid #000;">
					<div class="form-check-inline m-0 p-0 bullet-radio" style="min-height: 0">
						<input 
						class="form-check-input bullet-input m-0 p-0" 
						type="radio" 
						name="wordList" 
						id="useWords1" 
						value="words1" 
						checked>
						
						<label class="form-check-label bullet-label" for="useWords1"><span class="snap-bullet"></span>Pessoa</label>
					</div>
					
					<div class="form-check-inline ms-1 me-2 bullet-radio" style="min-height: 0">
						<input 
						class="form-check-input bullet-input m-0 p-0" 
						type="radio" 
						name="wordList" 
						id="useWords2" 
						value="words2">
						
						<label class="form-check-label bullet-label" for="useWords2"><span class="snap-bullet"></span>Sophia</label>
					</div>
				</div>
			</div>
		</div>
				<!--
					┌───────────────┐
					│   T E X T O   │
					└───────────────┘
					-->
		
		
		<div 
		    id="floating-menu-5" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; top: 55px; right: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-5" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<div class="text-editor input-group-sm">
					<input type="text" id="textInput" class="form-control" placeholder="..." style="font-family: 'Courier', monospace; background-color: rgba(255, 255, 255, 0);border-radius: 0; border-color: #000;">
					<!--<button id="printText" class="btn btn-outline-secondary btn-sm">texto</button>-->
				</div>
			</div>
		</div>
				<!--
					┌───────────────┐
					│   T U R B O   │
					└───────────────┘
					-->
		
		
		<div 
		    id="floating-menu-2" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; bottom: 90px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-2" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
				type="button" 
				id="turboSwitch" 
				class="btn btn-outline-dark text-uppercase truncate d-flex align-items-center btn-no-touch-hover"
				style="border-radius: 0;"><span 
					id="turboBullet" 
					style="width: 10px; height: 10px; margin-right: 3px; border-radius: 50%; background-color: #cdcdcd;"></span>
				Turbo</button>
								
				<button 
			    type="button" 
			    id="toggleResizeControls" 
			    class="btn btn-outline-dark text-uppercase toggle-button btn-no-touch-hover" 
			    style="border-radius: 0;" 
			    onclick="toggleResizeControls()">Fixo</button>

				<button 
				type="button" 
				id="toggleTextStyle" 
				class="btn btn-outline-dark text-uppercase toggle-button btn-no-touch-hover" 
				style="border-radius: 0;" 
				onclick="toggleButtonStyle()">Preto</button>

				<button 
				type="button" 
				class="btn btn-outline-dark text-uppercase btn-no-touch-hover" 
				id="addWord"
				style="border-radius: 0;">Palavra</button>
				
				<div 
				class="btn btn-outline-dark" 
				style="border-radius: 0; cursor: default;"><span id="itemCountDisplay">1</span></div>
			</div>
		</div>
				<!--
					┌─────────────────────────────┐
					│   B R A N C O / P R E T O   │
					└─────────────────────────────┘
					-->
				<!--
				<div 
					id="floating-menu-9" 
					class="position-fixed pe-1 pb-1"
					style="z-index: 1055; background-color: rgba(255, 255, 255, 0.85); border-radius: 0; box-shadow: 3px 3px 9px rgba(0, 0, 0, 0.2); top: 70px; left: 20px; cursor: grab;">
					<div class="btn-group btn-group-sm condensed text-uppercase no-decoration text-black font-spacing fs-6 ps-1 pt-1" role="group">
						<button 
							type="button" 
							id="drag-handle-9" 
							class="btn btn-dark text-uppercase" 
							style="border-radius: 0; background-color: #000; color: #fff; cursor: grab;"></button>
						<button 
							type="button" 
							id="toggleTextStyle" 
							class="btn text-uppercase toggle-button" 
							style="border-radius: 0; border: 1px solid #000;" 
							onclick="toggleButtonStyle()">Preto</button>
					</div>
				</div>
				-->
				<!--
					┌───────────────┐
					│   C O L A R   │
					└───────────────┘
					-->
		<div 
		    id="floating-menu-3" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; bottom: 55px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-3" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
				type="button" 
				id="toggleSnap" 
				class="btn btn-outline-dark text-uppercase truncate d-flex align-items-center btn-no-touch-hover"
				style="border-radius: 0;"><span 
					id="snapBullet" 
					style="width: 10px; height: 10px; margin-right: 3px; border-radius: 50%; background-color: #cdcdcd;"></span>
				Colar</button>
				
				<button 
				type="button" 
				class="btn btn-outline-dark text-uppercase btn-no-touch-hover" 
				id="duplicateSelected"
				style="border-radius: 0;">Duplicar</button>
				
				<button 
				type="button" 
				class="btn btn-outline-dark text-uppercase btn-no-touch-hover" 
				id="deleteSelected"
				style="border-radius: 0;">Apagar</button>
			</div>
		</div>
				<!--
					┌───────────────────────────────┐
					│   I M P O R T / E X P O R T   │
					└───────────────────────────────┘
					-->
		<div 
		    id="floating-menu-7" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; bottom: 20px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-7" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<input type="file" id="json-svg-btn" accept=".json" style="display: none;">
				
				<label for="json-svg-btn" class="btn btn-outline-dark text-uppercase"><span class="truncate">Importar</span></label>
				
				<button 
				type="button" 
				class="btn btn-outline-dark text-uppercase truncate btn-no-touch-hover" 
				id="svg-json-btn" 
				style="border-radius: 0;"><span class="truncate">Exportar</span></button>
			</div>
		</div>
				<!--
					┌───────────┐
					│   P D F   │
					└───────────┘
					-->
		<div 
		    id="floating-menu-4" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; bottom: 20px; right: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-4" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
				type="button" 
				class="btn btn-outline-dark text-uppercase btn-no-touch-hover" 
				id="word-btn" 
				style="border-radius: 0;"><span class="truncate">Imprimir PDF</span></button>
			</div>
		</div>
				<!--
					┌─────────────────┐
					│   P A L E T A   │
					└─────────────────┘
					-->		
		<div 
		    id="floating-menu-10" 
		    class="condensed position-fixed bg-85 shadow-sm rounded-0" 
		    style="z-index: 1055; bottom: 125px; left: 20px; cursor: grab;">
			<div class="btn-group btn-group-sm text-uppercase fw-bold fs-6" role="group">
				<button 
	            type="button" 
	            id="drag-handle-10" 
	            class="btn btn-dark text-white rounded-0" 
	            style="cursor: grab;"></button>
				
				<button 
		        type="button"
		        id="palette-toggle" 
		        class="btn btn-outline-dark text-uppercase btn-no-touch-hover" 
		        style="border-radius: 0;">Paleta</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: #efefef;" data-color="#fff"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: #ccc;" data-color="#ccc"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: red;" data-color="red"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: green;" data-color="green"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: blue;" data-color="blue"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: cyan;" data-color="cyan"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: magenta;" data-color="magenta"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: yellow;" data-color="yellow"></div>
				</button>
				
				<button 
		        type="button" 
		        class="btn btn-outline-dark text-uppercase paleta display-none" 
		        style="border-radius: 0;">
					<div class="color-circle" style="background-color: black;" data-color="black"></div>
				</button>
			</div>
		</div>
		
				
				
				<!--
			┌─────────────────┐
			│   S C R I P T   │
			└─────────────────┘
			-->
		<script>
			// Inicializar o canvas - Simples
			const canvas = new fabric.Canvas('canvas');



document.fonts.load('1rem CourierPrime').then(() => {
    console.log("Fonte CourierPrime carregada para Canvas do Fabric.js");
}).catch(err => {
    console.error("Erro ao carregar a fonte:", err);
});




			// Configurações adicionais do canvas
			canvas.objectCaching = true;
			canvas.setWidth(window.innerWidth);
			canvas.setHeight(window.innerHeight);
			
			///////////////////////////////
			///   CONTADOR DE PALAVRAS  ///
			///////////////////////////////
			let itemCounter = 0;
			
			//////////////////////////////////
			///   PALAVRA BRANCA / PRETA   ///
			//////////////////////////////////
			let isBlackBackground = true;
			
			////////////////////////////////////
			///   RESIZE DA PALAVRA ON/OFF   ///
			////////////////////////////////////
			let isResizeEnabled = false;	
			
			///////////////////
		    ///   RANDOM   ////
		    ///////////////////
		    function randomBetween(min, max) {
		        return Math.random() * (max - min) + min;
		    }

			const r1 = randomBetween(20, 40);
			const r2 = randomBetween(20, 40);

			/////////////////////////////////
		    ///   MOVIMENTO por TECLAS   ////
		    /////////////////////////////////
// Função para inicializar movimentação com as setas do teclado
function enableArrowKeyMovement(canvas) {
    document.addEventListener('keydown', function (event) {
        const activeObject = canvas.getActiveObject(); // Objeto atualmente selecionado

        if (!activeObject) return; // Se não houver objeto selecionado, sair da função

        let moved = false; // Flag para rastrear se houve movimento
        const step = event.shiftKey ? 10 : 1; // Define o passo: 10px se Shift for pressionado, 1px caso contrário

        switch (event.key) {
            case 'ArrowUp':
                activeObject.top = Math.max(activeObject.top - step, 0); // Move para cima, limita a posição mínima
                moved = true;
                break;
            case 'ArrowDown':
                activeObject.top = Math.min(activeObject.top + step, canvas.height - activeObject.height); // Move para baixo, limita a posição máxima
                moved = true;
                break;
            case 'ArrowLeft':
                activeObject.left = Math.max(activeObject.left - step, 0); // Move para a esquerda, limita a posição mínima
                moved = true;
                break;
            case 'ArrowRight':
                activeObject.left = Math.min(activeObject.left + step, canvas.width - activeObject.width); // Move para a direita, limita a posição máxima
                moved = true;
                break;
        }

        if (moved) {
            activeObject.setCoords(); // Atualiza as coordenadas para detecção de colisão
            canvas.renderAll(); // Re-renderiza o canvas para refletir o movimento
            event.preventDefault(); // Evita o comportamento padrão das setas (scroll da página)
        }
    });
}

// Ativar movimentação com setas
enableArrowKeyMovement(canvas);


			//////////////////
			///   PALETA   ///
			//////////////////
// Função para inicializar os eventos do menu de cores
function initializeColorMenu(canvas) {
    // Seleciona todos os botões de cores no menu
    const colorButtons = document.querySelectorAll('.paleta .color-circle');

    colorButtons.forEach(button => {
        button.addEventListener('click', function () {
            const selectedColor = this.getAttribute('data-color'); // Pega a cor do atributo data-color
            const activeObject = canvas.getActiveObject(); // Pega o objeto atualmente selecionado no canvas

            if (activeObject && activeObject.type === 'text') { // Verifica se o objeto selecionado é texto
                activeObject.set('backgroundColor', selectedColor); // Altera a cor de fundo
                canvas.renderAll(); // Re-renderiza o canvas para refletir a mudança
            }
        });
    });

    // Alternar visibilidade da paleta de cores
    const paletteToggle = document.getElementById('palette-toggle');
    paletteToggle.addEventListener('click', function () {
        const paletteButtons = document.querySelectorAll('.paleta');
        paletteButtons.forEach(btn => btn.classList.toggle('display-none'));
    });
}


initializeColorMenu(canvas);




	
	
			/////////////////////////
		    ///   CONTROLOS ON   ////
		    /////////////////////////
			const resizeControlsOff = {
			    mt: false, mb: false, ml: false, mr: false,
			    tr: false, tl: false, br: false, bl: false,
			    mtr: true,
			};

			/////////////////////////
		    ///   CONTROLOS OFF   ////
		    /////////////////////////
			const resizeControlsOn = {
			    mt: false, mb: false, ml: false, mr: false,
			    tr: false, tl: false, br: true, bl: false,
			    mtr: true,
			};

			
					
			///////////////////////////
			///   SEL. INDIVIDUAL   ///
			///////////////////////////
			// Configurações para seleção individual
			const commonSettings = {
			    transparentCorners: false,
			    cornerColor: '#79daf2',
			    cornerStyle: 'circle',
			    cornerSize: 9,
			    touchCornerSize: 24,
			    padding: 2,
			    hasBorders: true,
			    borderColor: '#79daf2',
			    borderDashArray: [2],
			    borderScaleFactor: 1,
			    borderOpacityWhenMoving: 0,
			    selectable: true, // Permitir seleção do objeto
			    evented: true,    // Permitir eventos do objeto
			};
			
			////////////////////////
			///  SEL. MULTIPLA   ///
			////////////////////////
			// Configurações para seleção múltipla (grupo)
			const groupSettings = {
			    transparentCorners: false,
			    cornerColor: '#79daf2',
			    cornerStyle: 'circle',
			    cornerSize: 9,
			    touchCornerSize: 24,
			    padding: 2, // Maior padding para o grupo
			    hasBorders: true,
			    borderColor: '#79daf2', // Cor da borda para o grupo
			    borderDashArray: [2],   // Padrão de borda diferente
			    borderScaleFactor: 1,
			    borderOpacityWhenMoving: 0,
			    selectable: true,
			    evented: true,
			};
				
// Função para aplicar visibilidade aos controles de um objeto
function applyControlVisibility(obj) {
    console.log('Aplicando visibilidade para:', obj);
    const controlsConfig = isResizeEnabled ? resizeControlsOn : resizeControlsOff;
    obj.setControlsVisibility(controlsConfig);
    obj.dirty = true; // Marca o objeto como alterado
}
			
			

				////////////////////////////
				///  ATUALIZA UM GRUPO   ///
				////////////////////////////
				// Evento para aplicar estilos e controles quando uma seleção é criada
				canvas.on('selection:created', function () {
				    const activeSelection = canvas.getActiveObject();
				
				    if (activeSelection && activeSelection.type === 'activeSelection') {
				        // Seleção múltipla (grupo)
				        const center = activeSelection.getCenterPoint(); // Calcula o centro da seleção
				        activeSelection.set({
				            ...groupSettings,  // Aplica as configurações de grupo
				            originX: 'center', // Define a origem no centro
				            originY: 'center', // Define a origem no centro
				            left: center.x,    // Ajusta a posição horizontal
				            top: center.y,     // Ajusta a posição vertical
				        });
				
				        // Aplica visibilidade de controles ao grupo
				        applyControlVisibility(activeSelection);
				
				        // Aplica visibilidade de controles a todos os objetos dentro do grupo
				        activeSelection.forEachObject(applyControlVisibility);
				    } else if (activeSelection) {
				        // Seleção individual
				        applyControlVisibility(activeSelection);  // Aplica visibilidade de controles ao objeto individual
				    }
				
				    canvas.renderAll(); // Atualiza o canvas
				});

				/////////////////////////////////////////
				///  ATUALIZA OS ELEMENTOS DO GRUPO   ///
				/////////////////////////////////////////
				// Evento para atualizar o estilo ao modificar a seleção
				canvas.on('selection:updated', function () {
				    const activeSelection = canvas.getActiveObject();
				
				    if (activeSelection && activeSelection.type === 'activeSelection') {
				        const center = activeSelection.getCenterPoint();
				        activeSelection.set({
				            ...groupSettings,  // Aplica as configurações de grupo
				            originX: 'center', // Define a origem no centro
				            originY: 'center', // Define a origem no centro
				            left: center.x,    // Ajusta a posição horizontal
				            top: center.y,     // Ajusta a posição vertical
				        });
				
				        // Aplica visibilidade de controles ao grupo
				        applyControlVisibility(activeSelection);
				
				        // Aplica visibilidade de controles a todos os objetos dentro do grupo
				        activeSelection.forEachObject(applyControlVisibility);
				    } else if (activeSelection) {
				        applyControlVisibility(activeSelection);  // Aplica visibilidade de controles ao objeto individual
				    }
				
				    canvas.renderAll(); // Atualiza o canvas
				});

// Limpar estilos ao desmarcar a seleção
canvas.on('selection:cleared', function () {
    canvas.getObjects().forEach((obj) => {
        // Apenas atualiza objetos que não têm o estilo aplicado
        if (!obj.selectable || !obj.evented || obj.cornerColor !== commonSettings.cornerColor) {
            obj.set({ ...commonSettings });
        }
    });

    canvas.renderAll(); // Atualiza o canvas
});


					
					
					
			/////////////////////////////////////
			///   BOTÃO - RESIZE DA PALAVRA   ///
			/////////////////////////////////////
		// Função para alternar a visibilidade dos controles globalmente
		function toggleResizeControls() {
			isResizeEnabled = !isResizeEnabled; // Alterna o estado
		
			///////////////////////////////
			///  ATUALIZA UM ELEMENTO   ///
			///////////////////////////////
		        // Atualiza visibilidade apenas para objetos de texto
				canvas.getObjects().forEach((obj) => {
				if (['i-text', 'text', 'textbox'].includes(obj.type)) {
					applyControlVisibility(obj);
				}
			});
			
			// Atualiza visibilidade dos grupos, se houver grupos selecionados
		    const activeSelection = canvas.getActiveObject();
			if (activeSelection && activeSelection.type === 'activeSelection') {
		        // Aplica controle de visibilidade ao grupo selecionado
		        activeSelection.forEachObject((obj) => {
					if (['i-text', 'text', 'textbox'].includes(obj.type)) {
		                // Aplica a visibilidade de controle ao grupo selecionado
						applyControlVisibility(activeSelection);  // Aplica ao grupo
							
					    // Aplica controle de visibilidade a todos os objetos dentro do grupo
					    activeSelection.forEachObject(applyControlVisibility);
					}
				});
			}
			
			canvas.renderAll(); // Atualiza o canvas
		

		    // Atualiza o texto do botão
		    const button = document.getElementById('toggleResizeControls');
			button.textContent = isResizeEnabled ? 'Elástico': 'Fixo';
		}
		
		
							
							
			//////////////////////////////////////////
			///   BOTÃO - PALAVRA BRANCA / PRETA   ///
			//////////////////////////////////////////
		    // Function to toggle button styles
		    function toggleButtonStyle() {
		        //const button = document.getElementById('drag-handle-9');
		        const toggleButton = document.getElementById('toggleTextStyle');
				isBlackBackground = !isBlackBackground; // Toggle the global state
		
		        // Update styles for the buttons
		        //const bgColor = isBlackBackground ? 'black' : null;
		        const textColor = isBlackBackground ? 'white': 'black';
				const buttonText = isBlackBackground ? 'Preto': 'Branco';
		
		
		
		        //toggleButton.style.backgroundColor = bgColor;
		        //toggleButton.style.color = textColor;
		        toggleButton.textContent = buttonText;
			}
				
			// Função para alternar estilos globais
			function toggleTextStyle() {
				isBlackBackground = !isBlackBackground; // Alterna o estado
			}		
			
				//////////////////////////
			    ///   FUNÇÃO ADD WORD  ///
			    //////////////////////////
			
				// Fernando Pessoa
			    const words1 = [' alma ', ' sonho ', ' viver ', ' mundo ', ' coração ', ' vida ', ' ser ', ' nada ', ' mar ', ' sentir ', ' pensar ', ' sombra ', ' destino ', ' morte ', ' olhar ', ' tristeza ', ' esperança ', ' silêncio ', ' dor ', ' verdade ', ' poesia ', ' mistério ', ' tempo ', ' medo ', ' ausência ', ' palavra ', ' infinito ', ' noite ', ' luz ', ' solidão ', ' céu ', ' som ', ' realidade ', ' ilusão ', ' caminho ', ' existência ', ' deus ', ' ânsia ', ' segredo ', ' amor ', ' natureza ', ' vazio ', ' fé ', ' impossível ', ' poeta ', ' imagem ', ' sofrer ', ' viagem ', ' angústia ', ' desejo ', ' ideal ', ' perder ', ' mente ', ' infinitude ', ' fingir ', ' serenidade ', ' razão ', ' nuvem ', ' espírito ', ' horizonte ', ' liberdade ', ' tédio ', ' essência ', ' fado ', ' fogo ', ' passado ', ' lembrança ', ' maravilha ', ' visão ', ' imaginário ', ' gravidade ', ' água ', ' terra ', ' chuva ', ' corpo ', ' força ', ' vento ', ' frio ', ' certeza ', ' bruma ', ' calma ', ' renúncia ', ' lágrima ', ' paisagem ', ' futuro ', ' desejos ', ' verso ', ' grandeza ', ' pensamento ', ' eternidade ', ' trevas ', ' aurora ', ' dia ', ' ver ', ' perceber ', ' lembrar ', ' esquecer ', ' amar ', ' querer ', ' estar ', ' crer ', ' voar ', ' caminhar ', ' gritar ', ' calado ', ' buscar ', ' achar ', ' sentimento ', ' voz ', ' mudez ', ' presença ', ' instante ', ' momento ', ' desespero ', ' alegria ', ' melancolia ', ' paz ', ' guerra ', ' finito ', ' misterioso ', ' concreto ', ' abstrato ', ' calor ', ' verão ', ' inverno ', ' treva ', ' vontade ', ' ordem ', ' desassossego ', ' arte ', ' criação ', ' beleza ', ' reflexão ', ' sensações ', ' inspiração ', ' absurdo ', ' humanidade ', ' palavras ', ' escrita ', ' universo ', ' memória ', ' imaginação ', ' presente ', ' cidade ', ' prazer ', ' busca ', ' história ', ' literatura ', ' entendimento ', ' descoberta ', ' obra ', ' dualidade ', ' opostos ', ' dúvida ', ' transcendência ', ' harmonia ', ' cosmos ', ' fuga ', ' ruína ', ' equilíbrio ', ' fragmentos ', ' sentido ', ' contemplação ', ' sabedoria ', ' manhã ', ' tarde ', ' fim ', ' começo ', ' saudade ', ' metáfora ', ' perda ', ' cansaço ', ' finitude ', ' fragmentação ', ' plenitude ', ' irrealidade ', ' meditação ', ' melancolia ', ' inquietação ', ' subjetividade ', ' desilusão ', ' silhueta ', ' arrebatamento ', ' essência ', ' essencialismo ', ' apogeu ', ' compreensão ', ' criatividade ', ' ambiguidade ', ' lógica ', ' presságio ', ' alegoria ', ' tradição ', ' simbolismo ', ' efemeridade ', ' ascensão ', ' queda ', ' sublime ', ' geração ', ' tensão ', ' existencialismo ', ' solitude ', ' misticismo ', ' enigma ', ' contradição ', ' mistura ', ' abissal ', ' escuridão ', ' sentido ', ' luz ', ' olhar ', ' conhecimento ', ' perdão ', ' ansiedade ', ' horizonte ', ' tristeza ', ' descoberta ', ' concepção ', ' instinto ', ' estudo ', ' visão '];
			
			// Sophia Mello Breyner Andresen
			    const words2 = [' mar ', ' abismo ', ' abraço ', ' afeto ', ' aliança ', ' alma ', ' alvorecer ', ' amor ', ' aragem ', ' areal ', ' areia ', ' aroma ', ' asas ', ' aurora ', ' autenticidade ', ' azul ', ' barco ', ' beleza ', ' bondade ', ' branco ', ' brilho ', ' brisa ', ' búzio ', ' cais ', ' calor ', ' caminho ', ' campo ', ' canto ', ' caos ', ' casa ', ' cascata ', ' certeza ', ' chegada ', ' chuva ', ' chão ', ' clareira ', ' clareza ', ' claridade ', ' colheita ', ' cometa ', ' começo ', ' compaixão ', ' companheirismo ', ' compasso ', ' concha ', ' conexão ', ' consciência ', ' constelações ', ' contraste ', ' coragem ', ' coral ', ' coração ', ' corpo ', ' costas ', ' crepúsculo ', ' céu ', ' dança ', ' descoberta ', ' desejo ', ' deserto ', ' destino ', ' dia ', ' dor ', ' dourado ', ' dúvida ', ' eco ', ' encanto ', ' encontro ', ' equilíbrio ', ' escuridão ', ' espaço ', ' espelho ', ' espera ', ' esperança ', ' espuma ', ' espírito ', ' essência ', ' estrela ', ' estrelas ', ' eternidade ', ' eterno ', ' existir ', ' existência ', ' farol ', ' fidelidade ', ' fim ', ' flor ', ' flores ', ' fogo ', ' folha ', ' folhagem ', ' folhas ', ' fonte ', ' força ', ' fragata ', ' fragilidade ', ' fronteira ', ' fruto ', ' frutos ', ' fundo ', ' futuro ', ' gaivota ', ' gaivotas ', ' galho ', ' galhos ', ' geada ', ' gelo ', ' grama ', ' grito ', ' harmonia ', ' horizonte ', ' humanidade ', ' ilha ', ' imagem ', ' infinito ', ' infância ', ' inspiração ', ' instante ', ' intensidade ', ' inverno ', ' início ', ' janelas ', ' jardim ', ' justiça ', ' lembrança ', ' leveza ', ' liberdade ', ' linha ', ' litoral ', ' lua ', ' luz ', ' magia ', ' manhã ', ' maravilha ', ' margem ', ' maré ', ' memória ', ' mistério ', ' montanha ', ' morada ', ' moral ', ' morte ', ' movimento ', ' mundo ', ' murmúrio ', ' mágoa ', ' mãos ', ' música ', ' natureza ', ' navegação ', ' neblina ', ' neve ', ' nevoeiro ', ' noite ', ' nuvem ', ' ocaso ', ' oceano ', ' olhar ', ' onda ', ' ondas ', ' ordem ', ' ouro ', ' outono ', ' paisagem ', ' palavra ', ' partida ', ' passado ', ' passagem ', ' paz ', ' pedra ', ' peixe ', ' pensamento ', ' penumbra ', ' perspectiva ', ' planeta ', ' planetas ', ' poesia ', ' porto ', ' prata ', ' prateado ', ' presente ', ' primavera ', ' princípio ', ' profundidade ', ' promessa ', ' pureza ', ' pássaro ', ' raiz ', ' raízes ', ' realidade ', ' recordação ', ' rede ', ' reflexo ', ' relógio ', ' renascimento ', ' renovação ', ' responsabilidade ', ' rio ', ' ritmo ', ' rocha ', ' rochas ', ' sal ', ' salvação ', ' saudade ', ' segredo ', ' semente ', ' sementes ', ' ser ', ' serenidade ', ' silhueta ', ' silêncio ', ' simples ', ' simplicidade ', ' sol ', ' solidão ', ' som ', ' sombra ', ' sombras ', ' sonho ', ' sorriso ', ' símbolo ', ' tardio ', ' tempo ', ' terra ', ' tolerância ', ' transformação ', ' transitoriedade ', ' transparência ', ' trigo ', ' trilha ', ' tronco ', ' universo ', ' vale ', ' vapor ', ' vazio ', ' vela ', ' vento ', ' ventos ', ' verdade ', ' verão ', ' viagem ', ' vida ', ' visão ', ' vontade ', ' voz ', ' água ', ' árvore ', ' árvores ', ' âncora ', ' ânsia ', ' ética '];
			
			// Matsuo Bashō (XVII)
			
			    //const words3 = [' flor ', ' lua ', ' vento ', ' chuva ', ' montanha ', ' rio ', ' céu ', ' oceano ', ' grilo ', ' árvore ', ' folha ', ' pedra ', ' silêncio ', ' outono ', ' inverno ', ' primavera ', ' verão ', ' estrela ', ' neve ', ' mar ', ' tempo ', ' amanhecer ', ' entardecer ', ' pôr-do-sol ', ' manhã ', ' noite ', ' sombra ', ' luz ', ' instante ', ' vida ', ' morte ', ' florescer ', ' cair ', ' brotar ', ' florir ', ' murchar ', ' raízes ', ' distância ', ' solitário ', ' presença ', ' ausência ', ' efêmero ', ' eternidade ', ' transitoriedade ', ' fugaz ', ' simplicidade ', ' beleza ', ' harmonia ', ' reflexão ', ' tranquilidade ', ' serenidade ', ' borboleta ', ' passarinho ', ' nuvem ', ' caminho ', ' destino ', ' abraço ', ' despertar ', ' esperança ', ' pesadelo ', ' amor ', ' amizade ', ' tristeza ', ' paz ', ' fúria ', ' transição ', ' mudança ', ' voo ', ' som ', ' eco ', ' maré ', ' tempestade ', ' névoa ', ' contemplação ', ' solitude ', ' campo ', ' grama ', ' sabedoria ', ' horizonte ', ' suspiro ', ' nostalgia ', ' memória ', ' larva ', ' arvoredo ', ' pérola ', ' fragilidade ', ' melancolia ', ' quietude ', ' seiva ', ' broto ', ' cheiro ', ' crepúsculo ', ' avelã ', ' orvalho ', ' musgo ', ' cogumelo ', ' brisa ', ' clareira ', ' galho ', ' figueira ', ' sorriso ', ' nuvens ', ' água ', ' estrelas ', ' manto ', ' pássaros ', ' alma ', ' voz ', ' arbusto ', ' pinceladas ', ' trilha ', ' sussurros ', ' ventos ', ' escorrendo ', ' rios ', ' perfume ', ' respiração ', ' dourado ', ' aves ', ' asas ', ' momentos ', ' ecos ', ' abismo ', ' calor ', ' sol ', ' nevoeiro ', ' folhas ', ' velhice ', ' gotas ', ' raiva ', ' recantos ', ' jornada ', ' corpo ', ' olhar ', ' imagem ', ' espelhos ', ' sons ', ' bosque ', ' brilho ', ' sopro ', ' sossego ', ' jardins ', ' rochas ', ' céus ', ' profundidade ', ' ondulação ', ' espelho ', ' sombras ', ' floração ', ' mente ', ' melodia ', ' calmaria ', ' murmúrios ', ' murmúrio ', ' passos ', ' reflexos ', ' ar ', ' madrugada ', ' fim '];
			
			let activeWordList = words1; // Lista ativa inicial
			
			///////////////////////////////
			///   FUNÇÃO ALEATÓRIA      ///
			///////////////////////////////
			function getRandomWord() {
			    return activeWordList[Math.floor(Math.random() * activeWordList.length)];
			}
			
			///////////////////////////////
			///   FUNÇÃO DE TROCA        ///
			///////////////////////////////
			function updateActiveWordList() {
			    const selectedList = document.querySelector('input[name="wordList"]:checked').value;
			
			    // Atualizar a lista ativa com base no radio selecionado
			    if (selectedList === 'words1') {
			        activeWordList = words1;
			    } else if (selectedList === 'words2') {
			        activeWordList = words2;
			    } else if (selectedList === 'words3') {
			        activeWordList = words3;
			    }
			}
			
			///////////////////////////////
			///   EVENTOS DO MENU        ///
			///////////////////////////////
			// Adicionar eventos para os radioboxes
			document.getElementById('useWords1').addEventListener('change', updateActiveWordList);
			document.getElementById('useWords2').addEventListener('change', updateActiveWordList);
			//document.getElementById('useWords3').addEventListener('change', updateActiveWordList);
			
			// Função para gerar uma posição aleatória no canvas, respeitando margens de 10% nas bordas
			function getRandomPosition() {
			    let marginX = canvas.getWidth() * 0.125; // Margem horizontal
			    let marginY = canvas.getHeight() * 0.1; // Margem vertical
			    
			    // Gera valores aleatórios dentro da área útil do canvas
			    let randomLeft = Math.random() * (canvas.getWidth() - marginX * 2) + marginX;
			    let randomTop = Math.random() * (canvas.getHeight() - marginY * 2) + marginY;
			    
			    return { left: randomLeft, top: randomTop }; // Retorna a posição calculada
			}
			
				// Função para trazer a palavra selecionada para o topo
				function bringWordToFront(event) {
				    const activeObject = event.target;
				    if (activeObject) {
				        canvas.bringToFront(activeObject); // Traz o objeto para o topo
				        canvas.renderAll(); // Atualiza o canvas
				    }
				}
				
				// Adicionar evento de seleção e movimentação
				canvas.on('object:selected', bringWordToFront); // Quando um objeto é selecionado
				canvas.on('object:moving', bringWordToFront);  // Quando um objeto é movido
				
				// Adicionar eventos para clique e toque
				canvas.on('mouse:down', function(event) {
				    if (event.target) {
				        bringWordToFront(event); // Chama a função ao clicar em um objeto
				    }
				});
			
			canvas.on('touch:gesture', function(event) {
			    if (event.target) {
			        bringWordToFront(event); // Chama a função ao tocar em um objeto
			    }
			});
			
			    // Função para adicionar uma palavra ao canvas
			    function addWord(randomWord, randomPosition) {
			
				    // Cria um objeto de texto com a palavra fornecida
				    const text = new fabric.Text(randomWord, {
				        left: randomPosition.left, // Posição horizontal
				        top: randomPosition.top, // Posição vertical
				        fontSize: 20, // Tamanho da fonte
        fill: isBlackBackground ? 'white' : 'black', // Cor do texto
        backgroundColor: isBlackBackground ? 'black' : null, // Cor do fundo
		textBackgroundColor: null,
				        fontFamily: 'CourierPrime', // Fonte usada
				        originX: 'center', // Origem horizontal no centro
				        originY: 'center', // Origem vertical no centro
				        padding: 10, // Espaço ao redor do texto
				        selectable: true, // Permite selecionar o texto
				        angle: 0, // Ângulo inicial zero
						stroke: null, // Sem contorno
						strokeWidth: 0, // Largura do contorno
						strokeMiterLimit: 1, // Sem limite do miter
						...commonSettings
			        });

					const scaleFactor = getDeviceScale(); // Adaptação aos ecrãs
					text.scale(scaleFactor);
			        
			        canvas.add(text); // Adiciona o texto ao canvas
				    text.setCoords(); // Atualiza as coordenadas do objeto
				    canvas.setActiveObject(text); // Define o texto como o objeto ativo
					//canvas.discardActiveObject();  // Desmarca qualquer seleção
					canvas.renderAll(); // Renderiza o canvas
			
			    	itemCounter++; // Contador
					document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
			
			    }
			
			// Adicionar evento ao botão
			// document.getElementById('addWord').addEventListener('click', addWord); // Inativo aqui // Ativo Turbo
			
			
			
			    ////////////////////////////////////////
			    ///   FUNÇÃO ADD WORD   - CENTER -   ///
			    ////////////////////////////////////////
			
			    // Função para adicionar uma palavra ao canvas
			    function addWordToCenter(randomWord) {
					
				    // Cria um objeto de texto com a palavra fornecida
				    const text = new fabric.Text(randomWord, {
				        fontSize: 20, // Tamanho da fonte
				        fill: 'white', // Cor do texto (branco)
				        backgroundColor: 'black', // Cor do fundo (preto)
				        textBackgroundColor: null,
						fontFamily: 'CourierPrime', // Fonte usada
				        originX: 'center', // Origem horizontal no centro
				        originY: 'center', // Origem vertical no centro
				        padding: 10, // Espaço ao redor do texto
				        selectable: true, // Permite selecionar o texto
				        angle: 0, // Ângulo inicial zero
						stroke: null, // Sem contorno
						strokeWidth: 0, // Largura do contorno
						strokeMiterLimit: 1, // Sem limite do miter
						...commonSettings
			        });

					const scaleFactor = getDeviceScale(); // Adaptação aos ecrãs
					text.scale(scaleFactor);
					
			        canvas.add(text); // Adiciona o texto ao canvas
				    text.center(); // Centraliza o texto no canvas
				    text.setCoords(); // Atualiza as coordenadas do objeto
				    canvas.setActiveObject(text); // Define o texto como o objeto ativo
					//canvas.discardActiveObject();  // Desmarca qualquer seleção
					canvas.renderAll(); // Renderiza o canvas
				
			    	itemCounter++; // Contador
					document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
			    }
			
			// Carrega uma palavra centralizada automaticamente ao carregar a página
			window.onload = function() {
			    const randomWord = getRandomWord(); // Obtém uma palavra aleatória
			    addWordToCenter(randomWord); // Adiciona a palavra ao centro do canvas
			};
			
			
			
			
			
			
			
			    ///////////////////////////////
			    ///   TEXTO   sem espaços   ///
			    ///////////////////////////////
				
				/// SEM ERROS
				
				/*
				// Atualiza o texto do objeto ativo ao modificar o campo de entrada
				document.getElementById('textInput').addEventListener('input', function() {
				    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
				    if (activeObject && activeObject.type === 'text') {
				        activeObject.set('text', ` ${this.value.trim()} `); // Atualiza o texto
				        canvas.renderAll(); // Renderiza o canvas
				    }
				});
				
				// Limpa a caixa de texto ao deselecionar
				document.getElementById('textInput').addEventListener('blur', function() {
				    this.value = ''; // Limpa o campo de entrada
				});
			
			// Referência ao campo de entrada
			const textInput = document.getElementById('textInput');
			
			// Variável para rastrear se uma nova palavra foi criada
			let isCreatingNewWord = false;
			
			// Atualiza o texto do objeto ativo ou cria um novo texto ao modificar o campo de entrada
			textInput.addEventListener('input', function () {
			    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
			    const textWithSpaces = ` ${this.value.trim()} `; // Adiciona os espaços antes e depois do texto
			
			    if (activeObject && activeObject.type === 'text') {
			        // Atualiza o texto do objeto ativo
			        activeObject.set('text', textWithSpaces); 
			    } else if (!isCreatingNewWord && this.value.trim().length > 0) {
			        // Cria um novo texto se nenhuma palavra estiver selecionada
			        const randomPosition = getRandomPosition(); // Gera uma posição aleatória
			        addWord(textWithSpaces, randomPosition);
			        isCreatingNewWord = true; // Marca que uma palavra foi criada
			    }
			
			    canvas.renderAll(); // Renderiza o canvas
			});
			
			// Limpa o campo de entrada ao deselecionar ou ao criar uma nova palavra
			textInput.addEventListener('blur', function () {
			    this.value = ''; // Limpa o campo de entrada
			    isCreatingNewWord = false; // Reseta o estado de criação
			});
			
			// Popula o campo de entrada ao receber foco, se houver um texto selecionado
			textInput.addEventListener('focus', function () {
			    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
			    if (activeObject && activeObject.type === 'text') {
			        this.value = activeObject.text.trim(); // Preenche o campo com o texto do objeto ativo
			    } else {
			        this.value = ''; // Caso contrário, mantém o campo vazio
			    }
			});
			*/
			
			
			    ///////////////////////////////
			    ///   TEXTO   com espaços   ///
			    ///////////////////////////////
				
				///   ERRO:
				///   Ao entrar num texto apenas com espaços acrescenta espaço antes e depois dos espaços já existentes
				///   Com vários espaços no início e no fim o SNAP fica inconsistente
				
				// Atualiza o texto do objeto ativo ao modificar o campo de entrada
				document.getElementById('textInput').addEventListener('input', function() {
				    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
				    if (activeObject && activeObject.type === 'text') {
				        activeObject.set('text', `${this.value.trim()}`); // Atualiza o texto
				        //activeObject.set('text', ` ${this.value} `); // Remove espaços
						canvas.renderAll(); // Renderiza o canvas
				    }
				});
				
				// Limpa a caixa de texto ao deselecionar
				document.getElementById('textInput').addEventListener('blur', function() {
				    this.value = ''; // Limpa o campo de entrada
				});
			
			
			
			
			// Referência ao campo de entrada
			const textInput = document.getElementById('textInput');
			
			// Variável para rastrear se uma nova palavra foi criada
			let isCreatingNewWord = false;
			
			// Função para ajustar o texto com espaços no início e no fim
			function ensureSpaces(text) {
			    if (!text.startsWith(' ')) {
			        text = ' ' + text; // Adiciona um espaço no início, se não houver
			    }
			    if (!text.endsWith(' ')) {
			        text = text + ' '; // Adiciona um espaço no fim, se não houver
			    }
			    return text;
			}
			
			// Atualiza o texto do objeto ativo ou cria um novo texto ao modificar o campo de entrada
			textInput.addEventListener('input', function () {
			    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
			    const textWithSpaces = ensureSpaces(this.value); // Ajusta o texto para garantir espaços
				//const textWithSpaces = ` ${this.value.trim()} `; // Remove espaços
			
			    if (activeObject && activeObject.type === 'text') {
			        // Atualiza o texto do objeto ativo
			        activeObject.set('text', textWithSpaces); 
			    } else if (!isCreatingNewWord && this.value.length > 0) {
			    //} else if (!isCreatingNewWord && this.value.trim().length > 0) { // Remove espaços
			        // Cria um novo texto se nenhuma palavra estiver selecionada
			        const randomPosition = getRandomPosition(); // Gera uma posição aleatória
			        addWord(textWithSpaces, randomPosition);
			        isCreatingNewWord = true; // Marca que uma palavra foi criada
			    }
			
			    canvas.renderAll(); // Renderiza o canvas
			});
			
			// Limpa o campo de entrada ao deselecionar ou ao criar uma nova palavra
			textInput.addEventListener('blur', function () {
			    this.value = ''; // Limpa o campo de entrada
			    isCreatingNewWord = false; // Reseta o estado de criação
			});
			
			
			
			
			// Popula o campo de entrada ao receber foco, se houver um texto selecionado
			textInput.addEventListener('focus', function () {
			    const activeObject = canvas.getActiveObject(); // Obtém o objeto ativo
			    if (activeObject && activeObject.type === 'text') {
			        this.value = activeObject.text; // Preenche o campo com o texto do objeto ativo
			        //this.value = activeObject.text.trim(); // Remove espaços
			    } else {
			        this.value = ''; // Caso contrário, mantém o campo vazio
			    }
			});
			
			
			
			
			
			
			
			
			
			
			
			///////////////////////
			///   BOTÃO TURBO   ///
			///////////////////////
			
			// Referência ao botão de múltipla adição
			const turboSwitch = document.getElementById('turboSwitch');
			const turboBullet = document.getElementById('turboBullet');
			
			// Estado inicial do Turbo (inativo)
			let turboMode = false;
			
			// Alterna o estado do Turbo ao clicar
			turboSwitch.addEventListener('click', () => {
			    turboMode = !turboMode;
			    turboBullet.style.backgroundColor = turboMode ? '#e619e5' : '#cdcdcd';
			});
			
			// Atualiza o valor de `turboSwitch.checked` com base no estado do Turbo
			Object.defineProperty(turboSwitch, 'checked', {
			    get() {
			        return turboMode;
			    }
			});
			
			function addMultipleObjects(addFunction) {
			    const count = turboSwitch.checked ? randomBetween(5, 10) : 1;
			    for (let i = 0; i < count; i++) {
			        setTimeout(() => {
			            const randomWord = getRandomWord(); // Obtém uma palavra aleatória
			            const randomPosition = getRandomPosition(); // Obtém uma posição aleatória
			            addFunction(randomWord, randomPosition); // Passa os parâmetros necessários
			        }, i * 100); // Pausa de 25 ms entre cada elemento
			    }
			}
			
			// Botão para adicionar palavras
			document.getElementById('addWord').addEventListener('click', () => addMultipleObjects(addWord));
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			    ////////////////////////////
			    ///   RODAR HANDLE 15º   ///
			    ////////////////////////////
				canvas.on('object:rotating', function(event) {
			        const target = event.target;
			        if (target) {
			            const angle = target.angle;
			            // Ajusta para o múltiplo de 15º mais próximo
			            const snapAngle = Math.round(angle / 15) * 15;
			            target.set({
			                angle: snapAngle
			            });
			        }
			    });
			
			
			
				////////////////////
				///  DUPLICATE   ///
				////////////////////
				document.getElementById('duplicateSelected').addEventListener('click', function() {
				    const activeObject = canvas.getActiveObject();
				
				    if (activeObject) {
				        if (activeObject.type === 'activeSelection') {
				            duplicateGroup(activeObject);
				        } else {
				            duplicateObject(activeObject);
				        }
				    }
				});
				
				// Função para duplicar objetos individuais
				function duplicateObject(object) {
				    object.clone(function(clonedObject) {
				        const offsetLeft = randomBetween(-100, 100);
				        const offsetTop = randomBetween(-100, 100);
				        
				        clonedObject.set({
				            left: object.left + offsetLeft,
				            top: object.top + offsetTop,
				            ...commonSettings // Aplica as configurações comuns, se houver
				        });
				
				        canvas.add(clonedObject);
				        canvas.setActiveObject(clonedObject);
				        canvas.renderAll();
						itemCounter++; // Contador
						document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
				    });
				}
				
				// Função para duplicar grupos de objetos
				function duplicateGroup(group) {
				    group.clone(function(clonedGroup) {
				        const offsetLeft = randomBetween(-100, 100);
				        const offsetTop = randomBetween(-100, 100);
				
				        clonedGroup.set({
				            left: group.left + offsetLeft,
				            top: group.top + offsetTop,
				            canvas: canvas,
				            ...commonSettings // Aplica as configurações comuns, se houver
				        });
				
				
				        // Define a visibilidade dos controles de cada objeto dentro do grupo duplicado
				        clonedGroup.forEachObject(obj => {

				            // Adiciona cada objeto do grupo duplicado ao canvas
				            canvas.add(obj);
							itemCounter++; // Contador
							document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
				        });
				
				        // Cria uma nova seleção ativa com o grupo duplicado
				        canvas.setActiveObject(clonedGroup); // Define o grupo duplicado como ativo
				        canvas.renderAll(); // Renderiza o canvas para exibir o grupo duplicado
				    });
				}
			
			
			
			
				///////////////////////
			    ///   CLEAR CANVAS  ///
			    ///////////////////////
			    document.getElementById('clearCanvas').addEventListener('click', function() {
			        canvas.clear();				// Limpa todo o canvas
					addCrossesInCorners();		// Adiciona as miras
					itemCounter = 0; // Contador
					document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
			    });
			
				
				
				
			    ///////////////////////////
			    ///   DELETE  ELEMENT   ///
			    ///////////////////////////
				document.getElementById('deleteSelected').addEventListener('click', function() {
				const activeObject = canvas.getActiveObject();
				    
				    if (activeObject) {  // Se está selecionado
				        if (activeObject.type === 'activeSelection') {  // Se é grupo
				            activeObject.getObjects().forEach(obj => {
				                canvas.remove(obj);  // Remover todos os objetos selecionados
								itemCounter--; // Contador
				            });
				            canvas.remove(activeObject);  // Remover o grupo em si
				        } else if (activeObject.evented) {  // Se permite eventos
				            canvas.remove(activeObject);  // Remover o objeto selecionado
							itemCounter--; // Contador
				        }
				
				        // Descartar a seleção atual
				        canvas.discardActiveObject();
				        canvas.renderAll();  // Atualizar o canvas
						
						document.getElementById('itemCountDisplay').textContent = itemCounter; // para apresentar em ID="itemCountDisplay" o número de elementos
				    }
				});
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			//////////////////////
			///   BOTÃO SNAP   ///
			//////////////////////
// Estado inicial para Snap
let applySnapActive = false;

// Referência ao bullet do Snap
const snapBullet = document.getElementById('snapBullet');

// Configuração do botão Snap
document.getElementById('toggleSnap').addEventListener('click', () => {
    applySnapActive = !applySnapActive;
    snapBullet.style.backgroundColor = applySnapActive ? '#e619e5' : '#cdcdcd';
});

///////////////////
///   P/ SNAP   ///
///////////////////
// Função para calcular os vértices de um objeto considerando a rotação e escala
function getVerticesAndCenter(object) {
    const angleInRadians = fabric.util.degreesToRadians(object.angle);
    const cosTheta = Math.cos(angleInRadians);
    const sinTheta = Math.sin(angleInRadians);

    const safeScale = (value) => Math.abs(value) > 0.1 ? value : 0; // Evitar valores muito pequenos
    const halfWidth = safeScale((object.width * object.scaleX) / 2);
    const halfHeight = safeScale((object.height * object.scaleY) / 2);

    const centerX = object.left;
    const centerY = object.top;

    // Vértices sem rotação
    const vertices = [
        { x: -halfWidth, y: -halfHeight },  // Top-left
        { x: halfWidth, y: -halfHeight },   // Top-right
        { x: -halfWidth, y: halfHeight },   // Bottom-left
        { x: halfWidth, y: halfHeight }     // Bottom-right
    ];

    // Aplicar rotação aos vértices
    const rotatedVertices = vertices.map(vertex => ({
        x: centerX + vertex.x * cosTheta - vertex.y * sinTheta,
        y: centerY + vertex.x * sinTheta + vertex.y * cosTheta
    }));

    // Adicionar o centro como ponto adicional
    rotatedVertices.push({ x: centerX, y: centerY });

    return rotatedVertices;
}

// Função para calcular a distância entre dois pontos
function distance(p1, p2) {
    return Math.sqrt(Math.pow(p1.x - p2.x, 2) + Math.pow(p1.y - p2.y, 2));
}

///////////////////////
///   SNAP VISUAL   ///
///////////////////////
/*
let lastSnapTime = 0; // Armazena o timestamp do último snap

function showSnapPoint(x, y) {
    const now = Date.now(); // Tempo atual em milissegundos
    if (now - lastSnapTime < 500) return; // Ignorar se menos de 500ms se passaram

    lastSnapTime = now; // Atualiza o timestamp do último snap

    const snapCircle = new fabric.Circle({
        left: x - 4, // Ajustar para centralizar
        top: y - 4,  // Ajustar para centralizar
        radius: 8 / 2, // Raio de 4px
        fill: '#f9a2f3',
        selectable: false,
        evented: false
    });

    canvas.add(snapCircle);
    snapCircle.bringToFront(); // Traz o círculo para frente
	
    // Remove o ponto após 25ms
    setTimeout(() => {
        canvas.remove(snapCircle);
        canvas.renderAll();
    }, 250);
}
*/

////////////////
///   SNAP   ///
////////////////
// Função para aplicar o snap
function applySnap(target) {
    if (!applySnapActive) return;

	const threshold = 9; // Ajuste a precisão do snap
    const targetVertices = getVerticesAndCenter(target);

    const nearbyObjects = canvas.getObjects().filter(other => {
        const dx = Math.abs(other.left - target.left);
        const dy = Math.abs(other.top - target.top);
        return other !== target && dx < 500 && dy < 500; // Limitar a área de busca
    });

    let snapApplied = false;

    nearbyObjects.forEach((other) => {
        if (snapApplied) return;

        const otherVertices = getVerticesAndCenter(other);

        for (const tVertex of targetVertices) {
            for (const oVertex of otherVertices) {
                if (distance(tVertex, oVertex) < threshold) {
                    // Mostrar o ponto de snap
                    //showSnapPoint(oVertex.x, oVertex.y);
					
					// Ajuste do alvo para os valores exatos dos vértices do objeto adjacente
                    const offsetX = oVertex.x - tVertex.x;
                    const offsetY = oVertex.y - tVertex.y;

                    target.set({
                        left: target.left + offsetX,
                        top: target.top + offsetY
                    });

                    canvas.renderAll();
                    snapApplied = true;
                    break;
                }
            }
            if (snapApplied) break;
        }
    });
}

////////////////////////
///   SNAP EVENTS    ///
////////////////////////
// Evento para verificar o snap quando o objeto está sendo movido
canvas.on('object:moving', function(event) {
    const target = event.target;
    if (target) {
        applySnap(target);
    }
});
			
			
			
			
			
			
			
			
			
			
			
			
			
			
			
				/////////////////
			    ///   MIRAS   ///
			    /////////////////
			
				function addCross(x, y) {
				    const crossSize = 11;
				    const crossColor = '#FF00FF';
				
				    const crossOptions = {			// Configurações padrão para as cruzes
				        stroke: crossColor,
				        strokeWidth: 1,
				        //strokeDashArray: [1],
				        selectable: false,			// Não pode ser selecionável
				        hasControls: false,			// Não deve ter controles
				        hasBorders: false,			// Não deve ter contorno
				        hoverCursor: 'default',		// Cursor padrão
				        evented: false,				// Ignora eventos de mouse
				        lockMovementX: true,			// Bloqueia movimento horizontal
				        lockMovementY: true,			// Bloqueia movimento vertical
				        lockScalingX: true,			// Bloqueia escalonamento horizontal
				        lockScalingY: true,			// Bloqueia escalonamento vertical
				        lockRotation: true,			// Bloqueia rotação
				        cornerMark: true,			// Marca como uma cruz de canto
						class: 'miras',
				    };
				
				    // Desenha linhas da cruz (horizontal e vertical) com opções comuns
				    const horizontalLine = new fabric.Line([x - crossSize / 2, y, x + crossSize / 2, y], crossOptions);
				    const verticalLine = new fabric.Line([x, y - crossSize / 2, x, y + crossSize / 2], crossOptions);
				
				    // Adiciona as linhas ao canvas
				    canvas.add(horizontalLine, verticalLine);
				}
				
				// Adiciona cruzes em todos os cantos do canvas
				function addCrossesInCorners() {
				    const offset = 6; // Offset para a posição das cruzes nos cantos
				    const corners = [
				        { x: offset, y: offset },                          // Canto superior esquerdo
				        { x: canvas.width - offset, y: offset },           // Canto superior direito
				        { x: offset, y: canvas.height - offset },          // Canto inferior esquerdo
				        { x: canvas.width - offset, y: canvas.height - offset } // Canto inferior direito
				    ];
				
				    // Remove todas as marcas de canto antes de adicionar novas
				    canvas.getObjects().forEach(obj => {
				        if (obj.cornerMark) canvas.remove(obj);
				    });
				
				    corners.forEach(corner => addCross(corner.x, corner.y));
				}
				
				// Escuta mudanças no canvas e reposiciona as cruzes
				canvas.on('object:moving', addCrossesInCorners);
				canvas.on('object:scaling', addCrossesInCorners);
				canvas.on('object:rotating', addCrossesInCorners);
				canvas.on('selection:created', addCrossesInCorners);
				canvas.on('selection:cleared', addCrossesInCorners);
				
				// Inicializa as cruzes nos cantos do canvas
				addCrossesInCorners();
			
			
			
			//////////////////
			//   RESIZE     //
			//////////////////
			function resizeCanvas() {
			    // Tamanho do viewport
			    const margin = 6; // Margem de 6px
			
			    // Tamanho do viewport com margem aplicada
			    const viewportWidth = window.innerWidth - margin * 2;
				const viewportHeight = window.innerHeight - margin * 2;
			
			    // Define o aspect ratio A4 dependendo da orientação
			    const aspectRatio = viewportWidth > viewportHeight ? 1.414 : 1 / 1.414;
				let canvasWidth, canvasHeight;
			
			    // Calcula a largura e altura para manter a proporção A4
			    if (viewportWidth / viewportHeight > aspectRatio) {
					canvasHeight = viewportHeight;
					canvasWidth = canvasHeight * aspectRatio;
				} else {
					canvasWidth = viewportWidth;
					canvasHeight = canvasWidth / aspectRatio;
				}
			
			    // Define a nova largura e altura do canvas
			    const prevCanvasWidth = canvas.getWidth();
				const prevCanvasHeight = canvas.getHeight();
			
			    // Calcula a diferença de deslocamento ao redimensionar o canvas
			    const offsetX = (canvasWidth - prevCanvasWidth) / 2;
				const offsetY = (canvasHeight - prevCanvasHeight) / 2;
			
			    // Atualiza as dimensões do canvas
			    canvas.setWidth(canvasWidth);
				canvas.setHeight(canvasHeight);
			
			    // Centraliza os objetos sem alterar o tamanho
			    canvas.getObjects().forEach((obj) => {
					obj.left += offsetX;
					obj.top += offsetY;
					obj.setCoords();
				});
			
			    // Remove todas as miras (cruzes e círculos) antes de reposicionar
			    canvas.getObjects().forEach((obj) => {
					if (!obj.selectable) canvas.remove(obj); // Remover todos os objetos não selecionáveis (as miras)
				});
			
			    // Reposiciona as cruzes
			    //repositionCrosses();
				
			    // Reposiciona as novas miras com base nas novas dimensões do canvas
			
			    //addCrosses(); // Re-add crosses
			    updateCrossesInCorners();
				centerCanvas();
				canvas.calcOffset();
				canvas.renderAll();
			}
			
			
			
			//////////////////
			///   ECRAN   ////
			//////////////////
			function getDeviceScale() {
				const screenWidth = window.innerWidth;
				if (screenWidth <= 768) {  // Celular (você pode ajustar esse valor para a sua necessidade)
			        return 0.75;  // Escala menor para celular
				} else if (screenWidth <= 1024) {  // Tablet
			        return 0.9;  // Escala menor para tablet
				} else {
					return 1;  // Tamanho normal para desktop
				}
			}
			
			function centerCanvas() {
				const canvasContainer = canvas.getElement().parentNode;
				canvasContainer.style.position = 'relative';
				canvas.getElement().style.position = 'absolute';
				canvas.getElement().style.left = `50 % `;
				canvas.getElement().style.top = `50 % `;
				canvas.getElement().style.transform = `translate(-50 % ,  -50 % )`;
			}
			
			// Atualiza cruzes nos cantos após redimensionar
			function updateCrossesInCorners() {
			    // Remove cruzes existentes
			    canvas.getObjects().forEach(obj => {
					if (obj.evented === false) canvas.remove(obj);
				});
			
			    // Adiciona cruzes nos novos cantos
			    addCrossesInCorners();
			}
			
			// Adiciona as marcas de canto e redimensiona na inicialização
			updateCrossesInCorners();
			
			// Redimensiona o canvas na inicialização
			resizeCanvas();
			
			// Redimensiona o canvas ao redimensionar a janela
			window.addEventListener('resize', resizeCanvas);
			
			
			
			
			
			
			
			
			
			
			///////////////////////
			//   FLOATING MENU   //
			///////////////////////
			// Rastrear o maior z-index usado
			let maxZIndex = 1055;
			
			// Função para inicializar o comportamento de arrastar para um menu e sua zona de arraste
			function makeDraggable(floatingMenuId, dragHandleId) {
			    const floatingMenu = document.getElementById(floatingMenuId);
			    const dragHandle = document.getElementById(dragHandleId);
			
			    if (!floatingMenu || !dragHandle) {
			        console.error(`Element not found: ${floatingMenuId} or ${dragHandleId}`);
			        return;
			    }
			
			    // Variáveis para controle de movimento
			    let isDragging = false;
			    let startX = 0;
			    let startY = 0;
			    let offsetX = 0;
			    let offsetY = 0;
			
			    // Converte bottom/right em top/left se necessário
			    function convertBottomRightToTopLeft() {
			        const rect = floatingMenu.getBoundingClientRect();
			        if (floatingMenu.style.bottom !== 'auto' && floatingMenu.style.right !== 'auto') {
			            floatingMenu.style.top = `${rect.top}px`;
			            floatingMenu.style.left = `${rect.left}px`;
			            floatingMenu.style.bottom = 'auto';
			            floatingMenu.style.right = 'auto';
			        }
			    }
			
			    // Função para trazer o menu para frente
			    function bringToFront() {
			        maxZIndex++;
			        floatingMenu.style.zIndex = maxZIndex;
			    }
			
			    // Função para iniciar o movimento (mouse e toque)
			    function startDrag(e) {
			        isDragging = true;
			
			        const event = e.touches ? e.touches[0] : e; // Suporte para toque e mouse
			        startX = event.clientX;
			        startY = event.clientY;
			
			        // Certifica-se de que está usando `top` e `left` para posicionamento
			        convertBottomRightToTopLeft();
			
			        offsetX = parseInt(floatingMenu.style.left || 0, 10);
			        offsetY = parseInt(floatingMenu.style.top || 0, 10);
			
			        dragHandle.style.cursor = 'grabbing'; // Muda o cursor para "grabbing"
			
			        // Traz o menu para frente
			        bringToFront();
			
			        e.preventDefault();
			    }
			
			    // Função para movimentar o menu
			    function moveDrag(e) {
			        if (isDragging) {
			            const event = e.touches ? e.touches[0] : e; // Suporte para toque e mouse
			            const moveX = event.clientX - startX + offsetX;
			            const moveY = event.clientY - startY + offsetY;
			
			            floatingMenu.style.left = `${moveX}px`;
			            floatingMenu.style.top = `${moveY}px`;
			        }
			    }
			
			    // Função para finalizar o movimento
			    function endDrag() {
			        if (isDragging) {
			            isDragging = false;
			            dragHandle.style.cursor = 'grab'; // Volta o cursor para "grab"
			        }
			    }
			
			    // Eventos de mouse
			    dragHandle.addEventListener('mousedown', startDrag);
			    document.addEventListener('mousemove', moveDrag);
			    document.addEventListener('mouseup', endDrag);
			
			    // Eventos de toque
			    dragHandle.addEventListener('touchstart', startDrag);
			    document.addEventListener('touchmove', moveDrag);
			    document.addEventListener('touchend', endDrag);
			
			    // Traz o menu para frente ao clicar na zona de arraste
			    dragHandle.addEventListener('click', bringToFront);
			}
			
			// Inicializar múltiplos menus
			makeDraggable('floating-menu-1', 'drag-handle-1');
			makeDraggable('floating-menu-2', 'drag-handle-2');
			makeDraggable('floating-menu-3', 'drag-handle-3');
			makeDraggable('floating-menu-4', 'drag-handle-4');
			makeDraggable('floating-menu-5', 'drag-handle-5');
			makeDraggable('floating-menu-6', 'drag-handle-6');
			makeDraggable('floating-menu-7', 'drag-handle-7');
			makeDraggable('floating-menu-8', 'drag-handle-8');
			//makeDraggable('floating-menu-9', 'drag-handle-9');
			makeDraggable('floating-menu-10', 'drag-handle-10');
			
			



			/*
			//////////////////////
			//   CANVAS 2 PNG   //
			//////////////////////
			function downloadCanvasAsPNG() {
			    // Verificar se o canvas possui objetos
			    if (canvas.getObjects().length === 0) {
			        alert("O canvas está vazio! Adicione algum conteúdo antes de exportar.");
			        return;
			    }
			
			    // Determinar a largura e altura do canvas
			    const canvasWidth = canvas.width;
			    const canvasHeight = canvas.height;
			
			    // Escolher a orientação do PDF com base nas dimensões do canvas
			    const orientation = canvasWidth > canvasHeight ? 'landscape' : 'portrait';
			
			    // Criar uma nova instância do jsPDF
			    const pdf = new jsPDF(orientation, 'pt', [canvasWidth, canvasHeight]);
			
			    // Converter o canvas para uma imagem base64
			    const dataURL = canvas.toDataURL('image/png', 1.0);
			
			    // Adicionar a imagem ao PDF
			    pdf.addImage(dataURL, 'PNG', 0, 0, canvasWidth, canvasHeight);
			
			    // Salvar o PDF com o nome 'canvas.pdf'
			    pdf.save('Palavras.pdf');
			}
			
			// Associa a função de exportação ao botão de download
			document.getElementById('word-btn').addEventListener('click', downloadCanvasAsPNG);
			*/
			
			
/*			
			/////////////////////////////////
			//   CANVAS 2 PNG   300 DPIs   //
			/////////////////////////////////
			function downloadCanvasAsPNG() {
			    // Verificar se o canvas possui objetos
			    if (canvas.getObjects().length === 0) {
			        alert("ecooo-ecooo-ecooo");
			        return;
			    }
			
			    // Ocultar elementos indesejados (como miras)
			    canvas.getObjects().forEach(obj => obj.cornerMark && (obj.visible = false)); // Remove as Miras
			    canvas.renderAll(); // Atualiza o canvas sem miras
			
			    // Definir resolução desejada
			    const dpi = 300; // 300 DPI
			    const pxToMm = 25.4 / dpi; // Conversão de pixels para milímetros
			
			    // Dimensões A4 em milímetros
			    const a4WidthMm = 210;
			    const a4HeightMm = 297;
			
			    // Converter A4 para pixels no DPI desejado
			    const a4WidthPx = Math.round((a4WidthMm / 25.4) * dpi);
			    const a4HeightPx = Math.round((a4HeightMm / 25.4) * dpi);
			
			    // Salvar as dimensões originais do canvas
			    const originalWidth = canvas.width;
			    const originalHeight = canvas.height;
			
			    // Calcular a orientação com base no canvas
			    const orientation = originalWidth > originalHeight ? 'landscape' : 'portrait';
			
			    // Ajustar as dimensões A4 de acordo com a orientação
			    const pageWidthMm = orientation === 'landscape' ? a4HeightMm : a4WidthMm;
			    const pageHeightMm = orientation === 'landscape' ? a4WidthMm : a4HeightMm;
			
			    // Calcular escala para ajustar o canvas ao tamanho A4
			    const scaleX = (pageWidthMm * dpi) / (25.4 * originalWidth);
			    const scaleY = (pageHeightMm * dpi) / (25.4 * originalHeight);
			    const scale = Math.min(scaleX, scaleY); // Usar a menor escala para manter proporção
			
			    // Ajustar o tamanho do canvas temporariamente para a escala A4
			    const scaledWidth = originalWidth * scale;
			    const scaledHeight = originalHeight * scale;
			
			    canvas.setDimensions({ width: scaledWidth, height: scaledHeight });
			    canvas.setZoom(scale);
			
			    // Converter o canvas para uma imagem base64
			    const dataURL = canvas.toDataURL('image/png', 1.0);
			
			    // Restaurar o canvas às dimensões originais
			    canvas.setDimensions({ width: originalWidth, height: originalHeight });
			    canvas.setZoom(1);
			
			    // Criar uma nova instância do jsPDF com A4 e 300 DPI
			    const pdf = new jsPDF(orientation, 'mm', [pageWidthMm, pageHeightMm]);
			
			    // Calcular posição da imagem no centro da página A4
			    const offsetX = (pageWidthMm - (scaledWidth * pxToMm)) / 2;
			    const offsetY = (pageHeightMm - (scaledHeight * pxToMm)) / 2;
			
			    // Adicionar a imagem ao PDF centralizada
			    pdf.addImage(dataURL, 'PNG', offsetX, offsetY, scaledWidth * pxToMm, scaledHeight * pxToMm);
			
			    // Salvar o PDF com o nome 'Palavras.pdf'
			    pdf.save('Palavras.pdf');
			}
			
			// Associa a função de exportação ao botão de download
			document.getElementById('word-btn').addEventListener('click', downloadCanvasAsPNG);
*/

			////////////////////
			//   PDF NAME    //
			////////////////////
			let loadedPDFFileName = ''; // Nome do arquivo PDF carregado
			const defaultPDFFileName = 'Palavra'; // Valor padrão para exportação em PDF
			
			////////////////////
			//   JSON NAME    //
			////////////////////
			let loadedFileName = ''; // Nome do arquivo carregado
			const defaultFileName = 'Palavra'; // Valor padrão para o botão de exportação
			
			document.addEventListener('DOMContentLoaded', () => {
			    // Inicializa o botão de exportação com o nome padrão
			    updateExportButton(defaultFileName);
			});
			
			// Atualiza o texto do botão de exportação
			function updateExportButton(name) {
			    const exportButton = document.getElementById('svg-json-btn');
			    exportButton.innerHTML = `<span class="truncate">Exportar ${name.toUpperCase()}</span>`;
			}

			//////////////////////
			//   CANVAS 2 SVG   //
			//////////////////////
			async function downloadCanvasAsSVGPDF() {
			    // Verifica se o canvas está vazio
			    if (canvas.getObjects().length === 0) {
			        alert("O canvas está vazio...");
			        return;
			    }
			
			    // Remove os elementos de marcação (como as miras) antes de exportar
			    canvas.getObjects().forEach(obj => obj.cornerMark && (obj.visible = false));
			    canvas.renderAll(); // Atualiza o canvas após remover as miras
			
			    // Converte o conteúdo do canvas para SVG
			    let svgData = canvas.toSVG();
			
			    // Substitui a fonte no SVG por "CourierPrime" para garantir consistência na exportação
			    svgData = svgData.replace(/font-family="[^"]+"/g, 'font-family="CourierPrime"');
			
			    // Define a orientação do PDF com base nas dimensões do canvas
			    const orientation = canvas.getWidth() > canvas.getHeight() ? 'landscape' : 'portrait';
			
			    // Importa o jsPDF do namespace global
			    const { jsPDF } = window.jspdf;
			
			    // Cria um novo documento PDF
			    const pdf = new jsPDF(orientation, 'mm', 'a4');
			
			    // Adiciona e configura a fonte "CourierPrime" no jsPDF
			    pdf.addFont('../bootstrap/css/font/CourierPrime-Regular.ttf', 'CourierPrime', 'normal'); // Adiciona o arquivo da fonte ao PDF
			    pdf.setFont('CourierPrime', 'normal'); // Define a fonte como padrão no PDF
			
			    // Obtém as dimensões da página PDF (A4)
			    const width = pdf.internal.pageSize.getWidth();
			    const height = pdf.internal.pageSize.getHeight();
			
			    // Converte o SVG em um elemento DOM para manipulação e renderização
			    const parser = new DOMParser();
			    const svgDoc = parser.parseFromString(svgData, "image/svg+xml");
			    const svgElement = svgDoc.documentElement;
			
			    // Redimensiona o SVG para se ajustar às dimensões do PDF
			    svgElement.setAttribute("width", width);
			    svgElement.setAttribute("height", height);
			
			    try {
			        // Renderiza o SVG no PDF usando svg2pdf
			        await window.svg2pdf(svgElement, pdf, { x: 0, y: 0, width: width, height: height });

		        // Solicita o nome do arquivo ao usuário
		        const initialFileName = loadedPDFFileName || defaultPDFFileName;
		        const userFileName = prompt('Nome do ficheiro PDF:', initialFileName);

			///////////////
			//   TEXTO   //
			///////////////
			/*
			        //pdf.setFont("dinishcondensed-regular", "normal"); // Definido em Carimbo-V3.2-normal.js
					
					// Ano
			        const currentYear = new Date().getFullYear();
					
					// À ESQUERDA //
					
					const text1 = `${itemCounter}`;
					const textWidth1a = pdf.getTextWidth(text1);
					const x1 = 10; // x a 10mm da margem direita
			        const y1 = height - 10; // y a 10mm da margem inferior
			        
			        pdf.setFontSize(7);
					pdf.setTextColor(0, 0, 0);
					pdf.text(x1, y1, text1);
					
			        
					// AO CENTRO //
			        const text2a = 'FCL';
			        const text2b = `${currentYear}`;
			        const text2c = 'https://francisco.pt/fabric/';
					const textWidth2a = pdf.getTextWidth(text2a); // Largura do texto
					const textWidth2b = pdf.getTextWidth(text2b); // Largura do texto
					const textWidth2c = pdf.getTextWidth(text2c); // Largura do texto
			        const x2a = (width - textWidth2a) / 2; // Centro no eixo x
			        const x2b = (width - textWidth2b) / 2; // Centro no eixo x
			        const x2c = (width - textWidth2c) / 2; // Centro no eixo x
			        const y2a = height - 16; // y a 10mm da margem inferior
			        const y2b = height - 13; // y a 10mm da margem inferior
			        const y2c = height - 10; // y a 10mm da margem inferior
			        
			        pdf.setFontSize(7);
					pdf.setTextColor(0, 0, 0);
					pdf.text(x2a, y2a, text2a); // x, y, texto
					pdf.text(x2b, y2b, text2b); // x, y, texto
					pdf.text(x2c, y2c, text2c); // x, y, texto
			        
			        // À DIREITA //
			        
					const text3 = '✕';
					const textWidth3 = pdf.getTextWidth(text3);
					const x3 = width - textWidth3 - 10;
					const y3 = height - 10;
					
					pdf.setFontSize(7);
					pdf.setTextColor(255, 255, 255);
					pdf.text(x3, y3, text3);
				*/
			
			        // META DATA PDF //
			        if (userFileName) {
			            // Adiciona meta dados ao PDF
			            pdf.setDocumentProperties({
			                title: userFileName,
			                subject: 'Palavra-a-Palavra',
			                author: 'FCL',
			                keywords: 'desenho, arte, criatividade, pensamento criativo, draw, drawing, art, creativity, creative thinking, ttct',
			                creator: 'francisco.pt'
			            });
			
			            // Salva o PDF com o nome fornecido pelo usuário
			            pdf.save(`${userFileName}.pdf`);
					} else {
						alert('Exportação cancelada. Nenhum nome foi fornecido.');
					}
			    } catch (error) {
			        // Captura e exibe erros durante o processo
			        console.error("Erro ao converter SVG para PDF:", error);
			    }
					
				addCrossesInCorners();		// Adiciona as miras
				canvas.renderAll(); // Atualiza o canvas com miras
			
			}
			
			///////////////////
			//   PRINT SVG  //
			///////////////////
			// Associa a função de exportação ao botão de download SVG
			document.getElementById('word-btn').addEventListener('click', downloadCanvasAsSVGPDF);
			
			/////////////////////
			//   EXPORT JSON   //
			/////////////////////
			document.getElementById('svg-json-btn').addEventListener('click', () => {
			    const textObjects = canvas.getObjects().filter((obj) => obj.type === 'text'); // Filtra objetos do tipo "text"
			
			    const filteredCanvasData = {
			        version: canvas.version, // Preserva a versão do Fabric.js
			        objects: textObjects.map(obj => obj.toObject()) // Exporta apenas os objetos "text"
			    };
			
			    const jsonData = JSON.stringify(filteredCanvasData); // Converte os dados para JSON
			    const blob = new Blob([jsonData], { type: 'application/json' }); // Cria um arquivo JSON
			
			    // Sugere o nome do arquivo carregado ou um padrão
			    const initialFileName = loadedFileName || defaultFileName;
			    const userFileName = prompt('Nome do ficheiro JSON:', initialFileName);
			
			    if (userFileName) {
			        const link = document.createElement('a');
			        link.href = URL.createObjectURL(blob);
			        link.download = `${userFileName}.json`; // Define o nome do arquivo salvo
			        link.click();
			    } else {
			        alert('Exportação cancelada. Nenhum nome foi fornecido.');
			    }
			});
			
			/////////////////////
			//   IMPORT JSON   //
			/////////////////////
		document.getElementById('json-svg-btn').addEventListener('change', (event) => {
			const file = event.target.files[0];
			if (!file) return;
			
			const reader = new FileReader();
			reader.onload = (e) => {
				try {
					const jsonData = JSON.parse(e.target.result); // Lê o conteúdo do arquivo JSON
		            canvas.loadFromJSON(jsonData, () => {
						itemCounter = 0; // Reseta o contador


		        // Atualiza visibilidade apenas para objetos de texto
				canvas.getObjects().forEach((obj) => {
				if (['i-text', 'text', 'textbox'].includes(obj.type)) {
					applyControlVisibility(obj);
				}

							itemCounter++;
							document.getElementById('itemCountDisplay').textContent = itemCounter;
						});
						//canvas.renderAll(); // Força uma renderização imediata do canvas
						canvas.requestRenderAll(); // Força uma renderização imediata do canvas
		                console.log(`Total de objetos carregados: ${itemCounter}`);
					});
					loadedFileName = file.name.replace(/\.json$/, ''); // Remove a extensão ".json"
		            updateExportButton(loadedFileName); // Atualiza o botão de exportação com o novo nome
				} catch (error) {
					console.error('Erro ao carregar JSON:', error);
					alert('Arquivo JSON inválido.');
				}
			};
			
			reader.readAsText(file); // Lê o arquivo como texto
		    event.target.value = ''; // Limpa o input para permitir recarregar o mesmo arquivo
		});
			
			
			
	</script>
		<!--
			┌───┬───┬───┬───┬───┬───┬───┐
			│ P │ Á │ G │ I │ N │ A │ S │
			└───┴───┴───┴───┴───┴───┴───┘
			-->
		<!-- GO! StatCounter -->
		<script>
			var sc_project = 9967729;
			var sc_invisible = 0;
			var sc_security = "a7c4a0de";
			var sc_text = 3; 
		</script>
		<!-- STOP! StatCounter -->
		<!-- GO! Google Analytics -->
		<!--
			<script>
			  (function(i, s, o, g, r, a, m) {
				i['GoogleAnalyticsObject'] = r;
				i[r] = i[r] || function() {
					(i[r].q = i[r].q || []).push(arguments)
				}, i[r].l = 1 * new Date();
				a = s.createElement(o),
					m = s.getElementsByTagName(o)[0];
				a.async = 1;
				a.src = g;
				m.parentNode.insertBefore(a, m)
			})(window, document, 'script', '//www.google-analytics.com/analytics.js', 'ga');
			ga('create', 'UA-1102158-4', 'auto');
			ga('send', 'pageview');
			</script>
			-->
		<!-- STOP! Google Analytics -->
		<!-- GO! Google Analytics #4 -->
		<!--
			<script async src="https://www.googletagmanager.com/gtag/js?id=G-Y12EBCZXZN"></script>
			<script>
			  window.dataLayer = window.dataLayer || [];
			  function gtag(){dataLayer.push(arguments);}
			  gtag('js', new Date());
			
			  gtag('config', 'G-Y12EBCZXZN');
			</script>
			-->
		<!-- STOP! Google Analytics -->
	</body>
</html>
<!--
	┌───┬───┬───┬───┬───┬───┬───┬───┐
	│ C │ O │ M │ P │ L │ E │ T │ E │
	├───┼───┼───┼───┼───┼───┼───┼───┘
	│ S │ I │ L │ E │ N │ C │ E │
	└───┴───┴───┴───┴───┴───┴───┘
	-->