You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
626 lines
22 KiB
626 lines
22 KiB
define([ |
|
"dojo/_base/declare", // declare |
|
"dojo/dom-construct", // domConstruct.destroy domConstruct.place |
|
"dojo/keys", // keys.ENTER |
|
"dojo/_base/lang", |
|
"dojo/on", |
|
"dojo/sniff", // has("ie") has("mozilla") has("webkit") |
|
"dojo/_base/window", // win.withGlobal |
|
"dojo/window", // winUtils.scrollIntoView |
|
"../_Plugin", |
|
"../RichText", |
|
"../range" |
|
], function(declare, domConstruct, keys, lang, on, has, win, winUtils, _Plugin, RichText, rangeapi){ |
|
|
|
// module: |
|
// dijit/_editor/plugins/EnterKeyHandling |
|
|
|
return declare("dijit._editor.plugins.EnterKeyHandling", _Plugin, { |
|
// summary: |
|
// This plugin tries to make all browsers behave consistently with regard to |
|
// how ENTER behaves in the editor window. It traps the ENTER key and alters |
|
// the way DOM is constructed in certain cases to try to commonize the generated |
|
// DOM and behaviors across browsers. |
|
// |
|
// description: |
|
// This plugin has three modes: |
|
// |
|
// - blockNodeForEnter=BR |
|
// - blockNodeForEnter=DIV |
|
// - blockNodeForEnter=P |
|
// |
|
// In blockNodeForEnter=P, the ENTER key starts a new |
|
// paragraph, and shift-ENTER starts a new line in the current paragraph. |
|
// For example, the input: |
|
// |
|
// | first paragraph <shift-ENTER> |
|
// | second line of first paragraph <ENTER> |
|
// | second paragraph |
|
// |
|
// will generate: |
|
// |
|
// | <p> |
|
// | first paragraph |
|
// | <br/> |
|
// | second line of first paragraph |
|
// | </p> |
|
// | <p> |
|
// | second paragraph |
|
// | </p> |
|
// |
|
// In BR and DIV mode, the ENTER key conceptually goes to a new line in the |
|
// current paragraph, and users conceptually create a new paragraph by pressing ENTER twice. |
|
// For example, if the user enters text into an editor like this: |
|
// |
|
// | one <ENTER> |
|
// | two <ENTER> |
|
// | three <ENTER> |
|
// | <ENTER> |
|
// | four <ENTER> |
|
// | five <ENTER> |
|
// | six <ENTER> |
|
// |
|
// It will appear on the screen as two 'paragraphs' of three lines each. Markupwise, this generates: |
|
// |
|
// BR: |
|
// | one<br/> |
|
// | two<br/> |
|
// | three<br/> |
|
// | <br/> |
|
// | four<br/> |
|
// | five<br/> |
|
// | six<br/> |
|
// |
|
// DIV: |
|
// | <div>one</div> |
|
// | <div>two</div> |
|
// | <div>three</div> |
|
// | <div> </div> |
|
// | <div>four</div> |
|
// | <div>five</div> |
|
// | <div>six</div> |
|
|
|
// blockNodeForEnter: String |
|
// This property decides the behavior of Enter key. It can be either P, |
|
// DIV, BR, or empty (which means disable this feature). Anything else |
|
// will trigger errors. The default is 'BR' |
|
// |
|
// See class description for more details. |
|
blockNodeForEnter: 'BR', |
|
|
|
constructor: function(args){ |
|
if(args){ |
|
if("blockNodeForEnter" in args){ |
|
args.blockNodeForEnter = args.blockNodeForEnter.toUpperCase(); |
|
} |
|
lang.mixin(this, args); |
|
} |
|
}, |
|
|
|
setEditor: function(editor){ |
|
// Overrides _Plugin.setEditor(). |
|
if(this.editor === editor){ |
|
return; |
|
} |
|
this.editor = editor; |
|
if(this.blockNodeForEnter == 'BR'){ |
|
// While Moz has a mode tht mostly works, it's still a little different, |
|
// So, try to just have a common mode and be consistent. Which means |
|
// we need to enable customUndo, if not already enabled. |
|
this.editor.customUndo = true; |
|
editor.onLoadDeferred.then(lang.hitch(this, function(d){ |
|
this.own(on(editor.document, "keydown", lang.hitch(this, function(e){ |
|
if(e.keyCode == keys.ENTER){ |
|
// Just do it manually. The handleEnterKey has a shift mode that |
|
// Always acts like <br>, so just use it. |
|
var ne = lang.mixin({}, e); |
|
ne.shiftKey = true; |
|
if(!this.handleEnterKey(ne)){ |
|
e.stopPropagation(); |
|
e.preventDefault(); |
|
} |
|
} |
|
}))); |
|
if(has("ie") >= 9 && has("ie") <= 10){ |
|
this.own(on(editor.document, "paste", lang.hitch(this, function(e){ |
|
setTimeout(lang.hitch(this, function(){ |
|
// Use the old range/selection code to kick IE 9 into updating |
|
// its range by moving it back, then forward, one 'character'. |
|
var r = this.editor.document.selection.createRange(); |
|
r.move('character', -1); |
|
r.select(); |
|
r.move('character', 1); |
|
r.select(); |
|
}), 0); |
|
}))); |
|
} |
|
return d; |
|
})); |
|
}else if(this.blockNodeForEnter){ |
|
// add enter key handler |
|
var h = lang.hitch(this, "handleEnterKey"); |
|
editor.addKeyHandler(13, 0, 0, h); //enter |
|
editor.addKeyHandler(13, 0, 1, h); //shift+enter |
|
this.own(this.editor.on('KeyPressed', lang.hitch(this, 'onKeyPressed'))); |
|
} |
|
}, |
|
onKeyPressed: function(){ |
|
// summary: |
|
// Handler for after the user has pressed a key, and the display has been updated. |
|
// Connected to RichText's onKeyPressed() method. |
|
// tags: |
|
// private |
|
if(this._checkListLater){ |
|
if(this.editor.selection.isCollapsed()){ |
|
var liparent = this.editor.selection.getAncestorElement('LI'); |
|
if(!liparent){ |
|
// circulate the undo detection code by calling RichText::execCommand directly |
|
RichText.prototype.execCommand.call(this.editor, 'formatblock', this.blockNodeForEnter); |
|
// set the innerHTML of the new block node |
|
var block = this.editor.selection.getAncestorElement(this.blockNodeForEnter); |
|
if(block){ |
|
block.innerHTML = this.bogusHtmlContent; |
|
if(has("ie") <= 9){ |
|
// move to the start by moving backwards one char |
|
var r = this.editor.document.selection.createRange(); |
|
r.move('character', -1); |
|
r.select(); |
|
} |
|
}else{ |
|
console.error('onKeyPressed: Cannot find the new block node'); // FIXME |
|
} |
|
}else{ |
|
if(has("mozilla")){ |
|
if(liparent.parentNode.parentNode.nodeName == 'LI'){ |
|
liparent = liparent.parentNode.parentNode; |
|
} |
|
} |
|
var fc = liparent.firstChild; |
|
if(fc && fc.nodeType == 1 && (fc.nodeName == 'UL' || fc.nodeName == 'OL')){ |
|
liparent.insertBefore(fc.ownerDocument.createTextNode('\xA0'), fc); |
|
var newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(liparent.firstChild, 0); |
|
var selection = rangeapi.getSelection(this.editor.window, true); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
} |
|
} |
|
} |
|
this._checkListLater = false; |
|
} |
|
if(this._pressedEnterInBlock){ |
|
// the new created is the original current P, so we have previousSibling below |
|
if(this._pressedEnterInBlock.previousSibling){ |
|
this.removeTrailingBr(this._pressedEnterInBlock.previousSibling); |
|
} |
|
delete this._pressedEnterInBlock; |
|
} |
|
}, |
|
|
|
// bogusHtmlContent: [private] String |
|
// HTML to stick into a new empty block |
|
bogusHtmlContent: ' ', // |
|
|
|
// blockNodes: [private] Regex |
|
// Regex for testing if a given tag is a block level (display:block) tag |
|
blockNodes: /^(?:P|H1|H2|H3|H4|H5|H6|LI)$/, |
|
|
|
handleEnterKey: function(e){ |
|
// summary: |
|
// Handler for enter key events when blockNodeForEnter is DIV or P. |
|
// description: |
|
// Manually handle enter key event to make the behavior consistent across |
|
// all supported browsers. See class description for details. |
|
// tags: |
|
// private |
|
|
|
var selection, range, newrange, startNode, endNode, brNode, doc = this.editor.document, br, rs, txt; |
|
if(e.shiftKey){ // shift+enter always generates <br> |
|
var parent = this.editor.selection.getParentElement(); |
|
var header = rangeapi.getAncestor(parent, this.blockNodes); |
|
if(header){ |
|
if(header.tagName == 'LI'){ |
|
return true; // let browser handle |
|
} |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
if(!range.collapsed){ |
|
range.deleteContents(); |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
} |
|
if(rangeapi.atBeginningOfContainer(header, range.startContainer, range.startOffset)){ |
|
br = doc.createElement('br'); |
|
newrange = rangeapi.create(this.editor.window); |
|
header.insertBefore(br, header.firstChild); |
|
newrange.setStartAfter(br); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
}else if(rangeapi.atEndOfContainer(header, range.startContainer, range.startOffset)){ |
|
newrange = rangeapi.create(this.editor.window); |
|
br = doc.createElement('br'); |
|
header.appendChild(br); |
|
header.appendChild(doc.createTextNode('\xA0')); |
|
newrange.setStart(header.lastChild, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
}else{ |
|
rs = range.startContainer; |
|
if(rs && rs.nodeType == 3){ |
|
// Text node, we have to split it. |
|
txt = rs.nodeValue; |
|
startNode = doc.createTextNode(txt.substring(0, range.startOffset)); |
|
endNode = doc.createTextNode(txt.substring(range.startOffset)); |
|
brNode = doc.createElement("br"); |
|
|
|
if(endNode.nodeValue == "" && has("webkit")){ |
|
endNode = doc.createTextNode('\xA0') |
|
} |
|
domConstruct.place(startNode, rs, "after"); |
|
domConstruct.place(brNode, startNode, "after"); |
|
domConstruct.place(endNode, brNode, "after"); |
|
domConstruct.destroy(rs); |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(endNode, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
return false; |
|
} |
|
return true; // let browser handle |
|
} |
|
}else{ |
|
selection = rangeapi.getSelection(this.editor.window); |
|
if(selection.rangeCount){ |
|
range = selection.getRangeAt(0); |
|
if(range && range.startContainer){ |
|
if(!range.collapsed){ |
|
range.deleteContents(); |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
} |
|
rs = range.startContainer; |
|
if(rs && rs.nodeType == 3){ |
|
// Text node, we have to split it. |
|
|
|
var offset = range.startOffset; |
|
if(rs.length < offset){ |
|
//We are not splitting the right node, try to locate the correct one |
|
ret = this._adjustNodeAndOffset(rs, offset); |
|
rs = ret.node; |
|
offset = ret.offset; |
|
} |
|
txt = rs.nodeValue; |
|
|
|
startNode = doc.createTextNode(txt.substring(0, offset)); |
|
endNode = doc.createTextNode(txt.substring(offset)); |
|
brNode = doc.createElement("br"); |
|
|
|
if(!endNode.length){ |
|
// Create dummy text with a   to go after the BR, to prevent IE crash. |
|
// See https://bugs.dojotoolkit.org/ticket/12008 for details. |
|
endNode = doc.createTextNode('\xA0'); |
|
} |
|
|
|
if(startNode.length){ |
|
domConstruct.place(startNode, rs, "after"); |
|
}else{ |
|
startNode = rs; |
|
} |
|
domConstruct.place(brNode, startNode, "after"); |
|
domConstruct.place(endNode, brNode, "after"); |
|
domConstruct.destroy(rs); |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(endNode, 0); |
|
newrange.setEnd(endNode, endNode.length); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
this.editor.selection.collapse(true); |
|
}else{ |
|
var targetNode; |
|
if(range.startOffset >= 0){ |
|
targetNode = rs.childNodes[range.startOffset]; |
|
} |
|
var brNode = doc.createElement("br"); |
|
var endNode = doc.createTextNode('\xA0'); |
|
if(!targetNode){ |
|
rs.appendChild(brNode); |
|
rs.appendChild(endNode); |
|
}else{ |
|
domConstruct.place(brNode, targetNode, "before"); |
|
domConstruct.place(endNode, brNode, "after"); |
|
} |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(endNode, 0); |
|
newrange.setEnd(endNode, endNode.length); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
this.editor.selection.collapse(true); |
|
} |
|
// \xA0 dummy text node remains, but is stripped before get("value") |
|
// by RichText._stripTrailingEmptyNodes(). Still, could we just use a plain |
|
// space (" ") instead? |
|
} |
|
}else{ |
|
// don't change this: do not call this.execCommand, as that may have other logic in subclass |
|
RichText.prototype.execCommand.call(this.editor, 'inserthtml', '<br>'); |
|
} |
|
} |
|
return false; |
|
} |
|
var _letBrowserHandle = true; |
|
|
|
// first remove selection |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
if(!range.collapsed){ |
|
range.deleteContents(); |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
} |
|
|
|
var block = rangeapi.getBlockAncestor(range.endContainer, null, this.editor.editNode); |
|
var blockNode = block.blockNode; |
|
|
|
// if this is under a LI or the parent of the blockNode is LI, just let browser to handle it |
|
if((this._checkListLater = (blockNode && (blockNode.nodeName == 'LI' || blockNode.parentNode.nodeName == 'LI')))){ |
|
if(has("mozilla")){ |
|
// press enter in middle of P may leave a trailing <br/>, let's remove it later |
|
this._pressedEnterInBlock = blockNode; |
|
} |
|
// if this li only contains spaces, set the content to empty so the browser will outdent this item |
|
if(/^(\s| | |\xA0|<span\b[^>]*\bclass=['"]Apple-style-span['"][^>]*>(\s| | |\xA0)<\/span>)?(<br>)?$/.test(blockNode.innerHTML)){ |
|
// empty LI node |
|
blockNode.innerHTML = ''; |
|
if(has("webkit")){ // WebKit tosses the range when innerHTML is reset |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(blockNode, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
} |
|
this._checkListLater = false; // nothing to check since the browser handles outdent |
|
} |
|
return true; |
|
} |
|
|
|
// text node directly under body, let's wrap them in a node |
|
if(!block.blockNode || block.blockNode === this.editor.editNode){ |
|
try{ |
|
RichText.prototype.execCommand.call(this.editor, 'formatblock', this.blockNodeForEnter); |
|
}catch(e2){ /*squelch FF3 exception bug when editor content is a single BR*/ |
|
} |
|
// get the newly created block node |
|
// FIXME |
|
block = {blockNode: this.editor.selection.getAncestorElement(this.blockNodeForEnter), |
|
blockContainer: this.editor.editNode}; |
|
if(block.blockNode){ |
|
if(block.blockNode != this.editor.editNode && |
|
(!(block.blockNode.textContent || block.blockNode.innerHTML).replace(/^\s+|\s+$/g, "").length)){ |
|
this.removeTrailingBr(block.blockNode); |
|
return false; |
|
} |
|
}else{ // we shouldn't be here if formatblock worked |
|
block.blockNode = this.editor.editNode; |
|
} |
|
selection = rangeapi.getSelection(this.editor.window); |
|
range = selection.getRangeAt(0); |
|
} |
|
|
|
var newblock = doc.createElement(this.blockNodeForEnter); |
|
newblock.innerHTML = this.bogusHtmlContent; |
|
this.removeTrailingBr(block.blockNode); |
|
var endOffset = range.endOffset; |
|
var node = range.endContainer; |
|
if(node.length < endOffset){ |
|
//We are not checking the right node, try to locate the correct one |
|
var ret = this._adjustNodeAndOffset(node, endOffset); |
|
node = ret.node; |
|
endOffset = ret.offset; |
|
} |
|
if(rangeapi.atEndOfContainer(block.blockNode, node, endOffset)){ |
|
if(block.blockNode === block.blockContainer){ |
|
block.blockNode.appendChild(newblock); |
|
}else{ |
|
domConstruct.place(newblock, block.blockNode, "after"); |
|
} |
|
_letBrowserHandle = false; |
|
// lets move caret to the newly created block |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(newblock, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
if(this.editor.height){ |
|
winUtils.scrollIntoView(newblock); |
|
} |
|
}else if(rangeapi.atBeginningOfContainer(block.blockNode, |
|
range.startContainer, range.startOffset)){ |
|
domConstruct.place(newblock, block.blockNode, block.blockNode === block.blockContainer ? "first" : "before"); |
|
if(newblock.nextSibling && this.editor.height){ |
|
// position input caret - mostly WebKit needs this |
|
newrange = rangeapi.create(this.editor.window); |
|
newrange.setStart(newblock.nextSibling, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
// browser does not scroll the caret position into view, do it manually |
|
winUtils.scrollIntoView(newblock.nextSibling); |
|
} |
|
_letBrowserHandle = false; |
|
}else{ //press enter in the middle of P/DIV/Whatever/ |
|
if(block.blockNode === block.blockContainer){ |
|
block.blockNode.appendChild(newblock); |
|
}else{ |
|
domConstruct.place(newblock, block.blockNode, "after"); |
|
} |
|
_letBrowserHandle = false; |
|
|
|
// Clone any block level styles. |
|
if(block.blockNode.style){ |
|
if(newblock.style){ |
|
if(block.blockNode.style.cssText){ |
|
newblock.style.cssText = block.blockNode.style.cssText; |
|
} |
|
} |
|
} |
|
|
|
// Okay, we probably have to split. |
|
rs = range.startContainer; |
|
var firstNodeMoved; |
|
if(rs && rs.nodeType == 3){ |
|
// Text node, we have to split it. |
|
var nodeToMove, tNode; |
|
endOffset = range.endOffset; |
|
if(rs.length < endOffset){ |
|
//We are not splitting the right node, try to locate the correct one |
|
ret = this._adjustNodeAndOffset(rs, endOffset); |
|
rs = ret.node; |
|
endOffset = ret.offset; |
|
} |
|
|
|
txt = rs.nodeValue; |
|
startNode = doc.createTextNode(txt.substring(0, endOffset)); |
|
endNode = doc.createTextNode(txt.substring(endOffset, txt.length)); |
|
|
|
// Place the split, then remove original nodes. |
|
domConstruct.place(startNode, rs, "before"); |
|
domConstruct.place(endNode, rs, "after"); |
|
domConstruct.destroy(rs); |
|
|
|
// Okay, we split the text. Now we need to see if we're |
|
// parented to the block element we're splitting and if |
|
// not, we have to split all the way up. Ugh. |
|
var parentC = startNode.parentNode; |
|
while(parentC !== block.blockNode){ |
|
var tg = parentC.tagName; |
|
var newTg = doc.createElement(tg); |
|
// Clone over any 'style' data. |
|
if(parentC.style){ |
|
if(newTg.style){ |
|
if(parentC.style.cssText){ |
|
newTg.style.cssText = parentC.style.cssText; |
|
} |
|
} |
|
} |
|
// If font also need to clone over any font data. |
|
if(parentC.tagName === "FONT"){ |
|
if(parentC.color){ |
|
newTg.color = parentC.color; |
|
} |
|
if(parentC.face){ |
|
newTg.face = parentC.face; |
|
} |
|
if(parentC.size){ // this check was necessary on IE |
|
newTg.size = parentC.size; |
|
} |
|
} |
|
|
|
nodeToMove = endNode; |
|
while(nodeToMove){ |
|
tNode = nodeToMove.nextSibling; |
|
newTg.appendChild(nodeToMove); |
|
nodeToMove = tNode; |
|
} |
|
domConstruct.place(newTg, parentC, "after"); |
|
startNode = parentC; |
|
endNode = newTg; |
|
parentC = parentC.parentNode; |
|
} |
|
|
|
// Lastly, move the split out tags to the new block. |
|
// as they should now be split properly. |
|
nodeToMove = endNode; |
|
if(nodeToMove.nodeType == 1 || (nodeToMove.nodeType == 3 && nodeToMove.nodeValue)){ |
|
// Non-blank text and non-text nodes need to clear out that blank space |
|
// before moving the contents. |
|
newblock.innerHTML = ""; |
|
} |
|
firstNodeMoved = nodeToMove; |
|
while(nodeToMove){ |
|
tNode = nodeToMove.nextSibling; |
|
newblock.appendChild(nodeToMove); |
|
nodeToMove = tNode; |
|
} |
|
} |
|
|
|
//lets move caret to the newly created block |
|
newrange = rangeapi.create(this.editor.window); |
|
var nodeForCursor; |
|
var innerMostFirstNodeMoved = firstNodeMoved; |
|
if(this.blockNodeForEnter !== 'BR'){ |
|
while(innerMostFirstNodeMoved){ |
|
nodeForCursor = innerMostFirstNodeMoved; |
|
tNode = innerMostFirstNodeMoved.firstChild; |
|
innerMostFirstNodeMoved = tNode; |
|
} |
|
if(nodeForCursor && nodeForCursor.parentNode){ |
|
newblock = nodeForCursor.parentNode; |
|
newrange.setStart(newblock, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
if(this.editor.height){ |
|
winUtils.scrollIntoView(newblock); |
|
} |
|
if(has("mozilla")){ |
|
// press enter in middle of P may leave a trailing <br/>, let's remove it later |
|
this._pressedEnterInBlock = block.blockNode; |
|
} |
|
}else{ |
|
_letBrowserHandle = true; |
|
} |
|
}else{ |
|
newrange.setStart(newblock, 0); |
|
selection.removeAllRanges(); |
|
selection.addRange(newrange); |
|
if(this.editor.height){ |
|
winUtils.scrollIntoView(newblock); |
|
} |
|
if(has("mozilla")){ |
|
// press enter in middle of P may leave a trailing <br/>, let's remove it later |
|
this._pressedEnterInBlock = block.blockNode; |
|
} |
|
} |
|
} |
|
return _letBrowserHandle; |
|
}, |
|
|
|
_adjustNodeAndOffset: function(/*DomNode*/node, /*Int*/offset){ |
|
// summary: |
|
// In the case there are multiple text nodes in a row the offset may not be within the node. If the offset is larger than the node length, it will attempt to find |
|
// the next text sibling until it locates the text node in which the offset refers to |
|
// node: |
|
// The node to check. |
|
// offset: |
|
// The position to find within the text node |
|
// tags: |
|
// private. |
|
while(node.length < offset && node.nextSibling && node.nextSibling.nodeType == 3){ |
|
//Adjust the offset and node in the case of multiple text nodes in a row |
|
offset = offset - node.length; |
|
node = node.nextSibling; |
|
} |
|
return {"node": node, "offset": offset}; |
|
}, |
|
|
|
removeTrailingBr: function(container){ |
|
// summary: |
|
// If last child of container is a `<br>`, then remove it. |
|
// tags: |
|
// private |
|
var para = /P|DIV|LI/i.test(container.tagName) ? |
|
container : this.editor.selection.getParentOfType(container, ['P', 'DIV', 'LI']); |
|
|
|
if(!para){ |
|
return; |
|
} |
|
if(para.lastChild){ |
|
if((para.childNodes.length > 1 && para.lastChild.nodeType == 3 && /^[\s\xAD]*$/.test(para.lastChild.nodeValue)) || |
|
para.lastChild.tagName == 'BR'){ |
|
|
|
domConstruct.destroy(para.lastChild); |
|
} |
|
} |
|
if(!para.childNodes.length){ |
|
para.innerHTML = this.bogusHtmlContent; |
|
} |
|
} |
|
}); |
|
|
|
});
|
|
|