Skip to content

Commit 3cc842f

Browse files
committed
Upload first version
1 parent cb8c627 commit 3cc842f

File tree

4 files changed

+362
-0
lines changed

4 files changed

+362
-0
lines changed

code-input.css

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/* Code-Input Compability */
2+
/* By WebCoder49 */
3+
/* First Published on CSS-Tricks.com */
4+
5+
6+
code-input {
7+
/* Allow other elems to be inside */
8+
position: relative;
9+
top: 0;
10+
left: 0;
11+
display: block;
12+
13+
/* Normal inline styles */
14+
padding: 8px;
15+
margin: 8px;
16+
width: calc(100% - 16px);
17+
height: 250px;
18+
19+
font-size: 15pt;
20+
font-family: monospace;
21+
line-height: 20pt;
22+
tab-size: 2;
23+
caret-color: darkgrey;
24+
white-space: pre;
25+
}
26+
27+
code-input textarea, code-input:not(.code-input_pre-element-styled) pre code, code-input.code-input_pre-element-styled pre {
28+
/* Both elements need the same text and space styling so they are directly on top of each other */
29+
margin: 0px!important;
30+
padding: var(--padding, 16px)!important;
31+
border: 0;
32+
width: calc(100% - (var(--padding, 16px)*2))!important;
33+
height: calc(100% - (var(--padding, 16px)*2))!important;
34+
}
35+
code-input:not(.code-input_pre-element-styled) pre, code-input.code-input_pre-element-styled pre code {
36+
margin: 0!important;
37+
border: 0!important;
38+
padding: 0!important;
39+
overflow: auto!important;
40+
width: 100%!important;
41+
height: 100%!important;
42+
}
43+
code-input textarea, code-input pre, code-input pre * {
44+
/* Also add text styles to highlighing tokens */
45+
font-size: inherit!important;
46+
font-family: inherit!important;
47+
line-height: inherit!important;
48+
tab-size: inherit!important;
49+
}
50+
51+
52+
code-input textarea, code-input pre {
53+
/* In the same place */
54+
position: absolute;
55+
top: 0;
56+
left: 0;
57+
}
58+
59+
60+
/* Move the textarea in front of the result */
61+
62+
code-input textarea {
63+
z-index: 1;
64+
}
65+
code-input pre {
66+
z-index: 0;
67+
}
68+
69+
70+
/* Make textarea almost completely transparent */
71+
72+
code-input textarea {
73+
color: transparent;
74+
background: transparent;
75+
caret-color: inherit!important; /* Or choose your favourite color */
76+
}
77+
78+
/* Can be scrolled */
79+
code-input textarea, code-input pre {
80+
overflow: auto!important;
81+
82+
white-space: inherit;
83+
word-spacing: normal;
84+
word-break: normal;
85+
word-wrap: normal;
86+
}
87+
88+
/* No resize on textarea; stop outline */
89+
code-input textarea {
90+
resize: none;
91+
outline: none!important;
92+
}

