<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Ace Editor with Block Code Overlay</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ace/1.32.8/ace.js"></script>
<style>
body {
margin: 0;
padding: 20px;
font-family: Arial, sans-serif;
background: #f5f5f5;
}
#editor {
width: 100%;
height: 500px;
border: 1px solid #ccc;
border-radius: 4px;
}
.controls {
margin-bottom: 10px;
}
button {
padding: 8px 16px;
margin-right: 10px;
background: #007acc;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
}
button:hover {
background: #005a9e;
}
.blocks-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
z-index: 1000;
display: none;
}
.blocks-container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 90%;
height: 85%;
background: #2c3e50;
border-radius: 10px;
padding: 20px;
overflow: auto;
}
.blocks-header {
color: white;
margin-bottom: 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.close-btn {
background: #e74c3c;
color: white;
border: none;
padding: 8px 12px;
border-radius: 4px;
cursor: pointer;
}
.blocks-canvas {
background: #34495e;
border-radius: 8px;
padding: 20px;
min-height: 600px;
position: relative;
}
.code-block {
background: #3498db;
border-radius: 8px;
padding: 15px;
margin: 10px;
color: white;
font-family: 'Courier New', monospace;
position: relative;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
cursor: move;
border: 2px solid transparent;
display: inline-block;
min-width: 200px;
}
.code-block:hover {
border-color: #2980b9;
transform: translateY(-2px);
}
.block-function {
background: #e74c3c;
}
.block-if {
background: #f39c12;
}
.block-loop {
background: #27ae60;
}
.block-variable {
background: #9b59b6;
}
.block-container {
border: 2px dashed rgba(255, 255, 255, 0.3);
border-radius: 8px;
padding: 10px;
margin: 5px;
background: rgba(0, 0, 0, 0.1);
}
.block-nested {
margin: 8px;
position: relative;
}
.block-label {
font-size: 12px;
background: rgba(0, 0, 0, 0.2);
padding: 2px 6px;
border-radius: 3px;
margin-bottom: 8px;
display: inline-block;
}
.block-content {
white-space: pre-wrap;
font-size: 14px;
line-height: 1.4;
}
.connector {
position: absolute;
width: 20px;
height: 20px;
background: rgba(255, 255, 255, 0.8);
border-radius: 50%;
top: 50%;
transform: translateY(-50%);
}
.connector-in {
left: -10px;
}
.connector-out {
right: -10px;
}
.block-close {
background: #7f8c8d;
font-size: 12px;
padding: 8px 12px;
margin: 5px;
border-radius: 4px;
color: white;
font-family: 'Courier New', monospace;
}
</style>
</head>
<body>
<h1>Ace Editor with Block Code View</h1>
<div class="controls">
<button id="blocksBtn">Open Blocks</button>
<button id="runBtn">Run Code</button>
</div>
<div id="editor">function calculateSum(a, b) {
console.log("Starting calculation");
if (a > 0 && b > 0) {
console.log("Both numbers are positive");
let result = a + b;
for (let i = 0; i < 3; i++) {
console.log("Processing step: " + i);
result = result + i;
}
return result;
} else {
console.log("One or both numbers are not positive");
return 0;
}
}
calculateSum(5, 10);</div>
<!-- Blocks Overlay -->
<div class="blocks-overlay" id="blocksOverlay">
<div class="blocks-container">
<div class="blocks-header">
<h2>Block Code View</h2>
<button class="close-btn" id="closeBlocks">Close</button>
</div>
<div class="blocks-canvas" id="blocksCanvas">
<!-- Blocks will be generated here -->
</div>
</div>
</div>
<script>
var editor = ace.edit("editor");
editor.setTheme("ace/theme/monokai");
editor.session.setMode("ace/mode/javascript");
function parseCodeToBlocks() {
const code = editor.getValue();
const lines = code.split('\n');
const canvas = document.getElementById('blocksCanvas');
canvas.innerHTML = '';
let blockStack = [canvas]; // Stack to keep track of containers
let openBlockStack = []; // Stack to track open blocks waiting for their closing brace
let lastIndentLevel = 0;
for (let i = 0; i < lines.length; i++) {
const line = lines[i].trim();
if (line === '' || line.startsWith('//')) continue;
// Calculate indentation level
const indent = lines[i].length - lines[i].trimLeft().length;
const currentIndentLevel = Math.floor(indent / 4);
// Handle closing braces
if (line === '}' || line.includes('}')) {
// Add the closing brace to the most recent open block
if (openBlockStack.length > 0) {
const openBlock = openBlockStack.pop();
const closeBlock = document.createElement('div');
closeBlock.className = 'block-close';
closeBlock.innerHTML = `<div class="block-content">${line}</div>`;
openBlock.appendChild(closeBlock);
}
// Pop the container since we're closing it
if (blockStack.length > 1) {
blockStack.pop();
}
lastIndentLevel = Math.max(0, currentIndentLevel);
continue;
}
// Handle indentation changes for opening blocks
if (currentIndentLevel > lastIndentLevel) {
// Going deeper - find the last created block and add container to it
if (openBlockStack.length > 0) {
const parentBlock = openBlockStack[openBlockStack.length - 1];
const container = document.createElement('div');
container.className = 'block-container';
parentBlock.appendChild(container);
blockStack.push(container);
}
} else if (currentIndentLevel < lastIndentLevel) {
// Going back up - pop containers from stack
const levelDiff = lastIndentLevel - currentIndentLevel;
for (let j = 0; j < levelDiff; j++) {
if (blockStack.length > 1) {
blockStack.pop();
}
if (openBlockStack.length > 0) {
openBlockStack.pop();
}
}
}
lastIndentLevel = currentIndentLevel;
// Determine block type and content
let blockType = 'code-block';
let blockLabel = 'Code';
let isBlockOpener = false;
if (line.includes('function')) {
blockType += ' block-function';
blockLabel = 'Function';
isBlockOpener = true;
} else if (line.includes('if') || line.includes('else')) {
blockType += ' block-if';
blockLabel = 'Condition';
isBlockOpener = true;
} else if (line.includes('for') || line.includes('while')) {
blockType += ' block-loop';
blockLabel = 'Loop';
isBlockOpener = true;
} else if (line.includes('let') || line.includes('const') || line.includes('var')) {
blockType += ' block-variable';
blockLabel = 'Variable';
} else if (line.includes('try') || line.includes('catch')) {
blockType += ' block-if';
blockLabel = 'Try/Catch';
isBlockOpener = true;
}
// Create block element
const block = document.createElement('div');
block.className = blockType;
block.innerHTML = `
<div class="connector connector-in"></div>
<div class="block-label">${blockLabel}</div>
<div class="block-content">${line}</div>
<div class="connector connector-out"></div>
`;
// Add to current container
const currentContainer = blockStack[blockStack.length - 1];
currentContainer.appendChild(block);
// If this block opens a new scope, track it for its closing brace
if (isBlockOpener && line.includes('{')) {
openBlockStack.push(block);
}
// Make blocks draggable
makeDraggable(block);
}
}
function makeDraggable(element) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
element.onmousedown = dragMouseDown;
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.position = "absolute";
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
}
// Event listeners
document.getElementById('blocksBtn').addEventListener('click', function() {
parseCodeToBlocks();
document.getElementById('blocksOverlay').style.display = 'block';
});
document.getElementById('closeBlocks').addEventListener('click', function() {
document.getElementById('blocksOverlay').style.display = 'none';
});
document.getElementById('runBtn').addEventListener('click', function() {
try {
const code = editor.getValue();
eval(code);
} catch (error) {
console.error('Error running code:', error);
}
});
// Close overlay when clicking outside
document.getElementById('blocksOverlay').addEventListener('click', function(e) {
if (e.target === this) {
this.style.display = 'none';
}
});
</script>
</body>
</html>