1
0
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:
Kris Maglione
2010-09-17 06:21:33 -04:00
parent bfbb4b1313
commit 1557b70f45
125 changed files with 4409 additions and 3969 deletions

11
teledactyl/AUTHORS Normal file
View 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
View File

@@ -0,0 +1,3 @@
2009:
* Andreas Nerf (biggest Muttator donor so far! thanks a lot)
* Kirill Korotaev

6
teledactyl/Makefile Normal file
View File

@@ -0,0 +1,6 @@
#### configuration
VERSION = 0.6a1pre
NAME = muttator
include ../common/Makefile

64
teledactyl/NEWS Normal file
View 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
View 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

View 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}

View File

@@ -0,0 +1 @@
../../common/components/commandline-handler.js

View File

@@ -0,0 +1 @@
../../common/components/protocols.js

View 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:

View 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:

View 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: -->

View File

@@ -0,0 +1,4 @@
<!ENTITY dactyl.mainWindow "msgcomposeWindow">
<!ENTITY dactyl.name "muttator">
<!ENTITY dactyl.statusBefore "">
<!ENTITY dactyl.statusAfter "statusText">

View 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: -->

View 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:

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

943
teledactyl/content/mail.js Normal file
View 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:

View 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: -->

View 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}

View File

@@ -0,0 +1,2 @@
" TODO: what's the Muttator filename extension?
au BufNewFile,BufRead *muttatorrc*,*.muttator set filetype=muttator

View File

@@ -0,0 +1,2 @@
syntax/muttator.vim
ftdetect/muttator.vim

View 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
View 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: -->

View File

@@ -0,0 +1,3 @@
NAME = muttator
BASE = ../../../common
include $(BASE)/Makefile.doc

View 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: -->

View 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>&lt;url></dt> <dd>The URL against which the event was selected.</dd>
<dt>&lt;title></dt> <dd>The page, bookmark or download title.</dd>
<dt>&lt;doc></dt> <dd>The document for which the event occurred. Only for <em>DOMLoad</em>, <em>PageLoad</em> and <em>PageLoadPre</em>.</dd>
<dt>&lt;tab></dt> <dd>The tab in which the event occurred. Only for <em>DOMLoad</em>, <em>PageLoad</em> and <em>PageLoadPre</em>.</dd>
<dt>&lt;size></dt> <dd>The size of a downloaded file. Only for <em>DownloadPost</em>.</dd>
<dt>&lt;file></dt> <dd>The target destination of a download. Only for <em>DownloadPost</em>.</dd>
<dt>&lt;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: -->

View 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: -->

View 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

Binary file not shown.

After

Width:  |  Height:  |  Size: 408 B