src/pyams_skin/resources/js/ext/tinymce/dev/classes/ui/KeyboardNavigation.js
changeset 69 a361355b55c7
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/pyams_skin/resources/js/ext/tinymce/dev/classes/ui/KeyboardNavigation.js	Wed Jun 17 10:00:10 2015 +0200
@@ -0,0 +1,398 @@
+/**
+ * KeyboardNavigation.js
+ *
+ * Copyright, Moxiecode Systems AB
+ * Released under LGPL License.
+ *
+ * License: http://www.tinymce.com/license
+ * Contributing: http://www.tinymce.com/contributing
+ */
+
+/**
+ * This class handles keyboard navigation of controls and elements.
+ *
+ * @class tinymce.ui.KeyboardNavigation
+ */
+define("tinymce/ui/KeyboardNavigation", [
+], function() {
+	"use strict";
+
+	/**
+	 * This class handles all keyboard navigation for WAI-ARIA support. Each root container
+	 * gets an instance of this class.
+	 *
+	 * @constructor
+	 */
+	return function(settings) {
+		var root = settings.root, focusedElement, focusedControl;
+
+		try {
+			focusedElement = document.activeElement;
+		} catch (ex) {
+			// IE sometimes fails to return a proper element
+			focusedElement = document.body;
+		}
+
+		focusedControl = root.getParentCtrl(focusedElement);
+
+		/**
+		 * Returns the currently focused elements wai aria role of the currently
+		 * focused element or specified element.
+		 *
+		 * @private
+		 * @param {Element} elm Optional element to get role from.
+		 * @return {String} Role of specified element.
+		 */
+		function getRole(elm) {
+			elm = elm || focusedElement;
+
+			return elm && elm.getAttribute('role');
+		}
+
+		/**
+		 * Returns the wai role of the parent element of the currently
+		 * focused element or specified element.
+		 *
+		 * @private
+		 * @param {Element} elm Optional element to get parent role from.
+		 * @return {String} Role of the first parent that has a role.
+		 */
+		function getParentRole(elm) {
+			var role, parent = elm || focusedElement;
+
+			while ((parent = parent.parentNode)) {
+				if ((role = getRole(parent))) {
+					return role;
+				}
+			}
+		}
+
+		/**
+		 * Returns a wai aria property by name for example aria-selected.
+		 *
+		 * @private
+		 * @param {String} name Name of the aria property to get for example "disabled".
+		 * @return {String} Aria property value.
+		 */
+		function getAriaProp(name) {
+			var elm = focusedElement;
+
+			if (elm) {
+				return elm.getAttribute('aria-' + name);
+			}
+		}
+
+		/**
+		 * Is the element a text input element or not.
+		 *
+		 * @private
+		 * @param {Element} elm Element to check if it's an text input element or not.
+		 * @return {Boolean} True/false if the element is a text element or not.
+		 */
+		function isTextInputElement(elm) {
+			var tagName = elm.tagName.toUpperCase();
+
+			// Notice: since type can be "email" etc we don't check the type
+			// So all input elements gets treated as text input elements
+			return tagName == "INPUT" || tagName == "TEXTAREA";
+		}
+
+		/**
+		 * Returns true/false if the specified element can be focused or not.
+		 *
+		 * @private
+		 * @param {Element} elm DOM element to check if it can be focused or not.
+		 * @return {Boolean} True/false if the element can have focus.
+		 */
+		function canFocus(elm) {
+			if (isTextInputElement(elm) && !elm.hidden) {
+				return true;
+			}
+
+			if (/^(button|menuitem|checkbox|tab|menuitemcheckbox|option|gridcell)$/.test(getRole(elm))) {
+				return true;
+			}
+
+			return false;
+		}
+
+		/**
+		 * Returns an array of focusable visible elements within the specified container element.
+		 *
+		 * @private
+		 * @param {Element} elm DOM element to find focusable elements within.
+		 * @return {Array} Array of focusable elements.
+		 */
+		function getFocusElements(elm) {
+			var elements = [];
+
+			function collect(elm) {
+				if (elm.nodeType != 1 || elm.style.display == 'none') {
+					return;
+				}
+
+				if (canFocus(elm)) {
+					elements.push(elm);
+				}
+
+				for (var i = 0; i < elm.childNodes.length; i++) {
+					collect(elm.childNodes[i]);
+				}
+			}
+
+			collect(elm || root.getEl());
+
+			return elements;
+		}
+
+		/**
+		 * Returns the navigation root control for the specified control. The navigation root
+		 * is the control that the keyboard navigation gets scoped to for example a menubar or toolbar group.
+		 * It will look for parents of the specified target control or the currently focused control if this option is omitted.
+		 *
+		 * @private
+		 * @param {tinymce.ui.Control} targetControl Optional target control to find root of.
+		 * @return {tinymce.ui.Control} Navigation root control.
+		 */
+		function getNavigationRoot(targetControl) {
+			var navigationRoot, controls;
+
+			targetControl = targetControl || focusedControl;
+			controls = targetControl.parents().toArray();
+			controls.unshift(targetControl);
+
+			for (var i = 0; i < controls.length; i++) {
+				navigationRoot = controls[i];
+
+				if (navigationRoot.settings.ariaRoot) {
+					break;
+				}
+			}
+
+			return navigationRoot;
+		}
+
+		/**
+		 * Focuses the first item in the specified targetControl element or the last aria index if the
+		 * navigation root has the ariaRemember option enabled.
+		 *
+		 * @private
+		 * @param {tinymce.ui.Control} targetControl Target control to focus the first item in.
+		 */
+		function focusFirst(targetControl) {
+			var navigationRoot = getNavigationRoot(targetControl);
+			var focusElements = getFocusElements(navigationRoot.getEl());
+
+			if (navigationRoot.settings.ariaRemember && "lastAriaIndex" in navigationRoot) {
+				moveFocusToIndex(navigationRoot.lastAriaIndex, focusElements);
+			} else {
+				moveFocusToIndex(0, focusElements);
+			}
+		}
+
+		/**
+		 * Moves the focus to the specified index within the elements list.
+		 * This will scope the index to the size of the element list if it changed.
+		 *
+		 * @private
+		 * @param {Number} idx Specified index to move to.
+		 * @param {Array} elements Array with dom elements to move focus within.
+		 * @return {Number} Input index or a changed index if it was out of range.
+		 */
+		function moveFocusToIndex(idx, elements) {
+			if (idx < 0) {
+				idx = elements.length - 1;
+			} else if (idx >= elements.length) {
+				idx = 0;
+			}
+
+			if (elements[idx]) {
+				elements[idx].focus();
+			}
+
+			return idx;
+		}
+
+		/**
+		 * Moves the focus forwards or backwards.
+		 *
+		 * @private
+		 * @param {Number} dir Direction to move in positive means forward, negative means backwards.
+		 * @param {Array} elements Optional array of elements to move within defaults to the current navigation roots elements.
+		 */
+		function moveFocus(dir, elements) {
+			var idx = -1, navigationRoot = getNavigationRoot();
+
+			elements = elements || getFocusElements(navigationRoot.getEl());
+
+			for (var i = 0; i < elements.length; i++) {
+				if (elements[i] === focusedElement) {
+					idx = i;
+				}
+			}
+
+			idx += dir;
+			navigationRoot.lastAriaIndex = moveFocusToIndex(idx, elements);
+		}
+
+		/**
+		 * Moves the focus to the left this is called by the left key.
+		 *
+		 * @private
+		 */
+		function left() {
+			var parentRole = getParentRole();
+
+			if (parentRole == "tablist") {
+				moveFocus(-1, getFocusElements(focusedElement.parentNode));
+			} else if (focusedControl.parent().submenu) {
+				cancel();
+			} else {
+				moveFocus(-1);
+			}
+		}
+
+		/**
+		 * Moves the focus to the right this is called by the right key.
+		 *
+		 * @private
+		 */
+		function right() {
+			var role = getRole(), parentRole = getParentRole();
+
+			if (parentRole == "tablist") {
+				moveFocus(1, getFocusElements(focusedElement.parentNode));
+			} else if (role == "menuitem" && parentRole == "menu" && getAriaProp('haspopup')) {
+				enter();
+			} else {
+				moveFocus(1);
+			}
+		}
+
+		/**
+		 * Moves the focus to the up this is called by the up key.
+		 *
+		 * @private
+		 */
+		function up() {
+			moveFocus(-1);
+		}
+
+		/**
+		 * Moves the focus to the up this is called by the down key.
+		 *
+		 * @private
+		 */
+		function down() {
+			var role = getRole(), parentRole = getParentRole();
+
+			if (role == "menuitem" && parentRole == "menubar") {
+				enter();
+			} else if (role == "button" && getAriaProp('haspopup')) {
+				enter({key: 'down'});
+			} else {
+				moveFocus(1);
+			}
+		}
+
+		/**
+		 * Moves the focus to the next item or previous item depending on shift key.
+		 *
+		 * @private
+		 * @param {DOMEvent} e DOM event object.
+		 */
+		function tab(e) {
+			var parentRole = getParentRole();
+
+			if (parentRole == "tablist") {
+				var elm = getFocusElements(focusedControl.getEl('body'))[0];
+
+				if (elm) {
+					elm.focus();
+				}
+			} else {
+				moveFocus(e.shiftKey ? -1 : 1);
+			}
+		}
+
+		/**
+		 * Calls the cancel event on the currently focused control. This is normally done using the Esc key.
+		 *
+		 * @private
+		 */
+		function cancel() {
+			focusedControl.fire('cancel');
+		}
+
+		/**
+		 * Calls the click event on the currently focused control. This is normally done using the Enter/Space keys.
+		 *
+		 * @private
+		 * @param {Object} aria Optional aria data to pass along with the enter event.
+		 */
+		function enter(aria) {
+			aria = aria || {};
+			focusedControl.fire('click', {target: focusedElement, aria: aria});
+		}
+
+		root.on('keydown', function(e) {
+			function handleNonTabOrEscEvent(e, handler) {
+				// Ignore non tab keys for text elements
+				if (isTextInputElement(focusedElement)) {
+					return;
+				}
+
+				if (handler(e) !== false) {
+					e.preventDefault();
+				}
+			}
+
+			if (e.isDefaultPrevented()) {
+				return;
+			}
+
+			switch (e.keyCode) {
+				case 37: // DOM_VK_LEFT
+					handleNonTabOrEscEvent(e, left);
+					break;
+
+				case 39: // DOM_VK_RIGHT
+					handleNonTabOrEscEvent(e, right);
+					break;
+
+				case 38: // DOM_VK_UP
+					handleNonTabOrEscEvent(e, up);
+					break;
+
+				case 40: // DOM_VK_DOWN
+					handleNonTabOrEscEvent(e, down);
+					break;
+
+				case 27: // DOM_VK_ESCAPE
+					cancel();
+					break;
+
+				case 14: // DOM_VK_ENTER
+				case 13: // DOM_VK_RETURN
+				case 32: // DOM_VK_SPACE
+					handleNonTabOrEscEvent(e, enter);
+					break;
+
+				case 9: // DOM_VK_TAB
+					if (tab(e) !== false) {
+						e.preventDefault();
+					}
+					break;
+			}
+		});
+
+		root.on('focusin', function(e) {
+			focusedElement = e.target;
+			focusedControl = e.control;
+		});
+
+		return {
+			focusFirst: focusFirst
+		};
+	};
+});
\ No newline at end of file