From af07ff3e731119d073156e2f111d684a653cee54 Mon Sep 17 00:00:00 2001 From: GaurangTandon <1gaurangtandon@gmail.com> Date: Mon, 24 Jun 2019 22:40:15 +0530 Subject: [PATCH 1/5] basic setup for #287 --- js/detector.js | 19 ++++++++++++++++++- manifest.json | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/js/detector.js b/js/detector.js index b09f60b..dcce956 100644 --- a/js/detector.js +++ b/js/detector.js @@ -299,9 +299,20 @@ primitiveExtender(); } } + function triggerFakeInput($elm) { + $elm.dispatchEvent( + new Event("input", { + cancelable: true, + bubbles: true, + }), + ); + } + function checkPlaceholdersInContentEditableNode() { let pArr = Placeholder.array, currND; + + triggerFakeInput(Placeholder.node); // debugLog(Placeholder); if (pArr && pArr.length > 0) { [currND] = pArr; @@ -324,8 +335,11 @@ primitiveExtender(); // might have been called from keyEventhandler if (Placeholder.isCENode) { checkPlaceholdersInContentEditableNode(); + return; } + triggerFakeInput(node); + // text area logic if (notCheckSelection) { selectedText = getUserSelection(node); @@ -486,6 +500,7 @@ primitiveExtender(); node.value = textBefore + characterStart + textMid + (characterEnd || "") + textAfter; node.selectionStart = startPos; node.selectionEnd = endPos; + triggerFakeInput(node); } } @@ -498,7 +513,7 @@ primitiveExtender(); ) { let textNode, positionIncrement = isStart ? 1 : 0; - + triggerFakeInput(rangeNode); if (rangeNode.nodeType !== 3) { textNode = document.createTextNode(singleCharacter); rangeNode.insertBefore(textNode, rangeNode.childNodes[position]); @@ -783,12 +798,14 @@ primitiveExtender(); [valueToSet, caretPosToSet] = evaluatedValue; if (isCENode) { + triggerFakeInput(rangeNode); rangeNode.textContent = valueToSet; range.setStart(rangeNode, caretPosToSet); range.setEnd(rangeNode, caretPosToSet); sel.removeAllRanges(); sel.addRange(range); } else { + triggerFakeInput(node); node.value = valueToSet; node.selectionStart = node.selectionEnd = caretPosToSet; } diff --git a/manifest.json b/manifest.json index 05b5700..cbda1d1 100644 --- a/manifest.json +++ b/manifest.json @@ -2,7 +2,7 @@ "manifest_version": 2, "name": "ProKeys", "description": "Save time and effort in emails, etc. with ProKeys! Define snippets, do math in browser, auto complete braces, and much more.", - "version": "3.6.0", + "version": "3.6.1", "author": "Aquila Softworks", "browser_action": { "default_icon": "imgs/r16.png" From 3160b661216082153789ec4ed8dcb8fd1a75115b Mon Sep 17 00:00:00 2001 From: GaurangTandon <1gaurangtandon@gmail.com> Date: Tue, 25 Jun 2019 15:58:18 +0530 Subject: [PATCH 2/5] basic strucuting is ready --- js/autoInsertFunctionality.js | 219 ++++++++++++++++++++++++++++++++++ js/detector.js | 186 ++--------------------------- js/pre.js | 11 ++ 3 files changed, 237 insertions(+), 179 deletions(-) create mode 100644 js/autoInsertFunctionality.js diff --git a/js/autoInsertFunctionality.js b/js/autoInsertFunctionality.js new file mode 100644 index 0000000..ce4f964 --- /dev/null +++ b/js/autoInsertFunctionality.js @@ -0,0 +1,219 @@ +/* global Data */ + +import { isContentEditable, isTextNode, getNodeWindow } from "./pre"; + +/** + * @param {String} character to match + * @param {Number} searchCharIndex (0 or 1) denoting whether to match the + starting (`{`) or the closing (`}`) characters of an auto-insert combo + with the `character` + * @returns {String[]} the auto insert pair if found, else + */ +function searchAutoInsertChars(character, searchCharIndex) { + const arr = Data.charsToAutoInsertUserList, + defaultReturn = ["", ""]; + + for (let i = 0, len = arr.length; i < len; i++) { + if (arr[i][searchCharIndex] === character) { + return arr[i]; + } + } + + return defaultReturn; +} + +// non-breaking space is useful when inserting four concsecutive for tab character +// HTML entities are for HTML nodes, so use \xA0 to insert   +function makeSpaceNonBreaking(string) { + return string.replace(/ /g, "\xA0"); +} + +function moveForwardForSpaces(range, textNode, position) { + const value = textNode.textContent; + + while (/\s/.test(value[position]) && position < value.length) { + position++; + } + + range.setStart(textNode, position); +} + +function moveBackwardForSpaces(range, textNode, position) { + const value = textNode.textContent; + + while (/\s/.test(value[position - 1]) && position >= 1) { + position--; + } + + range.setEnd(textNode, position); +} + +function moveSelectionForSurroundingWhitespace(range) { + if (isTextNode(range.startContainer)) { + moveForwardForSpaces(range, range.startContainer, range.startOffset); + } + if (isTextNode(range.endContainer)) { + moveBackwardForSpaces(range, range.endContainer, range.endOffset); + } +} + +/** + * + * @param {Range} range + * @param {String} textnodeString + * @param {Boolean} isStart + */ +function createNewTextNodeForAutoInsert(range, textnodeString, isStart) { + const textNode = document.createTextNode(textnodeString); + if (isStart) { + range.startContainer.insertBefore(textNode, range.startContainer.childNodes[range.startOffset]); + } else { + range.endContainer.insertBefore(textNode, range.endContainer.childNodes[range.endOffset]); + } + + range.setStart(textNode, 0); + range.setEnd(textNode, 0); + return textNode; +} + +function modifySelection(sel, range) { + sel.removeAllRanges(); + sel.addRange(range); +} + +// function setSamePosInTextNodeAndSave(sel, range, textNode, pos) { +// range.setStart(textNode, pos); +// range.setEnd(textNode, pos); +// modifySelection(sel, range); +// } + +function insertTextInNode(textNode, text, atPos) { + const valBefore = textNode.textContent.substr(0, atPos), + valAfter = textNode.textContent.substr(atPos); + + textNode.textContent = valBefore + text + valAfter; +} + +/** + * ONLY call this function when `startAndEndAreSame` + * flag is false in iCC + * @param {Range} range + * @param {String} content + */ +function insertSingleCharacterContentEditable(range, content, isStart = true, increment) { + let textNode, + startPos; + if (isStart) { + if (!isTextNode(range.startContainer)) { + // range node is an element node when it is empty + textNode = createNewTextNodeForAutoInsert(range, ""); + startPos = 0; + } else { + textNode = range.startContainer; + startPos = range.startOffset; + } + + if (isTextNode(textNode)) { + insertTextInNode(textNode, content, startPos); + range.setStart(textNode, startPos + increment); + } + } else { + if (!isTextNode(range.endContainer)) { + // range node is an element node when it is empty + textNode = createNewTextNodeForAutoInsert(range, "", false); + startPos = 0; + } else { + textNode = range.endContainer; + startPos = range.endOffset; + } + + if (isTextNode(textNode)) { + insertTextInNode(textNode, content, startPos); + range.setEnd(textNode, startPos + increment); + } + } +} + +/** + * + * @param {Element} node the parent node (event.target) + * @param {String} characterStart + * @param {String} [characterEnd] + */ +function insertCharacterContentEditable(node, characterStart, characterEnd) { + const win = getNodeWindow(node), + sel = win.getSelection(), + range = sel.getRangeAt(0), + rangeWasCollapsed = range.collapsed; + let textnodeString, + caretIncrement; + + // process the characters and their positioning + characterStart = makeSpaceNonBreaking(characterStart); + if (characterEnd) { + characterEnd = makeSpaceNonBreaking(characterEnd); + textnodeString = characterStart + characterEnd; + caretIncrement = 1; + } else { + textnodeString = characterStart; + caretIncrement = characterStart.length; + } + + const startAndEndAreSame = rangeWasCollapsed + || (!rangeWasCollapsed && !Data.wrapSelectionAutoInsert); + + if (startAndEndAreSame) { + range.deleteContents(); + + insertSingleCharacterContentEditable(range, textnodeString, true, caretIncrement); + modifySelection(); + } else { + moveSelectionForSurroundingWhitespace(range); + insertSingleCharacterContentEditable(range, characterStart, true, characterStart.length); + if (characterEnd) { + insertSingleCharacterContentEditable(range, characterEnd, false, 0); + } + } + + modifySelection(sel, range); +} + +/** + * + * @param {Element} node the parent node (event.target) + * @param {String} characterStart + * @param {String} characterEnd + */ +function insertCharacter(node, characterStart, characterEnd) { + if (isContentEditable(node)) { + insertCharacterContentEditable(node, characterStart, characterEnd); + } else { + let text = node.value, + startPos = node.selectionStart, + endPos = node.selectionEnd, + textBefore = text.substring(0, startPos), + textMid = text.substring(startPos, endPos), + textAfter = text.substring(endPos), + // handle trailing spaces + trimmedSelection = textMid.match(/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/) || [ + "", + "", + "", + "", + ]; + + textBefore += trimmedSelection[1]; + textAfter = trimmedSelection[3] + textAfter; + textMid = trimmedSelection[2]; + + textMid = Data.wrapSelectionAutoInsert ? textMid : ""; + startPos = textBefore.length + +!!characterEnd; + endPos = startPos + textMid.length; + + node.value = textBefore + characterStart + textMid + (characterEnd || "") + textAfter; + node.selectionStart = startPos; + node.selectionEnd = endPos; + } +} + +export { insertCharacter, searchAutoInsertChars }; diff --git a/js/detector.js b/js/detector.js index dcce956..452d5a1 100644 --- a/js/detector.js +++ b/js/detector.js @@ -8,6 +8,7 @@ import { isTextNode, isBlockedSite, PRIMITIVES_EXT_KEY, + getNodeWindow, } from "./pre"; import { Folder, Snip } from "./snippetClasses"; import { DBget } from "./commonDataHandlers"; @@ -16,6 +17,7 @@ import { updateAllValuesPerWin } from "./protoExtend"; import { getHTML } from "./textmethods"; import { showBlockSiteModal } from "./modalHandlers"; import { getCurrentTimestamp, getFormattedDate } from "./dateFns"; +import { insertCharacter, searchAutoInsertChars } from "./autoInsertFunctionality"; primitiveExtender(); (function () { @@ -67,7 +69,6 @@ primitiveExtender(); /* Helper functions for iframe related work - initiateIframeCheckForSpecialWebpages - - getNodeWindow */ function onIFrameLoad(iframe) { @@ -105,15 +106,6 @@ primitiveExtender(); } } - // in certain web apps, like mailchimp - // node refers to the editor inside iframe - // while `window` refers to top level window - // so selection and other methods do not work - // hence the need to get the `node's window` - function getNodeWindow(node) { - return node.ownerDocument.defaultView; - } - /* Snippet/Placeholder functions */ @@ -444,167 +436,6 @@ primitiveExtender(); insertSnippetInTextarea(caretPos, caretPos, snip, val, node); } - /* - Auto-Insert Character functions - - searchAutoInsertChars - - insertCharacter - */ - - /** - * @param {String} character to match - * @param {Number} searchCharIndex (0 or 1) denoting whether to match the - starting (`{`) or the closing (`}`) characters of an auto-insert combo - with the `character` - * @returns {String[]} the auto insert pair if found, else - */ - function searchAutoInsertChars(character, searchCharIndex) { - const arr = Data.charsToAutoInsertUserList, - defaultReturn = ["", ""]; - - for (let i = 0, len = arr.length; i < len; i++) { - if (arr[i][searchCharIndex] === character) { - return arr[i]; - } - } - - return defaultReturn; - } - - // auto-insert character functionality - function insertCharacter(node, characterStart, characterEnd) { - if (isContentEditable(node)) { - insertCharacterContentEditable(node, characterStart, characterEnd); - } else { - let text = node.value, - startPos = node.selectionStart, - endPos = node.selectionEnd, - textBefore = text.substring(0, startPos), - textMid = text.substring(startPos, endPos), - textAfter = text.substring(endPos), - // handle trailing spaces - trimmedSelection = textMid.match(/^(\s*)(\S?(?:.|\n|\r)*\S)(\s*)$/) || [ - "", - "", - "", - "", - ]; - - textBefore += trimmedSelection[1]; - textAfter = trimmedSelection[3] + textAfter; - textMid = trimmedSelection[2]; - - textMid = Data.wrapSelectionAutoInsert ? textMid : ""; - startPos = textBefore.length + +!!characterEnd; - endPos = startPos + textMid.length; - - node.value = textBefore + characterStart + textMid + (characterEnd || "") + textAfter; - node.selectionStart = startPos; - node.selectionEnd = endPos; - triggerFakeInput(node); - } - } - - function insertSingleCharacterContentEditable( - rangeNode, - position, - singleCharacter, - isStart, - wasRangeCollapsed, - ) { - let textNode, - positionIncrement = isStart ? 1 : 0; - triggerFakeInput(rangeNode); - if (rangeNode.nodeType !== 3) { - textNode = document.createTextNode(singleCharacter); - rangeNode.insertBefore(textNode, rangeNode.childNodes[position]); - return [positionIncrement, textNode]; - } - - const value = rangeNode.textContent, - len = value.length; - - // do not shift whitespaces if there actually was no selection - if (Data.wrapSelectionAutoInsert && !wasRangeCollapsed) { - if (isStart) { - while (/\s/.test(value[position]) && position < len) { - position++; - } - } else { - // value[position] corresponds to one character out of the current selection - while (/\s/.test(value[position - 1]) && position >= 1) { - position--; - } - } - } - - // HTML entities are for HTML, so use \xA0 to insert   - rangeNode.textContent = value.substring(0, position) - + singleCharacter.replace(/ /g, "\xA0") - + value.substring(position); - - return [position + positionIncrement, rangeNode]; - } - - function insertCharacterContentEditable(node, characterStart, characterEnd) { - let win = getNodeWindow(node), - sel = win.getSelection(), - range = sel.getRangeAt(0), - startNode, - endNode, - startPosition, - endPosition, - newStartNode, - newEndNode, - rangeWasCollapsed = range.collapsed, - singleCharacterReturnValue; - - if (!Data.wrapSelectionAutoInsert) { - range.deleteContents(); - } - - startNode = range.startContainer; - endNode = range.endContainer; - startPosition = range.startOffset; - endPosition = range.endOffset; - - // the rangeNode is a textnode EXCEPT when the node has no text - // eg:https://stackoverflow.com/a/5258024 - singleCharacterReturnValue = insertSingleCharacterContentEditable( - startNode, - startPosition, - characterStart, - true, - rangeWasCollapsed, - ); - [startPosition, newStartNode] = singleCharacterReturnValue; - - // because this method is also used for inserting single tabkey - if (characterEnd) { - // they just inserted a character above - if (startNode === endNode) { - endPosition++; - } - - singleCharacterReturnValue = insertSingleCharacterContentEditable( - endNode, - endPosition, - characterEnd, - false, - rangeWasCollapsed, - ); - [endPosition, newEndNode] = singleCharacterReturnValue; - } else { - startPosition--; - } - - range.setStart(newStartNode, startPosition); - if (characterEnd) { - range.setEnd(newEndNode, endPosition); - } - sel.removeAllRanges(); - sel.addRange(range); - } - // returns user-selected text in content-editable element function getUserSelection(node) { let win, @@ -767,16 +598,13 @@ primitiveExtender(); // with their required value after `=` has been pressed // which has been detected by handleKeyPress function provideDoubleBracketFunctionality(node, win) { - let sel = win.getSelection(), + const sel = win.getSelection(), range = sel.getRangeAt(0), rangeNode = range.startContainer, isCENode = isContentEditable(node), caretPos = isCENode ? range.endOffset : node.selectionEnd, - value = isCENode ? rangeNode.textContent : node.value, - operate = true, - evaluatedValue, - valueToSet, - caretPosToSet, + value = isCENode ? rangeNode.textContent : node.value; + let operate = true, i = caretPos; // check closing brackets are present @@ -794,8 +622,8 @@ primitiveExtender(); } if (operate) { - evaluatedValue = evaluateDoubleBrackets(value, i + 1, caretPos); - [valueToSet, caretPosToSet] = evaluatedValue; + const evaluatedValue = evaluateDoubleBrackets(value, i + 1, caretPos), + [valueToSet, caretPosToSet] = evaluatedValue; if (isCENode) { triggerFakeInput(rangeNode); diff --git a/js/pre.js b/js/pre.js index 904af85..279009a 100644 --- a/js/pre.js +++ b/js/pre.js @@ -309,6 +309,16 @@ function gTranlateImmune(text) { return `${text}`; } + +// in certain web apps, like mailchimp +// node refers to the editor inside iframe +// while `window` refers to top level window +// so selection and other methods do not work +// hence the need to get the `node's window` +function getNodeWindow(node) { + return node.ownerDocument.defaultView; +} + export { q, qCls, @@ -334,4 +344,5 @@ export { PRIMITIVES_EXT_KEY, appendBlobToLink, gTranlateImmune, + getNodeWindow, }; From 663f30fc8798217d2d7c2acf7ce4f3927f82b986 Mon Sep 17 00:00:00 2001 From: GaurangTandon <1gaurangtandon@gmail.com> Date: Tue, 25 Jun 2019 16:07:35 +0530 Subject: [PATCH 3/5] refactorminor, now verify #200 tests --- js/autoInsertFunctionality.js | 44 ++++++++++++++++------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/js/autoInsertFunctionality.js b/js/autoInsertFunctionality.js index ce4f964..1cb1950 100644 --- a/js/autoInsertFunctionality.js +++ b/js/autoInsertFunctionality.js @@ -24,7 +24,7 @@ function searchAutoInsertChars(character, searchCharIndex) { // non-breaking space is useful when inserting four concsecutive for tab character // HTML entities are for HTML nodes, so use \xA0 to insert   -function makeSpaceNonBreaking(string) { +function makeSpaceNonBreakingTextnode(string) { return string.replace(/ /g, "\xA0"); } @@ -48,6 +48,11 @@ function moveBackwardForSpaces(range, textNode, position) { range.setEnd(textNode, position); } +/** + * for content editable node, when selection is "| abc |" + * this moves the carets so that it becomes " |abc| " + * @param {Range} range + */ function moveSelectionForSurroundingWhitespace(range) { if (isTextNode(range.startContainer)) { moveForwardForSpaces(range, range.startContainer, range.startOffset); @@ -81,12 +86,6 @@ function modifySelection(sel, range) { sel.addRange(range); } -// function setSamePosInTextNodeAndSave(sel, range, textNode, pos) { -// range.setStart(textNode, pos); -// range.setEnd(textNode, pos); -// modifySelection(sel, range); -// } - function insertTextInNode(textNode, text, atPos) { const valBefore = textNode.textContent.substr(0, atPos), valAfter = textNode.textContent.substr(atPos); @@ -103,6 +102,7 @@ function insertTextInNode(textNode, text, atPos) { function insertSingleCharacterContentEditable(range, content, isStart = true, increment) { let textNode, startPos; + if (isStart) { if (!isTextNode(range.startContainer)) { // range node is an element node when it is empty @@ -112,23 +112,20 @@ function insertSingleCharacterContentEditable(range, content, isStart = true, in textNode = range.startContainer; startPos = range.startOffset; } + } else if (!isTextNode(range.endContainer)) { + // range node is an element node when it is empty + textNode = createNewTextNodeForAutoInsert(range, "", false); + startPos = 0; + } else { + textNode = range.endContainer; + startPos = range.endOffset; + } - if (isTextNode(textNode)) { - insertTextInNode(textNode, content, startPos); + if (isTextNode(textNode)) { + insertTextInNode(textNode, content, startPos); + if (isStart) { range.setStart(textNode, startPos + increment); - } - } else { - if (!isTextNode(range.endContainer)) { - // range node is an element node when it is empty - textNode = createNewTextNodeForAutoInsert(range, "", false); - startPos = 0; } else { - textNode = range.endContainer; - startPos = range.endOffset; - } - - if (isTextNode(textNode)) { - insertTextInNode(textNode, content, startPos); range.setEnd(textNode, startPos + increment); } } @@ -149,9 +146,9 @@ function insertCharacterContentEditable(node, characterStart, characterEnd) { caretIncrement; // process the characters and their positioning - characterStart = makeSpaceNonBreaking(characterStart); + characterStart = makeSpaceNonBreakingTextnode(characterStart); if (characterEnd) { - characterEnd = makeSpaceNonBreaking(characterEnd); + characterEnd = makeSpaceNonBreakingTextnode(characterEnd); textnodeString = characterStart + characterEnd; caretIncrement = 1; } else { @@ -166,7 +163,6 @@ function insertCharacterContentEditable(node, characterStart, characterEnd) { range.deleteContents(); insertSingleCharacterContentEditable(range, textnodeString, true, caretIncrement); - modifySelection(); } else { moveSelectionForSurroundingWhitespace(range); insertSingleCharacterContentEditable(range, characterStart, true, characterStart.length); From e6ea05355210a0ecac632d1d889518f0b8a55098 Mon Sep 17 00:00:00 2001 From: GaurangTandon <1gaurangtandon@gmail.com> Date: Tue, 25 Jun 2019 21:25:38 +0530 Subject: [PATCH 4/5] finally it works :) #287 --- js/autoInsertFunctionality.js | 37 ++++++++++++++++++++++++++++++----- js/detector.js | 14 +++---------- js/pre.js | 24 ++++++++++++++++++----- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/js/autoInsertFunctionality.js b/js/autoInsertFunctionality.js index 1cb1950..95f518d 100644 --- a/js/autoInsertFunctionality.js +++ b/js/autoInsertFunctionality.js @@ -1,6 +1,8 @@ /* global Data */ -import { isContentEditable, isTextNode, getNodeWindow } from "./pre"; +import { + isContentEditable, isTextNode, getNodeWindow, triggerFakeInput, +} from "./pre"; /** * @param {String} character to match @@ -86,11 +88,32 @@ function modifySelection(sel, range) { sel.addRange(range); } -function insertTextInNode(textNode, text, atPos) { +/** + * this inserts text into textnode at specified position + * however, this resets the node's range object + * pass in a range object if you want to preserve it + * @param {Text} textNode + * @param {String} text + * @param {Number} atPos + * @param {Range} [range] + */ +function insertTextInNode(textNode, text, atPos, range = false) { const valBefore = textNode.textContent.substr(0, atPos), - valAfter = textNode.textContent.substr(atPos); + valAfter = textNode.textContent.substr(atPos), + // sorry for hardcoding but Object.keys/etc. yielded no positives + // no need to use all props + propsToCopy = ["endContainer", "endOffset", "startContainer", "startOffset"], + oldRange = {}; + if (range) { for (const prop of propsToCopy) { oldRange[prop] = range[prop]; } } textNode.textContent = valBefore + text + valAfter; + + if (range) { + // trying to directly assign oldRange back into range results in error + // as properties are read-only + range.setStart(oldRange.startContainer, oldRange.startOffset); + range.setEnd(oldRange.endContainer, oldRange.endOffset); + } } /** @@ -118,11 +141,12 @@ function insertSingleCharacterContentEditable(range, content, isStart = true, in startPos = 0; } else { textNode = range.endContainer; - startPos = range.endOffset; + // move one step ahead to accommodate the previously inserted character + startPos = range.endOffset + (range.startContainer === range.endContainer); } if (isTextNode(textNode)) { - insertTextInNode(textNode, content, startPos); + insertTextInNode(textNode, content, startPos, range); if (isStart) { range.setStart(textNode, startPos + increment); } else { @@ -172,6 +196,8 @@ function insertCharacterContentEditable(node, characterStart, characterEnd) { } modifySelection(sel, range); + triggerFakeInput(range.startContainer); + triggerFakeInput(range.endContainer); } /** @@ -209,6 +235,7 @@ function insertCharacter(node, characterStart, characterEnd) { node.value = textBefore + characterStart + textMid + (characterEnd || "") + textAfter; node.selectionStart = startPos; node.selectionEnd = endPos; + triggerFakeInput(node); } } diff --git a/js/detector.js b/js/detector.js index 452d5a1..d27aff1 100644 --- a/js/detector.js +++ b/js/detector.js @@ -9,6 +9,7 @@ import { isBlockedSite, PRIMITIVES_EXT_KEY, getNodeWindow, + triggerFakeInput, } from "./pre"; import { Folder, Snip } from "./snippetClasses"; import { DBget } from "./commonDataHandlers"; @@ -291,15 +292,6 @@ primitiveExtender(); } } - function triggerFakeInput($elm) { - $elm.dispatchEvent( - new Event("input", { - cancelable: true, - bubbles: true, - }), - ); - } - function checkPlaceholdersInContentEditableNode() { let pArr = Placeholder.array, currND; @@ -626,16 +618,16 @@ primitiveExtender(); [valueToSet, caretPosToSet] = evaluatedValue; if (isCENode) { - triggerFakeInput(rangeNode); rangeNode.textContent = valueToSet; range.setStart(rangeNode, caretPosToSet); range.setEnd(rangeNode, caretPosToSet); sel.removeAllRanges(); sel.addRange(range); + triggerFakeInput(rangeNode); } else { - triggerFakeInput(node); node.value = valueToSet; node.selectionStart = node.selectionEnd = caretPosToSet; + triggerFakeInput(node); } } } diff --git a/js/pre.js b/js/pre.js index 279009a..9fd9861 100644 --- a/js/pre.js +++ b/js/pre.js @@ -310,15 +310,28 @@ function gTranlateImmune(text) { } -// in certain web apps, like mailchimp -// node refers to the editor inside iframe -// while `window` refers to top level window -// so selection and other methods do not work -// hence the need to get the `node's window` +/** + * in certain web apps, like mailchimp + * node refers to the editor inside iframe + * while `window` refers to top level window + * so selection and other methods do not work + * hence the need to get the `node's window` + * + * @param {Element} node + */ function getNodeWindow(node) { return node.ownerDocument.defaultView; } +function triggerFakeInput($elm) { + $elm.dispatchEvent( + new Event("input", { + cancelable: true, + bubbles: true, + }), + ); +} + export { q, qCls, @@ -345,4 +358,5 @@ export { appendBlobToLink, gTranlateImmune, getNodeWindow, + triggerFakeInput, }; From e3e29b768cb447d1f20b0b2677168e086b7a0398 Mon Sep 17 00:00:00 2001 From: GaurangTandon <1gaurangtandon@gmail.com> Date: Tue, 25 Jun 2019 21:56:48 +0530 Subject: [PATCH 5/5] fixed for successive image elements used in auto inserts --- js/autoInsertFunctionality.js | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/js/autoInsertFunctionality.js b/js/autoInsertFunctionality.js index 95f518d..91a8ae8 100644 --- a/js/autoInsertFunctionality.js +++ b/js/autoInsertFunctionality.js @@ -74,12 +74,12 @@ function createNewTextNodeForAutoInsert(range, textnodeString, isStart) { const textNode = document.createTextNode(textnodeString); if (isStart) { range.startContainer.insertBefore(textNode, range.startContainer.childNodes[range.startOffset]); + range.setStart(textNode, 0); } else { range.endContainer.insertBefore(textNode, range.endContainer.childNodes[range.endOffset]); + range.setEnd(textNode, 0); } - range.setStart(textNode, 0); - range.setEnd(textNode, 0); return textNode; } @@ -129,7 +129,7 @@ function insertSingleCharacterContentEditable(range, content, isStart = true, in if (isStart) { if (!isTextNode(range.startContainer)) { // range node is an element node when it is empty - textNode = createNewTextNodeForAutoInsert(range, ""); + textNode = createNewTextNodeForAutoInsert(range, "", true); startPos = 0; } else { textNode = range.startContainer; @@ -145,13 +145,11 @@ function insertSingleCharacterContentEditable(range, content, isStart = true, in startPos = range.endOffset + (range.startContainer === range.endContainer); } - if (isTextNode(textNode)) { - insertTextInNode(textNode, content, startPos, range); - if (isStart) { - range.setStart(textNode, startPos + increment); - } else { - range.setEnd(textNode, startPos + increment); - } + insertTextInNode(textNode, content, startPos, range); + if (isStart) { + range.setStart(textNode, startPos + increment); + } else { + range.setEnd(textNode, startPos + increment); } }