src/pyams_skin/resources/js/ext/tinymce/dev/plugins/lists/plugin.js
changeset 557 bca7a7e058a3
equal deleted inserted replaced
-1:000000000000 557:bca7a7e058a3
       
     1 /**
       
     2  * plugin.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 /*global tinymce:true */
       
    12 /*eslint consistent-this:0 */
       
    13 
       
    14 tinymce.PluginManager.add('lists', function(editor) {
       
    15 	var self = this;
       
    16 
       
    17 	function isListNode(node) {
       
    18 		return node && (/^(OL|UL|DL)$/).test(node.nodeName);
       
    19 	}
       
    20 
       
    21 	function isFirstChild(node) {
       
    22 		return node.parentNode.firstChild == node;
       
    23 	}
       
    24 
       
    25 	function isLastChild(node) {
       
    26 		return node.parentNode.lastChild == node;
       
    27 	}
       
    28 
       
    29 	function isTextBlock(node) {
       
    30 		return node && !!editor.schema.getTextBlockElements()[node.nodeName];
       
    31 	}
       
    32 
       
    33 	editor.on('init', function() {
       
    34 		var dom = editor.dom, selection = editor.selection;
       
    35 
       
    36 		/**
       
    37 		 * Returns a range bookmark. This will convert indexed bookmarks into temporary span elements with
       
    38 		 * index 0 so that they can be restored properly after the DOM has been modified. Text bookmarks will not have spans
       
    39 		 * added to them since they can be restored after a dom operation.
       
    40 		 *
       
    41 		 * So this: <p><b>|</b><b>|</b></p>
       
    42 		 * becomes: <p><b><span data-mce-type="bookmark">|</span></b><b data-mce-type="bookmark">|</span></b></p>
       
    43 		 *
       
    44 		 * @param  {DOMRange} rng DOM Range to get bookmark on.
       
    45 		 * @return {Object} Bookmark object.
       
    46 		 */
       
    47 		function createBookmark(rng) {
       
    48 			var bookmark = {};
       
    49 
       
    50 			function setupEndPoint(start) {
       
    51 				var offsetNode, container, offset;
       
    52 
       
    53 				container = rng[start ? 'startContainer' : 'endContainer'];
       
    54 				offset = rng[start ? 'startOffset' : 'endOffset'];
       
    55 
       
    56 				if (container.nodeType == 1) {
       
    57 					offsetNode = dom.create('span', {'data-mce-type': 'bookmark'});
       
    58 
       
    59 					if (container.hasChildNodes()) {
       
    60 						offset = Math.min(offset, container.childNodes.length - 1);
       
    61 
       
    62 						if (start) {
       
    63 							container.insertBefore(offsetNode, container.childNodes[offset]);
       
    64 						} else {
       
    65 							dom.insertAfter(offsetNode, container.childNodes[offset]);
       
    66 						}
       
    67 					} else {
       
    68 						container.appendChild(offsetNode);
       
    69 					}
       
    70 
       
    71 					container = offsetNode;
       
    72 					offset = 0;
       
    73 				}
       
    74 
       
    75 				bookmark[start ? 'startContainer' : 'endContainer'] = container;
       
    76 				bookmark[start ? 'startOffset' : 'endOffset'] = offset;
       
    77 			}
       
    78 
       
    79 			setupEndPoint(true);
       
    80 
       
    81 			if (!rng.collapsed) {
       
    82 				setupEndPoint();
       
    83 			}
       
    84 
       
    85 			return bookmark;
       
    86 		}
       
    87 
       
    88 		/**
       
    89 		 * Moves the selection to the current bookmark and removes any selection container wrappers.
       
    90 		 *
       
    91 		 * @param {Object} bookmark Bookmark object to move selection to.
       
    92 		 */
       
    93 		function moveToBookmark(bookmark) {
       
    94 			function restoreEndPoint(start) {
       
    95 				var container, offset, node;
       
    96 
       
    97 				function nodeIndex(container) {
       
    98 					var node = container.parentNode.firstChild, idx = 0;
       
    99 
       
   100 					while (node) {
       
   101 						if (node == container) {
       
   102 							return idx;
       
   103 						}
       
   104 
       
   105 						// Skip data-mce-type=bookmark nodes
       
   106 						if (node.nodeType != 1 || node.getAttribute('data-mce-type') != 'bookmark') {
       
   107 							idx++;
       
   108 						}
       
   109 
       
   110 						node = node.nextSibling;
       
   111 					}
       
   112 
       
   113 					return -1;
       
   114 				}
       
   115 
       
   116 				container = node = bookmark[start ? 'startContainer' : 'endContainer'];
       
   117 				offset = bookmark[start ? 'startOffset' : 'endOffset'];
       
   118 
       
   119 				if (!container) {
       
   120 					return;
       
   121 				}
       
   122 
       
   123 				if (container.nodeType == 1) {
       
   124 					offset = nodeIndex(container);
       
   125 					container = container.parentNode;
       
   126 					dom.remove(node);
       
   127 				}
       
   128 
       
   129 				bookmark[start ? 'startContainer' : 'endContainer'] = container;
       
   130 				bookmark[start ? 'startOffset' : 'endOffset'] = offset;
       
   131 			}
       
   132 
       
   133 			restoreEndPoint(true);
       
   134 			restoreEndPoint();
       
   135 
       
   136 			var rng = dom.createRng();
       
   137 
       
   138 			rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
   139 
       
   140 			if (bookmark.endContainer) {
       
   141 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
   142 			}
       
   143 
       
   144 			selection.setRng(rng);
       
   145 		}
       
   146 
       
   147 		function createNewTextBlock(contentNode, blockName) {
       
   148 			var node, textBlock, fragment = dom.createFragment(), hasContentNode;
       
   149 			var blockElements = editor.schema.getBlockElements();
       
   150 
       
   151 			if (editor.settings.forced_root_block) {
       
   152 				blockName = blockName || editor.settings.forced_root_block;
       
   153 			}
       
   154 
       
   155 			if (blockName) {
       
   156 				textBlock = dom.create(blockName);
       
   157 
       
   158 				if (textBlock.tagName === editor.settings.forced_root_block) {
       
   159 					dom.setAttribs(textBlock, editor.settings.forced_root_block_attrs);
       
   160 				}
       
   161 
       
   162 				fragment.appendChild(textBlock);
       
   163 			}
       
   164 
       
   165 			if (contentNode) {
       
   166 				while ((node = contentNode.firstChild)) {
       
   167 					var nodeName = node.nodeName;
       
   168 
       
   169 					if (!hasContentNode && (nodeName != 'SPAN' || node.getAttribute('data-mce-type') != 'bookmark')) {
       
   170 						hasContentNode = true;
       
   171 					}
       
   172 
       
   173 					if (blockElements[nodeName]) {
       
   174 						fragment.appendChild(node);
       
   175 						textBlock = null;
       
   176 					} else {
       
   177 						if (blockName) {
       
   178 							if (!textBlock) {
       
   179 								textBlock = dom.create(blockName);
       
   180 								fragment.appendChild(textBlock);
       
   181 							}
       
   182 
       
   183 							textBlock.appendChild(node);
       
   184 						} else {
       
   185 							fragment.appendChild(node);
       
   186 						}
       
   187 					}
       
   188 				}
       
   189 			}
       
   190 
       
   191 			if (!editor.settings.forced_root_block) {
       
   192 				fragment.appendChild(dom.create('br'));
       
   193 			} else {
       
   194 				// BR is needed in empty blocks on non IE browsers
       
   195 				if (!hasContentNode && (!tinymce.Env.ie || tinymce.Env.ie > 10)) {
       
   196 					textBlock.appendChild(dom.create('br', {'data-mce-bogus': '1'}));
       
   197 				}
       
   198 			}
       
   199 
       
   200 			return fragment;
       
   201 		}
       
   202 
       
   203 		function getSelectedListItems() {
       
   204 			return tinymce.grep(selection.getSelectedBlocks(), function(block) {
       
   205 				return /^(LI|DT|DD)$/.test(block.nodeName);
       
   206 			});
       
   207 		}
       
   208 
       
   209 		function splitList(ul, li, newBlock) {
       
   210 			var tmpRng, fragment, bookmarks, node;
       
   211 
       
   212 			function removeAndKeepBookmarks(targetNode) {
       
   213 				tinymce.each(bookmarks, function(node) {
       
   214 					targetNode.parentNode.insertBefore(node, li.parentNode);
       
   215 				});
       
   216 
       
   217 				dom.remove(targetNode);
       
   218 			}
       
   219 
       
   220 			bookmarks = dom.select('span[data-mce-type="bookmark"]', ul);
       
   221 			newBlock = newBlock || createNewTextBlock(li);
       
   222 			tmpRng = dom.createRng();
       
   223 			tmpRng.setStartAfter(li);
       
   224 			tmpRng.setEndAfter(ul);
       
   225 			fragment = tmpRng.extractContents();
       
   226 
       
   227 			for (node = fragment.firstChild; node; node = node.firstChild) {
       
   228 				if (node.nodeName == 'LI' && dom.isEmpty(node)) {
       
   229 					dom.remove(node);
       
   230 					break;
       
   231 				}
       
   232 			}
       
   233 
       
   234 			if (!dom.isEmpty(fragment)) {
       
   235 				dom.insertAfter(fragment, ul);
       
   236 			}
       
   237 
       
   238 			dom.insertAfter(newBlock, ul);
       
   239 
       
   240 			if (dom.isEmpty(li.parentNode)) {
       
   241 				removeAndKeepBookmarks(li.parentNode);
       
   242 			}
       
   243 
       
   244 			dom.remove(li);
       
   245 
       
   246 			if (dom.isEmpty(ul)) {
       
   247 				dom.remove(ul);
       
   248 			}
       
   249 		}
       
   250 
       
   251 		function mergeWithAdjacentLists(listBlock) {
       
   252 			var sibling, node;
       
   253 
       
   254 			sibling = listBlock.nextSibling;
       
   255 			if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
       
   256 				while ((node = sibling.firstChild)) {
       
   257 					listBlock.appendChild(node);
       
   258 				}
       
   259 
       
   260 				dom.remove(sibling);
       
   261 			}
       
   262 
       
   263 			sibling = listBlock.previousSibling;
       
   264 			if (sibling && isListNode(sibling) && sibling.nodeName == listBlock.nodeName) {
       
   265 				while ((node = sibling.firstChild)) {
       
   266 					listBlock.insertBefore(node, listBlock.firstChild);
       
   267 				}
       
   268 
       
   269 				dom.remove(sibling);
       
   270 			}
       
   271 		}
       
   272 
       
   273 		/**
       
   274 		 * Normalizes the all lists in the specified element.
       
   275 		 */
       
   276 		function normalizeList(element) {
       
   277 			tinymce.each(tinymce.grep(dom.select('ol,ul', element)), function(ul) {
       
   278 				var sibling, parentNode = ul.parentNode;
       
   279 
       
   280 				// Move UL/OL to previous LI if it's the only child of a LI
       
   281 				if (parentNode.nodeName == 'LI' && parentNode.firstChild == ul) {
       
   282 					sibling = parentNode.previousSibling;
       
   283 					if (sibling && sibling.nodeName == 'LI') {
       
   284 						sibling.appendChild(ul);
       
   285 
       
   286 						if (dom.isEmpty(parentNode)) {
       
   287 							dom.remove(parentNode);
       
   288 						}
       
   289 					}
       
   290 				}
       
   291 
       
   292 				// Append OL/UL to previous LI if it's in a parent OL/UL i.e. old HTML4
       
   293 				if (isListNode(parentNode)) {
       
   294 					sibling = parentNode.previousSibling;
       
   295 					if (sibling && sibling.nodeName == 'LI') {
       
   296 						sibling.appendChild(ul);
       
   297 					}
       
   298 				}
       
   299 			});
       
   300 		}
       
   301 
       
   302 		function outdent(li) {
       
   303 			var ul = li.parentNode, ulParent = ul.parentNode, newBlock;
       
   304 
       
   305 			function removeEmptyLi(li) {
       
   306 				if (dom.isEmpty(li)) {
       
   307 					dom.remove(li);
       
   308 				}
       
   309 			}
       
   310 
       
   311 			if (li.nodeName == 'DD') {
       
   312 				dom.rename(li, 'DT');
       
   313 				return true;
       
   314 			}
       
   315 
       
   316 			if (isFirstChild(li) && isLastChild(li)) {
       
   317 				if (ulParent.nodeName == "LI") {
       
   318 					dom.insertAfter(li, ulParent);
       
   319 					removeEmptyLi(ulParent);
       
   320 					dom.remove(ul);
       
   321 				} else if (isListNode(ulParent)) {
       
   322 					dom.remove(ul, true);
       
   323 				} else {
       
   324 					ulParent.insertBefore(createNewTextBlock(li), ul);
       
   325 					dom.remove(ul);
       
   326 				}
       
   327 
       
   328 				return true;
       
   329 			} else if (isFirstChild(li)) {
       
   330 				if (ulParent.nodeName == "LI") {
       
   331 					dom.insertAfter(li, ulParent);
       
   332 					li.appendChild(ul);
       
   333 					removeEmptyLi(ulParent);
       
   334 				} else if (isListNode(ulParent)) {
       
   335 					ulParent.insertBefore(li, ul);
       
   336 				} else {
       
   337 					ulParent.insertBefore(createNewTextBlock(li), ul);
       
   338 					dom.remove(li);
       
   339 				}
       
   340 
       
   341 				return true;
       
   342 			} else if (isLastChild(li)) {
       
   343 				if (ulParent.nodeName == "LI") {
       
   344 					dom.insertAfter(li, ulParent);
       
   345 				} else if (isListNode(ulParent)) {
       
   346 					dom.insertAfter(li, ul);
       
   347 				} else {
       
   348 					dom.insertAfter(createNewTextBlock(li), ul);
       
   349 					dom.remove(li);
       
   350 				}
       
   351 
       
   352 				return true;
       
   353 			} else {
       
   354 				if (ulParent.nodeName == 'LI') {
       
   355 					ul = ulParent;
       
   356 					newBlock = createNewTextBlock(li, 'LI');
       
   357 				} else if (isListNode(ulParent)) {
       
   358 					newBlock = createNewTextBlock(li, 'LI');
       
   359 				} else {
       
   360 					newBlock = createNewTextBlock(li);
       
   361 				}
       
   362 
       
   363 				splitList(ul, li, newBlock);
       
   364 				normalizeList(ul.parentNode);
       
   365 
       
   366 				return true;
       
   367 			}
       
   368 
       
   369 			return false;
       
   370 		}
       
   371 
       
   372 		function indent(li) {
       
   373 			var sibling, newList;
       
   374 
       
   375 			function mergeLists(from, to) {
       
   376 				var node;
       
   377 
       
   378 				if (isListNode(from)) {
       
   379 					while ((node = li.lastChild.firstChild)) {
       
   380 						to.appendChild(node);
       
   381 					}
       
   382 
       
   383 					dom.remove(from);
       
   384 				}
       
   385 			}
       
   386 
       
   387 			if (li.nodeName == 'DT') {
       
   388 				dom.rename(li, 'DD');
       
   389 				return true;
       
   390 			}
       
   391 
       
   392 			sibling = li.previousSibling;
       
   393 
       
   394 			if (sibling && isListNode(sibling)) {
       
   395 				sibling.appendChild(li);
       
   396 				return true;
       
   397 			}
       
   398 
       
   399 			if (sibling && sibling.nodeName == 'LI' && isListNode(sibling.lastChild)) {
       
   400 				sibling.lastChild.appendChild(li);
       
   401 				mergeLists(li.lastChild, sibling.lastChild);
       
   402 				return true;
       
   403 			}
       
   404 
       
   405 			sibling = li.nextSibling;
       
   406 
       
   407 			if (sibling && isListNode(sibling)) {
       
   408 				sibling.insertBefore(li, sibling.firstChild);
       
   409 				return true;
       
   410 			}
       
   411 
       
   412 			if (sibling && sibling.nodeName == 'LI' && isListNode(li.lastChild)) {
       
   413 				return false;
       
   414 			}
       
   415 
       
   416 			sibling = li.previousSibling;
       
   417 			if (sibling && sibling.nodeName == 'LI') {
       
   418 				newList = dom.create(li.parentNode.nodeName);
       
   419 				sibling.appendChild(newList);
       
   420 				newList.appendChild(li);
       
   421 				mergeLists(li.lastChild, newList);
       
   422 				return true;
       
   423 			}
       
   424 
       
   425 			return false;
       
   426 		}
       
   427 
       
   428 		function indentSelection() {
       
   429 			var listElements = getSelectedListItems();
       
   430 
       
   431 			if (listElements.length) {
       
   432 				var bookmark = createBookmark(selection.getRng(true));
       
   433 
       
   434 				for (var i = 0; i < listElements.length; i++) {
       
   435 					if (!indent(listElements[i]) && i === 0) {
       
   436 						break;
       
   437 					}
       
   438 				}
       
   439 
       
   440 				moveToBookmark(bookmark);
       
   441 				editor.nodeChanged();
       
   442 
       
   443 				return true;
       
   444 			}
       
   445 		}
       
   446 
       
   447 		function outdentSelection() {
       
   448 			var listElements = getSelectedListItems();
       
   449 
       
   450 			if (listElements.length) {
       
   451 				var bookmark = createBookmark(selection.getRng(true));
       
   452 				var i, y, root = editor.getBody();
       
   453 
       
   454 				i = listElements.length;
       
   455 				while (i--) {
       
   456 					var node = listElements[i].parentNode;
       
   457 
       
   458 					while (node && node != root) {
       
   459 						y = listElements.length;
       
   460 						while (y--) {
       
   461 							if (listElements[y] === node) {
       
   462 								listElements.splice(i, 1);
       
   463 								break;
       
   464 							}
       
   465 						}
       
   466 
       
   467 						node = node.parentNode;
       
   468 					}
       
   469 				}
       
   470 
       
   471 				for (i = 0; i < listElements.length; i++) {
       
   472 					if (!outdent(listElements[i]) && i === 0) {
       
   473 						break;
       
   474 					}
       
   475 				}
       
   476 
       
   477 				moveToBookmark(bookmark);
       
   478 				editor.nodeChanged();
       
   479 
       
   480 				return true;
       
   481 			}
       
   482 		}
       
   483 
       
   484 		function applyList(listName) {
       
   485 			var rng = selection.getRng(true), bookmark = createBookmark(rng), listItemName = 'LI';
       
   486 
       
   487 			listName = listName.toUpperCase();
       
   488 
       
   489 			if (listName == 'DL') {
       
   490 				listItemName = 'DT';
       
   491 			}
       
   492 
       
   493 			function getSelectedTextBlocks() {
       
   494 				var textBlocks = [], root = editor.getBody();
       
   495 
       
   496 				function getEndPointNode(start) {
       
   497 					var container, offset;
       
   498 
       
   499 					container = rng[start ? 'startContainer' : 'endContainer'];
       
   500 					offset = rng[start ? 'startOffset' : 'endOffset'];
       
   501 
       
   502 					// Resolve node index
       
   503 					if (container.nodeType == 1) {
       
   504 						container = container.childNodes[Math.min(offset, container.childNodes.length - 1)] || container;
       
   505 					}
       
   506 
       
   507 					while (container.parentNode != root) {
       
   508 						if (isTextBlock(container)) {
       
   509 							return container;
       
   510 						}
       
   511 
       
   512 						if (/^(TD|TH)$/.test(container.parentNode.nodeName)) {
       
   513 							return container;
       
   514 						}
       
   515 
       
   516 						container = container.parentNode;
       
   517 					}
       
   518 
       
   519 					return container;
       
   520 				}
       
   521 
       
   522 				var startNode = getEndPointNode(true);
       
   523 				var endNode = getEndPointNode();
       
   524 				var block, siblings = [];
       
   525 
       
   526 				for (var node = startNode; node; node = node.nextSibling) {
       
   527 					siblings.push(node);
       
   528 
       
   529 					if (node == endNode) {
       
   530 						break;
       
   531 					}
       
   532 				}
       
   533 
       
   534 				tinymce.each(siblings, function(node) {
       
   535 					if (isTextBlock(node)) {
       
   536 						textBlocks.push(node);
       
   537 						block = null;
       
   538 						return;
       
   539 					}
       
   540 
       
   541 					if (dom.isBlock(node) || node.nodeName == 'BR') {
       
   542 						if (node.nodeName == 'BR') {
       
   543 							dom.remove(node);
       
   544 						}
       
   545 
       
   546 						block = null;
       
   547 						return;
       
   548 					}
       
   549 
       
   550 					var nextSibling = node.nextSibling;
       
   551 					if (tinymce.dom.BookmarkManager.isBookmarkNode(node)) {
       
   552 						if (isTextBlock(nextSibling) || (!nextSibling && node.parentNode == root)) {
       
   553 							block = null;
       
   554 							return;
       
   555 						}
       
   556 					}
       
   557 
       
   558 					if (!block) {
       
   559 						block = dom.create('p');
       
   560 						node.parentNode.insertBefore(block, node);
       
   561 						textBlocks.push(block);
       
   562 					}
       
   563 
       
   564 					block.appendChild(node);
       
   565 				});
       
   566 
       
   567 				return textBlocks;
       
   568 			}
       
   569 
       
   570 			tinymce.each(getSelectedTextBlocks(), function(block) {
       
   571 				var listBlock, sibling;
       
   572 
       
   573 				sibling = block.previousSibling;
       
   574 				if (sibling && isListNode(sibling) && sibling.nodeName == listName) {
       
   575 					listBlock = sibling;
       
   576 					block = dom.rename(block, listItemName);
       
   577 					sibling.appendChild(block);
       
   578 				} else {
       
   579 					listBlock = dom.create(listName);
       
   580 					block.parentNode.insertBefore(listBlock, block);
       
   581 					listBlock.appendChild(block);
       
   582 					block = dom.rename(block, listItemName);
       
   583 				}
       
   584 
       
   585 				mergeWithAdjacentLists(listBlock);
       
   586 			});
       
   587 
       
   588 			moveToBookmark(bookmark);
       
   589 		}
       
   590 
       
   591 		function removeList() {
       
   592 			var bookmark = createBookmark(selection.getRng(true)), root = editor.getBody();
       
   593 
       
   594 			tinymce.each(getSelectedListItems(), function(li) {
       
   595 				var node, rootList;
       
   596 
       
   597 				if (dom.isEmpty(li)) {
       
   598 					outdent(li);
       
   599 					return;
       
   600 				}
       
   601 
       
   602 				for (node = li; node && node != root; node = node.parentNode) {
       
   603 					if (isListNode(node)) {
       
   604 						rootList = node;
       
   605 					}
       
   606 				}
       
   607 
       
   608 				splitList(rootList, li);
       
   609 			});
       
   610 
       
   611 			moveToBookmark(bookmark);
       
   612 		}
       
   613 
       
   614 		function toggleList(listName) {
       
   615 			var parentList = dom.getParent(selection.getStart(), 'OL,UL,DL');
       
   616 
       
   617 			if (parentList) {
       
   618 				if (parentList.nodeName == listName) {
       
   619 					removeList(listName);
       
   620 				} else {
       
   621 					var bookmark = createBookmark(selection.getRng(true));
       
   622 					mergeWithAdjacentLists(dom.rename(parentList, listName));
       
   623 					moveToBookmark(bookmark);
       
   624 				}
       
   625 			} else {
       
   626 				applyList(listName);
       
   627 			}
       
   628 		}
       
   629 
       
   630 		function queryListCommandState(listName) {
       
   631 			return function() {
       
   632 				var parentList = dom.getParent(editor.selection.getStart(), 'UL,OL,DL');
       
   633 
       
   634 				return parentList && parentList.nodeName == listName;
       
   635 			};
       
   636 		}
       
   637 
       
   638 		self.backspaceDelete = function(isForward) {
       
   639 			function findNextCaretContainer(rng, isForward) {
       
   640 				var node = rng.startContainer, offset = rng.startOffset;
       
   641 				var nonEmptyBlocks, walker;
       
   642 
       
   643 				if (node.nodeType == 3 && (isForward ? offset < node.data.length : offset > 0)) {
       
   644 					return node;
       
   645 				}
       
   646 
       
   647 				nonEmptyBlocks = editor.schema.getNonEmptyElements();
       
   648 				walker = new tinymce.dom.TreeWalker(rng.startContainer);
       
   649 
       
   650 				while ((node = walker[isForward ? 'next' : 'prev']())) {
       
   651 					if (node.nodeName == 'LI' && !node.hasChildNodes()) {
       
   652 						return node;
       
   653 					}
       
   654 
       
   655 					if (nonEmptyBlocks[node.nodeName]) {
       
   656 						return node;
       
   657 					}
       
   658 
       
   659 					if (node.nodeType == 3 && node.data.length > 0) {
       
   660 						return node;
       
   661 					}
       
   662 				}
       
   663 			}
       
   664 
       
   665 			function mergeLiElements(fromElm, toElm) {
       
   666 				var node, listNode, ul = fromElm.parentNode;
       
   667 
       
   668 				if (isListNode(toElm.lastChild)) {
       
   669 					listNode = toElm.lastChild;
       
   670 				}
       
   671 
       
   672 				node = toElm.lastChild;
       
   673 				if (node && node.nodeName == 'BR' && fromElm.hasChildNodes()) {
       
   674 					dom.remove(node);
       
   675 				}
       
   676 
       
   677 				if (dom.isEmpty(toElm)) {
       
   678 					dom.$(toElm).empty();
       
   679 				}
       
   680 
       
   681 				if (!dom.isEmpty(fromElm)) {
       
   682 					while ((node = fromElm.firstChild)) {
       
   683 						toElm.appendChild(node);
       
   684 					}
       
   685 				}
       
   686 
       
   687 				if (listNode) {
       
   688 					toElm.appendChild(listNode);
       
   689 				}
       
   690 
       
   691 				dom.remove(fromElm);
       
   692 
       
   693 				if (dom.isEmpty(ul)) {
       
   694 					dom.remove(ul);
       
   695 				}
       
   696 			}
       
   697 
       
   698 			if (selection.isCollapsed()) {
       
   699 				var li = dom.getParent(selection.getStart(), 'LI');
       
   700 
       
   701 				if (li) {
       
   702 					var rng = selection.getRng(true);
       
   703 					var otherLi = dom.getParent(findNextCaretContainer(rng, isForward), 'LI');
       
   704 
       
   705 					if (otherLi && otherLi != li) {
       
   706 						var bookmark = createBookmark(rng);
       
   707 
       
   708 						if (isForward) {
       
   709 							mergeLiElements(otherLi, li);
       
   710 						} else {
       
   711 							mergeLiElements(li, otherLi);
       
   712 						}
       
   713 
       
   714 						moveToBookmark(bookmark);
       
   715 
       
   716 						return true;
       
   717 					} else if (!otherLi) {
       
   718 						if (!isForward && removeList(li.parentNode.nodeName)) {
       
   719 							return true;
       
   720 						}
       
   721 					}
       
   722 				}
       
   723 			}
       
   724 		};
       
   725 
       
   726 		editor.on('BeforeExecCommand', function(e) {
       
   727 			var cmd = e.command.toLowerCase(), isHandled;
       
   728 
       
   729 			if (cmd == "indent") {
       
   730 				if (indentSelection()) {
       
   731 					isHandled = true;
       
   732 				}
       
   733 			} else if (cmd == "outdent") {
       
   734 				if (outdentSelection()) {
       
   735 					isHandled = true;
       
   736 				}
       
   737 			}
       
   738 
       
   739 			if (isHandled) {
       
   740 				editor.fire('ExecCommand', {command: e.command});
       
   741 				e.preventDefault();
       
   742 				return true;
       
   743 			}
       
   744 		});
       
   745 
       
   746 		editor.addCommand('InsertUnorderedList', function() {
       
   747 			toggleList('UL');
       
   748 		});
       
   749 
       
   750 		editor.addCommand('InsertOrderedList', function() {
       
   751 			toggleList('OL');
       
   752 		});
       
   753 
       
   754 		editor.addCommand('InsertDefinitionList', function() {
       
   755 			toggleList('DL');
       
   756 		});
       
   757 
       
   758 		editor.addQueryStateHandler('InsertUnorderedList', queryListCommandState('UL'));
       
   759 		editor.addQueryStateHandler('InsertOrderedList', queryListCommandState('OL'));
       
   760 		editor.addQueryStateHandler('InsertDefinitionList', queryListCommandState('DL'));
       
   761 
       
   762 		editor.on('keydown', function(e) {
       
   763 			// Check for tab but not ctrl/cmd+tab since it switches browser tabs
       
   764 			if (e.keyCode != 9 || tinymce.util.VK.metaKeyPressed(e)) {
       
   765 				return;
       
   766 			}
       
   767 
       
   768 			if (editor.dom.getParent(editor.selection.getStart(), 'LI,DT,DD')) {
       
   769 				e.preventDefault();
       
   770 
       
   771 				if (e.shiftKey) {
       
   772 					outdentSelection();
       
   773 				} else {
       
   774 					indentSelection();
       
   775 				}
       
   776 			}
       
   777 		});
       
   778 	});
       
   779 
       
   780 	editor.addButton('indent', {
       
   781 		icon: 'indent',
       
   782 		title: 'Increase indent',
       
   783 		cmd: 'Indent',
       
   784 		onPostRender: function() {
       
   785 			var ctrl = this;
       
   786 
       
   787 			editor.on('nodechange', function() {
       
   788 				var blocks = editor.selection.getSelectedBlocks();
       
   789 				var disable = false;
       
   790 
       
   791 				for (var i = 0, l = blocks.length; !disable && i < l; i++) {
       
   792 					var tag = blocks[i].nodeName;
       
   793 
       
   794 					disable = (tag == 'LI' && isFirstChild(blocks[i]) || tag == 'UL' || tag == 'OL' || tag == 'DD');
       
   795 				}
       
   796 
       
   797 				ctrl.disabled(disable);
       
   798 			});
       
   799 		}
       
   800 	});
       
   801 
       
   802 	editor.on('keydown', function(e) {
       
   803 		if (e.keyCode == tinymce.util.VK.BACKSPACE) {
       
   804 			if (self.backspaceDelete()) {
       
   805 				e.preventDefault();
       
   806 			}
       
   807 		} else if (e.keyCode == tinymce.util.VK.DELETE) {
       
   808 			if (self.backspaceDelete(true)) {
       
   809 				e.preventDefault();
       
   810 			}
       
   811 		}
       
   812 	});
       
   813 });