StoreGenerator / storeUI.html
drakosfire's picture
add id=pages to class pages, updated pageContainer to target id=pages, removed redundant event listerners
b1df057
raw
history blame
56.2 kB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="./dependencies/all.css" rel="stylesheet" />
<link href="./dependencies/css.css?family=Open+Sans:400,300,600,700" rel="stylesheet" type="text/css" />
<link href='./dependencies/bundle.css' rel='stylesheet' />
<link href="./dependencies/style.css" rel='stylesheet' />
<link href="./dependencies/5ePHBstyle.css" rel='stylesheet' />
<title>DnD Stat Block</title>
<link rel="stylesheet" href="styles.css">
<script src="https://unpkg.com/htmx.org@1.7.0/dist/htmx.min.js"></script>
</head>
<style>
:root {
--HB_Color_Background: #EEE5CE;
--HB_Color_Accent: #E0E5C1;
--HB_Color_HeaderUnderline: #C0AD6A;
--HB_Color_HorizontalRule: #9C2B1B;
--HB_Color_HeaderText: #58180D;
--HB_Color_MonsterStatBackground: #F2E5B5;
--HB_Color_CaptionText: #766649;
--HB_Color_WatercolorStain: #BBAD82;
--HB_Color_Footnotes: #C9AD6A;
}
input[type="text"], textarea {
width: auto;
padding: 8px;
margin: 5px 0;
border: 1px solid #ccc;
border-radius: 4px;
font-size: 14px;
background-color: #f9f9f9;
transition: background-color 0.3s ease, border-color 0.3s ease;
}
.grid-container {
display: grid;
grid-template-columns: 1fr 3fr; /* Two columns with the second column being three times as wide */
grid-gap: 20px;
padding: 20px;
height: 100vh;
}
.block-container {
position: fixed; /* Lock the block-container in place */
top: 20px; /* Distance from the top of the viewport */
left: 20px; /* Distance from the left of the viewport */
width: 450px; /* Set the width of the block-container */
height: calc(100vh - 40px); /* Full viewport height minus top and bottom padding */
overflow-y: auto; /* Enable vertical scrolling if needed */
border-right: 1px solid #ccc; /* Right border for visual separation */
padding-right: 20px; /* Padding inside the block-container */
box-sizing: border-box; /* Include padding and border in the element's total width and height */
background-color: #f9f9f9; /* Background color */
z-index: 1000; /* Ensure it is on top of other elements */
}
.block-container .page {
column-count: 1;
padding: 0;
width: 425px;
height: auto; /* Allow the page to expand to fit content */
overflow: visible; /* Allow content to overflow if necessary */
page-break-before: auto;
page-break-after: auto;
}
.page-container {
margin-left: 450px; /* Offset the page content by the width of block-container plus margin */
width: 900px;
padding: 20px;
overflow: auto; /* Enable scrolling if needed */
height: 100vh; /* Full viewport height */
box-sizing: border-box; /* Include padding and border in the element's total width and height */
}
.page {
column-count: 2;
column-gap: .9cm;
column-width: 8cm;
-webkit-column-count: 2;
-moz-column-count: 2;
-webkit-column-width: 8cm;
-moz-column-width: 8cm;
-webkit-column-gap: .9cm;
-moz-column-gap: .9cm;
position: relative;
z-index: 15;
box-sizing: border-box;
width: 215.9mm;
height: 279.4mm; /* Original height for print layout */
padding: 1.4cm 1.9cm 1.7cm;
overflow: hidden;
font-family: "BookInsanityRemake";
font-size: .34cm;
counter-increment: phb-page-numbers;
background-color: var(--HB_Color_Background);
background-image: url('./themes/assets/parchmentBackground.jpg');
text-rendering: optimizeLegibility;
page-break-before: always;
page-break-after: always;
contain: size;
}
.page .monster hr:last-of-type + * {
margin-top: .1cm;
}
.page * + h3 {
margin-top: .05cm;
}
.page * + h4 {
margin-top: .1cm;
}
.page h4 + * {
margin-top: .1cm;
}
.page dl + * {
margin-top: .1cm;
}
.page p + * {
margin-top: .1cm;
}
.page img {
width: 100%;
height: auto;
cursor: pointer;
}
.page .classTable.frame{
margin-right:0.1cm;
margin-left: 0.1cm;
}
/* Ensure the h1 tag is constrained within its column */
.block-content h1 {
column-span: none;
box-sizing: border-box; /* Include padding and border in the element's total width and height */
margin: 0 auto; /* Center the h1 within the column */
overflow: hidden; /* Handle overflow content */
word-wrap: break-word; /* Break long words to prevent overflow */
}
.columnWrapper {
column-gap: inherit;
max-height: 100%;
column-span: all;
columns: inherit;
height: 100%; /* Ensure it takes full height of the parent */
box-sizing: border-box; /* Ensures padding and border are included in the element's total width and height */
}
/* block-item styling */
.block-item {
border: 1px solid #ccc;
border-radius: 8px;
background-color: transparent;
padding: 15px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
transition: transform 0.3s;
}
.block-item:hover {
/* transform: translateY(-5px);
background-color: rgba(255, 255, 255, 0.5); /* Slightly visible background on hover */
}
.block-item img {
width: 100%;
height: auto;
cursor: pointer;
}
/* Modal styling */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 1001; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgb(0,0,0); /* Fallback color */
background-color: rgba(0,0,0,0.9); /* Black w/ opacity */
}
.modal-content {
margin: auto;
display: block;
width: 80%;
max-width: 700px;
}
.modal-content, #caption {
animation-name: zoom;
animation-duration: 0.6s;
}
@keyframes zoom {
from {transform: scale(0)}
to {transform: scale(1)}
}
.close {
position: absolute;
top: 15px;
right: 35px;
color: #f1f1f1;
font-size: 40px;
font-weight: bold;
transition: 0.3s;
}
.close:hover,
.close:focus {
color: #bbb;
text-decoration: none;
cursor: pointer;
}
input[type="text"]:focus, textarea:focus {
background-color: #e9e9e9;
border-color: #aaa;
outline: none;
}
/* Specific styles for different textboxes */
.user-description-textarea {
width: 400px;
height: 40px; /* Adjust as needed for 3 lines */
resize: vertical;
background: none;
font-family: "ScalySansRemake";
font-weight: 800;
}
/* Focus styles for description textbox */
.user-description-textarea:focus {
background-color: #e9e9e9;
border-color: #aaa;
outline: none;
}
.heading-textarea {
width: 100%;
font-size: .458cm; /* Matches the font size of an h4 heading */
line-height: .7em;
font-weight: 800;
border: none;
background: none;
margin: 0;
padding: 0;
resize: none; /* Prevents the textarea from being resizable */
overflow: hidden; /* Prevents scrollbars */
outline: none; /* Removes the focus outline */
font-family: "MrEavesRemake"; /* Ensures font family is inherited */
color: var(--HB_Color_HeaderText)
}
.title-textarea{
height:30px;
width: 100%;
margin-bottom: 5px;
column-span: all;
font-size: .89cm;
line-height: 1em;
font-family: "MrEavesRemake";
font-weight: 800;
color: var(--HB_Color_HeaderText);
border: 0;
font: inherit;
background: none;
padding: 0;
resize: none; /* Prevents the textarea from being resizable */
overflow: hidden; /* Prevents scrollbars */
outline: none; /* Removes the focus outline */
}
.subtitle-textarea{
height:20px;
width: 100%;
margin-bottom: 5px;
column-span: all;
font-size: .89cm;
line-height: 1em;
font-family: "MrEavesRemake";
font-weight: 800;
color: var(--HB_Color_HeaderText);
border: 0;
font: inherit;
background: none;
padding: 0;
resize: none; /* Prevents the textarea from being resizable */
overflow: hidden; /* Prevents scrollbars */
outline: none; /* Removes the focus outline */
}
div[contenteditable="true"]:focus {
background-color: #e9e9e9;
border-color: #aaa;
outline: none;
}
div[contenteditable="true"] p::first-letter {
float: left;
padding-bottom: 2px;
padding-left: 40px;
margin-top: 0cm;
margin-bottom: -20px;
margin-left: -40px;
font-family: "SolberaImitationRemake";
font-size: 3.5cm;
line-height: 1em;
color: rgba(0, 0, 0, 0);
background-image: linear-gradient(-45deg, #322814, #998250, #322814);
-webkit-background-clip: text;
background-clip: text;
border: 0;
}
.properties-textarea {
width: 100%;
font-size: 12px;
font-weight: 400;
line-height: .7em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being resizable */
}
.description-textarea {
width: 100%;
height: auto;
font-size: .318cm;
font-weight: 400;
line-height: .9em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansSmallCapsRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
}
.red-integer-stat-textarea {
width: 20px;
height:13px;
font-size: .318cm;
font-weight: 400;
line-height: 1.2em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansSmallCapsRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
color: var(--HB_Color_HeaderText);
white-space: pre-line;
}
.integer-stat-textarea {
width: 20px;
height:13px;
font-size: .318cm;
font-weight: 400;
line-height: 1.2em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
white-space: pre-line;
}
.string-stat-textarea {
width: 200px;
height:13px;
font-size: .318cm;
font-weight: 400;
line-height: 1.2em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
white-space: pre-wrap;
}
.string-action-name-textarea {
width: 100%;
height:13px;
font-size: .318cm;
font-style: italic;
font-weight: bold;
line-height: 1.2em;
margin-bottom: 0;
font-style: italic;
box-sizing: border-box;
border: 0;
font-family: "ScalySansRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
}
.string-action-description-textarea {
width: 100%;
height:16px;
font-size: 14px;
font-weight: 400;
line-height: 16px;
margin-bottom: 0;
box-sizing: border-box;
border: 0;
font-family: "ScalySansRemake";
vertical-align: baseline;
margin: 0;
padding: 0;
overflow-wrap: break-word;
text-rendering: optimizeLegibility;
background: none;
resize: none; /* Prevents the textarea from being manually resizable */
overflow: hidden; /* Hide scrollbars */
}
.block.monster.frame.wide {
column-count: inherit;
min-height: 100px; /* Set an appropriate minimum height */
height: 859px; /* Allow height to expand automatically */
column-fill: auto;
overflow: hidden; /* Ensure content overflow is visible */
width: 100%; /* Ensure it takes the full width of the container */
}
.highlight-page {
outline: 2px dashed #2196F3; /* Blue dashed border */
background-color: rgba(33, 150, 243, 0.1); /* Light blue background */
}
.highlight-block {
border-bottom: 2px solid #2196F3; /* Blue solid border */
background-color: rgba(33, 150, 243, 0.1); /* Light blue background */
}
.highlight-block-top {
border-top: 2px solid #2196F3; /* Blue solid border at the top */
background-color: rgba(33, 150, 243, 0.1); /* Light blue background */
}
.name-textbox {
width: 50px;
font-size: 1.5em;
padding: 10px;
}
.stat-textbox {
width: 50px;
text-align: center;
font-size: 1em;
padding: 5px;
}
.trash-area {
position: fixed;
bottom: 20px;
right: 20px;
width: 100px;
height: 100px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
background-image: url('./closed-mimic-trashcan.png'); /* Adjust the path to your image file */
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.trash-area:hover {
background-image: url('./mimic_trashcan.png');
}
.trash-area.over {
color: white;
background-image: url('./mimic_trashcan.png'); /* Example image change */
}
</style>
<body>
<div class="grid-container">
<div class="block-container" id="blockContainer" >
<div class="page" id = "block-page" data-page-id="block-container">
<!-- Blocks will be wrapped in a page div and loaded here -->
</div>
</div>
<div class="page-container" id="pageContainer">
<div class="brewRenderer" id="brewRenderer">
<h1>Describe your creature</h1>
<textarea id="user-description" class="user-description-textarea"
hx-post="/update-stats" hx-trigger="change"
hx-target="#user-description" hx-swap="outerHTML"
title="As much or as little description as you want to provide. You can provide specific employees, inventory etc">A very standard gear store run by a fae potted plant named Gorgeous</textarea>
<button id="submitDescription">Submit</button>
<button id="parseHTML">Parse HTML</button>
<button id="resetButton">Reset</button>
<button onclick="printPageContainer()">Print to PDF</button>
<div class="pages" id="pages">
<div id="page-1" class="page" data-page-id="page-0">
<div class="columnWrapper">
<div class="block monster frame wide">
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="trash-area" id="trashArea"></div>
<!-- The Modal -->
<div id="imageModal" class="modal">
<span class="close">&times;</span>
<img class="modal-content" id="modalImage">
<div id="caption"></div>
</div>
<script>
// Waits for DOM content to be fully loaded and assigns critical elements to variables.
let initialPositions = [];
document.addEventListener("DOMContentLoaded", function() {
const blockContainer = document.getElementById('blockContainer');
let blockContainerPage = document.getElementById('block-page');
const pageContainer = document.getElementById('pages');
const trashArea = document.getElementById('trashArea');
const resetButton = document.getElementById('resetButton');
let currentPage = pageContainer.querySelector('.block.monster.frame.wide');
const modal = document.getElementById('imageModal');
const modalImg = document.getElementById('modalImage');
const captionText = document.getElementById('caption');
const closeModal = document.getElementsByClassName('close')[0];
const MAX_COLUMN_HEIGHT = 847;
if (!blockContainer || !pageContainer || !trashArea || !currentPage) {
console.error('Required elements are null');
return;
}
if (!modal) {
console.error('modal element not found');
return;
}
if (!modalImg) {
console.error('modalImg element not found');
return;
}
if (!captionText) {
console.error('captionText element not found');
return;
}
if (!closeModal) {
console.error('closeModal element not found');
return;
}
// Event delegation for image clicks
blockContainer.addEventListener('click', function(event) {
console.log('Click detected in blockContainer:', event.target);
if (event.target.tagName === 'IMG' && event.target.id.startsWith('generated-image-')) {
console.log('Image clicked for modal display. Image ID:', event.target.id);
modal.style.display = 'block';
modalImg.src = event.target.src;
captionText.innerHTML = event.target.alt;
} else {
console.log('Clicked element is not an image or does not match ID pattern.');
}
});
// Function to close the modal
closeModal.onclick = function() {
modal.style.display = "none";
}
// Function to close the modal when clicking outside of the modal content
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
}
}
document.getElementById('submitDescription').addEventListener('click', function() {
const userInput = document.getElementById('user-description').value;
// Clear the block container before inserting new blocks
blockContainerPage.innerHTML = '';
fetch('http://127.0.0.1:5000/process-description', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ user_input: userInput })
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
initialPositions.length = 0; // Clear the initialPositions array
insertHtmlBlocks(data.html_blocks);
const blocks = blockContainerPage.querySelectorAll('.block-item');
blocks.forEach(block => {
block.setAttribute('data-page-id', 'block-container');
block.setAttribute('draggable', true);
block.addEventListener('dragstart', handleDragStart);
block.addEventListener('dragend', handleDragEnd);
});
storeInitialPositions();
})
.catch((error) => {
console.error('Error:', error);
});
});
// Print Function
window.printPageContainer = function() {
var pageContainer = document.getElementById('pages');
if (pageContainer) {
var printContents = pageContainer.innerHTML;
var originalContents = document.body.innerHTML;
document.body.innerHTML = printContents;
window.print();
document.body.innerHTML = originalContents;
} else {
console.error('Element with ID "pageContainer" not found.');
}
}
// Store initial positions of the blocks
function storeInitialPositions() {
initialPositions = []; // Clear initialPositions before updating
const blocks = blockContainer.querySelectorAll('.block-item');
blocks.forEach((block, index) => {
const blockId = block.getAttribute('data-block-id');
if (!blockId) {
console.error(`Block at index ${index} is missing data-block-id`);
}
initialPositions.push({
id: blockId,
index: index
});
});
console.log('Initial positions:', initialPositions);
}
function insertHtmlBlocks(blocks) {
console.log('blockContainerPage = ', blockContainerPage)
console.log('List of blocks:', blocks);
const parser = new DOMParser();
blocks.forEach(blockHtml => {
console.log('Original blockHtml:', blockHtml);
// Parse the HTML string
const doc = parser.parseFromString(blockHtml, 'text/html');
console.log('Parsed document:', doc);
const block = doc.body.firstChild;
console.log('Parsed block:', block);
if (block) {
blockContainerPage.appendChild(block); // Append the parsed block to the container
console.log('Appended block:', block);
}
});
// console.log('Final state of blockContainer:', blockContainer.innerHTML);
initializeTextareaResizing();
}
storeInitialPositions();
function adjustTextareaHeight(el) {
if (el.scrollHeight > 16){
el.style.height = 'auto';
el.style.height = (el.scrollHeight) + 'px';
}
console.log('Original height:', el.style.height);
}
function initializeTextareaResizing() {
const classes = [
'description-textarea',
'user-description-textarea',
'heading-textarea',
'properties-textarea',
'string-stat-textarea',
'string-action-description-textarea',
];
classes.forEach(className => {
const textareas = document.querySelectorAll(`.${className}`);
console.log(`Textareas found for ${className}:`, textareas.length); // Debugging line
textareas.forEach(textarea => {
console.log('scrollHeight:', textarea.scrollHeight);
console.log('clientHeight:', textarea.clientHeight);
console.log('offsetHeight:', textarea.offsetHeight);
console.log('Computed line-height:', window.getComputedStyle(textarea).lineHeight);
// Adjust height on page load
adjustTextareaHeight(textarea);
// Adjust height on input
textarea.addEventListener('input', function() {
adjustTextareaHeight(textarea);
console.log('Input event triggered for:', textarea.id); // Debugging line
});
});
});
}
// Initial run on page load
initializeTextareaResizing();
async function extractBlocks() {
try {
if (blockContainerPage.children.length > 0) {
console.log('Blocks already loaded, skipping fetch');
return;
}
const response = await fetch('The_Mirage_Emporium.html');
if (!response.ok) {
throw new Error('Network response was not ok ' + response.statusText);
}
const text = await response.text();
const parser = new DOMParser();
const doc = parser.parseFromString(text, 'text/html');
const blocks = doc.querySelectorAll('[class^="Block_"]');
blocks.forEach((block, index) => {
const blockContent = block.innerHTML;
const blockItem = document.createElement('div');
blockItem.classList.add('block-item');
blockItem.innerHTML = blockContent;
const blockId = `block-${index}`;
blockItem.setAttribute('data-block-id', blockId);
const pageId = 'block-container';
blockItem.setAttribute('data-page-id', pageId);
blockItem.setAttribute('draggable', true);
blockItem.addEventListener('dragstart', handleDragStart);
blockItem.addEventListener('dragend', handleDragEnd);
console.log(`Loaded block with ID: ${blockId}`);
blockContainerPage.appendChild(blockItem);
});
storeInitialPositions();
} catch (error) {
console.error('Error fetching and parsing template.html:', error);
}
}
blockContainer.addEventListener('click', function(event) {
if (event.target && event.target.classList.contains('generate-image-button')) {
const blockId = event.target.getAttribute('data-block-id');
generateImage(blockId);
}
});
// Function to generate image
function generateImage(blockId) {
const sdPromptElement = document.getElementById(`user-storefront-prompt-${blockId}`);
const imageElement = document.getElementById(`generated-image-${blockId}`);
if (!sdPromptElement) {
console.error('Element with ID user-storefront-prompt not found');
return;
}
if (!imageElement) {
console.error('Element with ID generated-image not found');
return;
}
const sdPrompt = sdPromptElement.value;
fetch('http://127.0.0.1:5000/generate-image', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ sd_prompt: sdPrompt })
})
.then(response => response.json())
.then(data => {
console.log('Received data:', data);
imageElement.src = data.image_url;
imageElement.style.display = 'block';
// Log the image element's HTML structure
console.log('Updated imageElement HTML:', imageElement.outerHTML);
})
.catch((error) => {
console.error('Error:', error);
});
}
function handleDragStart(e) {
const target = e.target.closest('.block-item, .block-content');
if (!target) {
console.error('Drag started for an element without a valid target');
return;
}
const blockId = target.getAttribute('data-block-id');
const pageId = target.getAttribute('data-page-id');
if (!blockId) {
console.error('Drag started for an element without a data-block-id');
return;
}
if (!pageId) {
console.error('Drag started for an element without a data-page-id');
return;
}
const innerHTML = target.innerHTML;
e.dataTransfer.setData('block-id', blockId);
e.dataTransfer.setData('text/plain', innerHTML); // Store inner HTML
e.dataTransfer.setData('data-page-id', pageId); // Store original page ID
e.dataTransfer.effectAllowed = 'move';
target.style.opacity = '0.4';
// Create an invisible drag image
const dragImage = document.createElement('div');
dragImage.style.width = '1px';
dragImage.style.height = '1px';
dragImage.style.opacity = '0';
document.body.appendChild(dragImage);
e.dataTransfer.setDragImage(dragImage, 0, 0);
console.log(`Drag started for block ID: ${blockId} page ID: ${pageId}`);
}
function handleDragEnd(e) {
const target = e.target.closest('.block-item, .block-content');
if (target) {
target.style.opacity = '1'; // Reset the opacity
const blockId = target.getAttribute('data-block-id');
console.log(`Drag ended for block ID: ${blockId}`);
}
// Remove highlight classes from pages and blocks
document.querySelectorAll('.highlight-page').forEach(el => el.classList.remove('highlight-page'));
document.querySelectorAll('.highlight-block').forEach(el => el.classList.remove('highlight-block'));
document.querySelectorAll('.highlight-block-top').forEach(el => el.classList.remove('highlight-block-top'));
}
function handleDragOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
console.log('Drag over event');
const targetPage = e.target.closest('.page');
if (targetPage) {
targetPage.classList.add('highlight-page'); // Add highlight class for pages
}
const targetBlock = e.target.closest('.block-item, .block-content');
if (targetBlock) {
const bounding = targetBlock.getBoundingClientRect();
const offset = e.clientY - bounding.top;
if (offset > bounding.height / 2) {
targetBlock.classList.add('highlight-block');
targetBlock.classList.remove('highlight-block-top');
} else {
targetBlock.classList.add('highlight-block-top');
targetBlock.classList.remove('highlight-block');
}
}
}
function handleDrop(e) {
e.preventDefault();
const blockId = e.dataTransfer.getData('block-id');
const originalPageId = e.dataTransfer.getData('data-page-id');
const innerHTML = e.dataTransfer.getData('text/plain');
console.log(`Drop event for block ID: ${blockId} from page ID: ${originalPageId}`);
// Ensure we are not dropping into a textarea or another block
if (event.target.classList.contains('block-item', 'block-content') || event.target.tagName === 'TEXTAREA') {
console.log('Cannot drop block inside another block or textarea');
return;
}
if (blockId && originalPageId) {
const originalBlock = document.querySelector(`[data-block-id="${blockId}"]`);
const newPage = e.target.closest('.page');
console.log(`Over page ${newPage} from page ID: ${originalPageId}`);
const newPageId = newPage.getAttribute('data-page-id');
// Ensure the original block exists before proceeding
if (!originalBlock || !newPage) {
console.error(`Block with ID ${blockId} on page ${originalPageId} not found`);
return;
}
const newBlockContent = document.createElement('div');
newBlockContent.classList.add('block-content');
newBlockContent.innerHTML = originalBlock.innerHTML; // Transfer inner content only
// Add necessary attributes and event listeners
newBlockContent.setAttribute('data-block-id', blockId);
newBlockContent.setAttribute('data-page-id', newPageId);
console.log('newPageID:', newPageId);
newBlockContent.setAttribute('draggable', true);
newBlockContent.addEventListener('dragstart', handleDragStart);
newBlockContent.addEventListener('dragend', handleDragEnd);
const target = e.target.closest('.block-item, .block-content');
let targetColumn = 1;
if (target) {
const bounding = target.getBoundingClientRect();
const offset = e.clientY - bounding.top;
console.log('Drop target found:', target);
console.log('Bounding rectangle:', bounding);
console.log('Offset from top:', offset);
console.log('Target height:', bounding.height);
console.log('Insert before or after decision point (height / 2):', bounding.height / 2);
targetColumn = getColumnFromOffset(target, offset);
if (offset > bounding.height / 2) {
console.log('Inserting after the target');
target.parentNode.insertBefore(newBlockContent, target.nextSibling);
} else {
console.log('Inserting before the target');
target.parentNode.insertBefore(newBlockContent, target);
}
// Remove highlight borders
target.style['border-bottom'] = '';
target.style['border-top'] = '';
} else {
console.log('No valid drop target found, appending to the end');
newPage.querySelector('.block.monster.frame.wide').appendChild(newBlockContent);
}
// Remove the original block from the original container
originalBlock.parentNode.removeChild(originalBlock);
// Reset opacity of dragged element
newBlockContent.style.opacity = '1';
console.log(`Moved existing block with ID: ${blockId} to page ID: ${newPageId}`);
initializeTextareaResizing();
// Adjust layouts
if (originalPageId !== 'block-container') {
adjustPageLayout(originalPageId);
}
adjustPageLayout(newPageId, targetColumn);
} else {
console.log('No data transferred');
}
}
function getColumnFromOffset(block, offset) {
const page = block.closest('.page');
if (!page) return 1;
const columnWrapper = page.querySelector('.columnWrapper');
const columnWrapperRect = columnWrapper.getBoundingClientRect();
const relativeOffset = offset - columnWrapperRect.left; // Calculate the offset relative to the column wrapper
const columnWidth = columnWrapper.clientWidth / 2; // Assuming two columns
// Log details for debugging
console.log('Block offset:', offset);
console.log('Relative offset:', relativeOffset);
const columnNumber = Math.ceil(relativeOffset / columnWidth);
// Ensure the column number is within valid bounds (1 or 2)
const validColumnNumber = Math.min(Math.max(columnNumber, 1), 2);
return validColumnNumber;
}
// Function to get the height of a column by index
function getColumnHeights(pageElement) {
const columns = [0, 0]; // Assuming two columns for simplicity
const blocks = pageElement.querySelectorAll('.block-content');
blocks.forEach(block => {
const column = getColumnFromOffset(block, block.getBoundingClientRect().left);
columns[column - 1] += block.offsetHeight;
});
return columns;
}
function adjustPageLayout(pageId) {
const page = document.querySelector(`[data-page-id="${pageId}"]`);
if (!page) {
console.error(`Page with ID ${pageId} not found`);
return;
}
const columnHeights = getColumnHeights(page);
console.log(`Total height of columns in ${pageId}: ${columnHeights}`);
for (let i = 0; i < columnHeights.length; i++) {
if (columnHeights[i] > MAX_COLUMN_HEIGHT) {
console.log(`Column ${i + 1} in ${pageId} exceeds max height, total height: ${columnHeights[i]}px`);
handleColumnOverflow(page, i + 1);
}
}
}
let pageCounter = 1;
// Function to create new page
function createNewPage() {
const newPage = document.createElement('div');
newPage.classList.add('page');
newPage.setAttribute('data-page-id', `page-${pageCounter}`);
newPage.id = `page-${pageCounter}`;
const columnWrapper = document.createElement('div');
columnWrapper.classList.add('columnWrapper');
const newMonsterFrame = document.createElement('div');
newMonsterFrame.classList.add('block', 'monster', 'frame', 'wide');
columnWrapper.appendChild(newMonsterFrame);
newPage.appendChild(columnWrapper);
pageContainer.appendChild(newPage);
currentPage = newMonsterFrame;
console.log(`Created new page with ID: ${newPage.id}`);
// Add event listeners to the new currentPage
currentPage.addEventListener('dragover', handleDragOver);
currentPage.addEventListener('drop', handleDrop);
pageCounter++;
return newPage;
}
function handleColumnOverflow(page, targetColumn) {
console.log(`Handling overflow for page ID: ${page.getAttribute('data-page-id')} in column ${targetColumn}`);
const blocks = Array.from(page.querySelectorAll('.block-content'));
let columnHeights = [0, 0];
let overflowStartIndex = -1;
// Find the start index where overflow begins in the target column
blocks.forEach((block, index) => {
const column = getColumnFromOffset(block, block.getBoundingClientRect().left);
columnHeights[column - 1] += block.offsetHeight;
if (columnHeights[targetColumn - 1] > MAX_COLUMN_HEIGHT && overflowStartIndex === -1) {
overflowStartIndex = index;
}
});
// If no overflow, return early
if (overflowStartIndex === -1) {
return;
}
const overflowBlocks = blocks.slice(overflowStartIndex);
const overflowHeight = overflowBlocks.reduce((acc, block) => acc + block.offsetHeight, 0);
// If the target column is the first column, check if the second column has enough space
if (targetColumn === 1) {
const secondColumnAvailableHeight = MAX_COLUMN_HEIGHT - columnHeights[1];
if (overflowHeight <= secondColumnAvailableHeight) {
// Move the overflowing blocks to the second column within the same page
overflowBlocks.forEach(block => {
const blockWrapper = block.closest('.block.monster.frame.wide');
if (blockWrapper) {
blockWrapper.appendChild(block);
block.setAttribute('data-page-id', page.getAttribute('data-page-id'));
}
});
return;
}
}
// Get the next page if it exists
const nextPage = getNextPage(page);
if (nextPage) {
const nextPageBlocks = nextPage.querySelectorAll('.block-content, .block-item');
let nextPageColumnHeights = [0, 0];
nextPageBlocks.forEach(block => {
const column = getColumnFromOffset(block, block.getBoundingClientRect().left);
nextPageColumnHeights[column - 1] += block.offsetHeight;
});
// Check if there's enough space in the target column of the next page
if (nextPageColumnHeights[targetColumn - 1] + overflowHeight <= MAX_COLUMN_HEIGHT) {
const nextPageContainer = nextPage.querySelector('.block.monster.frame.wide');
overflowBlocks.forEach(block => {
nextPageContainer.appendChild(block);
block.setAttribute('data-page-id', nextPage.getAttribute('data-page-id'));
});
return;
}
// If the next page's second column has enough space for overflow from the first column
if (targetColumn === 1 && nextPageColumnHeights[1] + overflowHeight <= MAX_COLUMN_HEIGHT) {
const nextPageContainer = nextPage.querySelector('.block.monster.frame.wide');
overflowBlocks.forEach(block => {
nextPageContainer.appendChild(block);
block.setAttribute('data-page-id', nextPage.getAttribute('data-page-id'));
});
return;
}
}
// Otherwise, create a new page and move the overflowing blocks there
const newPage = createNewPage();
if (!newPage) {
console.error('Failed to create a new page');
return;
}
const newMonsterFrame = newPage.querySelector('.block.monster.frame.wide');
if (!newMonsterFrame) {
console.error('New monster frame not found in the new page');
return;
}
overflowBlocks.forEach(block => {
newMonsterFrame.appendChild(block);
});
console.log(`Moved overflowing blocks to new page with ID: ${newPage.getAttribute('data-page-id')}`);
}
// Utility function to get the next page element
function getNextPage(currentPage) {
const nextPageId = parseInt(currentPage.getAttribute('data-page-id').split('-')[1]) + 1;
return document.querySelector(`[data-page-id="page-${nextPageId}"]`);
}
function moveBlockToPage(block, newPageId) {
block.setAttribute('data-page-id', newPageId);
const newPage = document.querySelector(`[data-page-id="${newPageId}"] .block-container`);
newPage.appendChild(block);
}
// Handle the drop event on the trash area
function handleTrashDrop(e) {
e.preventDefault();
const innerHTML = e.dataTransfer.getData('text/plain');
const blockId = e.dataTransfer.getData('block-id');
console.log('Trash Drop event:', e);
console.log('Dragged block ID to trash:', blockId);
if (innerHTML && blockId) {
// Find the dragged element and remove it from the DOM
let draggedElement = document.querySelector(`[data-block-id="${blockId}"].block-content`);
if (!draggedElement) {
draggedElement = document.querySelector(`[data-block-id="${blockId}"].block-item`);
}
if (draggedElement && draggedElement.parentElement) {
draggedElement.parentElement.removeChild(draggedElement);
console.log(`Removed block with ID: ${blockId} from the page`);
}
// Check if the block already exists in the block-container and remove it if it does
let existingBlock = blockContainer.querySelector(`[data-block-id="${blockId}"].block-content`);
if (!existingBlock) {
existingBlock = blockContainer.querySelector(`[data-block-id="${blockId}"].block-item`);
}
if (existingBlock && existingBlock.parentElement) {
existingBlock.parentElement.removeChild(existingBlock);
console.log(`Removed duplicate block with ID: ${blockId} from block-container`);
}
// Create a new block-item to be placed back in the block-container
const newBlock = document.createElement('div');
newBlock.classList.add('block-item');
newBlock.setAttribute('data-block-id', blockId);
newBlock.setAttribute('data-page-id', 'block-container');
newBlock.innerHTML = innerHTML;
newBlock.setAttribute('draggable', true);
newBlock.addEventListener('dragstart', handleDragStart);
newBlock.addEventListener('dragend', handleDragEnd);
// Ensure the block is appended to the page wrapper inside blockContainer
let pageWrapper = blockContainer.querySelector('.page');
if (!pageWrapper) {
pageWrapper = document.createElement('div');
pageWrapper.classList.add('page');
pageWrapper.setAttribute('data-page-id', 'block-container');
blockContainer.appendChild(pageWrapper);
}
// Debugging output
console.log('Page wrapper:', pageWrapper);
console.log('New block:', newBlock);
// Find the original position to insert the new block
const originalPosition = initialPositions.find(pos => pos.id === blockId);
console.log('Original position:', originalPosition);
if (originalPosition) {
const blocks = pageWrapper.querySelectorAll('.block-item');
console.log('Blocks in pageWrapper:', blocks);
console.log('Inserting at position:', originalPosition.index);
if (originalPosition.index < blocks.length) {
const referenceNode = blocks[originalPosition.index];
if (referenceNode && referenceNode.parentNode === pageWrapper) {
console.log('Inserting before block at index:', originalPosition.index);
pageWrapper.insertBefore(newBlock, referenceNode);
console.log(`Moved block back to original position ${originalPosition.index} in block-container`);
} else {
console.warn('Reference node does not belong to pageWrapper, appending to the end');
pageWrapper.appendChild(newBlock);
console.log('Appended block to the end of block-container');
}
} else {
console.log('Appending block to the end of pageWrapper');
pageWrapper.appendChild(newBlock);
console.log('Appended block to the end of block-container');
}
} else {
console.log('Original position not found, appending block to the end of pageWrapper');
pageWrapper.appendChild(newBlock);
console.log('Appended block to the end of block-container');
}
console.log(`Restored block with ID: ${blockId}`);
} else {
console.log('No data transferred');
}
// Remove the "over" class and reset the background image
trashArea.classList.remove('over');
trashArea.style.backgroundImage = "url('./closed-mimic-trashcan.png')";
initializeTextareaResizing();
}
function handleTrashOver(e) {
e.preventDefault();
e.dataTransfer.dropEffect = 'move';
trashArea.classList.add('over');
trashArea.style.backgroundImage = "url('./mimic_trashcan.png')";
console.log('Trash over event');
}
function handleTrashLeave(e) {
trashArea.classList.remove('over');
trashArea.style.backgroundImage = "url('./closed-mimic-trashcan.png')";
console.log('Trash leave event');
}
function handleReset() {
console.log('Reset button clicked');
// Collect all blocks from all pages
const allBlocks = [];
const pages = document.querySelectorAll('.page');
pages.forEach(page => {
const blocksOnPage = page.querySelectorAll('[data-block-id]');
blocksOnPage.forEach(block => {
const blockId = block.getAttribute('data-block-id');
allBlocks.push({
id: blockId,
innerHTML: block.innerHTML
});
block.remove();
console.log(`Removed block with ID: ${blockId} from page ID: ${page.getAttribute('data-page-id')}`);
});
});
// Clear all pages
pages.forEach(page => page.remove());
// Clear blockContainer before reinserting blocks
blockContainer.innerHTML = '';
// Reinsert blocks back into the blockContainer in their original order
let pageWrapper = blockContainer.querySelector('.page');
if (!pageWrapper) {
pageWrapper = document.createElement('div');
pageWrapper.classList.add('page');
pageWrapper.setAttribute('id', 'block-page');
blockContainer.appendChild(pageWrapper);
}
// Reassign blockContainerPage to the newly created block-page element
blockContainerPage = document.getElementById('block-page');
initialPositions.forEach(pos => {
const blockData = allBlocks.find(block => block.id === pos.id);
if (blockData) {
const newBlock = document.createElement('div');
newBlock.classList.add('block-item');
newBlock.setAttribute('data-block-id', blockData.id);
newBlock.setAttribute('data-page-id', 'block-container');
newBlock.innerHTML = blockData.innerHTML;
newBlock.setAttribute('draggable', true);
newBlock.addEventListener('dragstart', handleDragStart);
newBlock.addEventListener('dragend', handleDragEnd);
const blocks = pageWrapper.querySelectorAll('.block-item');
if (pos.index < blocks.length) {
pageWrapper.insertBefore(newBlock, blocks[pos.index]);
console.log(`Moved block back to original position ${pos.index} in block-container`);
} else {
pageWrapper.appendChild(newBlock);
console.log('Appended block to the end of block-container');
}
}
});
createNewPage();
console.log('Reset complete, all blocks moved back to block-container');
initializeTextareaResizing();
}
pageContainer.addEventListener('dragover', handleDragOver);
pageContainer.addEventListener('drop', handleDrop);
trashArea.addEventListener('dragover', handleTrashOver);
trashArea.addEventListener('dragleave', handleTrashLeave);
trashArea.addEventListener('drop', handleTrashDrop);
resetButton.addEventListener('click', handleReset);
extractBlocks();
});
</script>
</body>
</html>