src/pyams_skin/resources/js/ext/tinymce/dev/classes/FocusManager.js
changeset 69 a361355b55c7
equal deleted inserted replaced
68:fd8fb93e1b6a 69:a361355b55c7
       
     1 /**
       
     2  * FocusManager.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  * This class manages the focus/blur state of the editor. This class is needed since some
       
    13  * browsers fire false focus/blur states when the selection is moved to a UI dialog or similar.
       
    14  *
       
    15  * This class will fire two events focus and blur on the editor instances that got affected.
       
    16  * It will also handle the restore of selection when the focus is lost and returned.
       
    17  *
       
    18  * @class tinymce.FocusManager
       
    19  */
       
    20 define("tinymce/FocusManager", [
       
    21 	"tinymce/dom/DOMUtils",
       
    22 	"tinymce/Env"
       
    23 ], function(DOMUtils, Env) {
       
    24 	var selectionChangeHandler, documentFocusInHandler, documentMouseUpHandler, DOM = DOMUtils.DOM;
       
    25 
       
    26 	/**
       
    27 	 * Constructs a new focus manager instance.
       
    28 	 *
       
    29 	 * @constructor FocusManager
       
    30 	 * @param {tinymce.EditorManager} editorManager Editor manager instance to handle focus for.
       
    31 	 */
       
    32 	function FocusManager(editorManager) {
       
    33 		function getActiveElement() {
       
    34 			try {
       
    35 				return document.activeElement;
       
    36 			} catch (ex) {
       
    37 				// IE sometimes fails to get the activeElement when resizing table
       
    38 				// TODO: Investigate this
       
    39 				return document.body;
       
    40 			}
       
    41 		}
       
    42 
       
    43 		// We can't store a real range on IE 11 since it gets mutated so we need to use a bookmark object
       
    44 		// TODO: Move this to a separate range utils class since it's it's logic is present in Selection as well.
       
    45 		function createBookmark(dom, rng) {
       
    46 			if (rng && rng.startContainer) {
       
    47 				// Verify that the range is within the root of the editor
       
    48 				if (!dom.isChildOf(rng.startContainer, dom.getRoot()) || !dom.isChildOf(rng.endContainer, dom.getRoot())) {
       
    49 					return;
       
    50 				}
       
    51 
       
    52 				return {
       
    53 					startContainer: rng.startContainer,
       
    54 					startOffset: rng.startOffset,
       
    55 					endContainer: rng.endContainer,
       
    56 					endOffset: rng.endOffset
       
    57 				};
       
    58 			}
       
    59 
       
    60 			return rng;
       
    61 		}
       
    62 
       
    63 		function bookmarkToRng(editor, bookmark) {
       
    64 			var rng;
       
    65 
       
    66 			if (bookmark.startContainer) {
       
    67 				rng = editor.getDoc().createRange();
       
    68 				rng.setStart(bookmark.startContainer, bookmark.startOffset);
       
    69 				rng.setEnd(bookmark.endContainer, bookmark.endOffset);
       
    70 			} else {
       
    71 				rng = bookmark;
       
    72 			}
       
    73 
       
    74 			return rng;
       
    75 		}
       
    76 
       
    77 		function isUIElement(elm) {
       
    78 			return !!DOM.getParent(elm, FocusManager.isEditorUIElement);
       
    79 		}
       
    80 
       
    81 		function registerEvents(e) {
       
    82 			var editor = e.editor;
       
    83 
       
    84 			editor.on('init', function() {
       
    85 				// Gecko/WebKit has ghost selections in iframes and IE only has one selection per browser tab
       
    86 				if (editor.inline || Env.ie) {
       
    87 					// Use the onbeforedeactivate event when available since it works better see #7023
       
    88 					if ("onbeforedeactivate" in document && Env.ie < 9) {
       
    89 						editor.dom.bind(editor.getBody(), 'beforedeactivate', function(e) {
       
    90 							if (e.target != editor.getBody()) {
       
    91 								return;
       
    92 							}
       
    93 
       
    94 							try {
       
    95 								editor.lastRng = editor.selection.getRng();
       
    96 							} catch (ex) {
       
    97 								// IE throws "Unexcpected call to method or property access" some times so lets ignore it
       
    98 							}
       
    99 						});
       
   100 					} else {
       
   101 						// On other browsers take snapshot on nodechange in inline mode since they have Ghost selections for iframes
       
   102 						editor.on('nodechange mouseup keyup', function(e) {
       
   103 							var node = getActiveElement();
       
   104 
       
   105 							// Only act on manual nodechanges
       
   106 							if (e.type == 'nodechange' && e.selectionChange) {
       
   107 								return;
       
   108 							}
       
   109 
       
   110 							// IE 11 reports active element as iframe not body of iframe
       
   111 							if (node && node.id == editor.id + '_ifr') {
       
   112 								node = editor.getBody();
       
   113 							}
       
   114 
       
   115 							if (editor.dom.isChildOf(node, editor.getBody())) {
       
   116 								editor.lastRng = editor.selection.getRng();
       
   117 							}
       
   118 						});
       
   119 					}
       
   120 
       
   121 					// Handles the issue with WebKit not retaining selection within inline document
       
   122 					// If the user releases the mouse out side the body since a mouse up event wont occur on the body
       
   123 					if (Env.webkit && !selectionChangeHandler) {
       
   124 						selectionChangeHandler = function() {
       
   125 							var activeEditor = editorManager.activeEditor;
       
   126 
       
   127 							if (activeEditor && activeEditor.selection) {
       
   128 								var rng = activeEditor.selection.getRng();
       
   129 
       
   130 								// Store when it's non collapsed
       
   131 								if (rng && !rng.collapsed) {
       
   132 									editor.lastRng = rng;
       
   133 								}
       
   134 							}
       
   135 						};
       
   136 
       
   137 						DOM.bind(document, 'selectionchange', selectionChangeHandler);
       
   138 					}
       
   139 				}
       
   140 			});
       
   141 
       
   142 			editor.on('setcontent', function() {
       
   143 				editor.lastRng = null;
       
   144 			});
       
   145 
       
   146 			// Remove last selection bookmark on mousedown see #6305
       
   147 			editor.on('mousedown', function() {
       
   148 				editor.selection.lastFocusBookmark = null;
       
   149 			});
       
   150 
       
   151 			editor.on('focusin', function() {
       
   152 				var focusedEditor = editorManager.focusedEditor;
       
   153 
       
   154 				if (editor.selection.lastFocusBookmark) {
       
   155 					editor.selection.setRng(bookmarkToRng(editor, editor.selection.lastFocusBookmark));
       
   156 					editor.selection.lastFocusBookmark = null;
       
   157 				}
       
   158 
       
   159 				if (focusedEditor != editor) {
       
   160 					if (focusedEditor) {
       
   161 						focusedEditor.fire('blur', {focusedEditor: editor});
       
   162 					}
       
   163 
       
   164 					editorManager.setActive(editor);
       
   165 					editorManager.focusedEditor = editor;
       
   166 					editor.fire('focus', {blurredEditor: focusedEditor});
       
   167 					editor.focus(true);
       
   168 				}
       
   169 
       
   170 				editor.lastRng = null;
       
   171 			});
       
   172 
       
   173 			editor.on('focusout', function() {
       
   174 				window.setTimeout(function() {
       
   175 					var focusedEditor = editorManager.focusedEditor;
       
   176 
       
   177 					// Still the same editor the the blur was outside any editor UI
       
   178 					if (!isUIElement(getActiveElement()) && focusedEditor == editor) {
       
   179 						editor.fire('blur', {focusedEditor: null});
       
   180 						editorManager.focusedEditor = null;
       
   181 
       
   182 						// Make sure selection is valid could be invalid if the editor is blured and removed before the timeout occurs
       
   183 						if (editor.selection) {
       
   184 							editor.selection.lastFocusBookmark = null;
       
   185 						}
       
   186 					}
       
   187 				}, 0);
       
   188 			});
       
   189 
       
   190 			// Check if focus is moved to an element outside the active editor by checking if the target node
       
   191 			// isn't within the body of the activeEditor nor a UI element such as a dialog child control
       
   192 			if (!documentFocusInHandler) {
       
   193 				documentFocusInHandler = function(e) {
       
   194 					var activeEditor = editorManager.activeEditor;
       
   195 
       
   196 					if (activeEditor && e.target.ownerDocument == document) {
       
   197 						// Check to make sure we have a valid selection don't update the bookmark if it's
       
   198 						// a focusin to the body of the editor see #7025
       
   199 						if (activeEditor.selection && e.target != activeEditor.getBody()) {
       
   200 							activeEditor.selection.lastFocusBookmark = createBookmark(activeEditor.dom, activeEditor.lastRng);
       
   201 						}
       
   202 
       
   203 						// Fire a blur event if the element isn't a UI element
       
   204 						if (e.target != document.body && !isUIElement(e.target) && editorManager.focusedEditor == activeEditor) {
       
   205 							activeEditor.fire('blur', {focusedEditor: null});
       
   206 							editorManager.focusedEditor = null;
       
   207 						}
       
   208 					}
       
   209 				};
       
   210 
       
   211 				DOM.bind(document, 'focusin', documentFocusInHandler);
       
   212 			}
       
   213 
       
   214 			// Handle edge case when user starts the selection inside the editor and releases
       
   215 			// the mouse outside the editor producing a new selection. This weird workaround is needed since
       
   216 			// Gecko doesn't have the "selectionchange" event we need to do this. Fixes: #6843
       
   217 			if (editor.inline && !documentMouseUpHandler) {
       
   218 				documentMouseUpHandler = function(e) {
       
   219 					var activeEditor = editorManager.activeEditor;
       
   220 
       
   221 					if (activeEditor.inline && !activeEditor.dom.isChildOf(e.target, activeEditor.getBody())) {
       
   222 						var rng = activeEditor.selection.getRng();
       
   223 
       
   224 						if (!rng.collapsed) {
       
   225 							activeEditor.lastRng = rng;
       
   226 						}
       
   227 					}
       
   228 				};
       
   229 
       
   230 				DOM.bind(document, 'mouseup', documentMouseUpHandler);
       
   231 			}
       
   232 		}
       
   233 
       
   234 		function unregisterDocumentEvents(e) {
       
   235 			if (editorManager.focusedEditor == e.editor) {
       
   236 				editorManager.focusedEditor = null;
       
   237 			}
       
   238 
       
   239 			if (!editorManager.activeEditor) {
       
   240 				DOM.unbind(document, 'selectionchange', selectionChangeHandler);
       
   241 				DOM.unbind(document, 'focusin', documentFocusInHandler);
       
   242 				DOM.unbind(document, 'mouseup', documentMouseUpHandler);
       
   243 				selectionChangeHandler = documentFocusInHandler = documentMouseUpHandler = null;
       
   244 			}
       
   245 		}
       
   246 
       
   247 		editorManager.on('AddEditor', registerEvents);
       
   248 		editorManager.on('RemoveEditor', unregisterDocumentEvents);
       
   249 	}
       
   250 
       
   251 	/**
       
   252 	 * Returns true if the specified element is part of the UI for example an button or text input.
       
   253 	 *
       
   254 	 * @method isEditorUIElement
       
   255 	 * @param  {Element} elm Element to check if it's part of the UI or not.
       
   256 	 * @return {Boolean} True/false state if the element is part of the UI or not.
       
   257 	 */
       
   258 	FocusManager.isEditorUIElement = function(elm) {
       
   259 		// Needs to be converted to string since svg can have focus: #6776
       
   260 		return elm.className.toString().indexOf('mce-') !== -1;
       
   261 	};
       
   262 
       
   263 	return FocusManager;
       
   264 });