@webcoder49/code-input 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/minify.yml +22 -0
- package/LICENSE +21 -0
- package/README.md +115 -0
- package/code-input.css +100 -0
- package/code-input.js +422 -0
- package/code-input.min.css +1 -0
- package/code-input.min.js +1 -0
- package/package.json +31 -0
- package/plugins/README.md +68 -0
- package/plugins/autocomplete.css +15 -0
- package/plugins/autocomplete.js +80 -0
- package/plugins/autocomplete.min.css +1 -0
- package/plugins/autocomplete.min.js +1 -0
- package/plugins/autodetect.js +29 -0
- package/plugins/autodetect.min.js +1 -0
- package/plugins/debounce-update.js +41 -0
- package/plugins/debounce-update.min.js +1 -0
- package/plugins/indent.js +154 -0
- package/plugins/indent.min.js +1 -0
- package/plugins/prism-line-numbers.css +20 -0
- package/plugins/prism-line-numbers.min.css +1 -0
- package/plugins/special-chars.css +98 -0
- package/plugins/special-chars.js +219 -0
- package/plugins/special-chars.min.css +5 -0
- package/plugins/special-chars.min.js +1 -0
- package/plugins/test.js +38 -0
- package/plugins/test.min.js +1 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
code-input{position:relative;top:0;left:0;display:block;overflow:hidden;padding:8px;margin:0!important;width:calc(100% - 16px);height:250px;font-size:normal;font-family:monospace;line-height:1.5;tab-size:2;caret-color:#a9a9a9;white-space:pre}code-input textarea,code-input.code-input_pre-element-styled pre,code-input:not(.code-input_pre-element-styled) pre code{margin:0!important;padding:var(--padding,16px)!important;border:0;width:calc(100% - var(--padding,16px) * 2);height:calc(100% - var(--padding,16px) * 2)}code-input.code-input_pre-element-styled pre code,code-input:not(.code-input_pre-element-styled) pre{margin:0!important;padding:0!important;width:100%;height:100%}code-input pre,code-input pre *,code-input textarea{font-size:inherit!important;font-family:inherit!important;line-height:inherit!important;tab-size:inherit!important}code-input pre,code-input textarea{position:absolute;top:0;left:0}code-input textarea{z-index:1}code-input pre{z-index:0}code-input textarea{color:transparent;background:0 0;caret-color:inherit!important}code-input pre,code-input textarea{overflow:auto!important;white-space:inherit;word-spacing:normal;word-break:normal;word-wrap:normal}code-input textarea{resize:none;outline:0!important}code-input:not(.code-input_registered)::before{content:"Use codeInput.registerTemplate to set up.";display:block;color:grey}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var codeInput={observedAttributes:["value","name","placeholder","lang","template"],usedTemplates:{},defaultTemplate:void 0,templateQueue:{},plugins:{},Plugin:class{constructor(){console.log("code-input: plugin: Created plugin!"),codeInput.observedAttributes=codeInput.observedAttributes.concat(self.observedAttributes)}beforeHighlight(){}afterHighlight(){}beforeElementsAdded(){}afterElementsAdded(){}attributeChanged(){}observedAttributes=[]},CodeInput:class extends HTMLElement{constructor(){super()}bound_callbacks={};plugin_evt(a,b){for(let c in this.template.plugins){let d=this.template.plugins[c];a in d&&(b===void 0?d[a](this):d[a](this,...b))}}update(a){if(!this.ignoreValueUpdate){this.ignoreValueUpdate=!0,this.value=a,this.ignoreValueUpdate=!1,this.querySelector("textarea").value!=a&&(this.querySelector("textarea").value=a);let b=this.querySelector("pre code");"\n"==a[a.length-1]&&(a+=" "),b.innerHTML=this.escape_html(a),this.plugin_evt("beforeHighlight"),this.template.includeCodeInputInHighlightFunc?this.template.highlight(b,this):this.template.highlight(b),this.plugin_evt("afterHighlight")}}sync_scroll(){let a=this.querySelector("textarea"),b=this.template.preElementStyled?this.querySelector("pre"):this.querySelector("pre code");b.scrollTop=a.scrollTop,b.scrollLeft=a.scrollLeft}escape_html(a){return a.replace(/&/g,"&").replace(/</g,"<")}get_template(){let a;return a=null==this.getAttribute("template")?codeInput.defaultTemplate:this.getAttribute("template"),a in codeInput.usedTemplates?codeInput.usedTemplates[a]:(a in codeInput.templateQueue||(codeInput.templateQueue[a]=[]),void codeInput.templateQueue[a].push(this))}setup(){this.classList.add("code-input_registered"),this.template.preElementStyled&&this.classList.add("code-input_pre-element-styled"),this.plugin_evt("beforeElementsAdded");let a=this.getAttribute("lang"),b=this.getAttribute("placeholder")||this.getAttribute("lang")||"",c=this.value||this.innerHTML||"";this.innerHTML="";let d=document.createElement("textarea");d.placeholder=b,d.value=c,d.setAttribute("spellcheck","false"),this.getAttribute("name")&&d.setAttribute("name",this.getAttribute("name")),d.addEventListener("input",()=>{d.parentElement.update(d.value),d.parentElement.sync_scroll()}),d.addEventListener("scroll",()=>d.parentElement.sync_scroll()),this.append(d);let e=document.createElement("code"),f=document.createElement("pre");f.setAttribute("aria-hidden","true"),f.append(e),this.append(f),this.template.isCode&&a!=null&&""!=a&&e.classList.add("language-"+a),this.plugin_evt("afterElementsAdded"),this.update(c,this)}connectedCallback(){this.template=this.get_template(),this.template!=null&&this.setup()}static get observedAttributes(){return codeInput.observedAttributes}attributeChangedCallback(a,b,c){if(this.isConnected)switch(this.plugin_evt("attributeChanged",[a,b,c]),a){case"value":this.update(c);break;case"name":null!==this.querySelector("textarea")&&this.querySelector("textarea").setAttribute("name",c);break;case"placeholder":this.querySelector("textarea").placeholder=c;break;case"template":this.template=codeInput.usedTemplates[c||codeInput.defaultTemplate],this.template.preElementStyled?this.classList.add("code-input_pre-element-styled"):this.classList.remove("code-input_pre-element-styled"),this.update(this.value);break;case"lang":let a=this.querySelector("pre code"),d=this.querySelector("textarea");if(null!=c&&(c=c.toLowerCase(),a.classList.contains(`language-${c}`)))break;b=b.toLowerCase(),console.log("code-input: Language: REMOVE","language-"+b),a.classList.remove("language-"+b),a.parentElement.classList.remove("language-"+b),a.classList.remove("language-none"),a.parentElement.classList.remove("language-none"),null!=c&&""!=c&&(a.classList.add("language-"+c),console.log("code-input: Language:ADD","language-"+c)),d.placeholder==b&&(d.placeholder=c),this.update(this.value)}}addEventListener(a,b,c=null){let d=b.bind(this);this.bound_callbacks[b]=d,"change"==a?null===c?this.querySelector("textarea").addEventListener("change",d):this.querySelector("textarea").addEventListener("change",d,c):"selectionchange"==a&&(null===c?this.querySelector("textarea").addEventListener("selectionchange",d):this.querySelector("textarea").addEventListener("selectionchange",d,c))}removeEventListener(a,b,c=null){let d=this.bound_callbacks[b];"change"==a?null===c?this.querySelector("textarea").removeEventListener("change",d):this.querySelector("textarea").removeEventListener("change",d,c):"selectionchange"==a&&(null===c?this.querySelector("textarea").removeEventListener("selectionchange",d):this.querySelector("textarea").removeEventListener("selectionchange",d,c))}get value(){return this.getAttribute("value")}set value(a){return this.setAttribute("value",a)}get placeholder(){return this.getAttribute("placeholder")}set placeholder(a){return this.setAttribute("placeholder",a)}pluginData={}},registerTemplate:function(a,b){if(codeInput.usedTemplates[a]=b,a in codeInput.templateQueue){for(let c in codeInput.templateQueue[a])elem=codeInput.templateQueue[a][c],elem.template=b,elem.setup();console.log(`code-input: template: Added existing elements with template ${a}`)}if(null==codeInput.defaultTemplate){if(codeInput.defaultTemplate=a,void 0 in codeInput.templateQueue)for(let a in codeInput.templateQueue[void 0])elem=codeInput.templateQueue[void 0][a],elem.template=b,elem.setup();console.log(`code-input: template: Set template ${a} as default`)}console.log(`code-input: template: Created template ${a}`)},templates:{custom(a=function(){},b=!0,c=!0,d=!1,e=[]){return{highlight:a,includeCodeInputInHighlightFunc:d,preElementStyled:b,isCode:c,plugins:e}},prism(a,b=[]){return{includeCodeInputInHighlightFunc:!1,highlight:a.highlightElement,preElementStyled:!0,isCode:!0,plugins:b}},hljs(a,b=[]){return{includeCodeInputInHighlightFunc:!1,highlight:a.highlightElement,preElementStyled:!1,isCode:!0,plugins:b}},characterLimit(){return{highlight:function(a,b,c=[]){let d=+b.getAttribute("data-character-limit"),e=b.escape_html(b.value.slice(0,d)),f=b.escape_html(b.value.slice(d));a.innerHTML=`${e}<mark class="overflow">${f}</mark>`,0<f.length&&(a.innerHTML+=` <mark class="overflow-msg">${b.getAttribute("data-overflow-msg")||"(Character limit reached)"}</mark>`)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,plugins:plugins}},rainbowText(a=["red","orangered","orange","goldenrod","gold","green","darkgreen","navy","blue","magenta"],b="",c=[]){return{highlight:function(a,b){let c=[],d=b.value.split(b.template.delimiter);for(let e=0;e<d.length;e++)c.push(`<span style="color: ${b.template.rainbow_colors[e%b.template.rainbow_colors.length]}">${b.escape_html(d[e])}</span>`);a.innerHTML=c.join(b.template.delimiter)},includeCodeInputInHighlightFunc:!0,preElementStyled:!0,isCode:!1,rainbow_colors:a,delimiter:b,plugins:c}}}};customElements.define("code-input",codeInput.CodeInput);
|
package/package.json
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@webcoder49/code-input",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"description": "Fully customisable, editable syntax-highlighted textareas.",
|
|
5
|
+
"browser": "code-input.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "git+https://github.com/WebCoder49/code-input.git"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"front-end",
|
|
15
|
+
"syntax",
|
|
16
|
+
"highlight",
|
|
17
|
+
"textarea",
|
|
18
|
+
"editable",
|
|
19
|
+
"web-components"
|
|
20
|
+
],
|
|
21
|
+
"author": {
|
|
22
|
+
"name": "WebCoder49",
|
|
23
|
+
"email": "webcoder49.tutorials@gmail.com",
|
|
24
|
+
"url": "https://webcoder49.github.io/"
|
|
25
|
+
},
|
|
26
|
+
"license": "MIT",
|
|
27
|
+
"bugs": {
|
|
28
|
+
"url": "https://github.com/WebCoder49/code-input/issues"
|
|
29
|
+
},
|
|
30
|
+
"homepage": "https://github.com/WebCoder49/code-input#readme"
|
|
31
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Code-input: Plugins
|
|
2
|
+
## List Of Plugins
|
|
3
|
+
|
|
4
|
+
### Autocomplete
|
|
5
|
+
Display a popup under the caret using the text in the code-input element. This works well with autocomplete suggestions.
|
|
6
|
+
|
|
7
|
+
Files: [autocomplete.js](./autocomplete.js) / [autocomplete.css](./autocomplete.css)
|
|
8
|
+
|
|
9
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/xxapjXB)
|
|
10
|
+
|
|
11
|
+
### Autodetect
|
|
12
|
+
Autodetect the language live and change the `lang` attribute using the syntax highlighter's autodetect capabilities. Works with highlight.js.
|
|
13
|
+
|
|
14
|
+
Files: [autodetect.js](./autodetect.js)
|
|
15
|
+
|
|
16
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/eYLyMae)
|
|
17
|
+
|
|
18
|
+
### Debounce Update
|
|
19
|
+
Debounce the update and highlighting function ([What is Debouncing?](https://medium.com/@jamischarles/what-is-debouncing-2505c0648ff1))
|
|
20
|
+
|
|
21
|
+
Files: [debounce-update.js](./debounce-update.js)
|
|
22
|
+
|
|
23
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/GRXyxzV)
|
|
24
|
+
|
|
25
|
+
### Indent
|
|
26
|
+
Adds indentation using the `Tab` key, and auto-indents after a newline, as well as making it possible to indent/unindent multiple lines using Tab/Shift+Tab
|
|
27
|
+
|
|
28
|
+
Files: [indent.js](./indent.js)
|
|
29
|
+
|
|
30
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/WNgdzar)
|
|
31
|
+
|
|
32
|
+
### Prism Line Numbers
|
|
33
|
+
Allows code-input elements to be used with the Prism.js line-numbers plugin, as long as the code-input element or a parent element of it has the CSS class `line-numbers`. [Prism.js Plugin Docs](https://prismjs.com/plugins/line-numbers/)
|
|
34
|
+
|
|
35
|
+
Files: [prism-line-numbers.css](./prism-line-numbers.css) (NO JS FILE)
|
|
36
|
+
|
|
37
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/XWPVrWv)
|
|
38
|
+
|
|
39
|
+
### Special Chars
|
|
40
|
+
Render special characters and control characters as a symbol
|
|
41
|
+
with their hex code.
|
|
42
|
+
|
|
43
|
+
Files: [special-chars.js](./special-chars.js) / [special-chars.css](./special-chars.css)
|
|
44
|
+
|
|
45
|
+
[🚀 *CodePen Demo*](https://codepen.io/WebCoder49/pen/jOeYJbm)
|
|
46
|
+
|
|
47
|
+
## Using Plugins
|
|
48
|
+
Plugins allow you to add extra features to a template, like [automatic indentation](./indent.js) or [support for highlight.js's language autodetection](./autodetect.js). To use them, just:
|
|
49
|
+
- Import the plugins' JS/CSS files (there may only be one of these; import all of the files that exist) after you have imported `code-input` and before registering the template.
|
|
50
|
+
- If a JavaScript file is present, Place an instance of each plugin in the array of plugins argument when registering, like this:
|
|
51
|
+
```html
|
|
52
|
+
<script src="code-input.js"></script>
|
|
53
|
+
<!--...-->
|
|
54
|
+
<script src="plugins/autodetect.js"></script>
|
|
55
|
+
<script src="plugins/indent.js"></script>
|
|
56
|
+
<!--...-->
|
|
57
|
+
<script>
|
|
58
|
+
codeInput.registerTemplate("syntax-highlighted",
|
|
59
|
+
codeInput.templates.hljs(
|
|
60
|
+
hljs,
|
|
61
|
+
[
|
|
62
|
+
new codeInput.plugins.Autodetect(),
|
|
63
|
+
new codeInput.plugins.Indent()
|
|
64
|
+
]
|
|
65
|
+
)
|
|
66
|
+
);
|
|
67
|
+
</script>
|
|
68
|
+
```
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display a popup under the caret using the text in the code-input element. This works well with autocomplete suggestions.
|
|
3
|
+
* Files: autocomplete.js / autocomplete.css
|
|
4
|
+
*/
|
|
5
|
+
code-input .code-input_autocomplete_popup {
|
|
6
|
+
display: block;
|
|
7
|
+
position: absolute;
|
|
8
|
+
margin-top: 1em; /* Popup shows under the caret */
|
|
9
|
+
z-index: 100;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
code-input .code-input_autocomplete_testpos {
|
|
14
|
+
opacity: 0;
|
|
15
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Display a popup under the caret using the text in the code-input element. This works well with autocomplete suggestions.
|
|
3
|
+
* Files: autocomplete.js / autocomplete.css
|
|
4
|
+
*/
|
|
5
|
+
codeInput.plugins.Autocomplete = class extends codeInput.Plugin {
|
|
6
|
+
/**
|
|
7
|
+
* Pass in a function to display the popup that takes in (popup element, textarea, textarea.selectionEnd).
|
|
8
|
+
* @param {function} updatePopupCallback
|
|
9
|
+
*/
|
|
10
|
+
constructor(updatePopupCallback) {
|
|
11
|
+
super();
|
|
12
|
+
this.updatePopupCallback = updatePopupCallback;
|
|
13
|
+
}
|
|
14
|
+
/* When a key is pressed, or scrolling occurs, update the autocomplete */
|
|
15
|
+
updatePopup(codeInput, onlyScrolled) {
|
|
16
|
+
let textarea = codeInput.querySelector("textarea");
|
|
17
|
+
let caretCoords = this.getCaretCoordinates(codeInput, textarea, textarea.selectionEnd, onlyScrolled);
|
|
18
|
+
let popupElem = codeInput.querySelector(".code-input_autocomplete_popup");
|
|
19
|
+
popupElem.style.top = caretCoords.top + "px";
|
|
20
|
+
popupElem.style.left = caretCoords.left + "px";
|
|
21
|
+
|
|
22
|
+
if(!onlyScrolled) {
|
|
23
|
+
this.updatePopupCallback(popupElem, textarea, textarea.selectionEnd);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/* Runs after elements are added into a `code-input` (useful for adding events to the textarea); Params: codeInput element) */
|
|
27
|
+
afterElementsAdded(codeInput) {
|
|
28
|
+
let popupElem = document.createElement("div");
|
|
29
|
+
popupElem.classList.add("code-input_autocomplete_popup");
|
|
30
|
+
codeInput.appendChild(popupElem);
|
|
31
|
+
|
|
32
|
+
let testPosElem = document.createElement("pre");
|
|
33
|
+
testPosElem.classList.add("code-input_autocomplete_testpos");
|
|
34
|
+
codeInput.appendChild(testPosElem); // Styled like first pre, but first pre found to update
|
|
35
|
+
|
|
36
|
+
let textarea = codeInput.querySelector("textarea");
|
|
37
|
+
textarea.addEventListener("keyup", this.updatePopup.bind(this, codeInput, false)); // Override this+args in bind - not just scrolling
|
|
38
|
+
textarea.addEventListener("click", this.updatePopup.bind(this, codeInput, false)); // Override this+args in bind - not just scrolling
|
|
39
|
+
textarea.addEventListener("scroll", this.updatePopup.bind(this, codeInput, true)); // Override this+args in bind - just scrolling
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Return the coordinates of the caret in a code-input
|
|
43
|
+
* @param {codeInput.CodeInput} codeInput
|
|
44
|
+
* @param {HTMLElement} textarea
|
|
45
|
+
* @param {Number} charIndex
|
|
46
|
+
* @param {boolean} onlyScrolled True if no edits have been made to the text and the caret hasn't been repositioned
|
|
47
|
+
* @returns
|
|
48
|
+
*/
|
|
49
|
+
getCaretCoordinates(codeInput, textarea, charIndex, onlyScrolled) {
|
|
50
|
+
let afterSpan;
|
|
51
|
+
if(onlyScrolled) {
|
|
52
|
+
// No edits to text; don't update element - span at index 1 is after span
|
|
53
|
+
let spans = codeInput.querySelector(".code-input_autocomplete_testpos").querySelectorAll("span");
|
|
54
|
+
if(spans.length < 2) {
|
|
55
|
+
// Hasn't saved text in test pre to find pos
|
|
56
|
+
// Need to regenerate text in test pre
|
|
57
|
+
return this.getCaretCoordinates(codeInput, textarea, charIndex, false);
|
|
58
|
+
}
|
|
59
|
+
afterSpan = spans[1];
|
|
60
|
+
} else {
|
|
61
|
+
/* Inspired by https://github.com/component/textarea-caret-position */
|
|
62
|
+
let testPosElem = codeInput.querySelector(".code-input_autocomplete_testpos");
|
|
63
|
+
|
|
64
|
+
let beforeSpan = document.createElement("span");
|
|
65
|
+
beforeSpan.textContent = textarea.value.substring(0, charIndex);
|
|
66
|
+
afterSpan = document.createElement("span");
|
|
67
|
+
afterSpan.textContent = "."; // Placeholder
|
|
68
|
+
|
|
69
|
+
// Clear test pre - https://developer.mozilla.org/en-US/docs/Web/API/Node/removeChild
|
|
70
|
+
while (testPosElem.firstChild) {
|
|
71
|
+
testPosElem.removeChild(testPosElem.firstChild);
|
|
72
|
+
}
|
|
73
|
+
testPosElem.appendChild(beforeSpan);
|
|
74
|
+
testPosElem.appendChild(afterSpan);
|
|
75
|
+
}
|
|
76
|
+
return {"top": afterSpan.offsetTop - textarea.scrollTop, "left": afterSpan.offsetLeft - textarea.scrollLeft};
|
|
77
|
+
}
|
|
78
|
+
observedAttributes = [];
|
|
79
|
+
updatePopupCallback = function() {};
|
|
80
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
code-input .code-input_autocomplete_popup{display:block;position:absolute;margin-top:1em;z-index:100}code-input .code-input_autocomplete_testpos{opacity:0}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codeInput.plugins.Autocomplete=class extends codeInput.Plugin{constructor(a){super(),this.updatePopupCallback=a}updatePopup(a,b){let c=a.querySelector("textarea"),d=this.getCaretCoordinates(a,c,c.selectionEnd,b),e=a.querySelector(".code-input_autocomplete_popup");e.style.top=d.top+"px",e.style.left=d.left+"px",b||this.updatePopupCallback(e,c,c.selectionEnd)}afterElementsAdded(a){let b=document.createElement("div");b.classList.add("code-input_autocomplete_popup"),a.appendChild(b);let c=document.createElement("pre");c.classList.add("code-input_autocomplete_testpos"),a.appendChild(c);let d=a.querySelector("textarea");d.addEventListener("keyup",this.updatePopup.bind(this,a,!1)),d.addEventListener("click",this.updatePopup.bind(this,a,!1)),d.addEventListener("scroll",this.updatePopup.bind(this,a,!0))}getCaretCoordinates(a,b,c,d){let e;if(d){let d=a.querySelector(".code-input_autocomplete_testpos").querySelectorAll("span");if(2>d.length)return this.getCaretCoordinates(a,b,c,!1);e=d[1]}else{let d=a.querySelector(".code-input_autocomplete_testpos"),f=document.createElement("span");for(f.textContent=b.value.substring(0,c),e=document.createElement("span"),e.textContent=".";d.firstChild;)d.removeChild(d.firstChild);d.appendChild(f),d.appendChild(e)}return{top:e.offsetTop-b.scrollTop,left:e.offsetLeft-b.scrollLeft}}observedAttributes=[];updatePopupCallback=function(){}};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autodetect the language live and change the `lang` attribute using the syntax highlighter's
|
|
3
|
+
* autodetect capabilities. Works with highlight.js.
|
|
4
|
+
* Files: autodetect.js
|
|
5
|
+
*/
|
|
6
|
+
codeInput.plugins.Autodetect = class extends codeInput.Plugin {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
/* Remove previous language class */
|
|
11
|
+
beforeHighlight(codeInput) {
|
|
12
|
+
let result_element = codeInput.querySelector("pre code");
|
|
13
|
+
result_element.className = ""; // CODE
|
|
14
|
+
result_element.parentElement.className = ""; // PRE
|
|
15
|
+
}
|
|
16
|
+
/* Get new language class and set `lang` attribute */
|
|
17
|
+
afterHighlight(codeInput) {
|
|
18
|
+
let result_element = codeInput.querySelector("pre code");
|
|
19
|
+
let lang_class = result_element.className || result_element.parentElement.className;
|
|
20
|
+
let lang = lang_class.match(/lang(\w|-)*/i)[0]; // Get word starting with lang...; Get outer bracket
|
|
21
|
+
lang = lang.split("-")[1];
|
|
22
|
+
if(lang == "undefined") {
|
|
23
|
+
codeInput.removeAttribute("lang");
|
|
24
|
+
} else {
|
|
25
|
+
codeInput.setAttribute("lang", lang);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
observedAttributes = []
|
|
29
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codeInput.plugins.Autodetect=class extends codeInput.Plugin{constructor(){super()}beforeHighlight(a){let b=a.querySelector("pre code");b.className="",b.parentElement.className=""}afterHighlight(a){let b=a.querySelector("pre code"),c=b.className||b.parentElement.className,d=c.match(/lang(\w|-)*/i)[0];d=d.split("-")[1],"undefined"==d?a.removeAttribute("lang"):a.setAttribute("lang",d)}observedAttributes=[]};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Debounce the update and highlighting function
|
|
3
|
+
* https://medium.com/@jamischarles/what-is-debouncing-2505c0648ff1
|
|
4
|
+
* Files: debounce-update.js
|
|
5
|
+
*/
|
|
6
|
+
codeInput.plugins.DebounceUpdate = class extends codeInput.Plugin {
|
|
7
|
+
/**
|
|
8
|
+
* Create a debounced update plugin to pass into a template
|
|
9
|
+
* @param {Number} delayMs Delay, in ms, to wait until updating the syntax highlighting
|
|
10
|
+
*/
|
|
11
|
+
constructor(delayMs) {
|
|
12
|
+
super();
|
|
13
|
+
this.delayMs = delayMs;
|
|
14
|
+
}
|
|
15
|
+
/* Runs before elements are added into a `code-input`; Params: codeInput element) */
|
|
16
|
+
beforeElementsAdded(codeInput) {
|
|
17
|
+
console.log(codeInput, "before elements added");
|
|
18
|
+
this.update = codeInput.update.bind(codeInput); // Save previous update func
|
|
19
|
+
codeInput.update = this.updateDebounced.bind(this, codeInput);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Debounce the `update` function
|
|
24
|
+
*/
|
|
25
|
+
updateDebounced(codeInput, text) {
|
|
26
|
+
// Editing - cancel prev. timeout
|
|
27
|
+
if(this.debounceTimeout != null) {
|
|
28
|
+
window.clearTimeout(this.debounceTimeout);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
this.debounceTimeout = window.setTimeout(() => {
|
|
32
|
+
// Closure arrow function can take in variables like `text`
|
|
33
|
+
this.update(text);
|
|
34
|
+
}, this.delayMs);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// this.`update` function is original function
|
|
38
|
+
|
|
39
|
+
debounceTimeout = null; // Timeout until update
|
|
40
|
+
delayMs = 0; // Time until update
|
|
41
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codeInput.plugins.DebounceUpdate=class extends codeInput.Plugin{constructor(a){super(),this.delayMs=a}beforeElementsAdded(a){console.log(a,"before elements added"),this.update=a.update.bind(a),a.update=this.updateDebounced.bind(this,a)}updateDebounced(a,b){null!=this.debounceTimeout&&window.clearTimeout(this.debounceTimeout),this.debounceTimeout=window.setTimeout(()=>{this.update(b)},this.delayMs)}debounceTimeout=null;delayMs=0};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Adds indentation using the `Tab` key, and auto-indents after a newline, as well as making it
|
|
3
|
+
* possible to indent/unindent multiple lines using Tab/Shift+Tab
|
|
4
|
+
* Files: indent.js
|
|
5
|
+
*/
|
|
6
|
+
codeInput.plugins.Indent = class extends codeInput.Plugin {
|
|
7
|
+
constructor() {
|
|
8
|
+
super();
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/* Add keystroke events */
|
|
12
|
+
afterElementsAdded(codeInput) {
|
|
13
|
+
let textarea = codeInput.querySelector("textarea");
|
|
14
|
+
textarea.addEventListener('keydown', (event) => { this.check_tab(codeInput, event); this.check_enter(codeInput, event); });
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/* Event handlers */
|
|
18
|
+
check_tab(codeInput, event) {
|
|
19
|
+
if(event.key != "Tab") {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
let input_element = codeInput.querySelector("textarea");
|
|
23
|
+
let code = input_element.value;
|
|
24
|
+
event.preventDefault(); // stop normal
|
|
25
|
+
|
|
26
|
+
if(!event.shiftKey && input_element.selectionStart == input_element.selectionEnd) {
|
|
27
|
+
// Just place a tab here.
|
|
28
|
+
document.execCommand("insertText", false, "\t");
|
|
29
|
+
|
|
30
|
+
} else {
|
|
31
|
+
let lines = input_element.value.split("\n");
|
|
32
|
+
let letter_i = 0;
|
|
33
|
+
|
|
34
|
+
let selection_start = input_element.selectionStart; // where cursor moves after tab - moving forward by 1 indent
|
|
35
|
+
let selection_end = input_element.selectionEnd; // where cursor moves after tab - moving forward by 1 indent
|
|
36
|
+
|
|
37
|
+
let number_indents = 0;
|
|
38
|
+
let first_line_indents = 0;
|
|
39
|
+
|
|
40
|
+
for (let i = 0; i < lines.length; i++) {
|
|
41
|
+
// console.log(lines[i], ": start", selection_start, letter_i + lines[i].length + 1, "&& end", selection_end , letter_i + 1)
|
|
42
|
+
if((selection_start <= letter_i+lines[i].length && selection_end >= letter_i + 1)
|
|
43
|
+
|| (selection_start == selection_end && selection_start <= letter_i+lines[i].length+1 && selection_end >= letter_i)) { // + 1 so newlines counted
|
|
44
|
+
// Starts before or at last char and ends after or at first char
|
|
45
|
+
if(event.shiftKey) {
|
|
46
|
+
if(lines[i][0] == "\t") {
|
|
47
|
+
// Remove first tab
|
|
48
|
+
input_element.selectionStart = letter_i;
|
|
49
|
+
input_element.selectionEnd = letter_i+1;
|
|
50
|
+
document.execCommand("delete", false, "");
|
|
51
|
+
|
|
52
|
+
// Change selection
|
|
53
|
+
if(selection_start > letter_i) { // Indented outside selection
|
|
54
|
+
selection_start--;
|
|
55
|
+
}
|
|
56
|
+
selection_end--;
|
|
57
|
+
letter_i--;
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
// Add tab at start
|
|
61
|
+
input_element.selectionStart = letter_i;
|
|
62
|
+
input_element.selectionEnd = letter_i;
|
|
63
|
+
document.execCommand("insertText", false, "\t");
|
|
64
|
+
|
|
65
|
+
// Change selection
|
|
66
|
+
if(selection_start > letter_i) { // Indented outside selection
|
|
67
|
+
selection_start++;
|
|
68
|
+
}
|
|
69
|
+
selection_end++;
|
|
70
|
+
letter_i++;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
letter_i += lines[i].length+1; // newline counted
|
|
75
|
+
}
|
|
76
|
+
// input_element.value = lines.join("\n");
|
|
77
|
+
|
|
78
|
+
// move cursor
|
|
79
|
+
input_element.selectionStart = selection_start;
|
|
80
|
+
input_element.selectionEnd = selection_end;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
codeInput.update(input_element.value);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
check_enter(codeInput, event) {
|
|
87
|
+
if(event.key != "Enter") {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
event.preventDefault(); // stop normal
|
|
91
|
+
|
|
92
|
+
let input_element = codeInput.querySelector("textarea");
|
|
93
|
+
let lines = input_element.value.split("\n");
|
|
94
|
+
let letter_i = 0;
|
|
95
|
+
let current_line = lines.length - 1;
|
|
96
|
+
let new_line = "";
|
|
97
|
+
let number_indents = 0;
|
|
98
|
+
|
|
99
|
+
// find the index of the line our cursor is currently on
|
|
100
|
+
for (let i = 0; i < lines.length; i++) {
|
|
101
|
+
letter_i += lines[i].length + 1;
|
|
102
|
+
if(input_element.selectionEnd <= letter_i) {
|
|
103
|
+
current_line = i;
|
|
104
|
+
break;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// count the number of indents the current line starts with (up to our cursor position in the line)
|
|
109
|
+
let cursor_pos_in_line = lines[current_line].length - (letter_i - input_element.selectionEnd) + 1;
|
|
110
|
+
for (let i = 0; i < cursor_pos_in_line; i++) {
|
|
111
|
+
if (lines[current_line][i] == "\t") {
|
|
112
|
+
number_indents++;
|
|
113
|
+
} else {
|
|
114
|
+
break;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// determine the text before and after the cursor and chop the current line at the new line break
|
|
119
|
+
let text_after_cursor = "";
|
|
120
|
+
if (cursor_pos_in_line != lines[current_line].length) {
|
|
121
|
+
text_after_cursor = lines[current_line].substring(cursor_pos_in_line);
|
|
122
|
+
lines[current_line] = lines[current_line].substring(0, cursor_pos_in_line);
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// insert our indents and any text from the previous line that might have been after the line break
|
|
126
|
+
for (let i = 0; i < number_indents; i++) {
|
|
127
|
+
new_line += "\t";
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// save the current cursor position
|
|
131
|
+
let selection_start = input_element.selectionStart;
|
|
132
|
+
let selection_end = input_element.selectionEnd;
|
|
133
|
+
|
|
134
|
+
document.execCommand("insertText", false, "\n" + new_line); // Write new line, including auto-indentation
|
|
135
|
+
|
|
136
|
+
// move cursor to new position
|
|
137
|
+
input_element.selectionStart = selection_start + number_indents + 1; // count the indent level and the newline character
|
|
138
|
+
input_element.selectionEnd = selection_start + number_indents + 1;
|
|
139
|
+
|
|
140
|
+
codeInput.update(input_element.value);
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
// Update scrolls
|
|
144
|
+
input_element.scrollLeft = 0;
|
|
145
|
+
// Move down 1 line
|
|
146
|
+
let lineHeight = Number(getComputedStyle(input_element).lineHeight.split(0, -2));
|
|
147
|
+
// console.log(getComputedStyle(input_element).lineHeight);
|
|
148
|
+
if(lineHeight == NaN && getComputedStyle(input_element).lineHeight.split(-2) == "px") {
|
|
149
|
+
input_element.scrollTop += lineHeight;
|
|
150
|
+
} else {
|
|
151
|
+
input_element.scrollTop += 20; // px
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
codeInput.plugins.Indent=class extends codeInput.Plugin{constructor(){super()}afterElementsAdded(a){let b=a.querySelector("textarea");b.addEventListener("keydown",b=>{this.check_tab(a,b),this.check_enter(a,b)})}check_tab(a,b){if("Tab"!=b.key)return;let c=a.querySelector("textarea"),d=c.value;if(b.preventDefault(),!b.shiftKey&&c.selectionStart==c.selectionEnd)document.execCommand("insertText",!1,"\t");else{let a=c.value.split("\n"),d=0,e=c.selectionStart,f=c.selectionEnd;for(let g=0;g<a.length;g++)(e<=d+a[g].length&&f>=d+1||e==f&&e<=d+a[g].length+1&&f>=d)&&(b.shiftKey?"\t"==a[g][0]&&(c.selectionStart=d,c.selectionEnd=d+1,document.execCommand("delete",!1,""),e>d&&e--,f--,d--):(c.selectionStart=d,c.selectionEnd=d,document.execCommand("insertText",!1,"\t"),e>d&&e++,f++,d++)),d+=a[g].length+1;c.selectionStart=e,c.selectionEnd=f}a.update(c.value)}check_enter(a,b){if("Enter"!=b.key)return;b.preventDefault();let c=a.querySelector("textarea"),d=c.value.split("\n"),e=0,f=d.length-1,g="",h=0;for(let g=0;g<d.length;g++)if(e+=d[g].length+1,c.selectionEnd<=e){f=g;break}let j=d[f].length-(e-c.selectionEnd)+1;for(let c=0;c<j&&"\t"==d[f][c];c++)h++;let k="";j!=d[f].length&&(k=d[f].substring(j),d[f]=d[f].substring(0,j));for(let c=0;c<h;c++)g+="\t";let l=c.selectionStart;document.execCommand("insertText",!1,"\n"+g),c.selectionStart=l+h+1,c.selectionEnd=l+h+1,a.update(c.value),c.scrollLeft=0;let m=+getComputedStyle(c).lineHeight.split(0,-2);c.scrollTop+=m==NaN&&"px"==getComputedStyle(c).lineHeight.split(-2)?m:20}};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Allows code-input elements to be used with the Prism.js line-numbers plugin, as long as the code-input element
|
|
3
|
+
* or a parent element of it has the CSS class `line-numbers`.
|
|
4
|
+
* https://prismjs.com/plugins/line-numbers/
|
|
5
|
+
* Files: prism-line-numbers.css
|
|
6
|
+
*/
|
|
7
|
+
/* Update padding to match line-numbers plugin */
|
|
8
|
+
code-input.line-numbers textarea, code-input.line-numbers.code-input_pre-element-styled pre,
|
|
9
|
+
.line-numbers code-input textarea, .line-numbers code-input.code-input_pre-element-styled pre {
|
|
10
|
+
padding-top: 1em!important;
|
|
11
|
+
padding-bottom: 1em!important;
|
|
12
|
+
padding-right: 1em!important;
|
|
13
|
+
padding-left: 3.8em!important;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/* Remove unnecessary, interfering padding values */
|
|
17
|
+
code-input.line-numbers, code-input.line-numbers.code-input_pre-element-styled code,
|
|
18
|
+
.line-numbers code-input, .line-numbers code-input.code-input_pre-element-styled code{
|
|
19
|
+
padding: 0;
|
|
20
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
.line-numbers code-input textarea,.line-numbers code-input.code-input_pre-element-styled pre,code-input.line-numbers textarea,code-input.line-numbers.code-input_pre-element-styled pre{padding-top:1em!important;padding-bottom:1em!important;padding-right:1em!important;padding-left:3.8em!important}.line-numbers code-input,.line-numbers code-input.code-input_pre-element-styled code,code-input.line-numbers,code-input.line-numbers.code-input_pre-element-styled code{padding:0}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Render special characters and control characters as a symbol with their hex code.
|
|
3
|
+
* Files: special-chars.js, special-chars.css
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/* Main styling */
|
|
7
|
+
|
|
8
|
+
:root, body { /* Font for hex chars */
|
|
9
|
+
--code-input_special-chars_0: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdjZGBgYPj///9/RhCAMcA0bg6yHgAPmh/6BoxTcQAAAABJRU5ErkJgggAA');
|
|
10
|
+
--code-input_special-chars_1: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdjZGBgYPj///9/RhAggwMAitIUBr9U6sYAAAAASUVORK5CYII=');
|
|
11
|
+
--code-input_special-chars_2: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGCEMUCCjCgyYBFGRrAKFBkAuLYT9kYcIu0AAAAASUVORK5CYII=');
|
|
12
|
+
--code-input_special-chars_3: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABhJREFUGFdj/P///38GKGCEMUCCjMTJAACYiBPyG8sfAgAAAABJRU5ErkJggg==');
|
|
13
|
+
--code-input_special-chars_4: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///39GRkZGMI3BYYACRhgDrAKZAwAYxhvyz0DRIQAAAABJRU5ErkJggg==');
|
|
14
|
+
--code-input_special-chars_5: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACJJREFUGFdj/P///38GKGAEcRgZGRlBfDAHLgNjgFUgywAAuR4T9hxJl2YAAAAASUVORK5CYII=');
|
|
15
|
+
--code-input_special-chars_6: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdj/P///38GKGAEcRgZGRlBfDAHQwasAlkGABcdF/Y4yco2AAAAAElFTkSuQmCC');
|
|
16
|
+
--code-input_special-chars_7: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABZJREFUGFdj/P///38GKGCEMUCCRHIAWMgT8kue3bQAAAAASUVORK5CYII=');
|
|
17
|
+
--code-input_special-chars_8: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GKGAEcRgZGSE0cTIAvHcb8v+mIfAAAAAASUVORK5CYII=');
|
|
18
|
+
--code-input_special-chars_9: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB9JREFUGFdj/P///38GKGAEcRgZGSE0igxMCVgGmQMAPqcX8hWL1K0AAAAASUVORK5CYII=');
|
|
19
|
+
--code-input_special-chars_A: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAACBJREFUGFdjZGBgYPj///9/RhCAMcA0iADJggCmDEw5ALdxH/aGuYHqAAAAAElFTkSuQmCC');
|
|
20
|
+
--code-input_special-chars_B: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABlJREFUGFdj/P///38GBgYGRhAAceA0cTIAvc0b/vRDnVoAAAAASUVORK5CYII=');
|
|
21
|
+
--code-input_special-chars_C: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdjZGBgYPj///9/EM0IYjAyMjIS4CDrAQC57hP+uLwvFQAAAABJRU5ErkJggg==');
|
|
22
|
+
--code-input_special-chars_D: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABtJREFUGFdj/P///38GBgYGRhAAceA0fg5MDwAveh/6ToN9VwAAAABJRU5ErkJggg==');
|
|
23
|
+
--code-input_special-chars_E: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAABxJREFUGFdj/P///38GKGAEcRgZGRlBfDCHsAwA2UwT+mVIH1MAAAAASUVORK5CYII=');
|
|
24
|
+
--code-input_special-chars_F: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAMAAAAFCAYAAACAcVaiAAAAAXNSR0IArs4c6QAAAB5JREFUGFdj/P///38GKGAEcRgZGRlBfDAHtwxMGQDZZhP+BnB1kwAAAABJRU5ErkJggg==');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
.code-input_special-char_container { /* pre element */
|
|
28
|
+
font-size: 20px;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
.code-input_special-char {
|
|
32
|
+
display: inline-block;
|
|
33
|
+
position: relative;
|
|
34
|
+
top: 0;
|
|
35
|
+
left: 0;
|
|
36
|
+
height: 1em;
|
|
37
|
+
/* width: set by JS */
|
|
38
|
+
overflow: hidden;
|
|
39
|
+
text-decoration: none;
|
|
40
|
+
text-shadow: none;
|
|
41
|
+
vertical-align: middle;
|
|
42
|
+
outline: 0.1px solid currentColor;
|
|
43
|
+
|
|
44
|
+
--hex-0: var(
|
|
45
|
+
--code-input_special-chars_0);
|
|
46
|
+
--hex-1: var(
|
|
47
|
+
--code-input_special-chars_0);
|
|
48
|
+
--hex-2: var(
|
|
49
|
+
--code-input_special-chars_0);
|
|
50
|
+
--hex-3: var(
|
|
51
|
+
--code-input_special-chars_0);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/* Default - Two bytes - 4 hex chars */
|
|
55
|
+
|
|
56
|
+
.code-input_special-char::before {
|
|
57
|
+
margin-left: 50%;
|
|
58
|
+
transform: translate(-50%, 0);
|
|
59
|
+
content: " ";
|
|
60
|
+
|
|
61
|
+
background-color: var(--code-input_special-char_color, currentColor);
|
|
62
|
+
image-rendering: pixelated;
|
|
63
|
+
display: inline-block;
|
|
64
|
+
width: calc(100%-2px);
|
|
65
|
+
height: 100%;
|
|
66
|
+
|
|
67
|
+
mask-image: var(--hex-0), var(--hex-1), var(--hex-2), var(--hex-3);
|
|
68
|
+
mask-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
|
|
69
|
+
mask-size: 40%, 40%, 40%, 40%;
|
|
70
|
+
mask-position: 10% 10%, 90% 10%, 10% 90%, 90% 90%;
|
|
71
|
+
|
|
72
|
+
-webkit-mask-image: var(--hex-0), var(--hex-1), var(--hex-2), var(--hex-3);
|
|
73
|
+
-webkit-mask-repeat: no-repeat, no-repeat, no-repeat, no-repeat;
|
|
74
|
+
-webkit-mask-size: min(40%, 0.25em), min(40%, 0.25em), min(40%, 0.25em), min(40%, 0.25em);
|
|
75
|
+
-webkit-mask-position: 10% 10%, min(90%, 0.5em) 10%, 10% 90%, min(90%, 0.5em) 90%;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
.code-input_special-char_zero-width {
|
|
79
|
+
z-index: 1;
|
|
80
|
+
width: 1em;
|
|
81
|
+
margin-left: -0.5em;
|
|
82
|
+
margin-right: -0.5em;
|
|
83
|
+
position: relative;
|
|
84
|
+
|
|
85
|
+
opacity: 0.75;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/* One byte - 2 hex chars */
|
|
89
|
+
.code-input_special-char_one-byte::before {
|
|
90
|
+
height: 1.5em;
|
|
91
|
+
top: -1em;
|
|
92
|
+
content: attr(data-hex2);
|
|
93
|
+
}
|
|
94
|
+
.code-input_special-char_one-byte::after {
|
|
95
|
+
height: 1.5em;
|
|
96
|
+
bottom: -1em;
|
|
97
|
+
content: attr(data-hex3);
|
|
98
|
+
}
|