From 6843acc9fced3dc1261e019ad4552b6e4c340ec7 Mon Sep 17 00:00:00 2001
From: Thomas Bruederli <thomas@roundcube.net>
Date: Mon, 30 Jul 2012 06:08:58 -0400
Subject: [PATCH] Merged changes to skin folders

---
 program/js/tiny_mce/plugins/lists/editor_plugin_src.js |  282 +++++++++++++++++++++++++++++++++++++++++++++----------
 1 files changed, 228 insertions(+), 54 deletions(-)

diff --git a/program/js/tiny_mce/plugins/lists/editor_plugin_src.js b/program/js/tiny_mce/plugins/lists/editor_plugin_src.js
index 1db29f7..1000ef7 100644
--- a/program/js/tiny_mce/plugins/lists/editor_plugin_src.js
+++ b/program/js/tiny_mce/plugins/lists/editor_plugin_src.js
@@ -99,11 +99,7 @@
 			return e2.style.listStyleType === 'none' || containsOnlyAList(e2);
 		} else if (isList(e1)) {
 			return (e1.tagName === e2.tagName && (allowDifferentListStyles || e1.style.listStyleType === e2.style.listStyleType)) || isListForIndent(e2);
-		} else if (mergeParagraphs && e1.tagName === 'P' && e2.tagName === 'P') {
-			return true;
-		} else {
-			return false;
-		}
+		} else return mergeParagraphs && e1.tagName === 'P' && e2.tagName === 'P';
 	}
 
 	function isListForIndent(e) {
@@ -144,15 +140,18 @@
 	}
 
 	tinymce.create('tinymce.plugins.Lists', {
-		init: function(ed, url) {
-			var LIST_TABBING = 0;
-			var LIST_EMPTY_ITEM = 1;
-			var LIST_ESCAPE = 2;
-			var LIST_UNKNOWN = 3;
+		init: function(ed) {
+			var LIST_TABBING = 'TABBING';
+			var LIST_EMPTY_ITEM = 'EMPTY';
+			var LIST_ESCAPE = 'ESCAPE';
+			var LIST_PARAGRAPH = 'PARAGRAPH';
+			var LIST_UNKNOWN = 'UNKNOWN';
 			var state = LIST_UNKNOWN;
 
 			function isTabInList(e) {
-				return e.keyCode === 9 && (ed.queryCommandState('InsertUnorderedList') || ed.queryCommandState('InsertOrderedList'));
+				// Don't indent on Ctrl+Tab or Alt+Tab
+				return e.keyCode === tinymce.VK.TAB && !(e.altKey || e.ctrlKey) &&
+					(ed.queryCommandState('InsertUnorderedList') || ed.queryCommandState('InsertOrderedList'));
 			}
 
 			function isOnLastListItem() {
@@ -183,43 +182,93 @@
 			function isEmptyListItem(li) {
 				var numChildren = li.childNodes.length;
 				if (li.tagName === 'LI') {
-					return numChildren == 0 ? true : numChildren == 1 && (li.firstChild.tagName == '' || isEmptyWebKitLi(li) || isEmptyIE9Li(li));
+					return numChildren == 0 ? true : numChildren == 1 && (li.firstChild.tagName == '' || li.firstChild.tagName == 'BR' || isEmptyIE9Li(li));
 				}
 				return false;
 			}
 
-			function isEmptyWebKitLi(li) {
-				// Check for empty LI or a LI with just a child that is a BR since Gecko and WebKit uses BR elements to place the caret
-				return tinymce.isWebKit && li.firstChild.nodeName == 'BR';
-			}
-
 			function isEmptyIE9Li(li) {
 				// only consider this to be last item if there is no list item content or that content is nbsp or space since IE9 creates these
-				var lis = tinymce.grep(li.parentNode.childNodes, function(n) {return n.nodeName == 'LI'});
+				var lis = tinymce.grep(li.parentNode.childNodes, function(n) {return n.tagName == 'LI'});
 				var isLastLi = li == lis[lis.length - 1];
 				var child = li.firstChild;
 				return tinymce.isIE9 && isLastLi && (child.nodeValue == String.fromCharCode(160) || child.nodeValue == String.fromCharCode(32));
 			}
 
 			function isEnter(e) {
-				return e.keyCode === 13;
+				return e.keyCode === tinymce.VK.ENTER;
+			}
+
+			function isEnterWithoutShift(e) {
+				return isEnter(e) && !e.shiftKey;
 			}
 
 			function getListKeyState(e) {
 				if (isTabInList(e)) {
 					return LIST_TABBING;
-				} else if (isEnter(e) && isOnLastListItem()) {
-					return LIST_ESCAPE;
-				} else if (isEnter(e) && isInEmptyListItem()) {
+				} else if (isEnterWithoutShift(e) && isOnLastListItem()) {
+					// Returns LIST_UNKNOWN since breaking out of lists is handled by the EnterKey.js logic now
+					//return LIST_ESCAPE;
+					return LIST_UNKNOWN;
+				} else if (isEnterWithoutShift(e) && isInEmptyListItem()) {
 					return LIST_EMPTY_ITEM;
 				} else {
 					return LIST_UNKNOWN;
 				}
 			}
 
-			function cancelEnterAndTab(_, e) {
-				if (state == LIST_TABBING || state == LIST_EMPTY_ITEM) {
-					return Event.cancel(e);
+			function cancelDefaultEvents(ed, e) {
+				// list escape is done manually using outdent as it does not create paragraphs correctly in td's
+				if (state == LIST_TABBING || state == LIST_EMPTY_ITEM || tinymce.isGecko && state == LIST_ESCAPE) {
+					Event.cancel(e);
+				}
+			}
+
+			function isCursorAtEndOfContainer() {
+				var range = ed.selection.getRng(true);
+				var startContainer = range.startContainer;
+				if (startContainer.nodeType == 3) {
+					var value = startContainer.nodeValue;
+					if (tinymce.isIE9 && value.length > 1 && value.charCodeAt(value.length-1) == 32) {
+						// IE9 places a space on the end of the text in some cases so ignore last char
+						return (range.endOffset == value.length-1);
+					} else {
+						return (range.endOffset == value.length);
+					}
+				} else if (startContainer.nodeType == 1) {
+					return range.endOffset == startContainer.childNodes.length;
+				}
+				return false;
+			}
+
+			/*
+			 	If we are at the end of a list item surrounded with an element, pressing enter should create a
+			 	new list item instead without splitting the element e.g. don't want to create new P or H1 tag
+			  */
+			function isEndOfListItem() {
+				var node = ed.selection.getNode();
+				var validElements = 'h1,h2,h3,h4,h5,h6,p,div';
+				var isLastParagraphOfLi = ed.dom.is(node, validElements) && node.parentNode.tagName === 'LI' && node.parentNode.lastChild === node;
+				return ed.selection.isCollapsed() && isLastParagraphOfLi && isCursorAtEndOfContainer();
+			}
+
+			// Creates a new list item after the current selection's list item parent
+			function createNewLi(ed, e) {
+				if (isEnterWithoutShift(e) && isEndOfListItem()) {
+					var node = ed.selection.getNode();
+					var li = ed.dom.create("li");
+					var parentLi = ed.dom.getParent(node, 'li');
+					ed.dom.insertAfter(li, parentLi);
+
+					// Move caret to new list element.
+					if (tinymce.isIE6 || tinymce.isIE7 || tinyMCE.isIE8) {
+						// Removed this line since it would create an odd <&nbsp;> tag and placing the caret inside an empty LI is handled and should be handled by the selection logic
+						//li.appendChild(ed.dom.create("&nbsp;")); // IE needs an element within the bullet point
+						ed.selection.setCursorLocation(li, 1);
+					} else {
+						ed.selection.setCursorLocation(li, 0);
+					}
+					e.preventDefault();
 				}
 			}
 
@@ -230,7 +279,7 @@
 					return;
 
 				var n = ed.selection.getStart();
-				if (e.keyCode != 8 || n.tagName !== 'IMG')
+				if (e.keyCode != tinymce.VK.BACKSPACE || n.tagName !== 'IMG')
 					return;
 
 				function lastLI(node) {
@@ -296,7 +345,8 @@
 				var list = ed.dom.getParent(li, 'ol,ul');
 				if (list != null) {
 					var lastLi = list.lastChild;
-					lastLi.appendChild(ed.getDoc().createElement(''));
+					// Removed this line since IE9 would report an DOM character error and placing the caret inside an empty LI is handled and should be handled by the selection logic
+					//lastLi.appendChild(ed.getDoc().createElement(''));
 					ed.selection.setCursorLocation(lastLi, 0);
 				}
 			}
@@ -338,25 +388,120 @@
 					if (tinymce.isIE) {
 						setCursorPositionToOriginalLi(li);
 					}
+
 					return Event.cancel(e);
 				} else if (state == LIST_ESCAPE) {
-					if (tinymce.isIE8) {
-						// append a zero sized nbsp so that caret is positioned correctly in IE8 after escaping and applying formatting.
+					if (tinymce.isIE6 || tinymce.isIE7 || tinymce.isIE8) {
+						// append a zero sized nbsp so that caret is positioned correctly in IE after escaping and applying formatting.
 						// if there is no text then applying formatting for e.g a H1 to the P tag immediately following list after
 						// escaping from it will cause the caret to be positioned on the last li instead of staying the in P tag.
 						var n = ed.getDoc().createTextNode('\uFEFF');
 						ed.selection.getNode().appendChild(n);
-					} else if (tinymce.isIE9) {
+					} else if (tinymce.isIE9 || tinymce.isGecko) {
 						// IE9 does not escape the list so we use outdent to do this and cancel the default behaviour
+						// Gecko does not create a paragraph outdenting inside a TD so default behaviour is cancelled and we outdent ourselves
 						ed.execCommand('Outdent');
 						return Event.cancel(e);
 					}
 				}
 			});
+
+			function fixListItem(parent, reference) {
+				// a zero-sized non-breaking space is placed in the empty list item so that the nested list is
+				// displayed on the below line instead of next to it
+				var n = ed.getDoc().createTextNode('\uFEFF');
+				parent.insertBefore(n, reference);
+				ed.selection.setCursorLocation(n, 0);
+				// repaint to remove rendering artifact. only visible when creating new list
+				ed.execCommand('mceRepaint');
+			}
+
+			function fixIndentedListItemForGecko(ed, e) {
+				if (isEnter(e)) {
+					var li = getLi();
+					if (li) {
+						var parent = li.parentNode;
+						var grandParent = parent && parent.parentNode;
+						if (grandParent && grandParent.nodeName == 'LI' && grandParent.firstChild == parent && li == parent.firstChild) {
+							fixListItem(grandParent, parent);
+						}
+					}
+				}
+			}
+
+			function fixIndentedListItemForIE8(ed, e) {
+				if (isEnter(e)) {
+					var li = getLi();
+					if (ed.dom.select('ul li', li).length === 1) {
+						var list = li.firstChild;
+						fixListItem(li, list);
+					}
+				}
+			}
+
+			function fixDeletingFirstCharOfList(ed, e) {
+				function listElements(li) {
+					var elements = [];
+					var walker = new tinymce.dom.TreeWalker(li.firstChild, li);
+					for (var node = walker.current(); node; node = walker.next()) {
+						if (ed.dom.is(node, 'ol,ul,li')) {
+							elements.push(node);
+						}
+					}
+					return elements;
+				}
+
+				if (e.keyCode == tinymce.VK.BACKSPACE) {
+					var li = getLi();
+					if (li) {
+						var list = ed.dom.getParent(li, 'ol,ul'),
+							rng  = ed.selection.getRng();
+						if (list && list.firstChild === li && rng.startOffset == 0) {
+							var elements = listElements(li);
+							elements.unshift(li);
+							ed.execCommand("Outdent", false, elements);
+							ed.undoManager.add();
+							return Event.cancel(e);
+						}
+					}
+				}
+			}
+
+			function fixDeletingEmptyLiInWebkit(ed, e) {
+				var li = getLi();
+				if (e.keyCode === tinymce.VK.BACKSPACE && ed.dom.is(li, 'li') && li.parentNode.firstChild!==li) {
+					if (ed.dom.select('ul,ol', li).length === 1) {
+						var prevLi = li.previousSibling;
+						ed.dom.remove(ed.dom.select('br', li));
+						ed.dom.remove(li, true);
+						var textNodes = tinymce.grep(prevLi.childNodes, function(n){ return n.nodeType === 3 });
+						if (textNodes.length === 1) {
+							var textNode = textNodes[0];
+							ed.selection.setCursorLocation(textNode, textNode.length);
+						}
+						ed.undoManager.add();
+						return Event.cancel(e);
+					}
+				}
+			}
+
 			ed.onKeyDown.add(function(_, e) { state = getListKeyState(e); });
-			ed.onKeyDown.add(cancelEnterAndTab);
+			ed.onKeyDown.add(cancelDefaultEvents);
 			ed.onKeyDown.add(imageJoiningListItem);
-			ed.onKeyPress.add(cancelEnterAndTab);
+			ed.onKeyDown.add(createNewLi);
+
+			if (tinymce.isGecko) {
+				ed.onKeyUp.add(fixIndentedListItemForGecko);
+			}
+			if (tinymce.isIE8) {
+				ed.onKeyUp.add(fixIndentedListItemForIE8);
+			}
+			if (tinymce.isGecko || tinymce.isWebKit) {
+				ed.onKeyDown.add(fixDeletingFirstCharOfList);
+			}
+			if (tinymce.isWebKit) {
+				ed.onKeyDown.add(fixDeletingEmptyLiInWebkit);
+			}
 		},
 
 		applyList: function(targetListType, oppositeListType) {
@@ -382,16 +527,18 @@
 				if (element.tagName === 'LI') {
 					// No change required.
 				} else if (element.tagName === 'P' || element.tagName === 'DIV' || element.tagName === 'BODY') {
-					processBrs(element, function(startSection, br, previousBR) {
+					processBrs(element, function(startSection, br) {
 						doWrapList(startSection, br, element.tagName === 'BODY' ? null : startSection.parentNode);
 						li = startSection.parentNode;
 						adjustIndentForNewList(li);
 						cleanupBr(br);
 					});
-					if (element.tagName === 'P' || selectedBlocks.length > 1) {
-						dom.split(li.parentNode.parentNode, li.parentNode);
+					if (li) {
+						if (li.tagName === 'LI' && (element.tagName === 'P' || selectedBlocks.length > 1)) {
+							dom.split(li.parentNode.parentNode, li.parentNode);
+						}
+						attemptMergeWithAdjacent(li.parentNode, true);
 					}
-					attemptMergeWithAdjacent(li.parentNode, true);
 					return;
 				} else {
 					// Put the list around the element.
@@ -408,7 +555,7 @@
 			}
 
 			function doWrapList(start, end, template) {
-				var li, n = start, tmp, i;
+				var li, n = start, tmp;
 				while (!dom.isBlock(start.parentNode) && start.parentNode !== dom.getRoot()) {
 					start = dom.split(start.parentNode, start.previousSibling);
 					start = start.nextSibling;
@@ -464,7 +611,6 @@
 				// First mark the BRs that have any part of the previous section selected.
 				var trailingContentSelected = false;
 				each(dom.select(breakElements, element), function(br) {
-					var b;
 					if (br.hasAttribute && br.hasAttribute('_mce_bogus')) {
 						return true; // Skip the bogus Brs that are put in to appease Firefox and Safari.
 					}
@@ -560,7 +706,7 @@
 				}
 			});
 
-			if (hasNonList || hasOppositeType || selectedBlocks.length === 0) {
+			if (hasNonList &&!hasSameType || hasOppositeType || selectedBlocks.length === 0) {
 				actions = {
 					'LI': changeList,
 					'H1': makeList,
@@ -572,11 +718,14 @@
 					'P': makeList,
 					'BODY': makeList,
 					'DIV': selectedBlocks.length > 1 ? makeList : wrapList,
-					defaultAction: wrapList
+					defaultAction: wrapList,
+					elements: this.selectedBlocks()
 				};
 			} else {
 				actions = {
-					defaultAction: convertListItemToParagraph
+					defaultAction: convertListItemToParagraph,
+					elements: this.selectedBlocks(),
+					processEvenIfEmpty: true
 				};
 			}
 			this.process(actions);
@@ -619,12 +768,13 @@
 
 			this.process({
 				'LI': indentLI,
-				defaultAction: this.adjustPaddingFunction(true)
+				defaultAction: this.adjustPaddingFunction(true),
+				elements: this.selectedBlocks()
 			});
 
 		},
 
-		outdent: function() {
+		outdent: function(ui, elements) {
 			var t = this, ed = t.ed, dom = ed.dom, outdented = [];
 
 			function outdentLI(element) {
@@ -656,9 +806,11 @@
 				}
 			}
 
+			var listElements = elements && tinymce.is(elements, 'array') ? elements : this.selectedBlocks();
 			this.process({
 				'LI': outdentLI,
-				defaultAction: this.adjustPaddingFunction(false)
+				defaultAction: this.adjustPaddingFunction(false),
+				elements: listElements
 			});
 
 			each(outdented, attemptMergeWithAdjacent);
@@ -667,9 +819,17 @@
 		process: function(actions) {
 			var t = this, sel = t.ed.selection, dom = t.ed.dom, selectedBlocks, r;
 
+			function isEmptyElement(element) {
+				var excludeBrsAndBookmarks = tinymce.grep(element.childNodes, function(n) {
+					return !(n.nodeName === 'BR' || n.nodeName === 'SPAN' && dom.getAttrib(n, 'data-mce-type') == 'bookmark'
+							|| n.nodeType == 3 && (n.nodeValue == String.fromCharCode(160) || n.nodeValue == ''));
+				});
+				return excludeBrsAndBookmarks.length === 0;
+			}
+
 			function processElement(element) {
 				dom.removeClass(element, '_mce_act_on');
-				if (!element || element.nodeType !== 1) {
+				if (!element || element.nodeType !== 1 || ! actions.processEvenIfEmpty && selectedBlocks.length > 1 && isEmptyElement(element)) {
 					return;
 				}
 				element = findItemToOperateOn(element, dom);
@@ -681,7 +841,7 @@
 			}
 
 			function recurse(element) {
-				t.splitSafeEach(element.childNodes, processElement);
+				t.splitSafeEach(element.childNodes, processElement, true);
 			}
 
 			function brAtEdgeOfSelection(container, offset) {
@@ -689,10 +849,13 @@
 						container.childNodes[offset].tagName === 'BR';
 			}
 
-			selectedBlocks = sel.getSelectedBlocks();
-			if (selectedBlocks.length === 0) {
-				selectedBlocks = [ dom.getRoot() ];
+			function isInTable() {
+				var n = sel.getNode();
+				var p = dom.getParent(n, 'td');
+				return p !== null;
 			}
+
+			selectedBlocks = actions.elements;
 
 			r = sel.getRng(true);
 			if (!r.collapsed) {
@@ -721,13 +884,19 @@
 			t.splitSafeEach(selectedBlocks, processElement);
 			sel.moveToBookmark(bookmark);
 			bookmark = null;
-			// Avoids table or image handles being left behind in Firefox.
-			t.ed.execCommand('mceRepaint');
+
+			// we avoid doing repaint in a table as this will move the caret out of the table in Firefox 3.6
+			if (!isInTable()) {
+				// Avoids table or image handles being left behind in Firefox.
+				t.ed.execCommand('mceRepaint');
+			}
 		},
 
-		splitSafeEach: function(elements, f) {
-			if (tinymce.isGecko && (/Firefox\/[12]\.[0-9]/.test(navigator.userAgent) ||
-					/Firefox\/3\.[0-4]/.test(navigator.userAgent))) {
+		splitSafeEach: function(elements, f, forceClassBase) {
+			if (forceClassBase ||
+				(tinymce.isGecko &&
+					(/Firefox\/[12]\.[0-9]/.test(navigator.userAgent) ||
+					 /Firefox\/3\.[0-4]/.test(navigator.userAgent)))) {
 				this.classBasedEach(elements, f);
 			} else {
 				each(elements, f);
@@ -767,6 +936,11 @@
 			};
 		},
 
+		selectedBlocks: function() {
+			var ed = this.ed, selectedBlocks = ed.selection.getSelectedBlocks();
+			return selectedBlocks.length == 0 ? [ ed.dom.getRoot() ] : selectedBlocks;
+		},
+
 		getInfo: function() {
 			return {
 				longname : 'Lists',

--
Gitblit v1.9.1