/* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Skywriter. * * The Initial Developer of the Original Code is * Mozilla. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Julian Viereck (julian.viereck@gmail.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/keyboard/keybinding/vim', ['require', 'exports', 'module' , 'ace/keyboard/state_handler'], function(require, exports, module) { "use strict"; var StateHandler = require("../state_handler").StateHandler; var matchCharacterOnly = require("../state_handler").matchCharacterOnly; var vimcommand = function(key, exec, then) { return { regex: [ "([0-9]*)", key ], exec: exec, params: [ { name: "times", match: 1, type: "number", defaultValue: 1 } ], then: then } } var vimStates = { start: [ { key: "i", then: "insertMode" }, { key: "d", then: "deleteMode" }, { key: "a", exec: "gotoright", then: "insertMode" }, { key: "shift-i", exec: "gotolinestart", then: "insertMode" }, { key: "shift-a", exec: "gotolineend", then: "insertMode" }, { key: "shift-c", exec: "removetolineend", then: "insertMode" }, { key: "shift-r", exec: "overwrite", then: "replaceMode" }, vimcommand("(k|up)", "golineup"), vimcommand("(j|down)", "golinedown"), vimcommand("(l|right)", "gotoright"), vimcommand("(h|left)", "gotoleft"), { key: "shift-g", exec: "gotoend" }, vimcommand("b", "gotowordleft"), vimcommand("e", "gotowordright"), vimcommand("x", "del"), vimcommand("shift-x", "backspace"), vimcommand("shift-d", "removetolineend"), vimcommand("u", "undo"), // [count] on this may/may not work, depending on browser implementation... { comment: "Catch some keyboard input to stop it here", match: matchCharacterOnly } ], insertMode: [ { key: "esc", then: "start" } ], replaceMode: [ { key: "esc", exec: "overwrite", then: "start" } ], deleteMode: [ { key: "d", exec: "removeline", then: "start" } ] }; exports.Vim = new StateHandler(vimStates); }); /* ***** BEGIN LICENSE BLOCK ***** * Version: MPL 1.1/GPL 2.0/LGPL 2.1 * * The contents of this file are subject to the Mozilla Public License Version * 1.1 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License * for the specific language governing rights and limitations under the * License. * * The Original Code is Mozilla Skywriter. * * The Initial Developer of the Original Code is * Mozilla. * Portions created by the Initial Developer are Copyright (C) 2009 * the Initial Developer. All Rights Reserved. * * Contributor(s): * Julian Viereck (julian.viereck@gmail.com) * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), * in which case the provisions of the GPL or the LGPL are applicable instead * of those above. If you wish to allow use of your version of this file only * under the terms of either the GPL or the LGPL, and not to allow others to * use your version of this file under the terms of the MPL, indicate your * decision by deleting the provisions above and replace them with the notice * and other provisions required by the GPL or the LGPL. If you do not delete * the provisions above, a recipient may use your version of this file under * the terms of any one of the MPL, the GPL or the LGPL. * * ***** END LICENSE BLOCK ***** */ define('ace/keyboard/state_handler', ['require', 'exports', 'module' ], function(require, exports, module) { "use strict"; // If you're developing a new keymapping and want to get an idea what's going // on, then enable debugging. var DEBUG = false; function StateHandler(keymapping) { this.keymapping = this.$buildKeymappingRegex(keymapping); } StateHandler.prototype = { /** * Build the RegExp from the keymapping as RegExp can't stored directly * in the metadata JSON and as the RegExp used to match the keys/buffer * need to be adapted. */ $buildKeymappingRegex: function(keymapping) { for (var state in keymapping) { this.$buildBindingsRegex(keymapping[state]); } return keymapping; }, $buildBindingsRegex: function(bindings) { // Escape a given Regex string. bindings.forEach(function(binding) { if (binding.key) { binding.key = new RegExp('^' + binding.key + '$'); } else if (Array.isArray(binding.regex)) { if (!('key' in binding)) binding.key = new RegExp('^' + binding.regex[1] + '$'); binding.regex = new RegExp(binding.regex.join('') + '$'); } else if (binding.regex) { binding.regex = new RegExp(binding.regex + '$'); } }); }, $composeBuffer: function(data, hashId, key, e) { // Initialize the data object. if (data.state == null || data.buffer == null) { data.state = "start"; data.buffer = ""; } var keyArray = []; if (hashId & 1) keyArray.push("ctrl"); if (hashId & 8) keyArray.push("command"); if (hashId & 2) keyArray.push("option"); if (hashId & 4) keyArray.push("shift"); if (key) keyArray.push(key); var symbolicName = keyArray.join("-"); var bufferToUse = data.buffer + symbolicName; // Don't add the symbolic name to the key buffer if the alt_ key is // part of the symbolic name. If it starts with alt_, this means // that the user hit an alt keycombo and there will be a single, // new character detected after this event, which then will be // added to the buffer (e.g. alt_j will result in ∆). // // We test for 2 and not for & 2 as we only want to exclude the case where // the option key is pressed alone. if (hashId != 2) { data.buffer = bufferToUse; } var bufferObj = { bufferToUse: bufferToUse, symbolicName: symbolicName, }; if (e) { bufferObj.keyIdentifier = e.keyIdentifier } return bufferObj; }, $find: function(data, buffer, symbolicName, hashId, key, keyIdentifier) { // Holds the command to execute and the args if a command matched. var result = {}; // Loop over all the bindings of the keymap until a match is found. this.keymapping[data.state].some(function(binding) { var match; // Check if the key matches. if (binding.key && !binding.key.test(symbolicName)) { return false; } // Check if the regex matches. if (binding.regex && !(match = binding.regex.exec(buffer))) { return false; } // Check if the match function matches. if (binding.match && !binding.match(buffer, hashId, key, symbolicName, keyIdentifier)) { return false; } // Check for disallowed matches. if (binding.disallowMatches) { for (var i = 0; i < binding.disallowMatches.length; i++) { if (!!match[binding.disallowMatches[i]]) { return false; } } } // If there is a command to execute, then figure out the // command and the arguments. if (binding.exec) { result.command = binding.exec; // Build the arguments. if (binding.params) { var value; result.args = {}; binding.params.forEach(function(param) { if (param.match != null && match != null) { value = match[param.match] || param.defaultValue; } else { value = param.defaultValue; } if (param.type === 'number') { value = parseInt(value); } result.args[param.name] = value; }); } data.buffer = ""; } // Handle the 'then' property. if (binding.then) { data.state = binding.then; data.buffer = ""; } // If no command is set, then execute the "null" fake command. if (result.command == null) { result.command = "null"; } if (DEBUG) { console.log("KeyboardStateMapper#find", binding); } return true; }); if (result.command) { return result; } else { data.buffer = ""; return false; } }, /** * This function is called by keyBinding. */ handleKeyboard: function(data, hashId, key, keyCode, e) { // If we pressed any command key but no other key, then ignore the input. // Otherwise "shift-" is added to the buffer, and later on "shift-g" // which results in "shift-shift-g" which doesn't make sense. if (hashId != 0 && (key == "" || key == String.fromCharCode(0))) { return null; } // Compute the current value of the keyboard input buffer. var r = this.$composeBuffer(data, hashId, key, e); var buffer = r.bufferToUse; var symbolicName = r.symbolicName; var keyId = r.keyIdentifier; r = this.$find(data, buffer, symbolicName, hashId, key, keyId); if (DEBUG) { console.log("KeyboardStateMapper#match", buffer, symbolicName, r); } return r; } } /** * This is a useful matching function and therefore is defined here so that * users of KeyboardStateMapper can use it. * * @return boolean * If no command key (Command|Option|Shift|Ctrl) is pressed, it * returns true. If the only the Shift key is pressed + a character * true is returned as well. Otherwise, false is returned. * Summing up, the function returns true whenever the user typed * a normal character on the keyboard and no shortcut. */ exports.matchCharacterOnly = function(buffer, hashId, key, symbolicName) { // If no command keys are pressed, then catch the input. if (hashId == 0) { return true; } // If only the shift key is pressed and a character key, then // catch that input as well. else if ((hashId == 4) && key.length == 1) { return true; } // Otherwise, we let the input got through. else { return false; } }; exports.StateHandler = StateHandler; });