Skip to content
This repository was archived by the owner on Mar 8, 2019. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ JS_FILES = src/wysihtml5.js \
src/quirks/clean_pasted_html.js \
src/quirks/ensure_proper_clearing.js \
src/quirks/get_correct_inner_html.js \
src/quirks/insert_line_break_on_return.js \
src/quirks/redraw.js \
src/selection/selection.js \
src/selection/html_applier.js \
Expand Down
10 changes: 6 additions & 4 deletions examples/advanced.html
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@

#toolbar,
textarea {
width: 900px;
width: 920px;
padding: 5px;
-webkit-box-sizing: border-box;
-ms-box-sizing: border-box;
Expand Down Expand Up @@ -91,6 +91,7 @@ <h1>wysihtml5 - Advanced Editor Example</h1>
<a data-wysihtml5-command="insertImage">insert image</a> |
<a data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h1">h1</a> |
<a data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="h2">h2</a> |
<a data-wysihtml5-command="formatBlock" data-wysihtml5-command-value="p">p</a> |
<a data-wysihtml5-command="insertUnorderedList">insertUnorderedList</a> |
<a data-wysihtml5-command="insertOrderedList">insertOrderedList</a> |
<a data-wysihtml5-command="foreColor" data-wysihtml5-command-value="red">red</a> |
Expand Down Expand Up @@ -139,9 +140,10 @@ <h2>Events:</h2>
<script src="../dist/wysihtml5-0.3.0.js"></script>
<script>
var editor = new wysihtml5.Editor("textarea", {
toolbar: "toolbar",
stylesheets: "css/stylesheet.css",
parserRules: wysihtml5ParserRules
toolbar: "toolbar",
stylesheets: "css/stylesheet.css",
parserRules: wysihtml5ParserRules,
useLineBreaks: false
});

var log = document.getElementById("log");
Expand Down
5 changes: 3 additions & 2 deletions examples/simple.html
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,9 @@ <h2>Events:</h2>
<script src="../dist/wysihtml5-0.3.0.js"></script>
<script>
var editor = new wysihtml5.Editor("textarea", {
toolbar: "toolbar",
parserRules: wysihtml5ParserRules
toolbar: "toolbar",
parserRules: wysihtml5ParserRules,
useLineBreaks: false
});

var log = document.getElementById("log");
Expand Down
24 changes: 13 additions & 11 deletions src/browser.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ wysihtml5.browser = (function() {
* Firefox sometimes shows a huge caret in the beginning after focusing
*/
displaysCaretInEmptyContentEditableCorrectly: function() {
return !isGecko;
return isIE;
},

/**
Expand Down Expand Up @@ -167,8 +167,8 @@ wysihtml5.browser = (function() {
// When inserting unordered or ordered lists in Firefox, Chrome or Safari, the current selection or line gets
// converted into a list (<ul><li>...</li></ul>, <ol><li>...</li></ol>)
// IE and Opera act a bit different here as they convert the entire content of the current block element into a list
"insertUnorderedList": isIE || isOpera || isWebKit,
"insertOrderedList": isIE || isOpera || isWebKit
"insertUnorderedList": isIE || isWebKit,
"insertOrderedList": isIE || isWebKit
};

// Firefox throws errors for queryCommandSupported, so we have to build up our own object of supported commands
Expand Down Expand Up @@ -240,14 +240,6 @@ wysihtml5.browser = (function() {
return isGecko || isIE || isOpera;
},

/**
* When the caret is in an empty list (<ul><li>|</li></ul>) which is the first child in an contentEditable container
* pressing backspace doesn't remove the entire list as done in other browsers
*/
clearsListsInContentEditableCorrectly: function() {
return isGecko || isIE || isWebKit;
},

/**
* All browsers except Safari and Chrome automatically scroll the range/caret position into view
*/
Expand Down Expand Up @@ -335,6 +327,16 @@ wysihtml5.browser = (function() {

hasUndoInContextMenu: function() {
return isGecko || isChrome || isOpera;
},

/**
* Opera sometimes doesn't insert the node at the right position when range.insertNode(someNode)
* is used (regardless if rangy or native)
* This especially happens when the caret is positioned right after a <br> because then
* insertNode() will insert the node right before the <br>
*/
hasInsertNodeIssue: function() {
return isOpera;
}
};
})();
27 changes: 14 additions & 13 deletions src/commands/formatBlock.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
(function(wysihtml5) {
var dom = wysihtml5.dom,
DEFAULT_NODE_NAME = "DIV",
// Following elements are grouped
// when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
// instead of creating a H4 within a H1 which would result in semantically invalid html
BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "BLOCKQUOTE", DEFAULT_NODE_NAME];
BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "BLOCKQUOTE", "DIV"];

/**
* Remove similiar classes (based on classRegExp)
Expand Down Expand Up @@ -141,7 +140,7 @@
composer.selection.surround(element);
_removeLineBreakBeforeAndAfter(element);
_removeLastChildIfLineBreak(element);
composer.selection.selectNode(element);
composer.selection.selectNode(element, wysihtml5.browser.displaysCaretInEmptyContentEditableCorrectly());
}

function _hasClasses(element) {
Expand All @@ -150,26 +149,28 @@

wysihtml5.commands.formatBlock = {
exec: function(composer, command, nodeName, className, classRegExp) {
var doc = composer.doc,
blockElement = this.state(composer, command, nodeName, className, classRegExp),
var doc = composer.doc,
blockElement = this.state(composer, command, nodeName, className, classRegExp),
useLineBreaks = composer.config.useLineBreaks,
defaultNodeName = useLineBreaks ? "DIV" : "P",
selectedNode;

nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;

if (blockElement) {
composer.selection.executeAndRestoreSimple(function() {
if (classRegExp) {
_removeClass(blockElement, classRegExp);
}
var hasClasses = _hasClasses(blockElement);
if (!hasClasses && blockElement.nodeName === (nodeName || DEFAULT_NODE_NAME)) {
if (!hasClasses && (useLineBreaks || nodeName === "P")) {
// Insert a line break afterwards and beforewards when there are siblings
// that are not of type line break or block element
_addLineBreakBeforeAndAfter(blockElement);
dom.replaceWithChildNodes(blockElement);
} else if (hasClasses) {
// Make sure that styling is kept by renaming the element to <div> and copying over the class name
dom.renameElement(blockElement, DEFAULT_NODE_NAME);
} else {
// Make sure that styling is kept by renaming the element to a <div> or <p> and copying over the class name
dom.renameElement(blockElement, nodeName === "P" ? "DIV" : defaultNodeName);
}
});
return;
Expand All @@ -183,7 +184,7 @@
});

if (blockElement) {
composer.selection.executeAndRestoreSimple(function() {
composer.selection.executeAndRestore(function() {
// Rename current block element to new block element and add class
if (nodeName) {
blockElement = dom.renameElement(blockElement, nodeName);
Expand All @@ -197,11 +198,11 @@
}

if (composer.commands.support(command)) {
_execCommand(doc, command, nodeName || DEFAULT_NODE_NAME, className);
_execCommand(doc, command, nodeName || defaultNodeName, className);
return;
}

blockElement = doc.createElement(nodeName || DEFAULT_NODE_NAME);
blockElement = doc.createElement(nodeName || defaultNodeName);
if (className) {
blockElement.className = className;
}
Expand Down
12 changes: 6 additions & 6 deletions src/commands/insertOrderedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ wysihtml5.commands.insertOrderedList = {
isEmpty,
tempElement;

if (composer.commands.support(command)) {
if (!list && !otherList && composer.commands.support(command)) {
doc.execCommand(command, false, null);
return;
}
Expand All @@ -18,27 +18,27 @@ wysihtml5.commands.insertOrderedList = {
// <ol><li>foo</li><li>bar</li></ol>
// becomes:
// foo<br>bar<br>
composer.selection.executeAndRestoreSimple(function() {
wysihtml5.dom.resolveList(list);
composer.selection.executeAndRestore(function() {
wysihtml5.dom.resolveList(list, composer.config.useLineBreaks);
});
} else if (otherList) {
// Turn an unordered list into an ordered list
// <ul><li>foo</li><li>bar</li></ul>
// becomes:
// <ol><li>foo</li><li>bar</li></ol>
composer.selection.executeAndRestoreSimple(function() {
composer.selection.executeAndRestore(function() {
wysihtml5.dom.renameElement(otherList, "ol");
});
} else {
// Create list
composer.commands.exec("formatBlock", "div", tempClassName);
tempElement = doc.querySelector("." + tempClassName);
isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE || tempElement.innerHTML === "<br>";
composer.selection.executeAndRestoreSimple(function() {
composer.selection.executeAndRestore(function() {
list = wysihtml5.dom.convertToList(tempElement, "ol");
});
if (isEmpty) {
composer.selection.selectNode(list.querySelector("li"));
composer.selection.selectNode(list.querySelector("li"), true);
}
}
},
Expand Down
12 changes: 6 additions & 6 deletions src/commands/insertUnorderedList.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ wysihtml5.commands.insertUnorderedList = {
isEmpty,
tempElement;

if (composer.commands.support(command)) {
if (!list && !otherList && composer.commands.support(command)) {
doc.execCommand(command, false, null);
return;
}
Expand All @@ -18,27 +18,27 @@ wysihtml5.commands.insertUnorderedList = {
// <ul><li>foo</li><li>bar</li></ul>
// becomes:
// foo<br>bar<br>
composer.selection.executeAndRestoreSimple(function() {
wysihtml5.dom.resolveList(list);
composer.selection.executeAndRestore(function() {
wysihtml5.dom.resolveList(list, composer.config.useLineBreaks);
});
} else if (otherList) {
// Turn an ordered list into an unordered list
// <ol><li>foo</li><li>bar</li></ol>
// becomes:
// <ul><li>foo</li><li>bar</li></ul>
composer.selection.executeAndRestoreSimple(function() {
composer.selection.executeAndRestore(function() {
wysihtml5.dom.renameElement(otherList, "ul");
});
} else {
// Create list
composer.commands.exec("formatBlock", "div", tempClassName);
tempElement = doc.querySelector("." + tempClassName);
isEmpty = tempElement.innerHTML === "" || tempElement.innerHTML === wysihtml5.INVISIBLE_SPACE || tempElement.innerHTML === "<br>";
composer.selection.executeAndRestoreSimple(function() {
composer.selection.executeAndRestore(function() {
list = wysihtml5.dom.convertToList(tempElement, "ul");
});
if (isEmpty) {
composer.selection.selectNode(list.querySelector("li"));
composer.selection.selectNode(list.querySelector("li"), true);
}
}
},
Expand Down
4 changes: 4 additions & 0 deletions src/dom/convert_to_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,10 @@ wysihtml5.dom.convertToList = (function() {
currentListItem.appendChild(childNode);
}

if (childNodes.length === 0) {
_createListItem(doc, list);
}

element.parentNode.replaceChild(list, element);
return list;
}
Expand Down
19 changes: 12 additions & 7 deletions src/dom/insert_css.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@ wysihtml5.dom.insertCSS = function(rules) {

return {
into: function(doc) {
var head = doc.head || doc.getElementsByTagName("head")[0],
styleElement = doc.createElement("style");

var styleElement = doc.createElement("style");
styleElement.type = "text/css";

if (styleElement.styleSheet) {
styleElement.styleSheet.cssText = rules;
} else {
styleElement.appendChild(doc.createTextNode(rules));
}

if (head) {
head.appendChild(styleElement);

var link = doc.querySelector("head link");
if (link) {
link.parentNode.insertBefore(styleElement, link);
return;
} else {
var head = doc.querySelector("head");
if (head) {
head.appendChild(styleElement);
}
}
}
};
Expand Down
54 changes: 37 additions & 17 deletions src/dom/resolve_list.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@
element.appendChild(lineBreak);
}

function resolveList(list) {
if (list.nodeName !== "MENU" && list.nodeName !== "UL" && list.nodeName !== "OL") {
function resolveList(list, useLineBreaks) {
if (!list.nodeName.match(/^(MENU|UL|OL)$/)) {
return;
}

Expand All @@ -46,26 +46,46 @@
lastChild,
isLastChild,
shouldAppendLineBreak,
paragraph,
listItem;

if (previousSibling && !_isBlockElement(previousSibling)) {
_appendLineBreak(fragment);
}

while (listItem = list.firstChild) {
lastChild = listItem.lastChild;
while (firstChild = listItem.firstChild) {
isLastChild = firstChild === lastChild;
// This needs to be done before appending it to the fragment, as it otherwise will loose style information
shouldAppendLineBreak = isLastChild && !_isBlockElement(firstChild) && !_isLineBreak(firstChild);
fragment.appendChild(firstChild);
if (shouldAppendLineBreak) {
_appendLineBreak(fragment);
if (useLineBreaks) {
// Insert line break if list is after a non-block element
if (previousSibling && !_isBlockElement(previousSibling)) {
_appendLineBreak(fragment);
}

while (listItem = (list.firstElementChild || list.firstChild)) {
lastChild = listItem.lastChild;
while (firstChild = listItem.firstChild) {
isLastChild = firstChild === lastChild;
// This needs to be done before appending it to the fragment, as it otherwise will lose style information
shouldAppendLineBreak = isLastChild && !_isBlockElement(firstChild) && !_isLineBreak(firstChild);
fragment.appendChild(firstChild);
if (shouldAppendLineBreak) {
_appendLineBreak(fragment);
}
}

listItem.parentNode.removeChild(listItem);
}
} else {
while (listItem = (list.firstElementChild || list.firstChild)) {
if (listItem.querySelector && listItem.querySelector("div, p, ul, ol, menu, blockquote, h1, h2, h3, h4, h5, h6")) {
while (firstChild = listItem.firstChild) {
fragment.appendChild(firstChild);
}
} else {
paragraph = doc.createElement("p");
while (firstChild = listItem.firstChild) {
paragraph.appendChild(firstChild);
}
fragment.appendChild(paragraph);
}
listItem.parentNode.removeChild(listItem);
}

listItem.parentNode.removeChild(listItem);
}

list.parentNode.replaceChild(fragment, list);
}

Expand Down
Loading