415 lines
14 KiB
JavaScript
Executable File
415 lines
14 KiB
JavaScript
Executable File
/* ***** 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/emacs', ['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 emacsState = {
|
|
start: [
|
|
{
|
|
key: "ctrl-x",
|
|
then: "c-x"
|
|
},
|
|
{
|
|
regex: [ "(?:command-([0-9]*))*", "(down|ctrl-n)" ],
|
|
exec: "golinedown",
|
|
params: [
|
|
{
|
|
name: "times",
|
|
match: 1,
|
|
type: "number",
|
|
defaultValue: 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
regex: [ "(?:command-([0-9]*))*", "(right|ctrl-f)" ],
|
|
exec: "gotoright",
|
|
params: [
|
|
{
|
|
name: "times",
|
|
match: 1,
|
|
type: "number",
|
|
defaultValue: 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
regex: [ "(?:command-([0-9]*))*", "(up|ctrl-p)" ],
|
|
exec: "golineup",
|
|
params: [
|
|
{
|
|
name: "times",
|
|
match: 1,
|
|
type: "number",
|
|
defaultValue: 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
regex: [ "(?:command-([0-9]*))*", "(left|ctrl-b)" ],
|
|
exec: "gotoleft",
|
|
params: [
|
|
{
|
|
name: "times",
|
|
match: 1,
|
|
type: "number",
|
|
defaultValue: 1
|
|
}
|
|
]
|
|
},
|
|
{
|
|
comment: "This binding matches all printable characters except numbers as long as they are no numbers and print them n times.",
|
|
regex: [ "(?:command-([0-9]*))", "([^0-9]+)*" ],
|
|
match: matchCharacterOnly,
|
|
exec: "inserttext",
|
|
params: [
|
|
{
|
|
name: "times",
|
|
match: 1,
|
|
type: "number",
|
|
defaultValue: "1"
|
|
},
|
|
{
|
|
name: "text",
|
|
match: 2
|
|
}
|
|
]
|
|
},
|
|
{
|
|
comment: "This binding matches numbers as long as there is no meta_number in the buffer.",
|
|
regex: [ "(command-[0-9]*)*", "([0-9]+)" ],
|
|
match: matchCharacterOnly,
|
|
disallowMatches: [ 1 ],
|
|
exec: "inserttext",
|
|
params: [
|
|
{
|
|
name: "text",
|
|
match: 2,
|
|
type: "text"
|
|
}
|
|
]
|
|
},
|
|
{
|
|
regex: [ "command-([0-9]*)", "(command-[0-9]|[0-9])" ],
|
|
comment: "Stops execution if the regex /meta_[0-9]+/ matches to avoid resetting the buffer."
|
|
}
|
|
],
|
|
"c-x": [
|
|
{
|
|
key: "ctrl-g",
|
|
then: "start"
|
|
},
|
|
{
|
|
key: "ctrl-s",
|
|
exec: "save",
|
|
then: "start"
|
|
}
|
|
]
|
|
};
|
|
|
|
exports.Emacs = new StateHandler(emacsState);
|
|
|
|
});
|
|
/* ***** 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;
|
|
});
|
|
;
|
|
(function() {
|
|
window.require(["ace/ace"], function(a) {
|
|
if (!window.ace)
|
|
window.ace = {};
|
|
for (var key in a) if (a.hasOwnProperty(key))
|
|
ace[key] = a[key];
|
|
});
|
|
})();
|
|
|