mirror of
https://github.com/overleaf/overleaf.git
synced 2024-12-02 19:00:23 -05:00
199 lines
6.8 KiB
JavaScript
199 lines
6.8 KiB
JavaScript
|
/* ***** 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;
|
||
|
});
|