code-input.js

Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
// CodeInput
2+
// by WebCoder49
3+
// Based on a CSS-Tricks Post
4+
5+
var codeInput = {
6+
usedTemplates: {
7+
},
8+
defaultTemplate: undefined,
9+
CodeInput: class extends HTMLElement { // Create code input element
10+
constructor() {
11+
super(); // Element
12+
}
13+
14+
/* Syntax-highlighting functions */
15+
update(text) {
16+
17+
if(this.value != text) this.value = text; // Change value attribute if necessary.
18+
if(this.querySelector("textarea").value != text) this.querySelector("textarea").value = text;
19+
20+
21+
let result_element = this.querySelector("pre code");
22+
23+
// Handle final newlines (see article)
24+
if (text[text.length - 1] == "\n") {
25+
text += " ";
26+
}
27+
// Update code
28+
result_element.innerHTML = this.escape_html(text);
29+
// Syntax Highlight
30+
if(this.template.includeCodeInputInHighlightFunc) this.template.highlight(result_element, this);
31+
else this.template.highlight(result_element);
32+
}
33+
34+
sync_scroll() {
35+
/* Scroll result to scroll coords of event - sync with textarea */
36+
let input_element = this.querySelector("textarea");
37+
let result_element = this.template.preElementStyled ? this.querySelector("pre") : this.querySelector("pre code");
38+
// Get and set x and y
39+
result_element.scrollTop = input_element.scrollTop;
40+
result_element.scrollLeft = input_element.scrollLeft;
41+
}
42+
43+
check_tab(event) {
44+
if(this.template.isCode) {
45+
let input_element = this.querySelector("textarea");
46+
let code = input_element.value;
47+
if (event.key == "Tab") {
48+
/* Tab key pressed */
49+
event.preventDefault(); // stop normal
50+
51+
if(input_element.selectionStart == input_element.selectionEnd) {
52+
53+
let before_selection = code.slice(0, input_element.selectionStart); // text before tab
54+
let after_selection = code.slice(input_element.selectionEnd, input_element.value.length); // text after tab
55+
56+
let cursor_pos = input_element.selectionEnd + 1; // where cursor moves after tab - moving forward by 1 char to after tab
57+
input_element.value = before_selection + "\t" + after_selection; // add tab char
58+
59+
// move cursor
60+
input_element.selectionStart = cursor_pos;
61+
input_element.selectionEnd = cursor_pos;
62+
63+
} else {
64+
let lines = input_element.value.split("\n");
65+
let letter_i = 0;
66+
67+
let selection_start = input_element.selectionStart; // where cursor moves after tab - moving forward by 1 indent
68+
let selection_end = input_element.selectionEnd; // where cursor moves after tab - moving forward by 1 indent
69+
70+
let number_indents = 0;
71+
let first_line_indents = 0;
72+
73+
for (let i = 0; i < lines.length; i++) {
74+
letter_i += lines[i].length;
75+
if(input_element.selectionStart < letter_i && input_element.selectionEnd > letter_i - lines[i].length) {
76+
if(event.shiftKey) {
77+
if(lines[i][0] == "\t") {
78+
lines[i] = lines[i].slice(1);
79+
if(number_indents == 0) first_line_indents--;
80+
number_indents--;
81+
}
82+
} else {
83+
lines[i] = "\t" + lines[i];
84+
if(number_indents == 0) first_line_indents++;
85+
number_indents++;
86+
}
87+
88+
}
89+
}
90+
input_element.value = lines.join("\n");
91+
92+
// move cursor
93+
input_element.selectionStart = selection_start + first_line_indents;
94+
input_element.selectionEnd = selection_end + number_indents;
95+
96+
}
97+
98+
this.update(input_element.value);
99+
}
100+
}
101+
}
102+
escape_html(text) {
103+
return text.replace(new RegExp("&", "g"), "&amp;").replace(new RegExp("<", "g"), "&lt;"); /* Global RegExp */
104+
}
105+
106+
/* Callbacks */
107+
connectedCallback() {
108+
// Added to document
109+
this.template = codeInput.usedTemplates[this.getAttribute("template") || codeInput.defaultTemplate];
110+
if(this.template.preElementStyled) this.classList.add("code-input_pre-element-styled");
111+
112+
/* Defaults */
113+
let lang = this.getAttribute("lang");
114+
let placeholder = this.getAttribute("placeholder") || this.getAttribute("lang") || "";
115+
let value = this.value || this.innerHTML || "";
116+
117+
this.innerHTML = ""; // Clear Content
118+
119+
/* Create Textarea */
120+
let textarea = document.createElement("textarea");
121+
textarea.placeholder = placeholder;
122+
textarea.value = value;
123+
textarea.setAttribute("spellcheck", "false");
124+
125+
if (this.getAttribute("name")) {
126+
textarea.setAttribute("name", this.getAttribute("name")); // for use in forms
127+
this.removeAttribute("name");
128+
}
129+
130+
textarea.setAttribute("oninput", "this.parentElement.update(this.value); this.parentElement.sync_scroll();");
131+
textarea.setAttribute("onscroll", "this.parentElement.sync_scroll();");
132+
textarea.setAttribute("onkeydown", "this.parentElement.check_tab(event);");
133+
134+
this.append(textarea);
135+
136+
/* Create pre code */
137+
let code = document.createElement("code");
138+
if(this.template.isCode && lang != null) code.classList.add("language-" + lang);
139+
code.innerText = value;
140+
141+
let pre = document.createElement("pre");
142+
pre.setAttribute("aria-hidden", "true"); // Hide for screen readers
143+
pre.append(code);
144+
this.append(pre);
145+
146+
/* Add code from value attribute - useful for loading from backend */
147+
this.update(value, this);
148+
}
149+
static get observedAttributes() {
150+
return ["value", "placeholder", "lang", "template"]; // Attributes to monitor
151+
}
152+
attributeChangedCallback(name, oldValue, newValue) {
153+
154+
switch (name) {
155+
156+
case "value":
157+
158+
// Update code
159+
this.update(newValue);
160+
161+
break;
162+
163+
case "placeholder":
164+
this.querySelector("textarea").placeholder = newValue;
165+
break;
166+
case "template":
167+
this.template = codeInput.usedTemplates[newValue || codeInput.defaultTemplate];
168+
if(this.template.preElementStyled) this.classList.add("code-input_pre-element-styled");
169+
else this.classList.remove("code-input_pre-element-styled");
170+
// Syntax Highlight
171+
this.update(this.value);
172+
173+
case "lang":
174+
let code = this.querySelector("pre code");
175+
let textarea = this.querySelector("textarea");
176+
177+
if(newValue != null) code.className = ("language-" + newValue);
178+
else code.className = "";
179+
180+
if(textarea.placeholder == oldValue) textarea.placeholder = newValue
181+
182+
this.update(this.value);
183+
}
184+
}
185+
186+
/* Value attribute */
187+
get value() {
188+
return this.getAttribute("value");
189+
}
190+
set value(val) {
191+
return this.setAttribute("value", val);
192+
}
193+
/* Placeholder attribute */
194+
get placeholder() {
195+
return this.getAttribute("placeholder");
196+
}
197+
set placeholder(val) {
198+
return this.setAttribute("placeholder", val);
199+
}
200+
},
201+
registerTemplate: function(template_name, template) {
202+
// Set default class
203+
codeInput.usedTemplates[template_name] = template;
204+
codeInput.defaultTemplate = template_name;
205+
},
206+
templates: {
207+
custom(highlight=function() {}, preElementStyled=true, isCode=true, includeCodeInputInHighlightFunc=false) {
208+
return {
209+
highlight: highlight,
210+
includeCodeInputInHighlightFunc: includeCodeInputInHighlightFunc,
211+
preElementStyled: preElementStyled,
212+
isCode: isCode,
213+
};
214+
},
215+
prism(prism) { // Dependency: Prism.js (https://prismjs.com/)
216+
return {
217+
includeCodeInputInHighlightFunc: false,
218+
highlight: prism.highlightElement,
219+
preElementStyled: true,
220+
isCode: true
221+
};
222+
},
223+
hljs(hljs) { // Dependency: Highlight.js (https://highlightjs.org/)
224+
return {
225+
includeCodeInputInHighlightFunc: false,
226+
highlight: hljs.highlightElement,
227+
preElementStyled: false,
228+
isCode: true
229+
};
230+
},
231+
characterLimit() {
232+
return {
233+
highlight: function(result_element, code_input) {
234+
235+
let character_limit = Number(code_input.getAttribute("data-character-limit"));
236+
237+
let normal_characters = code_input.escape_html(code_input.value.slice(0, character_limit));
238+
let overflow_characters = code_input.escape_html(code_input.value.slice(character_limit));
239+
240+
result_element.innerHTML = `${normal_characters}<mark class="overflow">${overflow_characters}</mark>`;
241+
if(overflow_characters.length > 0) {
242+
result_element.innerHTML += ` <mark class="overflow-msg">${code_input.getAttribute("data-overflow-msg") || "(Character limit reached)"}</mark>`;
243+
}
244+
},
245+
preElementStyled: true,
246+
isCode: false
247+
}
248+
},
249+
rainbowText(rainbow_colors=["red", "orangered", "orange", "goldenrod", "gold", "green", "darkgreen", "navy", "blue", "magenta"], delimiter="") {
250+
return {
251+
highlight: function(result_element, code_input) {
252+
let html_result = [];
253+
let sections = code_input.value.split(code_input.template.delimiter);
254+
for (let i = 0; i < sections.length; i++) {
255+
html_result.push(`<span style="color: ${code_input.template.rainbow_colors[i % code_input.template.rainbow_colors.length]}">${code_input.escape_html(sections[i])}</span>`);
256+
}
257+
result_element.innerHTML = html_result.join(code_input.template.delimiter);
258+
},
259+
preElementStyled: true,
260+
isCode: false,
261+
rainbow_colors: rainbow_colors,
262+
delimiter: delimiter
263+
}
264+
}
265+
}
266+
}
267+
268+
customElements.define("code-input", codeInput.CodeInput); // Set tag

code-input.min.css

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

code-input.min.js

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)