Comparing version
@@ -18,3 +18,3 @@ declare type Options = { | ||
export declare function CodeJar(editor: HTMLElement, highlight: (e: HTMLElement, pos?: Position) => void, opt?: Partial<Options>): { | ||
updateOptions(options: Partial<Options>): void; | ||
updateOptions(newOptions: Partial<Options>): void; | ||
updateCode(code: string): void; | ||
@@ -21,0 +21,0 @@ onUpdate(cb: (code: string) => void): void; |
@@ -12,4 +12,3 @@ const globalWindow = window; | ||
let prev; // code content prior keydown event | ||
let isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1; | ||
editor.setAttribute('contentEditable', isFirefox ? 'true' : 'plaintext-only'); | ||
editor.setAttribute('contenteditable', 'plaintext-only'); | ||
editor.setAttribute('spellcheck', options.spellcheck ? 'true' : 'false'); | ||
@@ -19,5 +18,9 @@ editor.style.outline = 'none'; | ||
editor.style.overflowY = 'auto'; | ||
editor.style.resize = 'vertical'; | ||
editor.style.whiteSpace = 'pre-wrap'; | ||
let isLegacy = false; // true if plaintext-only is not supported | ||
highlight(editor); | ||
if (editor.contentEditable !== 'plaintext-only') | ||
isLegacy = true; | ||
if (isLegacy) | ||
editor.setAttribute('contenteditable', 'true'); | ||
const debounceHighlight = debounce(() => { | ||
@@ -53,3 +56,3 @@ const pos = save(); | ||
else | ||
firefoxNewLineFix(event); | ||
legacyNewLineFix(event); | ||
if (options.catchTab) | ||
@@ -66,2 +69,4 @@ handleTabCharacters(event); | ||
} | ||
if (isLegacy) | ||
restore(save()); | ||
}); | ||
@@ -95,11 +100,28 @@ on('keyup', event => { | ||
const pos = { start: 0, end: 0, dir: undefined }; | ||
let { anchorNode, anchorOffset, focusNode, focusOffset } = s; | ||
if (!anchorNode || !focusNode) | ||
throw 'error1'; | ||
// Selection anchor and focus are expected to be text nodes, | ||
// so normalize them. | ||
if (anchorNode.nodeType === Node.ELEMENT_NODE) { | ||
const node = document.createTextNode(''); | ||
anchorNode.insertBefore(node, anchorNode.childNodes[anchorOffset]); | ||
anchorNode = node; | ||
anchorOffset = 0; | ||
} | ||
if (focusNode.nodeType === Node.ELEMENT_NODE) { | ||
const node = document.createTextNode(''); | ||
focusNode.insertBefore(node, focusNode.childNodes[focusOffset]); | ||
focusNode = node; | ||
focusOffset = 0; | ||
} | ||
visit(editor, el => { | ||
if (el === s.anchorNode && el === s.focusNode) { | ||
pos.start += s.anchorOffset; | ||
pos.end += s.focusOffset; | ||
pos.dir = s.anchorOffset <= s.focusOffset ? '->' : '<-'; | ||
if (el === anchorNode && el === focusNode) { | ||
pos.start += anchorOffset; | ||
pos.end += focusOffset; | ||
pos.dir = anchorOffset <= focusOffset ? '->' : '<-'; | ||
return 'stop'; | ||
} | ||
if (el === s.anchorNode) { | ||
pos.start += s.anchorOffset; | ||
if (el === anchorNode) { | ||
pos.start += anchorOffset; | ||
if (!pos.dir) { | ||
@@ -112,4 +134,4 @@ pos.dir = '->'; | ||
} | ||
else if (el === s.focusNode) { | ||
pos.end += s.focusOffset; | ||
else if (el === focusNode) { | ||
pos.end += focusOffset; | ||
if (!pos.dir) { | ||
@@ -129,2 +151,4 @@ pos.dir = '<-'; | ||
}); | ||
// collapse empty text nodes | ||
editor.normalize(); | ||
return pos; | ||
@@ -153,3 +177,3 @@ } | ||
const len = (el.nodeValue || '').length; | ||
if (current + len >= pos.start) { | ||
if (current + len > pos.start) { | ||
if (!startNode) { | ||
@@ -159,3 +183,3 @@ startNode = el; | ||
} | ||
if (current + len >= pos.end) { | ||
if (current + len > pos.end) { | ||
endNode = el; | ||
@@ -168,7 +192,6 @@ endOffset = pos.end - current; | ||
}); | ||
// If everything deleted place cursor at editor | ||
if (!startNode) | ||
startNode = editor; | ||
startNode = editor, startOffset = editor.childNodes.length; | ||
if (!endNode) | ||
endNode = editor; | ||
endNode = editor, endOffset = editor.childNodes.length; | ||
// Flip back the selection | ||
@@ -214,3 +237,3 @@ if (pos.dir == '<-') { | ||
else { | ||
firefoxNewLineFix(event); | ||
legacyNewLineFix(event); | ||
} | ||
@@ -225,6 +248,6 @@ // Place adjacent "}" on next line | ||
} | ||
function firefoxNewLineFix(event) { | ||
function legacyNewLineFix(event) { | ||
// Firefox does not support plaintext-only mode | ||
// and puts <div><br></div> on Enter. Let's help. | ||
if (isFirefox && event.key === 'Enter') { | ||
if (isLegacy && event.key === 'Enter') { | ||
preventDefault(event); | ||
@@ -368,6 +391,6 @@ event.stopPropagation(); | ||
function isUndo(event) { | ||
return isCtrl(event) && !event.shiftKey && event.key === 'z'; | ||
return isCtrl(event) && !event.shiftKey && event.code === 'KeyZ'; | ||
} | ||
function isRedo(event) { | ||
return isCtrl(event) && event.shiftKey && event.key === 'z'; | ||
return isCtrl(event) && event.shiftKey && event.code === 'KeyZ'; | ||
} | ||
@@ -416,4 +439,4 @@ function insert(text) { | ||
return { | ||
updateOptions(options) { | ||
options = Object.assign(Object.assign({}, options), options); | ||
updateOptions(newOptions) { | ||
Object.assign(options, newOptions); | ||
}, | ||
@@ -420,0 +443,0 @@ updateCode(code) { |
@@ -49,5 +49,4 @@ const globalWindow = window | ||
let prev: string // code content prior keydown event | ||
let isFirefox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1 | ||
editor.setAttribute('contentEditable', isFirefox ? 'true' : 'plaintext-only') | ||
editor.setAttribute('contenteditable', 'plaintext-only') | ||
editor.setAttribute('spellcheck', options.spellcheck ? 'true' : 'false') | ||
@@ -57,6 +56,9 @@ editor.style.outline = 'none' | ||
editor.style.overflowY = 'auto' | ||
editor.style.resize = 'vertical' | ||
editor.style.whiteSpace = 'pre-wrap' | ||
let isLegacy = false // true if plaintext-only is not supported | ||
highlight(editor) | ||
if (editor.contentEditable !== 'plaintext-only') isLegacy = true | ||
if (isLegacy) editor.setAttribute('contenteditable', 'true') | ||
@@ -94,3 +96,3 @@ const debounceHighlight = debounce(() => { | ||
if (options.preserveIdent) handleNewLine(event) | ||
else firefoxNewLineFix(event) | ||
else legacyNewLineFix(event) | ||
if (options.catchTab) handleTabCharacters(event) | ||
@@ -105,2 +107,4 @@ if (options.addClosing) handleSelfClosingCharacters(event) | ||
} | ||
if (isLegacy) restore(save()) | ||
}) | ||
@@ -136,12 +140,30 @@ | ||
let {anchorNode, anchorOffset, focusNode, focusOffset} = s | ||
if (!anchorNode || !focusNode) throw 'error1' | ||
// Selection anchor and focus are expected to be text nodes, | ||
// so normalize them. | ||
if (anchorNode.nodeType === Node.ELEMENT_NODE) { | ||
const node = document.createTextNode('') | ||
anchorNode.insertBefore(node, anchorNode.childNodes[anchorOffset]) | ||
anchorNode = node | ||
anchorOffset = 0 | ||
} | ||
if (focusNode.nodeType === Node.ELEMENT_NODE) { | ||
const node = document.createTextNode('') | ||
focusNode.insertBefore(node, focusNode.childNodes[focusOffset]) | ||
focusNode = node | ||
focusOffset = 0 | ||
} | ||
visit(editor, el => { | ||
if (el === s.anchorNode && el === s.focusNode) { | ||
pos.start += s.anchorOffset | ||
pos.end += s.focusOffset | ||
pos.dir = s.anchorOffset <= s.focusOffset ? '->' : '<-' | ||
if (el === anchorNode && el === focusNode) { | ||
pos.start += anchorOffset | ||
pos.end += focusOffset | ||
pos.dir = anchorOffset <= focusOffset ? '->' : '<-' | ||
return 'stop' | ||
} | ||
if (el === s.anchorNode) { | ||
pos.start += s.anchorOffset | ||
if (el === anchorNode) { | ||
pos.start += anchorOffset | ||
if (!pos.dir) { | ||
@@ -152,4 +174,4 @@ pos.dir = '->' | ||
} | ||
} else if (el === s.focusNode) { | ||
pos.end += s.focusOffset | ||
} else if (el === focusNode) { | ||
pos.end += focusOffset | ||
if (!pos.dir) { | ||
@@ -168,2 +190,5 @@ pos.dir = '<-' | ||
// collapse empty text nodes | ||
editor.normalize() | ||
return pos | ||
@@ -194,3 +219,3 @@ } | ||
const len = (el.nodeValue || '').length | ||
if (current + len >= pos.start) { | ||
if (current + len > pos.start) { | ||
if (!startNode) { | ||
@@ -200,3 +225,3 @@ startNode = el | ||
} | ||
if (current + len >= pos.end) { | ||
if (current + len > pos.end) { | ||
endNode = el | ||
@@ -210,5 +235,4 @@ endOffset = pos.end - current | ||
// If everything deleted place cursor at editor | ||
if (!startNode) startNode = editor | ||
if (!endNode) endNode = editor | ||
if (!startNode) startNode = editor, startOffset = editor.childNodes.length | ||
if (!endNode) endNode = editor, endOffset = editor.childNodes.length | ||
@@ -261,3 +285,3 @@ // Flip back the selection | ||
} else { | ||
firefoxNewLineFix(event) | ||
legacyNewLineFix(event) | ||
} | ||
@@ -274,6 +298,6 @@ | ||
function firefoxNewLineFix(event: KeyboardEvent) { | ||
function legacyNewLineFix(event: KeyboardEvent) { | ||
// Firefox does not support plaintext-only mode | ||
// and puts <div><br></div> on Enter. Let's help. | ||
if (isFirefox && event.key === 'Enter') { | ||
if (isLegacy && event.key === 'Enter') { | ||
preventDefault(event) | ||
@@ -427,7 +451,7 @@ event.stopPropagation() | ||
function isUndo(event: KeyboardEvent) { | ||
return isCtrl(event) && !event.shiftKey && event.key === 'z' | ||
return isCtrl(event) && !event.shiftKey && event.code === 'KeyZ' | ||
} | ||
function isRedo(event: KeyboardEvent) { | ||
return isCtrl(event) && event.shiftKey && event.key === 'z' | ||
return isCtrl(event) && event.shiftKey && event.code === 'KeyZ' | ||
} | ||
@@ -480,4 +504,4 @@ | ||
return { | ||
updateOptions(options: Partial<Options>) { | ||
options = {...options, ...options} | ||
updateOptions(newOptions: Partial<Options>) { | ||
Object.assign(options, newOptions) | ||
}, | ||
@@ -484,0 +508,0 @@ updateCode(code: string) { |
{ | ||
"name": "codejar", | ||
"version": "3.4.0", | ||
"version": "3.5.0", | ||
"description": "An embeddable code editor for the browser", | ||
@@ -5,0 +5,0 @@ "license": "MIT", |
43319
4.23%1191
3.66%