src/pyams_skin/resources/js/ext/tinymce/dev/classes/EnterKey.js
changeset 557 bca7a7e058a3
equal deleted inserted replaced
-1:000000000000 557:bca7a7e058a3
       
     1 /**
       
     2  * EnterKey.js
       
     3  *
       
     4  * Copyright, Moxiecode Systems AB
       
     5  * Released under LGPL License.
       
     6  *
       
     7  * License: http://www.tinymce.com/license
       
     8  * Contributing: http://www.tinymce.com/contributing
       
     9  */
       
    10 
       
    11 /**
       
    12  * Contains logic for handling the enter key to split/generate block elements.
       
    13  */
       
    14 define("tinymce/EnterKey", [
       
    15 	"tinymce/dom/TreeWalker",
       
    16 	"tinymce/dom/RangeUtils",
       
    17 	"tinymce/Env"
       
    18 ], function(TreeWalker, RangeUtils, Env) {
       
    19 	var isIE = Env.ie && Env.ie < 11;
       
    20 
       
    21 	return function(editor) {
       
    22 		var dom = editor.dom, selection = editor.selection, settings = editor.settings;
       
    23 		var undoManager = editor.undoManager, schema = editor.schema, nonEmptyElementsMap = schema.getNonEmptyElements(),
       
    24 			moveCaretBeforeOnEnterElementsMap = schema.getMoveCaretBeforeOnEnterElements();
       
    25 
       
    26 		function handleEnterKey(evt) {
       
    27 			var rng, tmpRng, editableRoot, container, offset, parentBlock, documentMode, shiftKey,
       
    28 				newBlock, fragment, containerBlock, parentBlockName, containerBlockName, newBlockName, isAfterLastNodeInContainer;
       
    29 
       
    30 			// Returns true if the block can be split into two blocks or not
       
    31 			function canSplitBlock(node) {
       
    32 				return node &&
       
    33 					dom.isBlock(node) &&
       
    34 					!/^(TD|TH|CAPTION|FORM)$/.test(node.nodeName) &&
       
    35 					!/^(fixed|absolute)/i.test(node.style.position) &&
       
    36 					dom.getContentEditable(node) !== "true";
       
    37 			}
       
    38 
       
    39 			// Renders empty block on IE
       
    40 			function renderBlockOnIE(block) {
       
    41 				var oldRng;
       
    42 
       
    43 				if (dom.isBlock(block)) {
       
    44 					oldRng = selection.getRng();
       
    45 					block.appendChild(dom.create('span', null, '\u00a0'));
       
    46 					selection.select(block);
       
    47 					block.lastChild.outerHTML = '';
       
    48 					selection.setRng(oldRng);
       
    49 				}
       
    50 			}
       
    51 
       
    52 			// Remove the first empty inline element of the block so this: <p><b><em></em></b>x</p> becomes this: <p>x</p>
       
    53 			function trimInlineElementsOnLeftSideOfBlock(block) {
       
    54 				var node = block, firstChilds = [], i;
       
    55 
       
    56 				if (!node) {
       
    57 					return;
       
    58 				}
       
    59 
       
    60 				// Find inner most first child ex: <p><i><b>*</b></i></p>
       
    61 				while ((node = node.firstChild)) {
       
    62 					if (dom.isBlock(node)) {
       
    63 						return;
       
    64 					}
       
    65 
       
    66 					if (node.nodeType == 1 && !nonEmptyElementsMap[node.nodeName.toLowerCase()]) {
       
    67 						firstChilds.push(node);
       
    68 					}
       
    69 				}
       
    70 
       
    71 				i = firstChilds.length;
       
    72 				while (i--) {
       
    73 					node = firstChilds[i];
       
    74 					if (!node.hasChildNodes() || (node.firstChild == node.lastChild && node.firstChild.nodeValue === '')) {
       
    75 						dom.remove(node);
       
    76 					} else {
       
    77 						// Remove <a> </a> see #5381
       
    78 						if (node.nodeName == "A" && (node.innerText || node.textContent) === ' ') {
       
    79 							dom.remove(node);
       
    80 						}
       
    81 					}
       
    82 				}
       
    83 			}
       
    84 
       
    85 			// Moves the caret to a suitable position within the root for example in the first non
       
    86 			// pure whitespace text node or before an image
       
    87 			function moveToCaretPosition(root) {
       
    88 				var walker, node, rng, lastNode = root, tempElm;
       
    89 				function firstNonWhiteSpaceNodeSibling(node) {
       
    90 					while (node) {
       
    91 						if (node.nodeType == 1 || (node.nodeType == 3 && node.data && /[\r\n\s]/.test(node.data))) {
       
    92 							return node;
       
    93 						}
       
    94 
       
    95 						node = node.nextSibling;
       
    96 					}
       
    97 				}
       
    98 
       
    99 				if (!root) {
       
   100 					return;
       
   101 				}
       
   102 
       
   103 				// Old IE versions doesn't properly render blocks with br elements in them
       
   104 				// For example <p><br></p> wont be rendered correctly in a contentEditable area
       
   105 				// until you remove the br producing <p></p>
       
   106 				if (Env.ie && Env.ie < 9 && parentBlock && parentBlock.firstChild) {
       
   107 					if (parentBlock.firstChild == parentBlock.lastChild && parentBlock.firstChild.tagName == 'BR') {
       
   108 						dom.remove(parentBlock.firstChild);
       
   109 					}
       
   110 				}
       
   111 
       
   112 				if (/^(LI|DT|DD)$/.test(root.nodeName)) {
       
   113 					var firstChild = firstNonWhiteSpaceNodeSibling(root.firstChild);
       
   114 
       
   115 					if (firstChild && /^(UL|OL|DL)$/.test(firstChild.nodeName)) {
       
   116 						root.insertBefore(dom.doc.createTextNode('\u00a0'), root.firstChild);
       
   117 					}
       
   118 				}
       
   119 
       
   120 				rng = dom.createRng();
       
   121 
       
   122 				// Normalize whitespace to remove empty text nodes. Fix for: #6904
       
   123 				// Gecko will be able to place the caret in empty text nodes but it won't render propery
       
   124 				// Older IE versions will sometimes crash so for now ignore all IE versions
       
   125 				if (!Env.ie) {
       
   126 					root.normalize();
       
   127 				}
       
   128 
       
   129 				if (root.hasChildNodes()) {
       
   130 					walker = new TreeWalker(root, root);
       
   131 
       
   132 					while ((node = walker.current())) {
       
   133 						if (node.nodeType == 3) {
       
   134 							rng.setStart(node, 0);
       
   135 							rng.setEnd(node, 0);
       
   136 							break;
       
   137 						}
       
   138 
       
   139 						if (moveCaretBeforeOnEnterElementsMap[node.nodeName.toLowerCase()]) {
       
   140 							rng.setStartBefore(node);
       
   141 							rng.setEndBefore(node);
       
   142 							break;
       
   143 						}
       
   144 
       
   145 						lastNode = node;
       
   146 						node = walker.next();
       
   147 					}
       
   148 
       
   149 					if (!node) {
       
   150 						rng.setStart(lastNode, 0);
       
   151 						rng.setEnd(lastNode, 0);
       
   152 					}
       
   153 				} else {
       
   154 					if (root.nodeName == 'BR') {
       
   155 						if (root.nextSibling && dom.isBlock(root.nextSibling)) {
       
   156 							// Trick on older IE versions to render the caret before the BR between two lists
       
   157 							if (!documentMode || documentMode < 9) {
       
   158 								tempElm = dom.create('br');
       
   159 								root.parentNode.insertBefore(tempElm, root);
       
   160 							}
       
   161 
       
   162 							rng.setStartBefore(root);
       
   163 							rng.setEndBefore(root);
       
   164 						} else {
       
   165 							rng.setStartAfter(root);
       
   166 							rng.setEndAfter(root);
       
   167 						}
       
   168 					} else {
       
   169 						rng.setStart(root, 0);
       
   170 						rng.setEnd(root, 0);
       
   171 					}
       
   172 				}
       
   173 
       
   174 				selection.setRng(rng);
       
   175 
       
   176 				// Remove tempElm created for old IE:s
       
   177 				dom.remove(tempElm);
       
   178 				selection.scrollIntoView(root);
       
   179 			}
       
   180 
       
   181 			function setForcedBlockAttrs(node) {
       
   182 				var forcedRootBlockName = settings.forced_root_block;
       
   183 
       
   184 				if (forcedRootBlockName && forcedRootBlockName.toLowerCase() === node.tagName.toLowerCase()) {
       
   185 					dom.setAttribs(node, settings.forced_root_block_attrs);
       
   186 				}
       
   187 			}
       
   188 
       
   189 			// Creates a new block element by cloning the current one or creating a new one if the name is specified
       
   190 			// This function will also copy any text formatting from the parent block and add it to the new one
       
   191 			function createNewBlock(name) {
       
   192 				var node = container, block, clonedNode, caretNode, textInlineElements = schema.getTextInlineElements();
       
   193 
       
   194 				if (name || parentBlockName == "TABLE") {
       
   195 					block = dom.create(name || newBlockName);
       
   196 					setForcedBlockAttrs(block);
       
   197 				} else {
       
   198 					block = parentBlock.cloneNode(false);
       
   199 				}
       
   200 
       
   201 				caretNode = block;
       
   202 
       
   203 				// Clone any parent styles
       
   204 				if (settings.keep_styles !== false) {
       
   205 					do {
       
   206 						if (textInlineElements[node.nodeName]) {
       
   207 							// Never clone a caret containers
       
   208 							if (node.id == '_mce_caret') {
       
   209 								continue;
       
   210 							}
       
   211 
       
   212 							clonedNode = node.cloneNode(false);
       
   213 							dom.setAttrib(clonedNode, 'id', ''); // Remove ID since it needs to be document unique
       
   214 
       
   215 							if (block.hasChildNodes()) {
       
   216 								clonedNode.appendChild(block.firstChild);
       
   217 								block.appendChild(clonedNode);
       
   218 							} else {
       
   219 								caretNode = clonedNode;
       
   220 								block.appendChild(clonedNode);
       
   221 							}
       
   222 						}
       
   223 					} while ((node = node.parentNode));
       
   224 				}
       
   225 
       
   226 				// BR is needed in empty blocks on non IE browsers
       
   227 				if (!isIE) {
       
   228 					caretNode.innerHTML = '<br data-mce-bogus="1">';
       
   229 				}
       
   230 
       
   231 				return block;
       
   232 			}
       
   233 
       
   234 			// Returns true/false if the caret is at the start/end of the parent block element
       
   235 			function isCaretAtStartOrEndOfBlock(start) {
       
   236 				var walker, node, name;
       
   237 
       
   238 				// Caret is in the middle of a text node like "a|b"
       
   239 				if (container.nodeType == 3 && (start ? offset > 0 : offset < container.nodeValue.length)) {
       
   240 					return false;
       
   241 				}
       
   242 
       
   243 				// If after the last element in block node edge case for #5091
       
   244 				if (container.parentNode == parentBlock && isAfterLastNodeInContainer && !start) {
       
   245 					return true;
       
   246 				}
       
   247 
       
   248 				// If the caret if before the first element in parentBlock
       
   249 				if (start && container.nodeType == 1 && container == parentBlock.firstChild) {
       
   250 					return true;
       
   251 				}
       
   252 
       
   253 				// Caret can be before/after a table
       
   254 				if (container.nodeName === "TABLE" || (container.previousSibling && container.previousSibling.nodeName == "TABLE")) {
       
   255 					return (isAfterLastNodeInContainer && !start) || (!isAfterLastNodeInContainer && start);
       
   256 				}
       
   257 
       
   258 				// Walk the DOM and look for text nodes or non empty elements
       
   259 				walker = new TreeWalker(container, parentBlock);
       
   260 
       
   261 				// If caret is in beginning or end of a text block then jump to the next/previous node
       
   262 				if (container.nodeType == 3) {
       
   263 					if (start && offset === 0) {
       
   264 						walker.prev();
       
   265 					} else if (!start && offset == container.nodeValue.length) {
       
   266 						walker.next();
       
   267 					}
       
   268 				}
       
   269 
       
   270 				while ((node = walker.current())) {
       
   271 					if (node.nodeType === 1) {
       
   272 						// Ignore bogus elements
       
   273 						if (!node.getAttribute('data-mce-bogus')) {
       
   274 							// Keep empty elements like <img /> <input /> but not trailing br:s like <p>text|<br></p>
       
   275 							name = node.nodeName.toLowerCase();
       
   276 							if (nonEmptyElementsMap[name] && name !== 'br') {
       
   277 								return false;
       
   278 							}
       
   279 						}
       
   280 					} else if (node.nodeType === 3 && !/^[ \t\r\n]*$/.test(node.nodeValue)) {
       
   281 						return false;
       
   282 					}
       
   283 
       
   284 					if (start) {
       
   285 						walker.prev();
       
   286 					} else {
       
   287 						walker.next();
       
   288 					}
       
   289 				}
       
   290 
       
   291 				return true;
       
   292 			}
       
   293 
       
   294 			// Wraps any text nodes or inline elements in the specified forced root block name
       
   295 			function wrapSelfAndSiblingsInDefaultBlock(container, offset) {
       
   296 				var newBlock, parentBlock, startNode, node, next, rootBlockName, blockName = newBlockName || 'P';
       
   297 
       
   298 				// Not in a block element or in a table cell or caption
       
   299 				parentBlock = dom.getParent(container, dom.isBlock);
       
   300 				rootBlockName = editor.getBody().nodeName.toLowerCase();
       
   301 				if (!parentBlock || !canSplitBlock(parentBlock)) {
       
   302 					parentBlock = parentBlock || editableRoot;
       
   303 
       
   304 					if (!parentBlock.hasChildNodes()) {
       
   305 						newBlock = dom.create(blockName);
       
   306 						setForcedBlockAttrs(newBlock);
       
   307 						parentBlock.appendChild(newBlock);
       
   308 						rng.setStart(newBlock, 0);
       
   309 						rng.setEnd(newBlock, 0);
       
   310 						return newBlock;
       
   311 					}
       
   312 
       
   313 					// Find parent that is the first child of parentBlock
       
   314 					node = container;
       
   315 					while (node.parentNode != parentBlock) {
       
   316 						node = node.parentNode;
       
   317 					}
       
   318 
       
   319 					// Loop left to find start node start wrapping at
       
   320 					while (node && !dom.isBlock(node)) {
       
   321 						startNode = node;
       
   322 						node = node.previousSibling;
       
   323 					}
       
   324 
       
   325 					if (startNode && schema.isValidChild(rootBlockName, blockName.toLowerCase())) {
       
   326 						newBlock = dom.create(blockName);
       
   327 						setForcedBlockAttrs(newBlock);
       
   328 						startNode.parentNode.insertBefore(newBlock, startNode);
       
   329 
       
   330 						// Start wrapping until we hit a block
       
   331 						node = startNode;
       
   332 						while (node && !dom.isBlock(node)) {
       
   333 							next = node.nextSibling;
       
   334 							newBlock.appendChild(node);
       
   335 							node = next;
       
   336 						}
       
   337 
       
   338 						// Restore range to it's past location
       
   339 						rng.setStart(container, offset);
       
   340 						rng.setEnd(container, offset);
       
   341 					}
       
   342 				}
       
   343 
       
   344 				return container;
       
   345 			}
       
   346 
       
   347 			// Inserts a block or br before/after or in the middle of a split list of the LI is empty
       
   348 			function handleEmptyListItem() {
       
   349 				function isFirstOrLastLi(first) {
       
   350 					var node = containerBlock[first ? 'firstChild' : 'lastChild'];
       
   351 
       
   352 					// Find first/last element since there might be whitespace there
       
   353 					while (node) {
       
   354 						if (node.nodeType == 1) {
       
   355 							break;
       
   356 						}
       
   357 
       
   358 						node = node[first ? 'nextSibling' : 'previousSibling'];
       
   359 					}
       
   360 
       
   361 					return node === parentBlock;
       
   362 				}
       
   363 
       
   364 				function getContainerBlock() {
       
   365 					var containerBlockParent = containerBlock.parentNode;
       
   366 
       
   367 					if (/^(LI|DT|DD)$/.test(containerBlockParent.nodeName)) {
       
   368 						return containerBlockParent;
       
   369 					}
       
   370 
       
   371 					return containerBlock;
       
   372 				}
       
   373 
       
   374 				// Check if we are in an nested list
       
   375 				var containerBlockParentName = containerBlock.parentNode.nodeName;
       
   376 				if (/^(OL|UL|LI)$/.test(containerBlockParentName)) {
       
   377 					newBlockName = 'LI';
       
   378 				}
       
   379 
       
   380 				newBlock = newBlockName ? createNewBlock(newBlockName) : dom.create('BR');
       
   381 
       
   382 				if (isFirstOrLastLi(true) && isFirstOrLastLi()) {
       
   383 					if (containerBlockParentName == 'LI') {
       
   384 						// Nested list is inside a LI
       
   385 						dom.insertAfter(newBlock, getContainerBlock());
       
   386 					} else {
       
   387 						// Is first and last list item then replace the OL/UL with a text block
       
   388 						dom.replace(newBlock, containerBlock);
       
   389 					}
       
   390 				} else if (isFirstOrLastLi(true)) {
       
   391 					if (containerBlockParentName == 'LI') {
       
   392 						// List nested in an LI then move the list to a new sibling LI
       
   393 						dom.insertAfter(newBlock, getContainerBlock());
       
   394 						newBlock.appendChild(dom.doc.createTextNode(' ')); // Needed for IE so the caret can be placed
       
   395 						newBlock.appendChild(containerBlock);
       
   396 					} else {
       
   397 						// First LI in list then remove LI and add text block before list
       
   398 						containerBlock.parentNode.insertBefore(newBlock, containerBlock);
       
   399 					}
       
   400 				} else if (isFirstOrLastLi()) {
       
   401 					// Last LI in list then remove LI and add text block after list
       
   402 					dom.insertAfter(newBlock, getContainerBlock());
       
   403 					renderBlockOnIE(newBlock);
       
   404 				} else {
       
   405 					// Middle LI in list the split the list and insert a text block in the middle
       
   406 					// Extract after fragment and insert it after the current block
       
   407 					containerBlock = getContainerBlock();
       
   408 					tmpRng = rng.cloneRange();
       
   409 					tmpRng.setStartAfter(parentBlock);
       
   410 					tmpRng.setEndAfter(containerBlock);
       
   411 					fragment = tmpRng.extractContents();
       
   412 
       
   413 					if (newBlockName == 'LI' && fragment.firstChild.nodeName == 'LI') {
       
   414 						newBlock = fragment.firstChild;
       
   415 						dom.insertAfter(fragment, containerBlock);
       
   416 					} else {
       
   417 						dom.insertAfter(fragment, containerBlock);
       
   418 						dom.insertAfter(newBlock, containerBlock);
       
   419 					}
       
   420 				}
       
   421 
       
   422 				dom.remove(parentBlock);
       
   423 				moveToCaretPosition(newBlock);
       
   424 				undoManager.add();
       
   425 			}
       
   426 
       
   427 			// Inserts a BR element if the forced_root_block option is set to false or empty string
       
   428 			function insertBr() {
       
   429 				editor.execCommand("InsertLineBreak", false, evt);
       
   430 			}
       
   431 
       
   432 			// Trims any linebreaks at the beginning of node user for example when pressing enter in a PRE element
       
   433 			function trimLeadingLineBreaks(node) {
       
   434 				do {
       
   435 					if (node.nodeType === 3) {
       
   436 						node.nodeValue = node.nodeValue.replace(/^[\r\n]+/, '');
       
   437 					}
       
   438 
       
   439 					node = node.firstChild;
       
   440 				} while (node);
       
   441 			}
       
   442 
       
   443 			function getEditableRoot(node) {
       
   444 				var root = dom.getRoot(), parent, editableRoot;
       
   445 
       
   446 				// Get all parents until we hit a non editable parent or the root
       
   447 				parent = node;
       
   448 				while (parent !== root && dom.getContentEditable(parent) !== "false") {
       
   449 					if (dom.getContentEditable(parent) === "true") {
       
   450 						editableRoot = parent;
       
   451 					}
       
   452 
       
   453 					parent = parent.parentNode;
       
   454 				}
       
   455 
       
   456 				return parent !== root ? editableRoot : root;
       
   457 			}
       
   458 
       
   459 			// Adds a BR at the end of blocks that only contains an IMG or INPUT since
       
   460 			// these might be floated and then they won't expand the block
       
   461 			function addBrToBlockIfNeeded(block) {
       
   462 				var lastChild;
       
   463 
       
   464 				// IE will render the blocks correctly other browsers needs a BR
       
   465 				if (!isIE) {
       
   466 					block.normalize(); // Remove empty text nodes that got left behind by the extract
       
   467 
       
   468 					// Check if the block is empty or contains a floated last child
       
   469 					lastChild = block.lastChild;
       
   470 					if (!lastChild || (/^(left|right)$/gi.test(dom.getStyle(lastChild, 'float', true)))) {
       
   471 						dom.add(block, 'br');
       
   472 					}
       
   473 				}
       
   474 			}
       
   475 
       
   476 			rng = selection.getRng(true);
       
   477 
       
   478 			// Event is blocked by some other handler for example the lists plugin
       
   479 			if (evt.isDefaultPrevented()) {
       
   480 				return;
       
   481 			}
       
   482 
       
   483 			// Delete any selected contents
       
   484 			if (!rng.collapsed) {
       
   485 				editor.execCommand('Delete');
       
   486 				return;
       
   487 			}
       
   488 
       
   489 			// Setup range items and newBlockName
       
   490 			new RangeUtils(dom).normalize(rng);
       
   491 			container = rng.startContainer;
       
   492 			offset = rng.startOffset;
       
   493 			newBlockName = (settings.force_p_newlines ? 'p' : '') || settings.forced_root_block;
       
   494 			newBlockName = newBlockName ? newBlockName.toUpperCase() : '';
       
   495 			documentMode = dom.doc.documentMode;
       
   496 			shiftKey = evt.shiftKey;
       
   497 
       
   498 			// Resolve node index
       
   499 			if (container.nodeType == 1 && container.hasChildNodes()) {
       
   500 				isAfterLastNodeInContainer = offset > container.childNodes.length - 1;
       
   501 
       
   502 				container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
   503 				if (isAfterLastNodeInContainer && container.nodeType == 3) {
       
   504 					offset = container.nodeValue.length;
       
   505 				} else {
       
   506 					offset = 0;
       
   507 				}
       
   508 			}
       
   509 
       
   510 			// Get editable root node normaly the body element but sometimes a div or span
       
   511 			editableRoot = getEditableRoot(container);
       
   512 
       
   513 			// If there is no editable root then enter is done inside a contentEditable false element
       
   514 			if (!editableRoot) {
       
   515 				return;
       
   516 			}
       
   517 
       
   518 			undoManager.beforeChange();
       
   519 
       
   520 			// If editable root isn't block nor the root of the editor
       
   521 			if (!dom.isBlock(editableRoot) && editableRoot != dom.getRoot()) {
       
   522 				if (!newBlockName || shiftKey) {
       
   523 					insertBr();
       
   524 				}
       
   525 
       
   526 				return;
       
   527 			}
       
   528 
       
   529 			// Wrap the current node and it's sibling in a default block if it's needed.
       
   530 			// for example this <td>text|<b>text2</b></td> will become this <td><p>text|<b>text2</p></b></td>
       
   531 			// This won't happen if root blocks are disabled or the shiftKey is pressed
       
   532 			if ((newBlockName && !shiftKey) || (!newBlockName && shiftKey)) {
       
   533 				container = wrapSelfAndSiblingsInDefaultBlock(container, offset);
       
   534 			}
       
   535 
       
   536 			// Find parent block and setup empty block paddings
       
   537 			parentBlock = dom.getParent(container, dom.isBlock);
       
   538 			containerBlock = parentBlock ? dom.getParent(parentBlock.parentNode, dom.isBlock) : null;
       
   539 
       
   540 			// Setup block names
       
   541 			parentBlockName = parentBlock ? parentBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
   542 			containerBlockName = containerBlock ? containerBlock.nodeName.toUpperCase() : ''; // IE < 9 & HTML5
       
   543 
       
   544 			// Enter inside block contained within a LI then split or insert before/after LI
       
   545 			if (containerBlockName == 'LI' && !evt.ctrlKey) {
       
   546 				parentBlock = containerBlock;
       
   547 				parentBlockName = containerBlockName;
       
   548 			}
       
   549 
       
   550 			// Handle enter in list item
       
   551 			if (/^(LI|DT|DD)$/.test(parentBlockName)) {
       
   552 				if (!newBlockName && shiftKey) {
       
   553 					insertBr();
       
   554 					return;
       
   555 				}
       
   556 
       
   557 				// Handle enter inside an empty list item
       
   558 				if (dom.isEmpty(parentBlock)) {
       
   559 					handleEmptyListItem();
       
   560 					return;
       
   561 				}
       
   562 			}
       
   563 
       
   564 			// Don't split PRE tags but insert a BR instead easier when writing code samples etc
       
   565 			if (parentBlockName == 'PRE' && settings.br_in_pre !== false) {
       
   566 				if (!shiftKey) {
       
   567 					insertBr();
       
   568 					return;
       
   569 				}
       
   570 			} else {
       
   571 				// If no root block is configured then insert a BR by default or if the shiftKey is pressed
       
   572 				if ((!newBlockName && !shiftKey && parentBlockName != 'LI') || (newBlockName && shiftKey)) {
       
   573 					insertBr();
       
   574 					return;
       
   575 				}
       
   576 			}
       
   577 
       
   578 			// If parent block is root then never insert new blocks
       
   579 			if (newBlockName && parentBlock === editor.getBody()) {
       
   580 				return;
       
   581 			}
       
   582 
       
   583 			// Default block name if it's not configured
       
   584 			newBlockName = newBlockName || 'P';
       
   585 
       
   586 			// Insert new block before/after the parent block depending on caret location
       
   587 			if (isCaretAtStartOrEndOfBlock()) {
       
   588 				// If the caret is at the end of a header we produce a P tag after it similar to Word unless we are in a hgroup
       
   589 				if (/^(H[1-6]|PRE|FIGURE)$/.test(parentBlockName) && containerBlockName != 'HGROUP') {
       
   590 					newBlock = createNewBlock(newBlockName);
       
   591 				} else {
       
   592 					newBlock = createNewBlock();
       
   593 				}
       
   594 
       
   595 				// Split the current container block element if enter is pressed inside an empty inner block element
       
   596 				if (settings.end_container_on_empty_block && canSplitBlock(containerBlock) && dom.isEmpty(parentBlock)) {
       
   597 					// Split container block for example a BLOCKQUOTE at the current blockParent location for example a P
       
   598 					newBlock = dom.split(containerBlock, parentBlock);
       
   599 				} else {
       
   600 					dom.insertAfter(newBlock, parentBlock);
       
   601 				}
       
   602 
       
   603 				moveToCaretPosition(newBlock);
       
   604 			} else if (isCaretAtStartOrEndOfBlock(true)) {
       
   605 				// Insert new block before
       
   606 				newBlock = parentBlock.parentNode.insertBefore(createNewBlock(), parentBlock);
       
   607 				renderBlockOnIE(newBlock);
       
   608 				moveToCaretPosition(parentBlock);
       
   609 			} else {
       
   610 				// Extract after fragment and insert it after the current block
       
   611 				tmpRng = rng.cloneRange();
       
   612 				tmpRng.setEndAfter(parentBlock);
       
   613 				fragment = tmpRng.extractContents();
       
   614 				trimLeadingLineBreaks(fragment);
       
   615 				newBlock = fragment.firstChild;
       
   616 				dom.insertAfter(fragment, parentBlock);
       
   617 				trimInlineElementsOnLeftSideOfBlock(newBlock);
       
   618 				addBrToBlockIfNeeded(parentBlock);
       
   619 				moveToCaretPosition(newBlock);
       
   620 			}
       
   621 
       
   622 			dom.setAttrib(newBlock, 'id', ''); // Remove ID since it needs to be document unique
       
   623 
       
   624 			// Allow custom handling of new blocks
       
   625 			editor.fire('NewBlock', {newBlock: newBlock});
       
   626 
       
   627 			undoManager.add();
       
   628 		}
       
   629 
       
   630 		editor.on('keydown', function(evt) {
       
   631 			if (evt.keyCode == 13) {
       
   632 				if (handleEnterKey(evt) !== false) {
       
   633 					evt.preventDefault();
       
   634 				}
       
   635 			}
       
   636 		});
       
   637 	};
       
   638 });