Caret/js/command.js
Thomas Wilburn 2c34939a43 Open a new window if a command is received while inactive.
Commands will be buffered and passed in to be fired on initialization.
To suppress app launch, add the quiet flag to your command when firing
the message from an extension.
Fixes #122
2014-01-10 22:15:35 -08:00

98 lines
No EOL
2.9 KiB
JavaScript

define([
"util/text!config/commands.json",
"util/dom2"
], function(list) {
try {
list = JSON.parse(list);
} catch (e) {
console.error(e);
list = [];
}
/*
The command module is the heart of Caret's loosely-coupled modules. It
serves as an event bus for inter-module communication, but it also listens
to the DOM for click/change events and makes it easy to bind page interactions
to command callbacks.
Command is usually called as if synchronous, but it does support asynchronous
operation--it will pass a callback in to any subscribers, but will also
immediately pass back the return value (if any) from the those subscribers.
*/
var commands = {};
//commands can pass a callback, although most don't respond that way
var fire = function(command, argument, callback) {
if (!commands[command]) return;
var args = [].slice.call(arguments, 1);
//technically, a function as `argument` is a callback...
if (typeof argument == "function") {
callback = argument;
}
var registry = commands[command].slice();
registry.forEach(function(entry) {
var result = entry.callback.apply(null, args);
//immediately call back if sync-style return value was provided
if (typeof result !== "undefined" || entry.sync) {
//console.info("Immediate return from " + name, result);
if (callback) callback.call(null, result);
}
});
}
var register = function(command, listener, sync) {
if (!commands[command]) {
commands[command] = [];
}
//we allow a sync flag to be set for operations that will definitely return
commands[command].push({
callback: listener,
sync: sync
});
}
//delegate for all elements that have a command attribute
document.body.on("click", function(e) {
//cancel on inputs, selectboxes
if (["input", "select"].indexOf(e.target.tagName.toLowerCase()) >= 0) return;
if (e.button != 0) return;
//delegate all items with a command attribute
if (e.target.hasAttribute("command")) {
var command = e.target.getAttribute("command");
var arg = e.target.getAttribute("argument");
fire(command, arg);
e.preventDefault();
}
});
document.body.on("change", function(e) {
if (e.target.hasAttribute("command")) {
var command = e.target.getAttribute("command");
var arg = e.target.value;
fire(command, arg);
}
});
//register for startup and fire any commands that are pending
register("init:startup", function() {
if (window.launchCommands) {
window.launchCommands.forEach(function(bundle) {
fire(bundle.message.command, bundle.message.argument, bundle.sendResponse);
});
delete window.launchCommands;
}
});
var facade = {
fire: fire,
on: register,
list: list
};
return facade;
});