Skip to content

Commit 45be328

Browse files
committed
fix: bold italic type of tags break inside live preview and leaks internal attributes
1 parent 9e54784 commit 45be328

2 files changed

Lines changed: 64 additions & 7 deletions

File tree

src/LiveDevelopment/BrowserScripts/RemoteFunctions.js

Lines changed: 52 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1520,6 +1520,7 @@ function RemoteFunctions(config) {
15201520

15211521
// Save the original content for potential cancellation
15221522
element._originalContent = element.innerHTML;
1523+
element._originalTextContent = element.textContent;
15231524

15241525
// Add event listeners for editing
15251526
function onBlur() {
@@ -1549,6 +1550,53 @@ function RemoteFunctions(config) {
15491550
};
15501551
}
15511552

1553+
// this function is to remove the properties from elements before getting the innerHTML
1554+
// then add all the properties back to the elements
1555+
function cleanupElementProperties(element) {
1556+
// a temporary container to hold a clean copy
1557+
const tempContainer = document.createElement('div');
1558+
tempContainer.innerHTML = element.innerHTML;
1559+
1560+
// get all elements in the temporary container
1561+
const allElements = tempContainer.querySelectorAll('*');
1562+
1563+
// Store original attributes for later restoration
1564+
const originalAttributes = new Map();
1565+
const elementsInOriginal = element.querySelectorAll('*');
1566+
1567+
// Save original attributes
1568+
elementsInOriginal.forEach((el, index) => {
1569+
const attrs = {};
1570+
for (let i = 0; i < el.attributes.length; i++) {
1571+
const attr = el.attributes[i];
1572+
attrs[attr.name] = attr.value;
1573+
}
1574+
originalAttributes.set(index, attrs);
1575+
});
1576+
1577+
// Remove all attributes from elements in the temp container
1578+
allElements.forEach(el => {
1579+
while (el.attributes.length > 0) {
1580+
el.removeAttribute(el.attributes[0].name);
1581+
}
1582+
});
1583+
1584+
// Get the clean HTML content
1585+
const cleanContent = tempContainer.innerHTML;
1586+
1587+
// Restore original attributes to the actual elements
1588+
elementsInOriginal.forEach((el, index) => {
1589+
if (originalAttributes.has(index)) {
1590+
const attrs = originalAttributes.get(index);
1591+
for (const [name, value] of Object.entries(attrs)) {
1592+
el.setAttribute(name, value);
1593+
}
1594+
}
1595+
});
1596+
1597+
return cleanContent;
1598+
}
1599+
15521600
// Function to finish editing and apply changes
15531601
function finishEditing(element) {
15541602
if (!element || !element.hasAttribute("contenteditable")) {
@@ -1565,8 +1613,8 @@ function RemoteFunctions(config) {
15651613
delete element._editListeners;
15661614
}
15671615

1568-
// Get the new content
1569-
const newContent = element.innerHTML;
1616+
// Get the new content after cleaning up unwanted properties
1617+
const newContent = cleanupElementProperties(element);
15701618

15711619
// If content has changed, send the edit to the editor
15721620
if (newContent !== element._originalContent && element.hasAttribute("data-brackets-id")) {
@@ -1575,6 +1623,7 @@ function RemoteFunctions(config) {
15751623
livePreviewEditEnabled: true,
15761624
element: element,
15771625
oldContent: element._originalContent,
1626+
oldTextContent: element._originalTextContent,
15781627
newContent: newContent,
15791628
tagId: Number(tagId),
15801629
livePreviewTextEdit: true
@@ -1583,6 +1632,7 @@ function RemoteFunctions(config) {
15831632

15841633
// Clean up
15851634
delete element._originalContent;
1635+
delete element._originalTextContent;
15861636
}
15871637

15881638
// init

src/LiveDevelopment/livePreviewEdit.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,21 @@ define(function (require, exports, module) {
3232

3333
// this is the actual source code for the element that we need to duplicate
3434
const text = editor.getTextBetween(range.from, range.to);
35+
// remove the <b>, <i> and <u> tags from the text,
36+
// this is done because we split the text using the textContent and not the innerHTML
37+
// and textContent doesn't have all these tags
38+
const cleanedText = text.replace(/<\/?(b|i|u)>/gi, "");
39+
3540
// split the text as we want to remove the old content from the source code
3641
// for ex: if we have <h1>hello</h1> then splitting from hello will give us [<h1>, </h1>]
37-
const splittedText = text.split(message.oldContent);
38-
39-
// so now we just merge the whole thing back replacing the old content with the new one
40-
const finalText = splittedText[0] + message.newContent + splittedText[1];
42+
const splittedText = cleanedText.split(message.oldTextContent);
4143

42-
editor.replaceRange(finalText, range.from, range.to);
44+
// make sure that the split was successful
45+
if (splittedText.length === 2) {
46+
// so now we just merge the whole thing back replacing the old text content with the new one
47+
const finalText = splittedText[0] + message.newContent + splittedText[1];
48+
editor.replaceRange(finalText, range.from, range.to);
49+
}
4350
}
4451

4552
/**

0 commit comments

Comments
 (0)