overleaf/services/web/public/js/ace/keyboard/hash_handler.js

199 lines
6.8 KiB
JavaScript
Raw Normal View History

2014-02-12 10:23:40 +00:00
/* ***** BEGIN LICENSE BLOCK *****
* Distributed under the BSD license:
*
* Copyright (c) 2010, Ajax.org B.V.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Ajax.org B.V. nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* ***** END LICENSE BLOCK ***** */
define(function(require, exports, module) {
"use strict";
var keyUtil = require("../lib/keys");
var useragent = require("../lib/useragent");
function HashHandler(config, platform) {
this.platform = platform || (useragent.isMac ? "mac" : "win");
this.commands = {};
this.commandKeyBinding = {};
// todo remove this after a while
if (this.__defineGetter__ && this.__defineSetter__ && typeof console != "undefined" && console.error) {
var warned = false;
var warn = function() {
if (!warned) {
warned = true;
console.error("commmandKeyBinding has too many m's. use commandKeyBinding");
}
};
this.__defineGetter__("commmandKeyBinding", function() {
warn();
return this.commandKeyBinding;
});
this.__defineSetter__("commmandKeyBinding", function(val) {
warn();
return this.commandKeyBinding = val;
});
} else {
this.commmandKeyBinding = this.commandKeyBinding;
}
this.addCommands(config);
};
(function() {
this.addCommand = function(command) {
if (this.commands[command.name])
this.removeCommand(command);
this.commands[command.name] = command;
if (command.bindKey)
this._buildKeyHash(command);
};
this.removeCommand = function(command) {
var name = (typeof command === 'string' ? command : command.name);
command = this.commands[name];
delete this.commands[name];
// exhaustive search is brute force but since removeCommand is
// not a performance critical operation this should be OK
var ckb = this.commandKeyBinding;
for (var hashId in ckb) {
for (var key in ckb[hashId]) {
if (ckb[hashId][key] == command)
delete ckb[hashId][key];
}
}
};
this.bindKey = function(key, command) {
if(!key)
return;
if (typeof command == "function") {
this.addCommand({exec: command, bindKey: key, name: command.name || key});
return;
}
var ckb = this.commandKeyBinding;
key.split("|").forEach(function(keyPart) {
var binding = this.parseKeys(keyPart, command);
var hashId = binding.hashId;
(ckb[hashId] || (ckb[hashId] = {}))[binding.key] = command;
}, this);
};
this.addCommands = function(commands) {
commands && Object.keys(commands).forEach(function(name) {
var command = commands[name];
if (!command)
return;
if (typeof command === "string")
return this.bindKey(command, name);
if (typeof command === "function")
command = { exec: command };
if (typeof command !== "object")
return;
if (!command.name)
command.name = name;
this.addCommand(command);
}, this);
};
this.removeCommands = function(commands) {
Object.keys(commands).forEach(function(name) {
this.removeCommand(commands[name]);
}, this);
};
this.bindKeys = function(keyList) {
Object.keys(keyList).forEach(function(key) {
this.bindKey(key, keyList[key]);
}, this);
};
this._buildKeyHash = function(command) {
var binding = command.bindKey;
if (!binding)
return;
var key = typeof binding == "string" ? binding: binding[this.platform];
this.bindKey(key, command);
};
// accepts keys in the form ctrl+Enter or ctrl-Enter
// keys without modifiers or shift only
this.parseKeys = function(keys) {
// todo support keychains
if (keys.indexOf(" ") != -1)
keys = keys.split(/\s+/).pop();
var parts = keys.toLowerCase().split(/[\-\+]([\-\+])?/).filter(function(x){return x});
var key = parts.pop();
var keyCode = keyUtil[key];
if (keyUtil.FUNCTION_KEYS[keyCode])
key = keyUtil.FUNCTION_KEYS[keyCode].toLowerCase();
else if (!parts.length)
return {key: key, hashId: -1};
else if (parts.length == 1 && parts[0] == "shift")
return {key: key.toUpperCase(), hashId: -1};
var hashId = 0;
for (var i = parts.length; i--;) {
var modifier = keyUtil.KEY_MODS[parts[i]];
if (modifier == null) {
if (typeof console != "undefined")
console.error("invalid modifier " + parts[i] + " in " + keys);
return false;
}
hashId |= modifier;
}
return {key: key, hashId: hashId};
};
this.findKeyCommand = function findKeyCommand(hashId, keyString) {
var ckbr = this.commandKeyBinding;
return ckbr[hashId] && ckbr[hashId][keyString];
};
this.handleKeyboard = function(data, hashId, keyString, keyCode) {
return {
command: this.findKeyCommand(hashId, keyString)
};
};
}).call(HashHandler.prototype)
exports.HashHandler = HashHandler;
});