-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathmain.html
More file actions
81 lines (78 loc) · 15.4 KB
/
main.html
File metadata and controls
81 lines (78 loc) · 15.4 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
<!DOCTYPE html>
<!-- The `lang` and `dir` attributes will be set dynamically by JavaScript -->
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Online Code Editor</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-okaidia.min.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markup.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js" defer></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-go.min.js" defer></script>
<style>
:root{--background-color:#0d0221;--editor-bg:#282c34;--neon-blue:#00BFFF;--neon-pink:#FF00FF;--neon-purple:#9D00FF;--status-bar-bg:#1d1135;--line-numbers-bg:#21252b;--line-numbers-color:#6c757d;--indent-guide-color:rgba(108,117,125,0.3);--glow-strength:0 0 8px;--caret-color:var(--neon-blue)}
body{background-color:var(--background-color);font-family:'Fira Code',monospace;margin:0;padding:2rem;display:flex;flex-direction:column;justify-content:flex-start;align-items:center;min-height:100vh;overflow-y:auto}
.main-container{width:85vw;max-width:1400px}
h1{text-align:center;color:var(--neon-blue);text-shadow:var(--glow-strength) var(--neon-blue);margin-bottom:1rem}
.controls-container{display:flex;justify-content:space-between;align-items:center;margin-bottom:1rem;gap:15px;flex-wrap:wrap}
.file-controls{display:flex;gap:10px;flex-wrap:wrap;}
.edit-controls{display:flex;gap:10px;flex-wrap:wrap;}
.file-controls input[type=text],.controls-container button,.controls-container label{background-color:var(--status-bar-bg);border:1px solid var(--neon-purple);border-radius:5px;padding:8px 12px;font-family:inherit;color:#fff;outline:0;cursor:pointer;display:flex;align-items:center;justify-content:center;position:relative;white-space:nowrap;}
.file-controls button,.file-controls label{background-color:var(--neon-purple)}
.edit-controls button svg{width:18px;height:18px;fill:white;transition:fill.2s}
.edit-controls button:hover:not(:disabled){background-color:#3a2c5c}
.edit-controls button:disabled{opacity:.5;cursor:not-allowed}
.editor-wrapper{border:2px solid var(--neon-blue);border-radius:8px;box-shadow:inset 0 0 15px rgba(0,191,255,.4),var(--glow-strength) var(--neon-blue);overflow:hidden;background-color:var(--editor-bg);direction:ltr}
.editor-container{display:flex;height:65vh}
.line-numbers{background-color:var(--line-numbers-bg);color:var(--line-numbers-color);padding:1rem.5rem 1rem 1rem;font-size:16px;line-height:1.6;text-align:right;user-select:none;overflow:hidden}
html[dir=rtl].line-numbers{padding:1rem 1rem 1rem.5rem;}
.line-numbers>div{white-space:pre}
.code-area{position:relative;flex-grow:1;overflow:hidden}
.prism-live{background-image:repeating-linear-gradient(to right,var(--indent-guide-color) 0,var(--indent-guide-color) 1px,transparent 1px,transparent 2ch);background-size:2ch 100%}
#code-input{z-index:1;color:transparent;caret-color:var(--caret-color);background:0 0}
#code-input,.prism-live{margin:0;padding:1rem!important;border:none;outline:none;background:0 0;font-family:'Fira Code',monospace!important;font-size:16px!important;line-height:1.6!important;white-space:pre;overflow:auto;position:absolute;top:0;left:0;width:100%;height:100%;box-sizing:border-box}
.status-bar{display:flex;flex-wrap:wrap;justify-content:space-between;background-color:var(--status-bar-bg);padding:5px 15px;font-size:14px;color:#aaa;border-top:1px solid var(--neon-blue)}
.status-bar-left{display:flex;gap:20px;flex-wrap:wrap}
footer{margin-top:3rem;width:85vw;max-width:1200px;text-align:center;border-top:1px solid var(--neon-purple);padding-top:2rem;margin-bottom:2rem}
.footer-content{display:flex;flex-direction:column;align-items:center;gap:1.5rem}
.footer-content p{color:#aaa;font-size:14px;line-height:1.7;margin:0}
.footer-content a{background-color:var(--neon-purple);border:1px solid var(--neon-purple);border-radius:5px;padding:10px 15px;font-family:inherit;color:#fff;cursor:pointer;text-decoration:none;transition:all.2s ease}
#lang-toggle-btn{position:absolute;top:1.5rem;right:2rem;background:0 0;border:none;cursor:pointer;padding:5px}
#lang-toggle-btn svg{width:28px;height:28px;fill:var(--neon-purple);transition:fill.2s ease}
#lang-toggle-btn:hover svg{fill:var(--neon-pink)}
html[dir=rtl] #lang-toggle-btn{right:auto;left:2rem}
</style>
</head>
<body>
<button id="lang-toggle-btn" title="Switch Language"><svg viewBox="0 0 24 24"><path d="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.8,8H4.8C5.3,9.61 6.22,11.11 7.4,12.34L4.84,14.9L6.26,16.31L8.83,13.74L11.41,16.31L12.83,14.89L12.87,15.07M18.6,12.5L17.18,11.08L15.77,12.5L14.35,11.08L12.93,12.5L11.5,11.07L10.08,12.5L8.67,11.08L7.25,12.5L5.83,11.07L4.41,12.5L3,11.07L1.59,12.5L3,13.91L4.41,12.5L5.83,13.91L7.25,12.5L8.67,13.91L10.08,12.5L11.5,13.91L12.93,12.5L14.35,13.91L15.77,12.5L17.18,13.91L18.6,12.5Z"/></svg></button>
<div class="main-container"><h1 data-translate-key="mainTitle">Online Code Editor</h1><div class="controls-container"><div class="file-controls"><input type="text" id="filename-input" value="script.js"><button id="download-btn" data-translate-key="download">Download</button><label for="upload-input" data-translate-key="upload">Upload</label><input type="file" id="upload-input" hidden></div><div class="edit-controls"><button id="undo-btn"><svg viewBox="0 0 24 24"><path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z"></path></svg></button><button id="redo-btn"><svg viewBox="0 0 24 24"><path d="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.96 0 3.73.72 5.12 1.88L13 16h9V7l-3.6 3.6z"></path></svg></button><button id="copy-btn" data-translate-key="copy">Copy</button><button id="paste-btn" data-translate-key="paste">Paste</button></div></div><div class="editor-wrapper" id="editor-wrapper"><div class="editor-container"><div class="line-numbers"><div>1</div></div><div class="code-area"><textarea id="code-input" spellcheck="false"></textarea><pre class="prism-live language-js"><code id="highlighting-content"></code></pre></div></div><div class="status-bar"><div class="status-bar-left"><span data-translate-key="size">Size</span>: <span id="size-display-value">0 Bytes</span> <span data-translate-key="lines">Lines</span>: <span id="line-count-value">1</span> <span data-translate-key="chars">Chars</span>: <span id="char-count-value">0</span> <span data-translate-key="words">Words</span>: <span id="word-count-value">0</span></div><div><span id="language-display">JavaScript</span></div></div></div></div>
<footer><div class="footer-content"><p data-translate-key="footerText">...</p><a href="main.html" download="main.html" data-translate-key="downloadOffline">Download Offline Version</a></div></footer>
<script>
const translations={en:{mainTitle:"Online Code Editor",download:"Download",upload:"Upload",copy:"Copy",paste:"Paste",placeholder:"Drag & drop, upload, or start typing...",size:"Size",lines:"Lines",chars:"Chars",words:"Words",footerText:"This site was fully developed and created by AI, and all thanks to it. I apologize for not designing it myself as I am still a beginner training on CSS and HTML structure. I hope this editor will be a small help for beginners like me who cannot currently download a professional code editor. Of course, this is just a simple editor and not for full reliance. You can download the HTML file of the site from the button next to this text to run the site without internet.",downloadOffline:"Download Offline Version"},ar:{mainTitle:"محرر الأكواد أونلاين",download:"تحميل",upload:"رفع",copy:"نسخ",paste:"لصق",placeholder:"اسحب ملفًا، ارفع ملفًا، أو ابدأ الكتابة...",size:"الحجم",lines:"السطور",chars:"الحروف",words:"الكلمات",footerText:"هذا الموقع طور وصنع بالكامل بواسطة الذكاء الاصطناعي وكل الشكر له. أعتذر لأني لم أصممه بنفسي لأني لازلت مبتدء جدا وأتدرب على التصميم بلغة الـ CSS وهيكلة HTML. فأتمنى أن يكون هذا المحرر عون بسيط للمبتدئين مثلي والذين لا يستطيعون حاليا تحميل محرر أكواد إحترافي. طبعا هذا محرر بسيط فقط وليس للإعتماد الكامل. يمكنكم تحميل ملف HTML الخاص بالموقع من الزر بجانب هذا النص لتشغيل الموقع بدون إنترنت.",downloadOffline:"تحميل النسخة الأوفلاين"}};
const codeInput=document.getElementById("code-input"),langToggleBtn=document.getElementById("lang-toggle-btn"),uploadInput=document.getElementById("upload-input"),highlightingContent=document.getElementById("highlighting-content"),lineNumbersEl=document.querySelector(".line-numbers"),filenameInput=document.getElementById("filename-input"),editorWrapper=document.getElementById("editor-wrapper"),sizeDisplayValue=document.getElementById("size-display-value"),lineCountValue=document.getElementById("line-count-value"),charCountValue=document.getElementById("char-count-value"),wordCountValue=document.getElementById("word-count-value"),languageDisplayEl=document.getElementById("language-display");
const undoBtn=document.getElementById("undo-btn"),redoBtn=document.getElementById("redo-btn"),copyBtn=document.getElementById("copy-btn"),pasteBtn=document.getElementById("paste-btn");
// --- السطر المصحح هنا ---
const preElement = document.querySelector(".prism-live");
const pairChars={"(":")","[":"]","{":"}","\"":"\"","'":"'","`":"`"},history=[codeInput.value];
let historyIndex=0,lastValue=codeInput.value,currentLang=localStorage.getItem("editorLang")||"en";
function applyLanguage(e){const t=translations[e];document.querySelectorAll("[data-translate-key]").forEach(s=>{const n=s.dataset.translateKey;t[n]&&("TEXTAREA"===s.tagName?s.placeholder=t[n]:s.textContent=t[n])}),document.documentElement.lang=e,document.documentElement.dir="ar"===e?"rtl":"ltr",document.title=t.mainTitle}
function handleKeyDown(e){if("Tab"===e.key){e.preventDefault();const t=this.selectionStart;this.value=this.value.substring(0,t)+" "+this.value.substring(t),this.selectionStart=this.selectionEnd=t+2,updateAndSync(),recordHistory()}else if("Backspace"===e.key&&this.selectionStart===this.selectionEnd){const t=this.value.substring(this.selectionStart-1,this.selectionStart),s=this.value.substring(this.selectionStart,this.selectionStart+1);pairChars[t]===s&&(e.preventDefault(),this.value=this.value.substring(0,this.selectionStart-1)+this.value.substring(this.selectionStart+1),this.selectionStart=this.selectionEnd=this.selectionStart-1,updateAndSync(),recordHistory())}}
function handleAutoClose(e){const t=e.target.value,s=e.target.selectionStart;if(t.length>lastValue.length&&1===t.length-lastValue.length){const n=t.substring(s-1,s);pairChars[n]&&(e.target.value=t.substring(0,s)+pairChars[n]+t.substring(s),e.target.selectionStart=e.target.selectionEnd=s)}updateAndSync(),t!==history[historyIndex]&&recordHistory()}
function updateAndSync(){const e=codeInput.value;highlightingContent.textContent=e,Prism.highlightElement(highlightingContent),updateStatusAndLineNumbers(e),lastValue=e}
function updateStatusAndLineNumbers(e){const t=e.split("\n").length,s=new Blob([e]).size;sizeDisplayValue.textContent=formatBytes(s),lineCountValue.textContent=t,charCountValue.textContent=e.length;const n=e.trim().split(/\s+/).filter(Boolean);wordCountValue.textContent=e.length>0?n.length:0;const l=Array.from({length:t},(_,e)=>`<div>${e+1}</div>`).join("");lineNumbersEl.innerHTML=l}
function syncScroll(){const e=codeInput.scrollTop;preElement.scrollTop=e,lineNumbersEl.scrollTop=e,preElement.scrollLeft=codeInput.scrollLeft}
function formatBytes(e,t=2){if(0===e)return"0 Bytes";const s=1024,n=Math.floor(Math.log(e)/Math.log(s));return parseFloat((e/Math.pow(s,n)).toFixed(t<0?0:t))+" "+["Bytes","KB","MB","GB","TB"][n]}
function recordHistory(){history.splice(historyIndex+1),history.push(codeInput.value),historyIndex=history.length-1,updateUndoRedoButtons()}
function undo(){historyIndex>0&&(historyIndex--,codeInput.value=history[historyIndex],updateAndSync(),updateUndoRedoButtons())}
function redo(){historyIndex<history.length-1&&(historyIndex++,codeInput.value=history[historyIndex],updateAndSync(),updateUndoRedoButtons())}
function updateUndoRedoButtons(){undoBtn.disabled=historyIndex<=0,redoBtn.disabled=historyIndex>=history.length-1}
function setLanguageHighlight(){const e=filenameInput.value,t=e.split(".").pop();let s="js",n="JavaScript";switch(t){case"py":s="python",n="Python";break;case"go":s="go",n="Go";break;case"html":s="markup",n="HTML";break;case"css":s="css",n="CSS"}preElement.className=`prism-live language-${s}`,languageDisplayEl.textContent=n,updateAndSync()}
function handleFileUpload(e){const t=e.target.files[0];if(t){filenameInput.value=t.name;const s=new FileReader;s.onload=e=>{codeInput.value=e.target.result,setLanguageHighlight(),recordHistory()},s.readAsText(t)}}
langToggleBtn.addEventListener("click",()=>{currentLang="en"===currentLang?"ar":"en",localStorage.setItem("editorLang",currentLang),applyLanguage(currentLang)}),editorWrapper.addEventListener("dragover",e=>{e.preventDefault(),editorWrapper.classList.add("drag-over")}),editorWrapper.addEventListener("dragleave",()=>editorWrapper.classList.remove("drag-over")),editorWrapper.addEventListener("drop",e=>{e.preventDefault(),editorWrapper.classList.remove("drag-over");const t=e.dataTransfer.files[0];if(t){filenameInput.value=t.name;const s=new FileReader;s.onload=e=>{codeInput.value=e.target.result,setLanguageHighlight(),recordHistory()},s.readAsText(t)}}),codeInput.addEventListener("input",handleAutoClose),codeInput.addEventListener("keydown",handleKeyDown),codeInput.addEventListener("scroll",syncScroll),filenameInput.addEventListener("input",setLanguageHighlight),uploadInput.addEventListener("change",handleFileUpload),document.getElementById("download-btn").addEventListener("click",()=>{const e=new Blob([codeInput.value],{type:"text/plain;charset=utf-8"}),t=URL.createObjectURL(e),s=document.createElement("a");s.download=filenameInput.value||"download.txt",s.href=t,s.click(),URL.revokeObjectURL(t)}),undoBtn.addEventListener("click",undo),redoBtn.addEventListener("click",redo),copyBtn.addEventListener("click",()=>navigator.clipboard.writeText(codeInput.value)),pasteBtn.addEventListener("click",()=>{navigator.clipboard.readText().then(e=>{document.execCommand("insertText",!1,e),updateAndSync(),recordHistory()})});
applyLanguage(currentLang),setLanguageHighlight(),updateUndoRedoButtons();
</script>
</body>
</html>