2 var dom = wysihtml5.dom,
3 // Following elements are grouped
4 // when the caret is within a H1 and the H4 is invoked, the H1 should turn into H4
5 // instead of creating a H4 within a H1 which would result in semantically invalid html
6 BLOCK_ELEMENTS_GROUP = ["H1", "H2", "H3", "H4", "H5", "H6", "P", "PRE", "DIV"];
9 * Remove similiar classes (based on classRegExp)
10 * and add the desired class name
12 function _addClass(element, className, classRegExp) {
13 if (element.className) {
14 _removeClass(element, classRegExp);
15 element.className = wysihtml5.lang.string(element.className + " " + className).trim();
17 element.className = className;
21 function _addStyle(element, cssStyle, styleRegExp) {
22 _removeStyle(element, styleRegExp);
23 if (element.getAttribute('style')) {
24 element.setAttribute('style', wysihtml5.lang.string(element.getAttribute('style') + " " + cssStyle).trim());
26 element.setAttribute('style', cssStyle);
30 function _removeClass(element, classRegExp) {
31 var ret = classRegExp.test(element.className);
32 element.className = element.className.replace(classRegExp, "");
33 if (wysihtml5.lang.string(element.className).trim() == '') {
34 element.removeAttribute('class');
39 function _removeStyle(element, styleRegExp) {
40 var ret = styleRegExp.test(element.getAttribute('style'));
41 element.setAttribute('style', (element.getAttribute('style') || "").replace(styleRegExp, ""));
42 if (wysihtml5.lang.string(element.getAttribute('style') || "").trim() == '') {
43 element.removeAttribute('style');
48 function _removeLastChildIfLineBreak(node) {
49 var lastChild = node.lastChild;
50 if (lastChild && _isLineBreak(lastChild)) {
51 lastChild.parentNode.removeChild(lastChild);
55 function _isLineBreak(node) {
56 return node.nodeName === "BR";
60 * Execute native query command
61 * and if necessary modify the inserted node's className
63 function _execCommand(doc, composer, command, nodeName, className) {
64 var ranges = composer.selection.getOwnRanges();
65 for (var i = ranges.length; i--;){
66 composer.selection.getSelection().removeAllRanges();
67 composer.selection.setSelection(ranges[i]);
69 var eventListener = dom.observe(doc, "DOMNodeInserted", function(event) {
70 var target = event.target,
72 if (target.nodeType !== wysihtml5.ELEMENT_NODE) {
75 displayStyle = dom.getStyle("display").from(target);
76 if (displayStyle.substr(0, 6) !== "inline") {
77 // Make sure that only block elements receive the given class
78 target.className += " " + className;
82 doc.execCommand(command, false, nodeName);
90 function _selectionWrap(composer, options) {
91 if (composer.selection.isCollapsed()) {
92 composer.selection.selectLine();
95 var surroundedNodes = composer.selection.surround(options);
96 for (var i = 0, imax = surroundedNodes.length; i < imax; i++) {
97 wysihtml5.dom.lineBreaks(surroundedNodes[i]).remove();
98 _removeLastChildIfLineBreak(surroundedNodes[i]);
101 // rethink restoring selection
102 // composer.selection.selectNode(element, wysihtml5.browser.displaysCaretInEmptyContentEditableCorrectly());
105 function _hasClasses(element) {
106 return !!wysihtml5.lang.string(element.className).trim();
109 function _hasStyles(element) {
110 return !!wysihtml5.lang.string(element.getAttribute('style') || '').trim();
113 wysihtml5.commands.formatBlock = {
114 exec: function(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp) {
115 var doc = composer.doc,
116 blockElements = this.state(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp),
117 useLineBreaks = composer.config.useLineBreaks,
118 defaultNodeName = useLineBreaks ? "DIV" : "P",
119 selectedNodes, classRemoveAction, blockRenameFound, styleRemoveAction, blockElement;
120 nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
122 if (blockElements.length) {
123 composer.selection.executeAndRestoreRangy(function() {
124 for (var b = blockElements.length; b--;) {
126 classRemoveAction = _removeClass(blockElements[b], classRegExp);
129 styleRemoveAction = _removeStyle(blockElements[b], styleRegExp);
132 if ((styleRemoveAction || classRemoveAction) && nodeName === null && blockElements[b].nodeName != defaultNodeName) {
133 // dont rename or remove element when just setting block formating class or style
137 var hasClasses = _hasClasses(blockElements[b]),
138 hasStyles = _hasStyles(blockElements[b]);
140 if (!hasClasses && !hasStyles && (useLineBreaks || nodeName === "P")) {
141 // Insert a line break afterwards and beforewards when there are siblings
142 // that are not of type line break or block element
143 wysihtml5.dom.lineBreaks(blockElements[b]).add();
144 dom.replaceWithChildNodes(blockElements[b]);
146 // Make sure that styling is kept by renaming the element to a <div> or <p> and copying over the class name
147 dom.renameElement(blockElements[b], nodeName === "P" ? "DIV" : defaultNodeName);
155 // Find similiar block element and rename it (<h2 class="foo"></h2> => <h1 class="foo"></h1>)
156 if (nodeName === null || wysihtml5.lang.array(BLOCK_ELEMENTS_GROUP).contains(nodeName)) {
157 selectedNodes = composer.selection.findNodesInSelection(BLOCK_ELEMENTS_GROUP).concat(composer.selection.getSelectedOwnNodes());
158 composer.selection.executeAndRestoreRangy(function() {
159 for (var n = selectedNodes.length; n--;) {
160 blockElement = dom.getParentElement(selectedNodes[n], {
161 nodeName: BLOCK_ELEMENTS_GROUP
163 if (blockElement == composer.element) {
167 // Rename current block element to new block element and add class
169 blockElement = dom.renameElement(blockElement, nodeName);
172 _addClass(blockElement, className, classRegExp);
175 _addStyle(blockElement, cssStyle, styleRegExp);
177 blockRenameFound = true;
183 if (blockRenameFound) {
188 _selectionWrap(composer, {
189 "nodeName": (nodeName || defaultNodeName),
190 "className": className || null,
191 "cssStyle": cssStyle || null
195 state: function(composer, command, nodeName, className, classRegExp, cssStyle, styleRegExp) {
196 var nodes = composer.selection.getSelectedOwnNodes(),
200 nodeName = typeof(nodeName) === "string" ? nodeName.toUpperCase() : nodeName;
202 //var selectedNode = composer.selection.getSelectedNode();
203 for (var i = 0, maxi = nodes.length; i < maxi; i++) {
204 parent = dom.getParentElement(nodes[i], {
206 className: className,
207 classRegExp: classRegExp,
209 styleRegExp: styleRegExp
211 if (parent && wysihtml5.lang.array(parents).indexOf(parent) == -1) {
212 parents.push(parent);
215 if (parents.length == 0) {