diff --git a/code.js b/code.js new file mode 100644 index 0000000..f103977 --- /dev/null +++ b/code.js @@ -0,0 +1,75 @@ +"use strict"; + +import JavaScript from "./source/languages/JavaScript.js"; + +export default class Code extends HTMLElement{ + //// Native Form Behaviour + // Identify the element as a form-associated custom element + static formAssociated = true; + + static #template = document.createElement("template"); + + static { + Code.#template.innerHTML = ` +
+        
+        
+      
+ `; + } + + constructor(){ + super(); + + this.shadow = this.attachShadow({mode: 'closed'}); + this.RAW = this.textContent; + + // If language is not defined, then exit + if(!!this.hasAttribute("lang") === false) return; + + CSS: { + const style = document.createElement('style'); + style.textContent = ` + pre{ + background-color: hsla(230, 13%, 9%, 1); + width: 100%; + height: auto; + border-radius: 5px; + box-shadow: 0px 2px 5px 1px rgba(0, 0, 0, 0.5); + + & > code{ + color: white; + width: 100%; + height: 100%; + padding: 5px; + line-height: 150%; + + } + } + `; + this.shadow.appendChild(style); + } + + // Clone And Append Template + this.shadow.appendChild(Code.#template.content.cloneNode(true)); + + this.codeElement = this.shadow.querySelector("pre > code"); + + switch(this.lang){ + case "JavaScript": + this.codeElement.innerHTML = JavaScript.handle(this.RAW); + break; + case "HTML": + this.codeElement.innerHTML = this.RAW; + break; + default: + console.log("No match"); + } + } + +}; + +window.customElements.define('x-code', Code); + +// Make Code Usable W/O Importing It +window.Code = Code; diff --git a/example.html b/example.html new file mode 100644 index 0000000..ac49ff4 --- /dev/null +++ b/example.html @@ -0,0 +1,32 @@ + + + + + + X-Code + + + + + + // main.js + console.log("Hello world!"); + function fun(){ + return "Hello from fun!"; + } + + let a = 34 + 2; // asd + + for(let a = 0; a < b.length; a++){ + + } + + fun(23); + + + + diff --git a/source/languages/JavaScript.js b/source/languages/JavaScript.js new file mode 100644 index 0000000..f4881d5 --- /dev/null +++ b/source/languages/JavaScript.js @@ -0,0 +1,171 @@ +export default class JavaScript{ + static #tokens = null; + + static handle(code){ + JavaScript.#tokenize(code); + return JavaScript.#renderHighlightedCode(); + } + + static #tokenize(code){ + const keywords = ['if', 'else', 'while', 'for', 'function', 'var', 'let', 'const', 'return']; + const operators = ['+', '-', '*', '/', '=', '==', '!=', '>', '<', '>=', '<=', '(', ')', '{', '}']; + + const tokens = []; + let current = 0; + + while(current < code.length){ + let char = code[current]; + + // Whitespace + if([' ', '\s', '\t'].includes(char)){ + tokens.push({ type: 'whitespace', value: ' ' }); + + current++; + continue; + } + + // Newline + if(char == '\n'){ + tokens.push({ type: 'newline', value: '\n' }); + + current++; + continue; + } + + // Comments + if(char === '/' && code[current + 1] === '/'){ + let comment = '/'; + + while(code[++current] !== '\n' && current < code.length) comment += code[current]; + + comment += '\n' + + tokens.push({ type: 'comment', value: comment }); + + current++; + continue; + } + + // Dot + if(char === '.'){ + tokens.push({ type: 'dot', value: '.' }); + + current++; + continue; + } + + // Semicolon + if(char === ';'){ + tokens.push({ type: 'semicolon', value: ';' }); + + current++; + continue; + } + + // Operators + if(operators.includes(char)){ + let operator = char; + + while(operators.includes(operator + code[current + 1])) operator += code[++current]; + + tokens.push({ type: 'operator', value: operator }); + + current++; + continue; + } + + // Keywords and Identifiers + if(/[a-zA-Z_]/.test(char)){ + let identifier = char; + + while(/[a-zA-Z0-9_]/.test(code[current + 1])) identifier += code[++current]; + + if(keywords.includes(identifier)) tokens.push({ type: 'keyword', value: identifier }); + else tokens.push({ type: 'identifier', value: identifier }); + + current++; + continue; + } + + // Handle numbers + if(/[0-9]/.test(char)){ + let number = char; + + while(/[0-9.]/.test(code[current + 1])) number += code[++current]; + + tokens.push({ type: 'number', value: parseFloat(number) }); + + current++; + continue; + } + + // Handle strings + if(char === '"' || char === "'"){ + let string = `"`; + let quote = char; + + while(code[++current] !== quote) string += code[current]; + + string += `"`; + + tokens.push({ type: 'string', value: string }); + + current++; + continue; + } + + // Handle unrecognized characters + tokens.push({ type: 'unknown', value: char }); + + current++; + } + + JavaScript.#tokens = tokens; + } + + static #renderHighlightedCode(){ + let html = ''; + + for(const token of JavaScript.#tokens){ + let tokenHtml = ''; + + switch(token.type){ + case 'comment': + tokenHtml = `${token.value}`; + break; + + case 'dot': + case 'semicolon': + tokenHtml = `${token.value}`; + break; + + case 'operator': + tokenHtml = `${token.value}`; + break; + + case 'keyword': + tokenHtml = `${token.value}`; + break; + + case 'identifier': + tokenHtml = `${token.value}`; + break; + + case 'number': + tokenHtml = `${token.value}`; + break; + + case 'string': + tokenHtml = `${token.value}`; + break; + + default: + tokenHtml = `${token.value}`; + } + + html += tokenHtml; + } + + return html; + } +};