/*! * VisualEditor user interface Command class. * * @copyright 2011-2012 VisualEditor Team and others; see AUTHORS.txt * @license The MIT License (MIT); see LICENSE.txt */ /** * Key command. * * @class * @constructor * @param {jQuery.Event|string} [e] Event or string to create command from */ ve.Command = function ( e ) { // Properties this.modifiers = { 'meta': false, 'ctrl': false, 'alt': false, 'shift': false }; this.primary = false; // Initialiation var i, len, key, parts, keyAliases = ve.Command.static.keyAliases, primaryKeys = ve.Command.static.primaryKeys, primaryKeyMap = ve.Command.static.primaryKeyMap; if ( e instanceof jQuery.Event ) { this.modifiers.meta = e.metaKey || false; this.modifiers.ctrl = e.ctrlKey || false; this.modifiers.alt = e.altKey || false; this.modifiers.shift = e.shiftKey || false; this.primary = primaryKeyMap[e.which] || false; } else if ( typeof e === 'string' ) { // Normalization: remove whitespace and force lowercase parts = e.replace( /\s*/g, '' ).toLowerCase().split( '+' ); for ( i = 0, len = parts.length; i < len; i++ ) { key = parts[i]; // Resolve key aliases if ( key in keyAliases ) { key = keyAliases[key]; } // Apply key to command if ( key in this.modifiers ) { // Modifier key this.modifiers[key] = true; } else if ( primaryKeys.indexOf( key ) !== -1 ) { // WARNING: Only the last primary key will be used this.primary = key; } } } }; /* Static Properties */ /** * @static * @property * @inheritable */ ve.Command.static = {}; /** * Symbolic modifier key names. * * The order of this array affects the canonical order of a command string. * * @static * @property */ ve.Command.static.modifierKeys = ['meta', 'ctrl', 'alt', 'shift']; /** * Symbolic primary key names. * * @static * @property */ ve.Command.static.primaryKeys = [ // Special keys 'backspace', 'tab', 'enter', 'escape', 'page-up', 'page-down', 'end', 'home', 'left', 'up', 'right', 'down', 'delete', // Numbers '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', // Letters 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', // Numpad special keys 'multiply', 'add', 'subtract', 'decimal', 'divide', // Function keys 'f1', 'f2', 'f3', 'f4', 'f5', 'f6', 'f7', 'f8', 'f9', 'f10', 'f11', 'f12', // Punctuation ';', '=', ',', '-', '.', '/', '`', '[', '\\', ']', '\'', ]; /** * Aliases for modifier or primary key names. * * @static * @property */ ve.Command.static.keyAliases = { // Platform differences 'command': 'meta', 'apple': 'meta', 'windows': 'meta', 'option': 'alt', 'return': 'enter', // Shorthand 'esc': 'escape', 'cmd': 'meta', 'del': 'delete', // Longhand 'control': 'ctrl', 'alternate': 'alt', // Symbols '⌘': 'meta', '⎇': 'alt', '⇧': 'shift', '⏎': 'enter', '⌫': 'backspace', '⎋': 'escape', }; /** * Mapping of key codes and symbolic key names. * * @static * @property */ ve.Command.static.primaryKeyMap = { // Special keys 8: 'backspace', 9: 'tab', 13: 'enter', 27: 'escape', 33: 'page-up', 34: 'page-down', 35: 'end', 36: 'home', 37: 'left', 38: 'up', 39: 'right', 40: 'down', 46: 'delete', // Numbers 48: '0', 49: '1', 50: '2', 51: '3', 52: '4', 53: '5', 54: '6', 55: '7', 56: '8', 57: '9', // Punctuation 59: ';', 61: '=', // Letters 65: 'a', 66: 'b', 67: 'c', 68: 'd', 69: 'e', 70: 'f', 71: 'g', 72: 'h', 73: 'i', 74: 'j', 75: 'k', 76: 'l', 77: 'm', 78: 'n', 79: 'o', 80: 'p', 81: 'q', 82: 'r', 83: 's', 84: 't', 85: 'u', 86: 'v', 87: 'w', 88: 'x', 89: 'y', 90: 'z', // Numpad numbers 96: '0', 97: '1', 98: '2', 99: '3', 100: '4', 101: '5', 102: '6', 103: '7', 104: '8', 105: '9', // Numpad special keys 106: 'multiply', 107: 'add', 109: 'subtract', 110: 'decimal', 111: 'divide', // Function keys 112: 'f1', 113: 'f2', 114: 'f3', 115: 'f4', 116: 'f5', 117: 'f6', 118: 'f7', 119: 'f8', 120: 'f9', 121: 'f10', 122: 'f11', 123: 'f12', // Punctuation 186: ';', 187: '=', 188: ',', 189: '-', 190: '.', 191: '/', 192: '`', 219: '[', 220: '\\', 221: ']', 222: '\'' }; /* Methods */ /** * Checks if command is complete. * * For a command to be complete, there must be a valid primary key. * * @method * @returns {boolean} Command is complete */ ve.Command.prototype.isComplete = function () { return this.primary in ve.Command.static.primaryKeyMap; }; /** * Gets a command string. * * Command strings are canonical representations of commands made up of the symbolic names of all * active modifier keys and the primary key joined together with a '+' sign. * * To normalize a command string simply create a new command from a string and then run this method. * * An incomplete command will return an empty string. * * @method * @returns {string} Canonical command string */ ve.Command.prototype.toString = function () { var i, len, modifierKeys = ve.Command.static.modifierKeys, keys = []; // Add modifier keywords in the correct order for ( i = 0, len = modifierKeys.length; i < len; i++ ) { if ( this.modifiers[modifierKeys[i]] ) { keys.push( modifierKeys[i] ); } } // Check that there were modifiers and the primary key is whitelisted if ( this.primary ) { // Add a symbolic name for the primary key keys.push( this.primary ); // Join all key names together to form a canonical string return keys.join( '+' ); } // Alternatively return an empty string return ''; };