mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2026-03-03 00:35:47 +01:00
Major documentation updates and formatting fixes, and many, many other changes thanks to an MQ glitch, including:
* Significant completion speed improvements * Significantly improve startup speed, in large part by lazily instantiating Options and Commands, lazily installing highlight stylesheets, etc. * Update logos and icons, fix atrocious about page * Fix Teledactyl * JavaScript completion now avoids accessing property values * Add Option#persist to define which options are saved with :mkp * Add new Dactyl component which holds add-on-specific configuration information and removes need for separate components for each dactyl host * Several fixes for latest nightlies * Significant code cleanup and many bug fixes --HG-- rename : muttator/AUTHORS => teledactyl/AUTHORS rename : muttator/Donors => teledactyl/Donors rename : muttator/Makefile => teledactyl/Makefile rename : muttator/NEWS => teledactyl/NEWS rename : muttator/TODO => teledactyl/TODO rename : muttator/chrome.manifest => teledactyl/chrome.manifest rename : muttator/components/commandline-handler.js => teledactyl/components/commandline-handler.js rename : muttator/components/protocols.js => teledactyl/components/protocols.js rename : muttator/content/addressbook.js => teledactyl/content/addressbook.js rename : muttator/content/compose/compose.js => teledactyl/content/compose/compose.js rename : muttator/content/compose/compose.xul => teledactyl/content/compose/compose.xul rename : muttator/content/compose/dactyl.dtd => teledactyl/content/compose/dactyl.dtd rename : muttator/content/compose/dactyl.xul => teledactyl/content/compose/dactyl.xul rename : muttator/content/config.js => teledactyl/content/config.js rename : muttator/content/dactyl.dtd => teledactyl/content/dactyl.dtd rename : muttator/content/logo.png => teledactyl/content/logo.png rename : muttator/content/mail.js => teledactyl/content/mail.js rename : muttator/content/muttator.xul => teledactyl/content/pentadactyl.xul rename : muttator/contrib/vim/Makefile => teledactyl/contrib/vim/Makefile rename : muttator/contrib/vim/ftdetect/muttator.vim => teledactyl/contrib/vim/ftdetect/muttator.vim rename : muttator/contrib/vim/mkvimball.txt => teledactyl/contrib/vim/mkvimball.txt rename : muttator/contrib/vim/syntax/muttator.vim => teledactyl/contrib/vim/syntax/muttator.vim rename : muttator/install.rdf => teledactyl/install.rdf rename : muttator/locale/en-US/Makefile => teledactyl/locale/en-US/Makefile rename : muttator/locale/en-US/all.xml => teledactyl/locale/en-US/all.xml rename : muttator/locale/en-US/autocommands.xml => teledactyl/locale/en-US/autocommands.xml rename : muttator/locale/en-US/gui.xml => teledactyl/locale/en-US/gui.xml rename : muttator/locale/en-US/intro.xml => teledactyl/locale/en-US/intro.xml rename : muttator/skin/icon.png => teledactyl/skin/icon.png
This commit is contained in:
11
teledactyl/AUTHORS
Normal file
11
teledactyl/AUTHORS
Normal file
@@ -0,0 +1,11 @@
|
||||
Main developer/Project founder:
|
||||
* Martin Stubenschrott (stubenschrott@vimperator.org)
|
||||
|
||||
Inactive/former developers:
|
||||
* Daniel Bainton (dpb .AT. driftaway .DOT. org)
|
||||
|
||||
Patches:
|
||||
* Christian Dietrich (too many to list)
|
||||
|
||||
A lot of people contributed to Pentadactyl, which is the basis of Muttator, so please refer
|
||||
to that AUTHOR file for more contributors.
|
||||
3
teledactyl/Donors
Normal file
3
teledactyl/Donors
Normal file
@@ -0,0 +1,3 @@
|
||||
2009:
|
||||
* Andreas Nerf (biggest Muttator donor so far! thanks a lot)
|
||||
* Kirill Korotaev
|
||||
6
teledactyl/Makefile
Normal file
6
teledactyl/Makefile
Normal file
@@ -0,0 +1,6 @@
|
||||
#### configuration
|
||||
|
||||
VERSION = 0.6a1pre
|
||||
NAME = muttator
|
||||
|
||||
include ../common/Makefile
|
||||
64
teledactyl/NEWS
Normal file
64
teledactyl/NEWS
Normal file
@@ -0,0 +1,64 @@
|
||||
2009-XX-XX
|
||||
* version 0.6a1pre
|
||||
* asciidoc is no longer required to build Muttator
|
||||
* The full help system now available for Muttator.
|
||||
* add 'titlestring' option
|
||||
* rename *FolderLoaded* autocommand event to *FolderLoad*
|
||||
* add the *DOMLoad* autocommand event
|
||||
* add 'online' option
|
||||
* add 'smtpserver' option
|
||||
* add 'jsdebugger' option - switch on/off javascript debugger service
|
||||
|
||||
2009-03-29
|
||||
* version 0.5
|
||||
* fixes for recent TB nightly changes
|
||||
* new 'threads' option (non-functional for now)
|
||||
* new 'archivefolder' option
|
||||
* small bug fixes
|
||||
|
||||
2008-08-03:
|
||||
* version 0.4
|
||||
* new 'autoexternal' option to edit new messages/reply with the external editor by default.
|
||||
NOTE: You must set this option in your .muttatorrc, as options are not shared between the
|
||||
main thunderbird window and the compose window!
|
||||
* various *l* mappings for labeling messages
|
||||
* [m]p[m] to open RSS message in browser
|
||||
* [m]y[m] to yank sender or RSS URL
|
||||
* [m]Y[m] to yank subject
|
||||
* [m]R[m] to reply to all
|
||||
* new compose mappings: [m]s[m], [m]t[m] and [m]i[m] - Focus subject:, To: or message body
|
||||
* [m]q[m] quits composer now
|
||||
* new [m]x[m] mapping to toggle HTML display (is there a need for "simple HTML"?)
|
||||
* new [m]h[m] mapping to toggle headers
|
||||
|
||||
2008-06-04:
|
||||
* version 0.3
|
||||
* new tab related mappings + commands
|
||||
* new [c]:contacts[c] and [c]:contact[c] commands for address book operations
|
||||
* new [c]:message[c] command
|
||||
* new -- COMPOSE -- mode with mutt-like keybindings ([m]y[m] to send message, [m]e[m] to edit message)
|
||||
* external editor support for sending messages
|
||||
* [m]m[m] to open a new message compose window
|
||||
* new [m]M[m] command to send a new message to the sender of the currently selected message
|
||||
* completions for [c]:goto[c], [c]:copyto[c], [c]:moveto[c] commands
|
||||
* [c]:emptytrash[c] command
|
||||
* new [m]*[m] and [m]#[m] mappings to select next/prev message from same author (not perfect matching however)
|
||||
* [m]J[m], [m]K[m], [m]]s[m], etc. search in closed threads now
|
||||
* [m]gj[m] and [m]gk[m] to select messages in closed threads
|
||||
* [m]t[m] to select current thread, use [m]lr[m] now to label a message as read
|
||||
* KMail-like mode-independent scrolling with [m]<Left>[m]/[m]<Right>[m] and [m]<Up>[m]/[m]<Down>[m]
|
||||
* many small improvements
|
||||
|
||||
2008-04-30:
|
||||
* version 0.2
|
||||
* [m]<C-s>[m] mappings to move messages to an "Archive" folder (inspired by gmail)
|
||||
* new [c]:moveto[c] and [c]:copyto[c] commands
|
||||
* [m]u[m] and [m]<C-r>[m] mappings to undo/redo
|
||||
* new Muttator specific :help screen
|
||||
* small bug fixes
|
||||
|
||||
2008-04-29:
|
||||
* version 0.1
|
||||
* first public release, straight port from Pentadactyl with many basic mappings
|
||||
|
||||
// vim: set filetype=asciidoc:
|
||||
16
teledactyl/TODO
Normal file
16
teledactyl/TODO
Normal file
@@ -0,0 +1,16 @@
|
||||
Priority list:
|
||||
1-9 as in vim (9=required for next release, 5=would be nice, 1=probably not)
|
||||
|
||||
BUGS:
|
||||
- several tab related commands like :tab are enabled but don't work
|
||||
- stal=1 doesn't work
|
||||
|
||||
(recent CVS regressions):
|
||||
- 'autoexternal' doesn't work
|
||||
|
||||
FEATURES:
|
||||
9 edit messages with vim
|
||||
8 the archives need to be mailbox specific
|
||||
- currently it archives mail to the first Archive folder found
|
||||
7 :set! mailnews.wraplength=140 or similar
|
||||
6 add a feature to show emails from threads under each other like in gmail
|
||||
34
teledactyl/chrome.manifest
Normal file
34
teledactyl/chrome.manifest
Normal file
@@ -0,0 +1,34 @@
|
||||
# Thunderbird
|
||||
content teledactyl content/
|
||||
skin teledactyl classic/1.0 skin/
|
||||
locale dactyl en-US locale/en-US/
|
||||
|
||||
content dactyl ../common/content/
|
||||
resource dactyl ../common/modules/
|
||||
skin dactyl classic/1.0 ../common/skin/
|
||||
|
||||
override chrome://dactyl/content/dactyl.dtd chrome://teledactyl/content/dactyl.dtd
|
||||
override chrome://dactyl/content/config.js chrome://teledactyl/content/config.js
|
||||
|
||||
overlay chrome://messenger/content/messenger.xul chrome://dactyl/content/dactyl.xul
|
||||
overlay chrome://messenger/content/messenger.xul chrome://teledactyl/content/teledactyl.xul
|
||||
|
||||
overlay chrome://messenger/content/messengercompose/messengercompose.xul chrome://teledactyl/content/compose/dactyl.xul
|
||||
overlay chrome://messenger/content/messengercompose/messengercompose.xul chrome://teledactyl/content/compose/compose.xul
|
||||
|
||||
component {8e4a8e2f-95a0-4d8f-90ac-fc9d7d8f5468} components/dactyl.js
|
||||
contract @dactyl.googlecode.com/base/dactyl {8e4a8e2f-95a0-4d8f-90ac-fc9d7d8f5468}
|
||||
|
||||
component {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69} components/commandline-handler.js
|
||||
contract @mozilla.org/commandlinehandler/general-startup;1?type=teledactyl {16dc34f7-6d22-4aa4-a67f-2921fb5dcb69}
|
||||
category command-line-handler m-teledactyl @mozilla.org/commandlinehandler/general-startup;1?type=pentadactyl
|
||||
|
||||
component {c1b67a07-18f7-4e13-b361-2edcc35a5a0d} components/protocols.js
|
||||
contract @mozilla.org/network/protocol;1?name=chrome-data {c1b67a07-18f7-4e13-b361-2edcc35a5a0d}
|
||||
component {9c8f2530-51c8-4d41-b356-319e0b155c44} components/protocols.js
|
||||
contract @mozilla.org/network/protocol;1?name=dactyl {9c8f2530-51c8-4d41-b356-319e0b155c44}
|
||||
component {f4506a17-5b4d-4cd9-92d4-2eb4630dc388} components/protocols.js
|
||||
contract @dactyl.googlecode.com/base/xpc-interface-shim {f4506a17-5b4d-4cd9-92d4-2eb4630dc388}
|
||||
component {81495d80-89ee-4c36-a88d-ea7c4e5ac63f} components/protocols.js
|
||||
contract @mozilla.org/network/protocol/about;1?what=teledactyl {81495d80-89ee-4c36-a88d-ea7c4e5ac63f}
|
||||
|
||||
1
teledactyl/components/commandline-handler.js
Symbolic link
1
teledactyl/components/commandline-handler.js
Symbolic link
@@ -0,0 +1 @@
|
||||
../../common/components/commandline-handler.js
|
||||
1
teledactyl/components/protocols.js
Symbolic link
1
teledactyl/components/protocols.js
Symbolic link
@@ -0,0 +1 @@
|
||||
../../common/components/protocols.js
|
||||
151
teledactyl/content/addressbook.js
Normal file
151
teledactyl/content/addressbook.js
Normal file
@@ -0,0 +1,151 @@
|
||||
// Copyright (c) 2008 by Christian Dietrich <stettberger@dokucode.de>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
const Addressbook = Module("addressbook", {
|
||||
init: function () {
|
||||
},
|
||||
|
||||
// TODO: add option for a format specifier, like:
|
||||
// :set displayname=%l, %f
|
||||
generateDisplayName: function (firstName, lastName) {
|
||||
if (firstName && lastName)
|
||||
return lastName + ", " + firstName;
|
||||
else if (firstName)
|
||||
return firstName;
|
||||
else if (lastName)
|
||||
return lastName;
|
||||
else
|
||||
return "";
|
||||
},
|
||||
|
||||
getDirectoryFromURI: function (uri) services.get("rdf").GetResource(uri).QueryInterface(Ci.nsIAbDirectory),
|
||||
|
||||
add: function (address, firstName, lastName, displayName) {
|
||||
const personalAddressbookURI = "moz-abmdbdirectory://abook.mab";
|
||||
let directory = this.getDirectoryFromURI(personalAddressbookURI);
|
||||
let card = Cc["@mozilla.org/addressbook/cardproperty;1"].createInstance(Ci.nsIAbCard);
|
||||
|
||||
if (!address || !directory || !card)
|
||||
return false;
|
||||
|
||||
card.primaryEmail = address;
|
||||
card.firstName = firstName;
|
||||
card.lastName = lastName;
|
||||
card.displayName = displayName;
|
||||
|
||||
return directory.addCard(card);
|
||||
},
|
||||
|
||||
// TODO: add telephone number support
|
||||
list: function (filter, newMail) {
|
||||
let addresses = [];
|
||||
let dirs = abManager.directories;
|
||||
let lowerFilter = filter.toLowerCase();
|
||||
|
||||
while (dirs.hasMoreElements()) {
|
||||
let addrbook = dirs.getNext().QueryInterface(Ci.nsIAbDirectory);
|
||||
let cards = addrbook.childCards;
|
||||
while (cards.hasMoreElements()) {
|
||||
let card = cards.getNext().QueryInterface(Ci.nsIAbCard);
|
||||
//var mail = card.primaryEmail || ""; //XXX
|
||||
let displayName = card.displayName;
|
||||
if (!displayName)
|
||||
displayName = this.generateDisplayName(card.firstName, card.lastName);
|
||||
|
||||
if (displayName.toLowerCase().indexOf(lowerFilter) > -1
|
||||
|| card.primaryEmail.toLowerCase().indexOf(lowerFilter) > -1)
|
||||
addresses.push([displayName, card.primaryEmail]);
|
||||
}
|
||||
}
|
||||
|
||||
if (addresses.length < 1) {
|
||||
if (!filter)
|
||||
dactyl.echoerr("Exxx: No contacts", commandline.FORCE_SINGLELINE);
|
||||
else
|
||||
dactyl.echoerr("Exxx: No contacts matching string '" + filter + "'", commandline.FORCE_SINGLELINE);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (newMail) {
|
||||
// Now we have to create a new message
|
||||
let args = {};
|
||||
args.to = addresses.map(
|
||||
function (address) "\"" + address[0].replace(/"/g, "") + " <" + address[1] + ">\""
|
||||
).join(", ");
|
||||
|
||||
mail.composeNewMail(args);
|
||||
}
|
||||
else {
|
||||
let list = template.tabular(["Name", "Address"], [],
|
||||
[[util.clip(address[0], 50), address[1]] for ([, address] in Iterator(addresses))]
|
||||
);
|
||||
commandline.echo(list, commandline.HL_NORMAL, commandline.FORCE_MULTILINE);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["con[tact]"],
|
||||
"Add an address book entry",
|
||||
function (args) {
|
||||
let mailAddr = args[0]; // TODO: support more than one email address
|
||||
let firstName = args["-firstname"] || null;
|
||||
let lastName = args["-lastname"] || null;
|
||||
let displayName = args["-name"] || null;
|
||||
if (!displayName)
|
||||
displayName = this.generateDisplayName(firstName, lastName);
|
||||
|
||||
if (addressbook.add(mailAddr, firstName, lastName, displayName))
|
||||
dactyl.echomsg("Added address: " + displayName + " <" + mailAddr + ">", 1, commandline.FORCE_SINGLELINE);
|
||||
else
|
||||
dactyl.echoerr("Exxx: Could not add contact `" + mailAddr + "'", commandline.FORCE_SINGLELINE);
|
||||
|
||||
},
|
||||
{
|
||||
argCount: "+",
|
||||
options: [[["-firstname", "-f"], commands.OPTION_STRING],
|
||||
[["-lastname", "-l"], commands.OPTION_STRING],
|
||||
[["-name", "-n"], commands.OPTION_STRING]]
|
||||
});
|
||||
|
||||
commands.add(["contacts", "addr[essbook]"],
|
||||
"List or open multiple addresses",
|
||||
function (args) { addressbook.list(args.string, args.bang); },
|
||||
{ bang: true });
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.mailModes;
|
||||
|
||||
mappings.add(myModes, ["a"],
|
||||
"Open a prompt to save a new addressbook entry for the sender of the selected message",
|
||||
function () {
|
||||
try {
|
||||
var to = gDBView.hdrForFirstSelectedMessage.mime2DecodedAuthor;
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.beep();
|
||||
}
|
||||
|
||||
if (!to)
|
||||
return;
|
||||
|
||||
let address = to.substring(to.indexOf("<") + 1, to.indexOf(">"));
|
||||
|
||||
let displayName = to.substr(0, to.indexOf("<") - 1);
|
||||
if (/^\S+\s+\S+\s*$/.test(displayName)) {
|
||||
let names = displayName.split(/\s+/);
|
||||
displayName = "-firstname=" + names[0].replace(/"/g, "")
|
||||
+ " -lastname=" + names[1].replace(/"/g, "");
|
||||
}
|
||||
else
|
||||
displayName = "-name=\"" + displayName.replace(/"/g, "") + "\"";
|
||||
|
||||
commandline.open(":", "contact " + address + " " + displayName, modes.EX);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
103
teledactyl/content/compose/compose.js
Normal file
103
teledactyl/content/compose/compose.js
Normal file
@@ -0,0 +1,103 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
function Compose() { //{{{
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
////////////////////// PRIVATE SECTION /////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
config.features = ["addressbook"]; // the composer has no special features
|
||||
|
||||
var stateListener = {
|
||||
QueryInterface: function (id) {
|
||||
if (id.equals(Ci.nsIDocumentStateListener))
|
||||
return this;
|
||||
throw Cr.NS_NOINTERFACE;
|
||||
},
|
||||
|
||||
// this is (also) fired once the new compose window loaded the message for the first time
|
||||
NotifyDocumentStateChanged: function (nowDirty) {
|
||||
// only edit with external editor if this window was not cached!
|
||||
if (options["autoexternal"] && !window.messageWasEditedExternally/* && !gMsgCompose.recycledWindow*/) {
|
||||
window.messageWasEditedExternally = true;
|
||||
editor.editFieldExternally();
|
||||
}
|
||||
|
||||
},
|
||||
NotifyDocumentCreated: function () {},
|
||||
NotifyDocumentWillBeDestroyed: function () {}
|
||||
};
|
||||
|
||||
// XXX: Hack!
|
||||
window.document.addEventListener("load", function () {
|
||||
if (window.messageWasEditedExternally === undefined) {
|
||||
window.messageWasEditedExternally = false;
|
||||
GetCurrentEditor().addDocumentStateListener(stateListener);
|
||||
}
|
||||
}, true);
|
||||
|
||||
window.addEventListener("compose-window-close", function () {
|
||||
window.messageWasEditedExternally = false;
|
||||
}, true);
|
||||
|
||||
/*window.document.addEventListener("unload", function () {
|
||||
GetCurrentEditor().removeDocumentStateListener(config.stateListener);
|
||||
}, true);*/
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// OPTIONS /////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// MAPPINGS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// COMMANDS ////////////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["e"], "Edit message",
|
||||
function () { editor.editFieldExternally(); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["y"], "Send message now",
|
||||
function () { window.goDoCommand("cmd_sendNow"); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["Y"], "Send message later",
|
||||
function () { window.goDoCommand("cmd_sendLater"); });
|
||||
|
||||
// FIXME: does not really work reliably
|
||||
mappings.add([modes.COMPOSE],
|
||||
["t"], "Select To: field",
|
||||
function () { awSetFocus(0, awGetInputElement(1)); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["s"], "Select Subject: field",
|
||||
function () { GetMsgSubjectElement().focus(); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["i"], "Select message body",
|
||||
function () { SetMsgBodyFrameFocus(); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["q"], "Close composer, ask when for unsaved changes",
|
||||
function () { DoCommandClose(); });
|
||||
|
||||
mappings.add([modes.COMPOSE],
|
||||
["Q", "ZQ"], "Force closing composer",
|
||||
function () { MsgComposeCloseWindow(true); /* cache window for better performance*/ });
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////}}}
|
||||
////////////////////// PUBLIC SECTION //////////////////////////////////////////
|
||||
/////////////////////////////////////////////////////////////////////////////{{{
|
||||
|
||||
return {};
|
||||
|
||||
//}}}
|
||||
} //}}}
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
19
teledactyl/content/compose/compose.xul
Normal file
19
teledactyl/content/compose/compose.xul
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- ***** BEGIN LICENSE BLOCK ***** {{{
|
||||
Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
|
||||
This work is licensed for reuse under an MIT license. Details are
|
||||
given in the LICENSE.txt file included with this file.
|
||||
}}} ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<!--?xml-stylesheet href="chrome://browser/skin/" type="text/css"?-->
|
||||
|
||||
<overlay id="muttator"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:nc="http://home.netscape.com/NC-rdf#"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
</overlay>
|
||||
|
||||
<!-- vim: set fdm=marker sw=4 ts=4 et: -->
|
||||
4
teledactyl/content/compose/dactyl.dtd
Normal file
4
teledactyl/content/compose/dactyl.dtd
Normal file
@@ -0,0 +1,4 @@
|
||||
<!ENTITY dactyl.mainWindow "msgcomposeWindow">
|
||||
<!ENTITY dactyl.name "muttator">
|
||||
<!ENTITY dactyl.statusBefore "">
|
||||
<!ENTITY dactyl.statusAfter "statusText">
|
||||
98
teledactyl/content/compose/dactyl.xul
Normal file
98
teledactyl/content/compose/dactyl.xul
Normal file
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<!-- ***** BEGIN LICENSE BLOCK ***** {{{
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
}}} ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<?xml-stylesheet href="chrome://dactyl/skin/dactyl.css" type="text/css"?>
|
||||
<!DOCTYPE overlay SYSTEM "dactyl.dtd" [
|
||||
<!ENTITY dactyl.content "chrome://dactyl/content/">
|
||||
]>
|
||||
|
||||
<overlay id="dactyl"
|
||||
xmlns:dactyl="http://vimperator.org/namespaces/liberator"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:nc="http://home.netscape.com/NC-rdf#"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/x-javascript;version=1.8" src="&dactyl.content;dactyl-overlay.js"/>
|
||||
|
||||
<window id="&dactyl.mainWindow;">
|
||||
|
||||
<keyset id="mainKeyset">
|
||||
<key id="key_open_vimbar" key=":" oncommand="dactyl.modules.commandline.open(':', '', dactyl.modules.modes.EX);" modifiers=""/>
|
||||
<key id="key_stop" keycode="VK_ESCAPE" oncommand="dactyl.modules.events.onEscape();"/>
|
||||
<!-- other keys are handled inside the event loop in events.js -->
|
||||
</keyset>
|
||||
|
||||
<popupset>
|
||||
<panel id="dactyl-visualbell" dactyl:highlight="Bell"/>
|
||||
</popupset>
|
||||
|
||||
<!--this notifies us also of focus events in the XUL
|
||||
from: http://developer.mozilla.org/en/docs/XUL_Tutorial:Updating_Commands !-->
|
||||
<commandset id="onPentadactylFocus"
|
||||
commandupdater="true"
|
||||
events="focus"
|
||||
oncommandupdate="if (dactyl.modules.events != undefined) dactyl.modules.events.onFocusChange(event);"/>
|
||||
<commandset id="onPentadactylSelect"
|
||||
commandupdater="true"
|
||||
events="select"
|
||||
oncommandupdate="if (dactyl.modules.events != undefined) dactyl.modules.events.onSelectionChange(event);"/>
|
||||
|
||||
<!-- As of Firefox 3.1pre, <iframe>.height changes do not seem to have immediate effect,
|
||||
therefore we need to put them into a <vbox> for which that works just fine -->
|
||||
<vbox class="dactyl-container" hidden="false" collapsed="true">
|
||||
<iframe id="dactyl-multiline-output" src="chrome://dactyl/content/buffer.xhtml"
|
||||
flex="1" hidden="false" collapsed="false"
|
||||
onclick="dactyl.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
</vbox>
|
||||
|
||||
<vbox class="dactyl-container" hidden="false" collapsed="true">
|
||||
<iframe id="dactyl-completions" src="chrome://dactyl/content/buffer.xhtml"
|
||||
flex="1" hidden="false" collapsed="false"
|
||||
onclick="dactyl.modules.commandline.onMultilineOutputEvent(event)"/>
|
||||
</vbox>
|
||||
|
||||
<stack orient="horizontal" align="stretch" class="dactyl-container" dactyl:highlight="CmdLine">
|
||||
<textbox class="plain" id="dactyl-message" flex="1" readonly="true" dactyl:highlight="Normal"/>
|
||||
<hbox id="dactyl-commandline" hidden="false" collapsed="true" class="dactyl-container" dactyl:highlight="Normal">
|
||||
<label class="plain" id="dactyl-commandline-prompt" flex="0" crop="end" value="" collapsed="true"/>
|
||||
<textbox class="plain" id="dactyl-commandline-command" flex="1" type="timed" timeout="100"
|
||||
oninput="dactyl.modules.commandline.onEvent(event);"
|
||||
onkeyup="dactyl.modules.commandline.onEvent(event);"
|
||||
onfocus="dactyl.modules.commandline.onEvent(event);"
|
||||
onblur="dactyl.modules.commandline.onEvent(event);"/>
|
||||
</hbox>
|
||||
</stack>
|
||||
|
||||
<vbox class="dactyl-container" hidden="false" collapsed="false">
|
||||
<textbox id="dactyl-multiline-input" class="plain" flex="1" rows="1" hidden="false" collapsed="true" multiline="true"
|
||||
onkeypress="dactyl.modules.commandline.onMultilineInputEvent(event);"
|
||||
oninput="dactyl.modules.commandline.onMultilineInputEvent(event);"
|
||||
onblur="dactyl.modules.commandline.onMultilineInputEvent(event);"/>
|
||||
</vbox>
|
||||
|
||||
</window>
|
||||
|
||||
<statusbar id="status-bar" dactyl:highlight="StatusLine">
|
||||
<hbox insertbefore="&dactyl.statusBefore;" insertafter="&dactyl.statusAfter;"
|
||||
id="dactyl-statusline" flex="1" hidden="false" align="center">
|
||||
<textbox class="plain" id="dactyl-statusline-field-url" readonly="false" flex="1" crop="end"/>
|
||||
<label class="plain" id="dactyl-statusline-field-inputbuffer" flex="0"/>
|
||||
<label class="plain" id="dactyl-statusline-field-progress" flex="0"/>
|
||||
<label class="plain" id="dactyl-statusline-field-tabcount" flex="0"/>
|
||||
<label class="plain" id="dactyl-statusline-field-bufferposition" flex="0"/>
|
||||
</hbox>
|
||||
<!-- just hide them since other elements expect them -->
|
||||
<statusbarpanel id="statusbar-display" hidden="true"/>
|
||||
<statusbarpanel id="statusbar-progresspanel" hidden="true"/>
|
||||
</statusbar>
|
||||
|
||||
</overlay>
|
||||
|
||||
<!-- vim: set fdm=marker sw=4 ts=4 et: -->
|
||||
179
teledactyl/content/config.js
Normal file
179
teledactyl/content/config.js
Normal file
@@ -0,0 +1,179 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
const Config = Module("config", ConfigBase, {
|
||||
init: function init() {
|
||||
init.supercall(this);
|
||||
// don't wait too long when selecting new messages
|
||||
// GetThreadTree()._selectDelay = 300; // TODO: make configurable
|
||||
},
|
||||
|
||||
autocommands: {
|
||||
DOMLoad: "Triggered when a page's DOM content has fully loaded",
|
||||
FolderLoad: "Triggered after switching folders in Thunderbird",
|
||||
PageLoadPre: "Triggered after a page load is initiated",
|
||||
PageLoad: "Triggered when a page gets (re)loaded/opened",
|
||||
Enter: "Triggered after Thunderbird starts",
|
||||
Leave: "Triggered before exiting Thunderbird",
|
||||
LeavePre: "Triggered before exiting Thunderbird",
|
||||
},
|
||||
|
||||
get browser() getBrowser(),
|
||||
|
||||
dialogs: {
|
||||
about: ["About Thunderbird",
|
||||
function () { window.openAboutDialog(); }],
|
||||
addons: ["Manage Add-ons",
|
||||
function () { window.openAddonsMgr(); }],
|
||||
addressbook: ["Address book",
|
||||
function () { window.toAddressBook(); }],
|
||||
checkupdates: ["Check for updates",
|
||||
function () { window.checkForUpdates(); }],
|
||||
console: ["JavaScript console",
|
||||
function () { window.toJavaScriptConsole(); }],
|
||||
dominspector: ["DOM Inspector",
|
||||
function () { window.inspectDOMDocument(content.document); }],
|
||||
downloads: ["Manage Downloads",
|
||||
function () { window.toOpenWindowByType('Download:Manager', 'chrome://mozapps/content/downloads/downloads.xul', 'chrome,dialog=no,resizable'); }],
|
||||
preferences: ["Show Thunderbird preferences dialog",
|
||||
function () { openOptionsDialog(); }],
|
||||
printsetup: ["Setup the page size and orientation before printing",
|
||||
function () { PrintUtils.showPageSetup(); }],
|
||||
print: ["Show print dialog",
|
||||
function () { PrintUtils.print(); }],
|
||||
saveframe: ["Save frame to disk",
|
||||
function () { window.saveFrameDocument(); }],
|
||||
savepage: ["Save page to disk",
|
||||
function () { window.saveDocument(window.content.document); }],
|
||||
},
|
||||
|
||||
defaults: {
|
||||
guioptions: "frb",
|
||||
showtabline: 1,
|
||||
titlestring: "Teledactyl"
|
||||
},
|
||||
|
||||
/*** optional options, there are checked for existence and a fallback provided ***/
|
||||
features: ["hints", "mail", "marks", "addressbook", "tabs"],
|
||||
|
||||
focusChange: function (win) {
|
||||
// we switch to -- MESSAGE -- mode for Teledactyl when the main HTML widget gets focus
|
||||
if (win && win.document instanceof HTMLDocument || dactyl.focus instanceof HTMLAnchorElement) {
|
||||
if (config.isComposeWindow)
|
||||
modes.set(modes.INSERT, modes.TEXTAREA);
|
||||
else if (dactyl.mode != modes.MESSAGE)
|
||||
dactyl.mode = modes.MESSAGE;
|
||||
}
|
||||
},
|
||||
|
||||
guioptions: {
|
||||
m: ["MenuBar", ["mail-toolbar-menubar2"]],
|
||||
T: ["Toolbar" , ["mail-bar2"]],
|
||||
f: ["Folder list", ["folderPaneBox", "folderpane_splitter"]],
|
||||
F: ["Folder list header", ["folderPaneHeader"]]
|
||||
},
|
||||
|
||||
// they are sorted by relevance, not alphabetically
|
||||
helpFiles: ["intro.html", "version.html"],
|
||||
|
||||
get isComposeWindow() window.wintype == "msgcompose",
|
||||
|
||||
get mainWidget() this.isComposeWindow ? document.getElementById("content-frame") : GetThreadTree(),
|
||||
|
||||
get mainWindowId() this.isComposeWindow ? "msgcomposeWindow" : "messengerWindow",
|
||||
|
||||
modes: [
|
||||
["MESSAGE", { char: "m" }],
|
||||
["COMPOSE"]
|
||||
],
|
||||
get browserModes() [modes.MESSAGE],
|
||||
get mailModes() [modes.NORMAL],
|
||||
|
||||
// NOTE: as I don't use TB I have no idea how robust this is. --djk
|
||||
get outputHeight() {
|
||||
if (!this.isComposeWindow) {
|
||||
let container = document.getElementById("tabpanelcontainer").boxObject;
|
||||
let deck = document.getElementById("displayDeck");
|
||||
let box = document.getElementById("messagepanebox");
|
||||
let splitter = document.getElementById("threadpane-splitter").boxObject;
|
||||
|
||||
if (splitter.width > splitter.height)
|
||||
return container.height - deck.minHeight - box.minHeight- splitter.height;
|
||||
else
|
||||
return container.height - Math.max(deck.minHeight, box.minHeight);
|
||||
}
|
||||
else
|
||||
return document.getElementById("appcontent").boxObject.height;
|
||||
},
|
||||
|
||||
removeTab: function (tab) {
|
||||
if (config.tabbrowser.mTabs.length > 1)
|
||||
config.tabbrowser.removeTab(tab);
|
||||
else
|
||||
dactyl.beep();
|
||||
},
|
||||
|
||||
get scripts() this.isComposeWindow ? ["compose/compose.js"] : [
|
||||
"addressbook",
|
||||
"mail",
|
||||
"tabs",
|
||||
],
|
||||
|
||||
styleableChrome: ["chrome://messenger/content/messenger.xul",
|
||||
"chrome://messenger/content/messengercompose/messengercompose.xul"],
|
||||
|
||||
tabbrowser: {
|
||||
__proto__: document.getElementById("tabmail"),
|
||||
get mTabContainer() this.tabContainer,
|
||||
get mTabs() this.tabContainer.childNodes,
|
||||
get mCurrentTab() this.tabContainer.selectedItem,
|
||||
get mStrip() this.tabStrip,
|
||||
get browsers() [browser for (browser in Iterator(this.mTabs))]
|
||||
},
|
||||
|
||||
// to allow Vim to :set ft=mail automatically
|
||||
tempFile: "mutt-ator-mail",
|
||||
|
||||
get visualbellWindow() document.getElementById(this.mainWindowId),
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["pref[erences]", "prefs"],
|
||||
"Show " + config.host + " preferences",
|
||||
function () { window.openOptionsDialog(); },
|
||||
{ argCount: "0" });
|
||||
},
|
||||
modes: function () {
|
||||
this.ignoreKeys = {
|
||||
"<Return>": modes.NORMAL | modes.INSERT,
|
||||
"<Space>": modes.NORMAL | modes.INSERT,
|
||||
"<Up>": modes.NORMAL | modes.INSERT,
|
||||
"<Down>": modes.NORMAL | modes.INSERT
|
||||
};
|
||||
},
|
||||
optons: function () {
|
||||
// FIXME: comment obviously incorrect
|
||||
// 0: never automatically edit externally
|
||||
// 1: automatically edit externally when message window is shown the first time
|
||||
// 2: automatically edit externally, once the message text gets focus (not working currently)
|
||||
options.add(["autoexternal", "ae"],
|
||||
"Edit message with external editor by default",
|
||||
"boolean", false);
|
||||
|
||||
options.add(["online"],
|
||||
"Set the 'work offline' option",
|
||||
"boolean", true,
|
||||
{
|
||||
setter: function (value) {
|
||||
if (MailOfflineMgr.isOnline() != value)
|
||||
MailOfflineMgr.toggleOfflineStatus();
|
||||
return value;
|
||||
},
|
||||
getter: function () MailOfflineMgr.isOnline()
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
13
teledactyl/content/dactyl.dtd
Normal file
13
teledactyl/content/dactyl.dtd
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
<!ENTITY % dactylBranding SYSTEM "chrome://branding/locale/brand.dtd">
|
||||
%dactylBranding;
|
||||
|
||||
<!ENTITY dactyl.mainWindow "messengerWindow">
|
||||
<!ENTITY dactyl.name "teledactyl">
|
||||
<!ENTITY dactyl.idname "TELEDACTYL">
|
||||
<!ENTITY dactyl.appname "Teledactyl">
|
||||
<!ENTITY dactyl.host "&brandShortName;">
|
||||
<!ENTITY dactyl.hostbin "thunderbird">
|
||||
<!ENTITY dactyl.statusBefore "">
|
||||
<!ENTITY dactyl.statusAfter "statusTextBox">
|
||||
|
||||
BIN
teledactyl/content/logo.png
Normal file
BIN
teledactyl/content/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
943
teledactyl/content/mail.js
Normal file
943
teledactyl/content/mail.js
Normal file
@@ -0,0 +1,943 @@
|
||||
// Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
//
|
||||
// This work is licensed for reuse under an MIT license. Details are
|
||||
// given in the LICENSE.txt file included with this file.
|
||||
|
||||
const Mail = Module("mail", {
|
||||
init: function () {
|
||||
services.add("smtpService", "@mozilla.org/messengercompose/smtp;1", Ci.nsISmtpService);
|
||||
|
||||
// used for asynchronously selecting messages after wrapping folders
|
||||
this._selectMessageKeys = [];
|
||||
this._selectMessageCount = 1;
|
||||
this._selectMessageReverse = false;
|
||||
|
||||
this._mailSession = Cc["@mozilla.org/messenger/services/session;1"].getService(Ci.nsIMsgMailSession);
|
||||
this._notifyFlags = Ci.nsIFolderListener.intPropertyChanged | Ci.nsIFolderListener.event;
|
||||
this._mailSession.AddFolderListener(this._folderListener, this._notifyFlags);
|
||||
},
|
||||
|
||||
_folderListener: {
|
||||
OnItemAdded: function (parentItem, item) {},
|
||||
OnItemRemoved: function (parentItem, item) {},
|
||||
OnItemPropertyChanged: function (item, property, oldValue, newValue) {},
|
||||
OnItemIntPropertyChanged: function (item, property, oldValue, newValue) {},
|
||||
OnItemBoolPropertyChanged: function (item, property, oldValue, newValue) {},
|
||||
OnItemUnicharPropertyChanged: function (item, property, oldValue, newValue) {},
|
||||
OnItemPropertyFlagChanged: function (item, property, oldFlag, newFlag) {},
|
||||
|
||||
OnItemEvent: function (folder, event) {
|
||||
let eventType = event.toString();
|
||||
if (eventType == "FolderLoaded") {
|
||||
if (folder) {
|
||||
let msgFolder = folder.QueryInterface(Ci.nsIMsgFolder);
|
||||
autocommands.trigger("FolderLoaded", { url: msgFolder });
|
||||
|
||||
// Jump to a message when requested
|
||||
let indices = [];
|
||||
if (mail._selectMessageKeys.length > 0) {
|
||||
for (let j = 0; j < mail._selectMessageKeys.length; j++)
|
||||
indices.push([gDBView.findIndexFromKey(mail._selectMessageKeys[j], true), mail._selectMessageKeys[j]]);
|
||||
|
||||
indices.sort();
|
||||
let index = mail._selectMessageCount - 1;
|
||||
if (mail._selectMessageReverse)
|
||||
index = mail._selectMessageKeys.length - 1 - index;
|
||||
|
||||
gDBView.selectMsgByKey(indices[index][1]);
|
||||
mail._selectMessageKeys = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
/*else if (eventType == "ImapHdrDownloaded") {}
|
||||
else if (eventType == "DeleteOrMoveMsgCompleted") {}
|
||||
else if (eventType == "DeleteOrMoveMsgFailed") {}
|
||||
else if (eventType == "AboutToCompact") {}
|
||||
else if (eventType == "CompactCompleted") {}
|
||||
else if (eventType == "RenameCompleted") {}
|
||||
else if (eventType == "JunkStatusChanged") {}*/
|
||||
}
|
||||
},
|
||||
|
||||
_getCurrentFolderIndex: function () {
|
||||
// for some reason, the index is interpreted as a string, therefore the parseInt
|
||||
return parseInt(gFolderTreeView.getIndexOfFolder(gFolderTreeView.getSelectedFolders()[0]));
|
||||
},
|
||||
|
||||
_getRSSUrl: function () {
|
||||
return gDBView.hdrForFirstSelectedMessage.messageId.replace(/(#.*)?@.*$/, "");
|
||||
},
|
||||
|
||||
_moveOrCopy: function (copy, destinationFolder, operateOnThread) {
|
||||
let folders = mail.getFolders(destinationFolder);
|
||||
if (folders.length == 0)
|
||||
return void dactyl.echoerr("Exxx: No matching folder for " + destinationFolder);
|
||||
else if (folders.length > 1)
|
||||
return dactyl.echoerr("Exxx: More than one match for " + destinationFolder);
|
||||
|
||||
let count = gDBView.selection.count;
|
||||
if (!count)
|
||||
return void dactyl.beep();
|
||||
|
||||
(copy ? MsgCopyMessage : MsgMoveMessage)(folders[0]);
|
||||
util.timeout(function () {
|
||||
dactyl.echomsg(count + " message(s) " + (copy ? "copied" : "moved") + " to " + folders[0].prettyName, 1);
|
||||
}, 100);
|
||||
},
|
||||
|
||||
_parentIndex: function (index) {
|
||||
let parent = index;
|
||||
let tree = GetThreadTree();
|
||||
|
||||
while (true) {
|
||||
let tmp = tree.view.getParentIndex(parent);
|
||||
if (tmp >= 0)
|
||||
parent = tmp;
|
||||
else
|
||||
break;
|
||||
}
|
||||
return parent;
|
||||
},
|
||||
|
||||
// does not wrap yet, intentional?
|
||||
_selectUnreadFolder: function (backwards, count) {
|
||||
count = Math.max(1, count);
|
||||
let direction = backwards ? -1 : 1;
|
||||
let c = this._getCurrentFolderIndex();
|
||||
let i = direction;
|
||||
let folder;
|
||||
while (count > 0 && (c + i) < gFolderTreeView.rowCount && (c + i) >= 0) {
|
||||
let resource = gFolderTreeView._rowMap[c+i]._folder;
|
||||
if (!resource.isServer && resource.getNumUnread(false)) {
|
||||
count -= 1;
|
||||
folder = i;
|
||||
}
|
||||
i += direction;
|
||||
}
|
||||
if (!folder || count > 0)
|
||||
dactyl.beep();
|
||||
else
|
||||
gFolderTreeView.selection.timedSelect(c + folder, 500);
|
||||
},
|
||||
|
||||
_escapeRecipient: function (recipient) {
|
||||
// strip all ":
|
||||
recipient = recipient.replace(/"/g, "");
|
||||
return "\"" + recipient + "\"";
|
||||
},
|
||||
|
||||
get currentAccount() this.currentFolder.rootFolder,
|
||||
|
||||
get currentFolder() gFolderTreeView.getSelectedFolders()[0],
|
||||
|
||||
/** @property {nsISmtpServer[]} The list of configured SMTP servers. */
|
||||
get smtpServers() {
|
||||
let servers = services.get("smtpService").smtpServers;
|
||||
let ret = [];
|
||||
|
||||
while (servers.hasMoreElements()) {
|
||||
let server = servers.getNext();
|
||||
if (server instanceof Ci.nsISmtpServer)
|
||||
ret.push(server);
|
||||
}
|
||||
|
||||
return ret;
|
||||
},
|
||||
|
||||
composeNewMail: function (args) {
|
||||
let params = Cc["@mozilla.org/messengercompose/composeparams;1"].createInstance(Ci.nsIMsgComposeParams);
|
||||
params.composeFields = Cc["@mozilla.org/messengercompose/composefields;1"].createInstance(Ci.nsIMsgCompFields);
|
||||
|
||||
if (args) {
|
||||
if (args.originalMsg)
|
||||
params.originalMsgURI = args.originalMsg;
|
||||
if (args.to)
|
||||
params.composeFields.to = args.to;
|
||||
if (args.cc)
|
||||
params.composeFields.cc = args.cc;
|
||||
if (args.bcc)
|
||||
params.composeFields.bcc = args.bcc;
|
||||
if (args.newsgroups)
|
||||
params.composeFields.newsgroups = args.newsgroups;
|
||||
if (args.subject)
|
||||
params.composeFields.subject = args.subject;
|
||||
if (args.body)
|
||||
params.composeFields.body = args.body;
|
||||
|
||||
if (args.attachments) {
|
||||
while (args.attachments.length > 0) {
|
||||
let url = args.attachments.pop();
|
||||
let file = io.getFile(url);
|
||||
if (!file.exists())
|
||||
return void dactyl.echoerr("Exxx: Could not attach file `" + url + "'", commandline.FORCE_SINGLELINE);
|
||||
|
||||
attachment = Cc["@mozilla.org/messengercompose/attachment;1"].createInstance(Ci.nsIMsgAttachment);
|
||||
attachment.url = "file://" + file.path;
|
||||
params.composeFields.addAttachment(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
params.type = Ci.nsIMsgCompType.New;
|
||||
|
||||
const msgComposeService = Cc["@mozilla.org/messengercompose;1"].getService();
|
||||
msgComposeService = msgComposeService.QueryInterface(Ci.nsIMsgComposeService);
|
||||
msgComposeService.OpenComposeWindowWithParams(null, params);
|
||||
},
|
||||
|
||||
// returns an array of nsIMsgFolder objects
|
||||
getFolders: function (filter, includeServers, includeMsgFolders) {
|
||||
let folders = [];
|
||||
if (!filter)
|
||||
filter = "";
|
||||
else
|
||||
filter = filter.toLowerCase();
|
||||
|
||||
if (includeServers === undefined)
|
||||
includeServers = false;
|
||||
if (includeMsgFolders === undefined)
|
||||
includeMsgFolders = true;
|
||||
|
||||
for (let i = 0; i < gFolderTreeView.rowCount; i++) {
|
||||
let resource = gFolderTreeView._rowMap[i]._folder;
|
||||
if ((resource.isServer && !includeServers) || (!resource.isServer && !includeMsgFolders))
|
||||
continue;
|
||||
|
||||
let folderString = resource.server.prettyName + ": " + resource.name;
|
||||
|
||||
if (resource.prettiestName.toLowerCase().indexOf(filter) >= 0)
|
||||
folders.push(resource);
|
||||
else if (folderString.toLowerCase().indexOf(filter) >= 0)
|
||||
folders.push(resource);
|
||||
}
|
||||
return folders;
|
||||
},
|
||||
|
||||
getNewMessages: function (currentAccountOnly) {
|
||||
if (currentAccountOnly)
|
||||
MsgGetMessagesForAccount();
|
||||
else
|
||||
GetMessagesForAllAuthenticatedAccounts();
|
||||
},
|
||||
|
||||
getStatistics: function (currentAccountOnly) {
|
||||
let accounts = currentAccountOnly ? [this.currentAccount]
|
||||
: this.getFolders("", true, false);
|
||||
|
||||
let unreadCount = 0, totalCount = 0, newCount = 0;
|
||||
for (let i = 0; i < accounts.length; i++) {
|
||||
let account = accounts[i];
|
||||
unreadCount += account.getNumUnread(true); // true == deep (includes subfolders)
|
||||
totalCount += account.getTotalMessages(true);
|
||||
newCount += account.getNumUnread(true);
|
||||
}
|
||||
|
||||
return { numUnread: unreadCount, numTotal: totalCount, numNew: newCount };
|
||||
},
|
||||
|
||||
collapseThread: function () {
|
||||
let tree = GetThreadTree();
|
||||
if (tree) {
|
||||
let parent = this._parentIndex(tree.currentIndex);
|
||||
if (tree.changeOpenState(parent, false)) {
|
||||
tree.view.selection.select(parent);
|
||||
tree.treeBoxObject.ensureRowIsVisible(parent);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
expandThread: function () {
|
||||
let tree = GetThreadTree();
|
||||
if (tree) {
|
||||
let row = tree.currentIndex;
|
||||
if (row >= 0 && tree.changeOpenState(row, true))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
/**
|
||||
* General-purpose method to find messages.
|
||||
*
|
||||
* @param {function(nsIMsgDBHdr):boolean} validatorFunc Return
|
||||
* true/false whether msg should be selected or not.
|
||||
* @param {boolean} canWrap When true, wraps around folders.
|
||||
* @param {boolean} openThreads Should we open closed threads?
|
||||
* @param {boolean} reverse Change direction of searching.
|
||||
*/
|
||||
selectMessage: function (validatorFunc, canWrap, openThreads, reverse, count) {
|
||||
function currentIndex() {
|
||||
let index = gDBView.selection.currentIndex;
|
||||
if (index < 0)
|
||||
index = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
function closedThread(index) {
|
||||
if (!(gDBView.viewFlags & nsMsgViewFlagsType.kThreadedDisplay))
|
||||
return false;
|
||||
|
||||
index = (typeof index == "number") ? index : currentIndex();
|
||||
return !gDBView.isContainerOpen(index) && !gDBView.isContainerEmpty(index);
|
||||
}
|
||||
|
||||
if (typeof validatorFunc != "function")
|
||||
return;
|
||||
|
||||
if (typeof count != "number" || count < 1)
|
||||
count = 1;
|
||||
|
||||
// first try to find in current folder
|
||||
if (gDBView) {
|
||||
for (let i = currentIndex() + (reverse ? -1 : (openThreads && closedThread() ? 0 : 1));
|
||||
reverse ? (i >= 0) : (i < gDBView.rowCount);
|
||||
reverse ? i-- : i++) {
|
||||
let key = gDBView.getKeyAt(i);
|
||||
let msg = gDBView.db.GetMsgHdrForKey(key);
|
||||
|
||||
// a closed thread
|
||||
if (openThreads && closedThread(i)) {
|
||||
let thread = gDBView.db.GetThreadContainingMsgHdr(msg);
|
||||
let originalCount = count;
|
||||
|
||||
for (let j = (i == currentIndex() && !reverse) ? 1 : (reverse ? thread.numChildren - 1 : 0);
|
||||
reverse ? (j >= 0) : (j < thread.numChildren);
|
||||
reverse ? j-- : j++) {
|
||||
msg = thread.getChildAt(j);
|
||||
if (validatorFunc(msg) && --count == 0) {
|
||||
// this hack is needed to get the correct message, because getChildAt() does not
|
||||
// necessarily return the messages in the order they are displayed
|
||||
gDBView.selection.timedSelect(i, GetThreadTree()._selectDelay || 500);
|
||||
GetThreadTree().treeBoxObject.ensureRowIsVisible(i);
|
||||
if (j > 0) {
|
||||
GetThreadTree().changeOpenState(i, true);
|
||||
this.selectMessage(validatorFunc, false, false, false, originalCount);
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else { // simple non-threaded message
|
||||
if (validatorFunc(msg) && --count == 0) {
|
||||
gDBView.selection.timedSelect(i, GetThreadTree()._selectDelay || 500);
|
||||
GetThreadTree().treeBoxObject.ensureRowIsVisible(i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then in other folders
|
||||
if (canWrap) {
|
||||
this._selectMessageReverse = reverse;
|
||||
|
||||
let folders = this.getFolders("", true, true);
|
||||
let ci = this._getCurrentFolderIndex();
|
||||
for (let i = 1; i < folders.length; i++) {
|
||||
let index = (i + ci) % folders.length;
|
||||
if (reverse)
|
||||
index = folders.length - 1 - index;
|
||||
|
||||
let folder = folders[index];
|
||||
if (folder.isServer)
|
||||
continue;
|
||||
|
||||
this._selectMessageCount = count;
|
||||
this._selectMessageKeys = [];
|
||||
|
||||
// sometimes folder.getMessages can fail with an exception
|
||||
// TODO: find out why, and solve the problem
|
||||
try {
|
||||
var msgs = folder.messages;
|
||||
}
|
||||
catch (e) {
|
||||
msgs = folder.getMessages(msgWindow); // for older thunderbirds
|
||||
dactyl.dump("WARNING: " + folder.prettyName + " failed to getMessages, trying old API");
|
||||
//continue;
|
||||
}
|
||||
|
||||
while (msgs.hasMoreElements()) {
|
||||
let msg = msgs.getNext().QueryInterface(Ci.nsIMsgDBHdr);
|
||||
if (validatorFunc(msg)) {
|
||||
count--;
|
||||
this._selectMessageKeys.push(msg.messageKey);
|
||||
}
|
||||
}
|
||||
|
||||
if (count <= 0) {
|
||||
// SelectFolder is asynchronous, message is selected in this._folderListener
|
||||
SelectFolder(folder.URI);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: finally for the "rest" of the current folder
|
||||
|
||||
dactyl.beep();
|
||||
},
|
||||
|
||||
setHTML: function (value) {
|
||||
let values = [[true, 1, gDisallow_classes_no_html], // plaintext
|
||||
[false, 0, 0], // HTML
|
||||
[false, 3, gDisallow_classes_no_html]]; // sanitized/simple HTML
|
||||
|
||||
if (typeof value != "number" || value < 0 || value > 2)
|
||||
value = 1;
|
||||
|
||||
gPrefBranch.setBoolPref("mailnews.display.prefer_plaintext", values[value][0]);
|
||||
gPrefBranch.setIntPref("mailnews.display.html_as", values[value][1]);
|
||||
gPrefBranch.setIntPref("mailnews.display.disallow_mime_handlers", values[value][2]);
|
||||
ReloadMessage();
|
||||
}
|
||||
}, {
|
||||
}, {
|
||||
commands: function () {
|
||||
commands.add(["go[to]"],
|
||||
"Select a folder",
|
||||
function (args) {
|
||||
let count = Math.max(0, args.count - 1);
|
||||
let arg = args.literalArg || "Inbox";
|
||||
|
||||
let folder = mail.getFolders(arg, true, true)[count];
|
||||
if (!folder)
|
||||
dactyl.echoerr("Exxx: Folder \"" + arg + "\" does not exist");
|
||||
else if (dactyl.forceNewTab)
|
||||
MsgOpenNewTabForFolder(folder.URI);
|
||||
else
|
||||
SelectFolder(folder.URI);
|
||||
},
|
||||
{
|
||||
argCount: "?",
|
||||
completer: function (context) completion.mailFolder(context),
|
||||
count: true,
|
||||
literal: 0
|
||||
});
|
||||
|
||||
commands.add(["m[ail]"],
|
||||
"Write a new message",
|
||||
function (args) {
|
||||
let mailargs = {};
|
||||
mailargs.to = args.join(", ");
|
||||
mailargs.subject = args["-subject"];
|
||||
mailargs.bcc = args["-bcc"];
|
||||
mailargs.cc = args["-cc"];
|
||||
mailargs.body = args["-text"];
|
||||
mailargs.attachments = args["-attachment"] || [];
|
||||
|
||||
let addresses = args;
|
||||
if (mailargs.bcc)
|
||||
addresses = addresses.concat(mailargs.bcc);
|
||||
if (mailargs.cc)
|
||||
addresses = addresses.concat(mailargs.cc);
|
||||
|
||||
// TODO: is there a better way to check for validity?
|
||||
if (addresses.some(function (recipient) !(/\S@\S+\.\S/.test(recipient))))
|
||||
return void dactyl.echoerr("Exxx: Invalid e-mail address");
|
||||
|
||||
mail.composeNewMail(mailargs);
|
||||
},
|
||||
{
|
||||
options: [[["-subject", "-s"], commands.OPTION_STRING],
|
||||
[["-attachment", "-a"], commands.OPTION_LIST],
|
||||
[["-bcc", "-b"], commands.OPTION_STRING],
|
||||
[["-cc", "-c"], commands.OPTION_STRING],
|
||||
[["-text", "-t"], commands.OPTION_STRING]]
|
||||
});
|
||||
|
||||
commands.add(["copy[to]"],
|
||||
"Copy selected messages",
|
||||
function (args) { this._moveOrCopy(true, args.literalArg); },
|
||||
{
|
||||
argCount: 1,
|
||||
completer: function (context) completion.mailFolder(context),
|
||||
literal: 0
|
||||
});
|
||||
|
||||
commands.add(["move[to]"],
|
||||
"Move selected messages",
|
||||
function (args) { this._moveOrCopy(false, args.literalArg); },
|
||||
{
|
||||
argCount: 1,
|
||||
completer: function (context) completion.mailFolder(context),
|
||||
literal: 0
|
||||
});
|
||||
|
||||
commands.add(["empty[trash]"],
|
||||
"Empty trash of the current account",
|
||||
function () { window.goDoCommand("cmd_emptyTrash"); },
|
||||
{ argCount: "0" });
|
||||
|
||||
commands.add(["get[messages]"],
|
||||
"Check for new messages",
|
||||
function (args) mail.getNewMessages(!args.bang),
|
||||
{
|
||||
argCount: "0",
|
||||
bang: true,
|
||||
});
|
||||
},
|
||||
completion: function () {
|
||||
completion.mailFolder = function mailFolder(context) {
|
||||
let folders = mail.getFolders(context.filter);
|
||||
context.anchored = false;
|
||||
context.quote = false;
|
||||
context.completions = folders.map(function (folder)
|
||||
[folder.server.prettyName + ": " + folder.name,
|
||||
"Unread: " + folder.getNumUnread(false)]);
|
||||
};
|
||||
},
|
||||
mappings: function () {
|
||||
var myModes = config.mailModes;
|
||||
|
||||
mappings.add(myModes, ["<Return>", "i"],
|
||||
"Inspect (focus) message",
|
||||
function () { content.focus(); });
|
||||
|
||||
mappings.add(myModes, ["I"],
|
||||
"Open the message in new tab",
|
||||
function () {
|
||||
if (gDBView && gDBView.selection.count < 1)
|
||||
return void dactyl.beep();
|
||||
|
||||
MsgOpenNewTabForMessage();
|
||||
});
|
||||
|
||||
/*mappings.add([modes.NORMAL],
|
||||
["o"], "Open a message",
|
||||
function () { commandline.open(":", "open ", modes.EX); });*/
|
||||
|
||||
mappings.add(myModes, ["<Space>"],
|
||||
"Scroll message or select next unread one",
|
||||
function () true,
|
||||
{ route: true });
|
||||
|
||||
mappings.add(myModes, ["t"],
|
||||
"Select thread",
|
||||
function () { gDBView.ExpandAndSelectThreadByIndex(GetThreadTree().currentIndex, false); });
|
||||
|
||||
mappings.add(myModes, ["d", "<Del>"],
|
||||
"Move mail to Trash folder",
|
||||
function () { window.goDoCommand("cmd_delete"); });
|
||||
|
||||
mappings.add(myModes, ["j", "<Right>"],
|
||||
"Select next message",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, false, false, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["gj"],
|
||||
"Select next message, including closed threads",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, true, false, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["J", "<Tab>"],
|
||||
"Select next unread message",
|
||||
function (count) { mail.selectMessage(function (msg) !msg.isRead, true, true, false, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["k", "<Left>"],
|
||||
"Select previous message",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, false, true, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["gk"],
|
||||
"Select previous message",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, true, true, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["K"],
|
||||
"Select previous unread message",
|
||||
function (count) { mail.selectMessage(function (msg) !msg.isRead, true, true, true, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["*"],
|
||||
"Select next message from the same sender",
|
||||
function (count) {
|
||||
try {
|
||||
let author = gDBView.hdrForFirstSelectedMessage.mime2DecodedAuthor.toLowerCase();
|
||||
mail.selectMessage(function (msg) msg.mime2DecodedAuthor.toLowerCase().indexOf(author) == 0, true, true, false, count);
|
||||
}
|
||||
catch (e) { dactyl.beep(); }
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["#"],
|
||||
"Select previous message from the same sender",
|
||||
function (count) {
|
||||
try {
|
||||
let author = gDBView.hdrForFirstSelectedMessage.mime2DecodedAuthor.toLowerCase();
|
||||
mail.selectMessage(function (msg) msg.mime2DecodedAuthor.toLowerCase().indexOf(author) == 0, true, true, true, count);
|
||||
}
|
||||
catch (e) { dactyl.beep(); }
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
// SENDING MESSAGES
|
||||
mappings.add(myModes, ["m"],
|
||||
"Compose a new message",
|
||||
function () { commandline.open(":", "mail -subject=", modes.EX); });
|
||||
|
||||
mappings.add(myModes, ["M"],
|
||||
"Compose a new message to the sender of selected mail",
|
||||
function () {
|
||||
try {
|
||||
let to = this._escapeRecipient(gDBView.hdrForFirstSelectedMessage.mime2DecodedAuthor);
|
||||
commandline.open(":", "mail " + to + " -subject=", modes.EX);
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.beep();
|
||||
}
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["r"],
|
||||
"Reply to sender",
|
||||
function () { window.goDoCommand("cmd_reply"); });
|
||||
|
||||
mappings.add(myModes, ["R"],
|
||||
"Reply to all",
|
||||
function () { window.goDoCommand("cmd_replyall"); });
|
||||
|
||||
mappings.add(myModes, ["f"],
|
||||
"Forward message",
|
||||
function () { window.goDoCommand("cmd_forward"); });
|
||||
|
||||
mappings.add(myModes, ["F"],
|
||||
"Forward message inline",
|
||||
function () { window.goDoCommand("cmd_forwardInline"); });
|
||||
|
||||
// SCROLLING
|
||||
mappings.add(myModes, ["<Down>"],
|
||||
"Scroll message down",
|
||||
function (count) { buffer.scrollLines(Math.max(count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<Up>"],
|
||||
"Scroll message up",
|
||||
function (count) { buffer.scrollLines(-Math.max(count, 1)); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.MESSAGE], ["<Left>"],
|
||||
"Select previous message",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, false, true, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add([modes.MESSAGE], ["<Right>"],
|
||||
"Select next message",
|
||||
function (count) { mail.selectMessage(function (msg) true, false, false, false, count); },
|
||||
{ count: true });
|
||||
|
||||
// UNDO/REDO
|
||||
mappings.add(myModes, ["u"],
|
||||
"Undo",
|
||||
function () {
|
||||
if (messenger.canUndo())
|
||||
messenger.undo(msgWindow);
|
||||
else
|
||||
dactyl.beep();
|
||||
});
|
||||
mappings.add(myModes, ["<C-r>"],
|
||||
"Redo",
|
||||
function () {
|
||||
if (messenger.canRedo())
|
||||
messenger.redo(msgWindow);
|
||||
else
|
||||
dactyl.beep();
|
||||
});
|
||||
|
||||
// GETTING MAIL
|
||||
mappings.add(myModes, ["gm"],
|
||||
"Get new messages",
|
||||
function () { mail.getNewMessages(); });
|
||||
|
||||
mappings.add(myModes, ["gM"],
|
||||
"Get new messages for current account only",
|
||||
function () { mail.getNewMessages(true); });
|
||||
|
||||
// MOVING MAIL
|
||||
mappings.add(myModes, ["c"],
|
||||
"Change folders",
|
||||
function () { commandline.open(":", "goto ", modes.EX); });
|
||||
|
||||
mappings.add(myModes, ["s"],
|
||||
"Move selected messages",
|
||||
function () { commandline.open(":", "moveto ", modes.EX); });
|
||||
|
||||
mappings.add(myModes, ["S"],
|
||||
"Copy selected messages",
|
||||
function () { commandline.open(":", "copyto ", modes.EX); });
|
||||
|
||||
mappings.add(myModes, ["<C-s>"],
|
||||
"Archive message",
|
||||
function () { this._moveOrCopy(false, options["archivefolder"]); });
|
||||
|
||||
mappings.add(myModes, ["]s"],
|
||||
"Select next starred message",
|
||||
function (count) { mail.selectMessage(function (msg) msg.isFlagged, true, true, false, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["[s"],
|
||||
"Select previous starred message",
|
||||
function (count) { mail.selectMessage(function (msg) msg.isFlagged, true, true, true, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["]a"],
|
||||
"Select next message with an attachment",
|
||||
function (count) { mail.selectMessage(function (msg) gDBView.db.HasAttachments(msg.messageKey), true, true, false, count); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["[a"],
|
||||
"Select previous message with an attachment",
|
||||
function (count) { mail.selectMessage(function (msg) gDBView.db.HasAttachments(msg.messageKey), true, true, true, count); },
|
||||
{ count: true });
|
||||
|
||||
// FOLDER SWITCHING
|
||||
mappings.add(myModes, ["gi"],
|
||||
"Go to inbox",
|
||||
function (count) {
|
||||
let folder = mail.getFolders("Inbox", false, true)[(count > 0) ? (count - 1) : 0];
|
||||
if (folder)
|
||||
SelectFolder(folder.URI);
|
||||
else
|
||||
dactyl.beep();
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-n>"],
|
||||
"Select next folder",
|
||||
function (count) {
|
||||
count = Math.max(1, count);
|
||||
let newPos = this._getCurrentFolderIndex() + count;
|
||||
if (newPos >= gFolderTreeView.rowCount) {
|
||||
newPos = newPos % gFolderTreeView.rowCount;
|
||||
commandline.echo("search hit BOTTOM, continuing at TOP", commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
||||
}
|
||||
gFolderTreeView.selection.timedSelect(newPos, 500);
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-N>"],
|
||||
"Go to next mailbox with unread messages",
|
||||
function (count) {
|
||||
this._selectUnreadFolder(false, count);
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-p>"],
|
||||
"Select previous folder",
|
||||
function (count) {
|
||||
count = Math.max(1, count);
|
||||
let newPos = this._getCurrentFolderIndex() - count;
|
||||
if (newPos < 0) {
|
||||
newPos = (newPos % gFolderTreeView.rowCount) + gFolderTreeView.rowCount;
|
||||
commandline.echo("search hit TOP, continuing at BOTTOM", commandline.HL_WARNINGMSG, commandline.APPEND_TO_MESSAGES);
|
||||
}
|
||||
gFolderTreeView.selection.timedSelect(newPos, 500);
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-P>"],
|
||||
"Go to previous mailbox with unread messages",
|
||||
function (count) {
|
||||
this._selectUnreadFolder(true, count);
|
||||
},
|
||||
{ count: true });
|
||||
|
||||
// THREADING
|
||||
mappings.add(myModes, ["za"],
|
||||
"Toggle thread collapsed/expanded",
|
||||
function () { if (!mail.expandThread()) mail.collapseThread(); });
|
||||
|
||||
mappings.add(myModes, ["zc"],
|
||||
"Collapse thread",
|
||||
function () { mail.collapseThread(); });
|
||||
|
||||
mappings.add(myModes, ["zo"],
|
||||
"Open thread",
|
||||
function () { mail.expandThread(); });
|
||||
|
||||
mappings.add(myModes, ["zr", "zR"],
|
||||
"Expand all threads",
|
||||
function () { window.goDoCommand("cmd_expandAllThreads"); });
|
||||
|
||||
mappings.add(myModes, ["zm", "zM"],
|
||||
"Collapse all threads",
|
||||
function () { window.goDoCommand("cmd_collapseAllThreads"); });
|
||||
|
||||
mappings.add(myModes, ["<C-i>"],
|
||||
"Go forward",
|
||||
function (count) { if (count < 1) count = 1; while (count--) GoNextMessage(nsMsgNavigationType.forward, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["<C-o>"],
|
||||
"Go back",
|
||||
function (count) { if (count < 1) count = 1; while (count--) GoNextMessage(nsMsgNavigationType.back, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["gg"],
|
||||
"Select first message",
|
||||
function (count) { if (count < 1) count = 1; while (count--) GoNextMessage(nsMsgNavigationType.firstMessage, true); },
|
||||
{ count: true });
|
||||
|
||||
mappings.add(myModes, ["G"],
|
||||
"Select last message",
|
||||
function (count) { if (count < 1) count = 1; while (count--) GoNextMessage(nsMsgNavigationType.lastMessage, false); },
|
||||
{ count: true });
|
||||
|
||||
// tagging messages
|
||||
mappings.add(myModes, ["l"],
|
||||
"Label message",
|
||||
function (arg) {
|
||||
if (!GetSelectedMessages())
|
||||
return void dactyl.beep();
|
||||
|
||||
switch (arg) {
|
||||
case "r": MsgMarkMsgAsRead(); break;
|
||||
case "s": MsgMarkAsFlagged(); break;
|
||||
case "i": ToggleMessageTagKey(1); break; // Important
|
||||
case "w": ToggleMessageTagKey(2); break; // Work
|
||||
case "p": ToggleMessageTagKey(3); break; // Personal
|
||||
case "t": ToggleMessageTagKey(4); break; // TODO
|
||||
case "l": ToggleMessageTagKey(5); break; // Later
|
||||
default: dactyl.beep();
|
||||
}
|
||||
},
|
||||
{
|
||||
arg: true
|
||||
});
|
||||
|
||||
// TODO: change binding?
|
||||
mappings.add(myModes, ["T"],
|
||||
"Mark current folder as read",
|
||||
function () {
|
||||
if (mail.currentFolder.isServer)
|
||||
return dactyl.beep();
|
||||
|
||||
mail.currentFolder.markAllMessagesRead(msgWindow);
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["<C-t>"],
|
||||
"Mark all messages as read",
|
||||
function () {
|
||||
mail.getFolders("", false).forEach(function (folder) { folder.markAllMessagesRead(msgWindow); });
|
||||
});
|
||||
|
||||
// DISPLAY OPTIONS
|
||||
mappings.add(myModes, ["h"],
|
||||
"Toggle displayed headers",
|
||||
function () {
|
||||
let value = gPrefBranch.getIntPref("mail.show_headers", 2);
|
||||
gPrefBranch.setIntPref("mail.show_headers", value == 2 ? 1 : 2);
|
||||
ReloadMessage();
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["x"],
|
||||
"Toggle HTML message display",
|
||||
function () {
|
||||
let wantHtml = (gPrefBranch.getIntPref("mailnews.display.html_as", 1) == 1);
|
||||
mail.setHTML(wantHtml ? 1 : 0);
|
||||
});
|
||||
|
||||
// YANKING TEXT
|
||||
mappings.add(myModes, ["Y"],
|
||||
"Yank subject",
|
||||
function () {
|
||||
try {
|
||||
let subject = gDBView.hdrForFirstSelectedMessage.mime2DecodedSubject;
|
||||
util.copyToClipboard(subject, true);
|
||||
}
|
||||
catch (e) { dactyl.beep(); }
|
||||
});
|
||||
|
||||
mappings.add(myModes, ["y"],
|
||||
"Yank sender or feed URL",
|
||||
function () {
|
||||
try {
|
||||
if (mail.currentAccount.server.type == "rss")
|
||||
util.copyToClipboard(this._getRSSUrl(), true);
|
||||
else
|
||||
util.copyToClipboard(gDBView.hdrForFirstSelectedMessage.mime2DecodedAuthor, true);
|
||||
}
|
||||
catch (e) { dactyl.beep(); }
|
||||
});
|
||||
|
||||
// RSS specific mappings
|
||||
mappings.add(myModes, ["p"],
|
||||
"Open RSS message in browser",
|
||||
function () {
|
||||
try {
|
||||
if (mail.currentAccount.server.type == "rss")
|
||||
messenger.launchExternalURL(this._getRSSUrl());
|
||||
// TODO: what to do for non-rss message?
|
||||
}
|
||||
catch (e) {
|
||||
dactyl.beep();
|
||||
}
|
||||
});
|
||||
},
|
||||
options: function () {
|
||||
// FIXME: why does this default to "Archive", I don't have one? The default
|
||||
// value won't validate now. mst please fix. --djk
|
||||
options.add(["archivefolder"],
|
||||
"Set the archive folder",
|
||||
"string", "Archive",
|
||||
{
|
||||
completer: function (context) completion.mailFolder(context),
|
||||
validator: Option.validateCompleter
|
||||
});
|
||||
|
||||
// TODO: generate the possible values dynamically from the menu
|
||||
options.add(["layout"],
|
||||
"Set the layout of the mail window",
|
||||
"string", "inherit",
|
||||
{
|
||||
setter: function (value) {
|
||||
switch (value) {
|
||||
case "classic": ChangeMailLayout(0); break;
|
||||
case "wide": ChangeMailLayout(1); break;
|
||||
case "vertical": ChangeMailLayout(2); break;
|
||||
// case "inherit" just does nothing
|
||||
}
|
||||
|
||||
return value;
|
||||
},
|
||||
completer: function (context) [
|
||||
["inherit", "Default View"], // FIXME: correct description?
|
||||
["classic", "Classic View"],
|
||||
["wide", "Wide View"],
|
||||
["vertical", "Vertical View"]
|
||||
],
|
||||
validator: Option.validateCompleter
|
||||
});
|
||||
|
||||
options.add(["smtpserver", "smtp"],
|
||||
"Set the default SMTP server",
|
||||
"string", services.get("smtpService").defaultServer.key, // TODO: how should we handle these persistent external defaults - "inherit" or null?
|
||||
{
|
||||
getter: function () services.get("smtpService").defaultServer.key,
|
||||
setter: function (value) {
|
||||
let server = mail.smtpServers.filter(function (s) s.key == value)[0];
|
||||
services.get("smtpService").defaultServer = server;
|
||||
return value;
|
||||
},
|
||||
completer: function (context) [[s.key, s.serverURI] for ([, s] in Iterator(mail.smtpServers))],
|
||||
validator: Option.validateCompleter
|
||||
});
|
||||
|
||||
/*options.add(["threads"],
|
||||
"Use threading to group messages",
|
||||
"boolean", true,
|
||||
{
|
||||
setter: function (value) {
|
||||
if (value)
|
||||
MsgSortThreaded();
|
||||
else
|
||||
MsgSortUnthreaded();
|
||||
|
||||
return value;
|
||||
}
|
||||
});*/
|
||||
}
|
||||
});
|
||||
|
||||
// vim: set fdm=marker sw=4 ts=4 et:
|
||||
20
teledactyl/content/pentadactyl.xul
Normal file
20
teledactyl/content/pentadactyl.xul
Normal file
@@ -0,0 +1,20 @@
|
||||
<?xml version="1.0"?>
|
||||
|
||||
<!-- ***** BEGIN LICENSE BLOCK ***** {{{
|
||||
Copyright (c) 2006-2009 by Martin Stubenschrott <stubenschrott@vimperator.org>
|
||||
|
||||
This work is licensed for reuse under an MIT license. Details are
|
||||
given in the LICENSE.txt file included with this file.
|
||||
}}} ***** END LICENSE BLOCK ***** -->
|
||||
|
||||
<!--?xml-stylesheet href="chrome://browser/skin/" type="text/css"?-->
|
||||
|
||||
<overlay id="teledactyl"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:nc="http://home.netscape.com/NC-rdf#"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
</overlay>
|
||||
|
||||
<!-- vim: set fdm=marker sw=4 ts=4 et: -->
|
||||
9
teledactyl/contrib/vim/Makefile
Normal file
9
teledactyl/contrib/vim/Makefile
Normal file
@@ -0,0 +1,9 @@
|
||||
VIMBALL = muttator.vba
|
||||
|
||||
vimball: mkvimball.txt syntax/muttator.vim ftdetect/muttator.vim
|
||||
-echo '%MkVimball! ${VIMBALL} .' | vim -u NORC -N -e -s mkvimball.txt
|
||||
|
||||
all: vimball
|
||||
|
||||
clean:
|
||||
rm -f ${VIMBALL}
|
||||
2
teledactyl/contrib/vim/ftdetect/muttator.vim
Normal file
2
teledactyl/contrib/vim/ftdetect/muttator.vim
Normal file
@@ -0,0 +1,2 @@
|
||||
" TODO: what's the Muttator filename extension?
|
||||
au BufNewFile,BufRead *muttatorrc*,*.muttator set filetype=muttator
|
||||
2
teledactyl/contrib/vim/mkvimball.txt
Normal file
2
teledactyl/contrib/vim/mkvimball.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
syntax/muttator.vim
|
||||
ftdetect/muttator.vim
|
||||
103
teledactyl/contrib/vim/syntax/muttator.vim
Normal file
103
teledactyl/contrib/vim/syntax/muttator.vim
Normal file
@@ -0,0 +1,103 @@
|
||||
" Vim syntax file
|
||||
" Language: Muttator configuration file
|
||||
" Maintainer: Doug Kearns <dougkearns@gmail.com>
|
||||
" Last Change: 2009 Aug 27
|
||||
|
||||
" TODO: make this muttator specific - shared dactyl config?
|
||||
|
||||
if exists("b:current_syntax")
|
||||
finish
|
||||
endif
|
||||
|
||||
let s:cpo_save = &cpo
|
||||
set cpo&vim
|
||||
|
||||
syn include @javascriptTop syntax/javascript.vim
|
||||
unlet b:current_syntax
|
||||
|
||||
syn include @cssTop syntax/css.vim
|
||||
unlet b:current_syntax
|
||||
|
||||
syn match muttatorCommandStart "\%(^\s*:\=\)\@<=" nextgroup=muttatorCommand,muttatorAutoCmd
|
||||
|
||||
syn keyword muttatorCommand ab[breviate] ab[clear] addo[ns] addr[essbook] bN[ext] bd[elete] beep bf[irst] bl[ast] bn[ext]
|
||||
\ bp[revious] br[ewind] bufd[o] bun[load] bw[ipeout] ca[bbrev] cabc[lear] cd chd[ir] cm[ap] cmapc[lear] cno[remap]
|
||||
\ colo[rscheme] com[mand] comc[lear] con[tact] contacts copy[to] cu[nmap] cuna[bbrev] delc[ommand] delm[arks] delmac[ros]
|
||||
\ dels[tyle] dia[log] do[autocmd] doautoa[ll] ec[ho] echoe[rr] echom[sg] em[enu] empty[trash] exe[cute] exta[dd] extd[isable]
|
||||
\ extde[lete] exte[nable] extens[ions] exto[ptions] extp[references] exu[sage] fini[sh] frameo[nly] get[messages] go[to]
|
||||
\ h[elp] helpa[ll] ha[rdcopy] hi[ghlight] ia[bbrev] iabc[lear] im[ap] imapc[lear] ino[remap] iu[nmap] iuna[bbrev] javas[cript]
|
||||
\ js let loadplugins lpl m[ail] ma[rk] macros map mapc[lear] marks mes[sages] messc[lear] mkm[uttatorrc] mm[ap] mmapc[lear]
|
||||
\ mno[remap] move[to] mu[nmap] nm[ap] nmapc[lear] nno[remap] no[remap] norm[al] nu[nmap] optionu[sage] pa[geinfo] pagest[yle]
|
||||
\ pas pl[ay] pref[erences] prefs pw[d] q[uit] re[load] res[tart] run runt[ime] sav[eas] scrip[tnames] se[t] setg[lobal]
|
||||
\ setl[ocal] sil[ent] so[urce] st[op] sty[le] tN[ext] t[open] tab tabN[ext] tabc[lose] tabd[o] tabfir[st] tabl[ast] tabn[ext]
|
||||
\ tabp[revious] tabr[ewind] tbh[ide] tbs[how] tbt[oggle] time tn[ext] toolbarh[ide] toolbars[how] toolbart[oggle] tp[revious]
|
||||
\ una[bbreviate] unl[et] unm[ap] verb[ose] ve[rsion] vie[wsource] viu[sage] vm[ap] vmapc[lear] vno[remap] vu[nmap] w[rite] zo[om]
|
||||
\ contained
|
||||
|
||||
syn match muttatorCommand "!" contained
|
||||
|
||||
syn keyword muttatorAutoCmd au[tocmd] contained nextgroup=muttatorAutoEventList skipwhite
|
||||
|
||||
syn keyword muttatorAutoEvent FolderLoad PageLoadPre PageLoad ShellCmdPost muttatorEnter muttatorLeavePre muttatorLeave
|
||||
\ contained
|
||||
|
||||
syn match muttatorAutoEventList "\(\a\+,\)*\a\+" contained contains=muttatorAutoEvent
|
||||
|
||||
syn region muttatorSet matchgroup=muttatorCommand start="\%(^\s*:\=\)\@<=\<\%(setl\%[ocal]\|setg\%[lobal]\|set\=\)\=\>"
|
||||
\ end="$" keepend oneline contains=muttatorOption,muttatorString
|
||||
|
||||
syn keyword muttatorOption archivefolder cdpath cd complete cpt editor eventignore ei extendedhinttags eht fileencoding fenc
|
||||
\ followhints fh guioptions go helpfile hf hintinputs hin hintmatching hm hinttags ht hinttimeout hto history hi laststatus ls
|
||||
\ layout maxitems messages msgs nextpattern pageinfo pa previouspattern runtimepath rtp scroll scr shell sh shellcmdflag shcf
|
||||
\ showstatuslinks ssli showtabline stal smtpserver smtp suggestengines titlestring urlseparator verbose vbs wildcase wic
|
||||
\ wildignore wig wildmode wim wildoptions wop wordseparators wsp
|
||||
\ contained nextgroup=muttatorSetMod
|
||||
|
||||
" toggle options
|
||||
syn match muttatorOption "\<\%(no\|inv\)\=\%(autoexternal\|errorbells\|eb\|exrc\|ex\|focuscontent\|fc\|fullscreen\|fs\)\>!\="
|
||||
\ contained nextgroup=muttatorSetMod
|
||||
syn match muttatorOption "\<\%(no\|inv\)\=\%(insertmode\|im\|loadplugins\|lpl\|more\|online\|showmode\|smd\|visualbell\|vb\)\>!\="
|
||||
\ contained nextgroup=muttatorSetMod
|
||||
syn match muttatorOption "\<\%(no\|inv\)\=\%(usermode\|um\)\>!\="
|
||||
\ contained nextgroup=muttatorSetMod
|
||||
|
||||
syn match muttatorSetMod "\%(\<[a-z_]\+\)\@<=&" contained
|
||||
|
||||
syn region muttatorJavaScript start="\%(^\s*\%(javascript\|js\)\s\+\)\@<=" end="$" contains=@javascriptTop keepend oneline
|
||||
syn region muttatorJavaScript matchgroup=muttatorJavascriptDelimiter
|
||||
\ start="\%(^\s*\%(javascript\|js\)\s\+\)\@<=<<\s*\z(\h\w*\)"hs=s+2 end="^\z1$" contains=@javascriptTop fold
|
||||
|
||||
let s:cssRegionStart = '\%(^\s*sty\%[le]!\=\s\+\%(-\%(n\|name\)\%(\s\+\|=\)\S\+\s\+\)\=[^-]\S\+\s\+\)\@<='
|
||||
execute 'syn region muttatorCss start="' . s:cssRegionStart . '" end="$" contains=@cssTop keepend oneline'
|
||||
execute 'syn region muttatorCss matchgroup=muttatorCssDelimiter'
|
||||
\ 'start="' . s:cssRegionStart . '<<\s*\z(\h\w*\)"hs=s+2 end="^\z1$" contains=@cssTop fold'
|
||||
|
||||
syn match muttatorNotation "<[0-9A-Za-z-]\+>"
|
||||
|
||||
syn match muttatorComment +".*$+ contains=muttatorTodo,@Spell
|
||||
syn keyword muttatorTodo FIXME NOTE TODO XXX contained
|
||||
|
||||
syn region muttatorString start="\z(["']\)" end="\z1" skip="\\\\\|\\\z1" oneline
|
||||
|
||||
syn match muttatorLineComment +^\s*".*$+ contains=muttatorTodo,@Spell
|
||||
|
||||
" NOTE: match vim.vim highlighting group names
|
||||
hi def link muttatorAutoCmd muttatorCommand
|
||||
hi def link muttatorAutoEvent Type
|
||||
hi def link muttatorCommand Statement
|
||||
hi def link muttatorComment Comment
|
||||
hi def link muttatorJavascriptDelimiter Delimiter
|
||||
hi def link muttatorCssDelimiter Delimiter
|
||||
hi def link muttatorNotation Special
|
||||
hi def link muttatorLineComment Comment
|
||||
hi def link muttatorOption PreProc
|
||||
hi def link muttatorSetMod muttatorOption
|
||||
hi def link muttatorString String
|
||||
hi def link muttatorTodo Todo
|
||||
|
||||
let b:current_syntax = "muttator"
|
||||
|
||||
let &cpo = s:cpo_save
|
||||
unlet s:cpo_save
|
||||
|
||||
" vim: tw=130 et ts=4 sw=4:
|
||||
28
teledactyl/install.rdf
Normal file
28
teledactyl/install.rdf
Normal file
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0"?>
|
||||
<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:em="http://www.mozilla.org/2004/em-rdf#">
|
||||
<Description about="urn:mozilla:install-manifest">
|
||||
<em:id>teledactyl@dactyl.googlecode.com</em:id>
|
||||
<em:name>Teledactyl</em:name>
|
||||
<em:version>###VERSION###</em:version>
|
||||
<em:description>Thunderbird for Mutt and Vim addicts</em:description>
|
||||
<em:creator>Kris Maglione</em:creator>
|
||||
<em:homepageURL>http://dactyl.sf.net/Teledactyl</em:homepageURL>
|
||||
<em:iconURL>chrome://muttator/skin/icon.png</em:iconURL>
|
||||
<em:optionsURL>chrome://dactyl/content/preferences.xul</em:optionsURL>
|
||||
<em:file>
|
||||
<Description about="urn:mozilla:extension:file:teledactyl.jar">
|
||||
<em:package>content/teledactyl/</em:package>
|
||||
</Description>
|
||||
</em:file>
|
||||
<em:targetApplication>
|
||||
<Description>
|
||||
<em:id>{3550f703-e582-4d05-9a08-453d09bdfdc6}</em:id>
|
||||
<em:minVersion>3.0b3</em:minVersion>
|
||||
<em:maxVersion>3.2</em:maxVersion>
|
||||
</Description>
|
||||
</em:targetApplication>
|
||||
</Description>
|
||||
</RDF>
|
||||
|
||||
<!-- vim: set fdm=marker sw=4 ts=4 et: -->
|
||||
3
teledactyl/locale/en-US/Makefile
Normal file
3
teledactyl/locale/en-US/Makefile
Normal file
@@ -0,0 +1,3 @@
|
||||
NAME = muttator
|
||||
BASE = ../../../common
|
||||
include $(BASE)/Makefile.doc
|
||||
11
teledactyl/locale/en-US/all.xml
Normal file
11
teledactyl/locale/en-US/all.xml
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://dactyl/content/dactyl.dtd">
|
||||
|
||||
<overlay
|
||||
xmlns="http://vimperator.org/namespaces/liberator"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
</overlay>
|
||||
|
||||
<!-- vim:se sts=4 sw=4 et: -->
|
||||
38
teledactyl/locale/en-US/autocommands.xml
Normal file
38
teledactyl/locale/en-US/autocommands.xml
Normal file
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://dactyl/content/dactyl.dtd">
|
||||
|
||||
<overlay
|
||||
xmlns="http://vimperator.org/namespaces/liberator"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<dl tag="autocommand-list" replace="autocommand-list">
|
||||
<dt>ColorScheme</dt> <dd>Triggered after a color scheme has been loaded</dd>
|
||||
<dt>DOMLoad</dt> <dd>Triggered when a page's DOM content has fully loaded</dd>
|
||||
<dt>DownloadPost</dt> <dd>Triggered when a download has completed</dd>
|
||||
<dt>Fullscreen</dt> <dd>Triggered when the browser's fullscreen state changes</dd>
|
||||
<dt>LocationChange</dt> <dd>Triggered when changing tabs or when navigating to a new location</dd>
|
||||
<dt>PageLoadPre</dt> <dd>Triggered after a page load is initiated</dd>
|
||||
<dt>PageLoad</dt> <dd>Triggered when a page gets (re)loaded/opened</dd>
|
||||
<dt>ShellCmdPost</dt> <dd>Triggered after executing a shell command with <ex>:!</ex><a>cmd</a></dd>
|
||||
<dt>&dactyl.appname;Enter</dt> <dd>Triggered after &dactyl.host; starts</dd>
|
||||
<dt>&dactyl.appname;LeavePre</dt><dd>Triggered before exiting &dactyl.host;, just before destroying each module</dd>
|
||||
<dt>&dactyl.appname;Leave</dt> <dd>Triggered before exiting &dactyl.host;</dd>
|
||||
|
||||
<dt>FolderLoad</dt> <dd>Triggered after switching folders in &dactyl.host;</dd>
|
||||
</dl>
|
||||
|
||||
<dl tag="autocommand-args" replace="autocommand-args">
|
||||
<dt><url></dt> <dd>The URL against which the event was selected.</dd>
|
||||
<dt><title></dt> <dd>The page, bookmark or download title.</dd>
|
||||
<dt><doc></dt> <dd>The document for which the event occurred. Only for <em>DOMLoad</em>, <em>PageLoad</em> and <em>PageLoadPre</em>.</dd>
|
||||
<dt><tab></dt> <dd>The tab in which the event occurred. Only for <em>DOMLoad</em>, <em>PageLoad</em> and <em>PageLoadPre</em>.</dd>
|
||||
<dt><size></dt> <dd>The size of a downloaded file. Only for <em>DownloadPost</em>.</dd>
|
||||
<dt><file></dt> <dd>The target destination of a download. Only for <em>DownloadPost</em>.</dd>
|
||||
<dt><name></dt> <dd>The name of the item. Only for <em>ColorScheme</em> and <em>Sanitize</em>.</dd>
|
||||
</dl>
|
||||
|
||||
</overlay>
|
||||
|
||||
<!-- vim:se sts=4 sw=4 et: -->
|
||||
30
teledactyl/locale/en-US/gui.xml
Normal file
30
teledactyl/locale/en-US/gui.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>
|
||||
|
||||
<!DOCTYPE overlay SYSTEM "chrome://dactyl/content/dactyl.dtd">
|
||||
|
||||
<overlay
|
||||
xmlns="http://vimperator.org/namespaces/liberator"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<dl tag="dialog-list" replace="dialog-list">
|
||||
<dt>about</dt> <dd>About &dactyl.host;</dd>
|
||||
<dt>addons</dt> <dd>Manage Add-ons</dd>
|
||||
<dt>addressbook</dt> <dd>Address book</dd>
|
||||
<dt>checkupdates</dt> <dd>Check for updates</dd>
|
||||
<dt>console</dt> <dd>JavaScript console</dd>
|
||||
<dt>dominspector</dt> <dd>DOM Inspector</dd>
|
||||
<dt>downloads</dt> <dd>Manage Downloads</dd>
|
||||
<dt>openfile</dt> <dd>Open the file selector dialog</dd>
|
||||
<dt>pageinfo</dt> <dd>Show information about the current page</dd>
|
||||
<dt>pagesource</dt> <dd>View page source</dd>
|
||||
<dt>preferences</dt> <dd>Show &dactyl.host; preferences dialog</dd>
|
||||
<dt>printsetup</dt> <dd>Setup the page size and orientation before printing</dd>
|
||||
<dt>print</dt> <dd>Show print dialog</dd>
|
||||
<dt>saveframe</dt> <dd>Save frame to disk</dd>
|
||||
<dt>savepage</dt> <dd>Save page to disk</dd>
|
||||
</dl>
|
||||
|
||||
</overlay>
|
||||
|
||||
<!-- vim:se sts=4 sw=4 et: -->
|
||||
179
teledactyl/locale/en-US/intro.xml
Normal file
179
teledactyl/locale/en-US/intro.xml
Normal file
@@ -0,0 +1,179 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<?xml-stylesheet type="text/xsl" href="chrome://dactyl/content/help.xsl"?>
|
||||
|
||||
<!DOCTYPE document SYSTEM "chrome://dactyl/content/dactyl.dtd">
|
||||
|
||||
<document
|
||||
name="intro"
|
||||
title="&dactyl.appname; Introduction"
|
||||
xmlns="http://vimperator.org/namespaces/liberator"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
|
||||
<logo/>
|
||||
|
||||
<h1 tag="intro">Introduction</h1>
|
||||
|
||||
<p>
|
||||
<link topic="&dactyl.apphome;">&dactyl.appname;</link> is a free
|
||||
browser add-on for &dactyl.host;, which makes it look and behave
|
||||
like the <link topic="http://www.vim.org">Vim</link> text
|
||||
editor. It has similar key bindings, and you could call it a
|
||||
modal web browser, as key bindings differ according to which
|
||||
mode you are in.
|
||||
</p>
|
||||
|
||||
<p tag="first-run">
|
||||
If this is your first time running &dactyl.appname;, you may need some
|
||||
time to adjust to the standard interface, which hides the menu,
|
||||
navigation, and tool bars by default. If you find it uncomfortable to work
|
||||
without them, you can re-enable them by typing,
|
||||
</p>
|
||||
<code><ex>:set</ex> <link topic="'guioptions'"><hl key="HelpOpt">guioptions</hl></link><hl key="HelpEx">+=mT</hl><k name="Return"/></code>
|
||||
<p>
|
||||
If you have trouble remembering commands or keyboard shortcuts, you can
|
||||
bring up this help page at any time by typing <ex>:help</ex> or the
|
||||
<k name="F1"/>. If you find that you don't like &dactyl.appname; at all,
|
||||
you can disable it by typing <ex>:extdisable &dactyl.appname;</ex> or
|
||||
delete it entirely by typing <ex>:extdelete &dactyl.appname;</ex>
|
||||
</p>
|
||||
|
||||
<h2 tag="overview">Help topics</h2>
|
||||
|
||||
<ol>
|
||||
<li>
|
||||
<link topic="tutorial">Quick-start tutorial</link>:
|
||||
A quick-start tutorial for new users.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="starting">Starting &dactyl.appname;</link>:
|
||||
How &dactyl.appname; starts up, where it reads the config file…
|
||||
</li>
|
||||
<li>
|
||||
<link topic="browsing">Browsing</link>:
|
||||
Basic mappings and commands needed for a browsing session (how to open
|
||||
a web page or go back in history).
|
||||
</li>
|
||||
<li>
|
||||
<link topic="buffer">Buffer</link>:
|
||||
Operations on the current document like scrolling or copying text.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="cmdline">Command-line mode</link>:
|
||||
Command-line editing.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="insert">Insert mode</link>:
|
||||
Insert-mode editing.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="options">Options</link>:
|
||||
A description of all options.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="pattern">Text search commands</link>:
|
||||
Searching for text in the current buffer.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="tabs">Tabs</link>:
|
||||
Managing your tabbed browsing session.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="hints">Hints</link>:
|
||||
Selecting hyperlinks and other page elements.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="">Key mappings, abbreviations, and user-defined commands</link>:
|
||||
Defining new key mappings, abbreviations and user commands.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="eval">Expression evaluation</link>:
|
||||
Executing JavaScript.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="marks">Marks</link>:
|
||||
Using bookmarks, QuickMarks, history and local marks.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="repeat">Repeating commands</link>:
|
||||
Using macros to repeat recurring workflows.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="autocommands">Automatic commands</link>:
|
||||
Automatically executing code on certain events.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="print">Printing</link>:
|
||||
Printing pages.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="gui">&dactyl.appname;'s GUI</link>:
|
||||
Accessing &dactyl.host; menus, dialogs and the sidebar.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="styling">Styling the GUI and web pages</link>:
|
||||
Changing the styling of content pages and &dactyl.appname; itself.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="message">Error and informational messages</link>:
|
||||
A description of messages and error messages.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="developer">Developer information</link>:
|
||||
How to write docs or plugins.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="various">Various commands</link>:
|
||||
Other help which didn't fit into any other category.
|
||||
</li>
|
||||
<li>
|
||||
<link topic="index">Index</link>:
|
||||
An index of all commands and options.
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<p>
|
||||
You can also jump directly to the help of a specific command with <ex>:help o</ex>
|
||||
or <ex>:help :set</ex>.
|
||||
</p>
|
||||
|
||||
<h2 tag="features">Features</h2>
|
||||
|
||||
<ul>
|
||||
<li>Vim-like keybindings (<k>h</k>, <k>j</k>, <k>k</k>, <k>l</k>, <k>gg</k>, <k>G</k>, <k>0</k>, <k>$</k>, <k>ZZ</k>, <k name="C-f"/>, etc.)</li>
|
||||
<li>Ex commands (<ex>:quit</ex>, <ex>:open www.foo.com</ex>, …)</li>
|
||||
<li>Tab completion available for all commands with support for "longest" matching when set in 'wildmode'</li>
|
||||
<li>Hit-a-hint like navigation of links (start with <k>f</k> to follow a link)</li>
|
||||
<li>Advanced completion of bookmark and history URLs (searching also in title, not only URL)</li>
|
||||
<li>Vim-like statusline with a Wget-like progress bar</li>
|
||||
<li>Minimal GUI (easily hide useless menubar and toolbar with <ex>:set guioptions=</ex>)</li>
|
||||
<li>Ability to <ex>:source</ex> JavaScript files, and to use a <em>~/.pentadactylrc</em> file with syntax highlighting if you install pentadactyl.vim</li>
|
||||
<li>Easy quick searches (<ex>:open foo</ex> will search for "foo" in google, <ex>:open ebay terminator</ex> will search for "terminator" on ebay) with support for &dactyl.host; keyword bookmarks and search engines</li>
|
||||
<li>Count supported for many commands (<em>3</em><k name="C-o"/> will go back 3 pages)</li>
|
||||
<li>Beep on errors</li>
|
||||
<li>Marks support (<k>m</k><em>a</em> to set mark a on a web page, <k>'</k><em>a</em> to go there)</li>
|
||||
<li>QuickMarks support (quickly go to previously marked web pages with <k>go</k><a>a-zA-Z0-9</a>)</li>
|
||||
<li><ex>:map</ex> and <ex>:command</ex> support (and feedkeys() for script writers)</li>
|
||||
<li><ex>:time</ex> support for profiling</li>
|
||||
<li>Move the text cursor and select text with Vim keys and a Visual mode</li>
|
||||
<li>External editor support</li>
|
||||
<li>Macros to replay key strokes</li>
|
||||
<li>AutoCommands to execute actions on certain events</li>
|
||||
<li>A comprehensive help system, explaining all commands, mappings and options</li>
|
||||
</ul>
|
||||
|
||||
<h2 tag="contact">Contact</h2>
|
||||
|
||||
<p>
|
||||
Please send comments, questions, or patches to the
|
||||
<link topic="mailto:&dactyl.maillist;">mailing list</link>,
|
||||
where we will do our best to answer any questions. You can also
|
||||
check the
|
||||
<link topic="http://code.google.com/p/dactyl/wiki">wiki</link> for
|
||||
<link topic="http://code.google.com/p/dactyl/wiki/FAQ">FAQ</link>.
|
||||
Issue reports can be entered in the
|
||||
<link topic="http://code.google.com/p/dactyl/issues/list">issue tracker</link>.
|
||||
</p>
|
||||
|
||||
|
||||
</document>
|
||||
<!-- vim:se sts=4 sw=4 et: -->
|
||||
BIN
teledactyl/skin/icon.png
Normal file
BIN
teledactyl/skin/icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 408 B |
Reference in New Issue
Block a user