Wikia

FastMailWiki

NewInterfaceJS

Talk8
261pages on
this wiki

This page is about customizing the new FastMail interface using Javascript. You can also request a tweak.
For information on CSS customization, see NewInterfaceCSS.

Setup Edit

Create directory & file Edit

First, you will need to enable the "Custom mode". To do this, go to Options -> Account Preferences.
In the "Display" section, make sure the "Custom Styles" checkbox is checked and click "Save changes".

Secondly, you will need to follow the steps bellow to create some files and folders in your file storage. To do this, go to Files.

  • In the "More Actions" drop down, select "Create subfolder".
  • Type in the "called" box, custom-ui, and click "Do".
  • Click on "custom-ui" in your file list on the left side.
  • In the "More Actions" drop down, select "Create subfolder"
  • Type in the "called" box, js, and click "Do".
  • Click on "js" in your file list on the left side.


Finally, use a text editor on your computer (eg. Notepad (Windows); TextEdit or SimpleText (Mac)), to create a new text file called tweaks.js.
You can add any JavaScript that you want into tweaks.js.
Then in your browser, upload tweaks.js to the "js" folder.
For more about writing JavaScript in the FastMail interface see Developer Information.

Developer information Edit

Overview & Warning Edit

The tweaks.js file allows users to insert their own javascript onto every page of FastMail. This is a very powerful feature, that allows lots of tweaks and customisation to the interface.

While we have created this feature, we should state quite clearly that it's experimental and subject to change at any time. For now, to make sure we can continuously improve the interface, we're explicitly not defining any APIs or interfaces to internal code. In all likelihood, any changes you make in tweaks.js will continue to work for some time, just don't rely on it, and if things break, don't expect official support in fixing your scripts.

When the JS runs Edit

The tweaks.js file is included after the core FastMail JS files. Currently that means it's included in the <head> section of the page. However don't rely on that, we may move it to the end of the <body> section at some stage. Because of that, you shouldn't rely on using document.write() on features like that.

Currently we are using the MooTools 1.2.1 library, so your code can make use of all the features of that library.

Because you don't know when your JS is loaded, you should attach into the browser event system to have your code called when you want it. The most common event you probably want to attach to is "domready", which is when the browser DOM is ready for script manipulation. Thus the content of your tweaks.js file will most probably consist of a number of these constructs:

window.addEvent('domready', function () {
   ... tweaks code in here ...
});

This code snippet will be referred to as the domready wrapper.

See the here for "prettier" code for this.

Mootools Edit

As stated above, the new interface makes use of the Mootools 1.2.1 library. Mootools is a Javascript framework that makes a couple of things easier. This section provides a very small introduction to mootools.

For example, the $$(selector) function returns an array of all elements that match the selector. The selector is formatted just as a CSS selector is formatted. Combined with the array each() function, you could use this to replace all dates in the mailbox date column with custom text as follows:

$$('td.date span').each(function (s) {
  s.innerHTML = "foo";
});

The above code will fetch ($$) all span elements within a td element with class 'date'. It will then apply to each of these elements the inline defined function. Upon iterating over these elements, the argument s is replaced with the actual element. As you can see, the function sets the HTML code of that element (s.innerHTML) to the string foo.

As another example, mootools allows you to read and write cookies easily. The following code writes a cookie, and then displays the (read) content of the cookie:

var myCookie = Cookie.write('username', 'Harald');
alert(Cookie.read("username"));

For more information, check the mootools homepage.

For learning Mootools check out the MooTorial.

Helper functions Edit

Here are scripts that define functions that perform some basic functions like checking what FastMail screen they're in. Code would be more modular and easy to update if they are put in the beginning of tweaks.js and used in other scripts.

There are various ways to use the examples here. One is to copy those functions you use into the first lines of your tweaks.js file. That is the most sensible to do it if you just need a little tweak or two. Another way is to put some or all of them in a separate file and load it (see here). And you can use them by copying their code right into your tweak if you want your tweak to work independently.

Some of the functions here are to make life easier for developers by not having to repeat code that does the same thing in many places. Some are to help make code easier to comprehend.

Adding external JavaScript Edit

Instead of putting all of your JavaScript code into tweaks.js, you can add other external js files that are in the same or different folder than custom-ui.

Add the following code to tweaks.js

// function to load additional javascript files from custom-ui/js/
function twkLoadScript(filename)
{ window.addEvent('domready',function(){
       new Asset.javascript($$('script[src*=tweaks.js]')[0].src.replace("tweaks.js", filename));
)};

Now, to load the file test.js that is in custom-ui/js, you would use the following code in tweaks.js to add test.js:

twkLoadScript("test.js");

Note that that the file is loaded when 'domready' so you cannot use anything in it before that! You may load files in other folders by including a relative path (including .. to refer to a parent folder). So if myfunctions.js is in custom-ui/lib you would use:

twkLoadScript("../lib/myfunctions.js");

Detection functions Edit

The following are functions that detect what screen they are in and sometimes what's happening in the screen. Some are partial or commented out and many are missing. Feel free to add yours or to replace them with better detection methods. You may also add a function that detects the same situation using an alternate method without replacing the old one if you are not sure yours is better. Then they can be compared by others.

function twkIsBeta(){return $defined($('betaVersion'))};

function twkIsMailbox(){return $defined($('mailbox'))};
function twkIsMessage(){return $defined($('message'))};
function twkIsCompose(){return $defined($('compose'))};
function twkIsAddressBook(){return $defined($('addressBook'))};
function twkIsNotepad(){return $defined($('notepad'))};
function twkIsFiles(){return $defined($('files'))};
function twkIsOptions(){return $defined($('options'))};

function twkIsCustomFrom(){return $('messageFrom').get('tag') == 'input'; };
function twkIsPesonalityFrom(){return $('messageFrom').get('tag') == 'select'; };

//function twkIsAddressBookEdit(){return ??????? };
//function twkIsAdvancedRules(){return ??????};
//function twkIsDefineRules(){return ??????};

//function twkIsFilesWebsites(){return ??????};
//function twkIsFilesAccessLog(){return ??????};
//function twkIsFilesUsage(){return ??????};
//function twkIsFilesWebsiteEdit(){return ??????};
//function twkIsFilesFileProperties(){return ??????};
//function twkIsFilesUploadMultiple(){return ??????};

//function twkIs??????????(){return ??????};
//function twkIs??????????(){return ??????};

function twkIsHTMLcompose(){return $defined($('MessageBodyHTMLEditor___Frame'))};
function twkIsComposePreview(){return $defined($('composepreview'))};

// twkIsFilesEditor() checks whether it is files editor
// twkIsFilesEditor("text/html") checks also if the filetype is "text/html"
function twkIsFilesEditor(filetype){
   if (!$defined($$('textarea[name=FFP-Contents]')[0]))
      { return false; }
   else if (!$defined(filetype)){ return true; }
        else { return $$('option[selected]')[0].value == filetype; }
};

// this function returns file type (check first whether it is the file editor)
function twkFilesEditorFiletype()
   { return $$('option[selected]')[0].value;};

One way to use this is to put all the needed functions (or all of them) in a separate file (such as detect.js and then to load that file near the beginning of tweaks.js. When checking new detection code the following code can be used (with twkIsOptions replaced by the name of the function you're testing.

// The following is code to check if a condition is detected
alert("detecting...");
cond = twkIsOptions();
if (cond) {alert("Detected!"    )}
else      {alert("Not detected!")};

Making code nicer to read Edit

Here is code that can be used to make commonly used code snippets more readable and to help non-programmers to construct simple tweaks without having to decipher "low level" code.

domready Edit

The domready wrapper can be made a bit shorter your code slightly more readable if packed in the following one line function:

/*******************************************************/
// To execute "myfunction();" when DOM is ready
// simply use "whenDOMready(myfunction);"
// To execute "myfunction(arguments);" when DOM is ready
// use "whenDOMready(myfunction.pass(arguments));"
/*******************************************************/
function whenDOMready(fn){window.addEvent('domready',fn)};

With this function the domready wrapper becomes:

whenDOMready( function () {
   ... tweaks code in here ...
});

It would have been nicer to have a whenDOMreadyDO function that allows:

whenDOMreadyDO( ... tweaks code in here ... );

If you need to know if the DOM is ready you may use use the following code (though there must be a method in MooTools that returns a boolean for this):

/*******************************************************/
// domReady is false until dom is ready (domready event)
/*******************************************************/
var domReady=false;
window.addEvent('domready',function(){domReady=true;});

If you want to see how to pass a function with arguments to be executed when domready you can play with the following code:

// Code to see how myfunction.pass(arguments) work
// to see the difference delete the string ".pass"
arg="before whenDOMready";
vvv="before whenDOMready";
function myalert(msg){alert(
             "this is " + this
         + "\ndomready before myalert is " + domreadybefore 
         + "\ndomready in myalert is " + domReady
         + "\nargument "+ msg
         + "\nvariable "+ vvv);};
domreadybefore = domReady;
whenDOMready(myalert.pass(arg));
arg="after whenDOMready";
vvv="after whenDOMready";

Hiding common messy JavaScript hacks Edit

Here are some shortcut functions for common Javascript hacks that can make code more readable than if the hacks are written directly inline.

One common hack that's used often in tweaks is testing for the existence of a substring in a string by comparing the result of indexOf to -1. Here is a function that does it.

// function to test for substring
function containsSubstring(string,substring)
   {return string.indexOf(substring)!=-1};

Reverse Engineering Edit

When writing code for tweaks one needs to learn about the code that already runs in the web application being tweaked (reverse engineering). Part of the code is the web page itself (accessible from the browser by the "view source" function). The rest of the code is available through URLs in the page's source code. That code is mostly packaged in a form that's hardly readable by humans (to reduce the size of files) but it's possible to tell FastMail to use the original human readable source by using the following URL:

http://www.fastmail.fm/?SLN-Raw=1

It's really only useful if you're digging into the FM code. It will slow browser response on the order of a few tenths of a second more per page (but reduce developer's time spent on digging into code on the order of a few minutes per hour). Eventually it will be reset so you don't have to worry about undoing it.

On Firefox there are several extensions that are useful for sniffing into a webpage and for debugging code in a webpage, such as Web Developer and Firebug.

Scripts Edit

Most of the examples below can be used directly, just copy and paste the ones you like one after another in your tweaks.js file. They also provide a good starting point for your own customizations.

General Edit

Dynamically load css style sheets Edit

Like extra JavaScript files extra css files can be loaded dynamically using Javascript in tweaks.js. Then we can do cool things like load a random style sheet with each FastMail screen by using code as simple as:

twkLoadCss(['cooltweaks.css', 'coolertweaks.css', 'awesometweaks.css'].getRandom());

Other uses may be conditional loading of different formatting (such as loading a different style sheet when the folder being displayed is Inbox). This is made possible with the following code.

The code detects the location of and defines a function do load style sheets from the same folder (or another folder by providing a relative path). The detection of where tweaks.css can be avoided by relying on the URL found for tweaks.js.

First we find URL for tweaks.css:

/************************************/
// Set twkCssURL to URL of tweaks.css file
/****** CAN ONLY WORK ON domready ? ******/
// seems to work anyway!
/*****************************************/
var twkCssURL = null;
// Find the <link> element that loads tweaks.css
$$('link').each(function(e)
    {  if(e.href.indexOf("tweaks.css") != -1)
         {  twkCssURL = e.href;
         };
    });

Then define a function to use it to load a style sheet by filename:

/***************************************/
// Function to load additional css files
// from custom-ui/css/
/***************************************/
function twkLoadCss(filename)
{ // point new <link> element to file and add to DOM
  new Asset.css(twkCssURL.replace("tweaks.css", filename));
};

Now to load an extra css stylesheet named moretweaks.css located in the same folder with tweaks.css use:

twkLoadCss('moretweaks.css');

To load a stylesheet named moretweaks.css located in the same folder with the JavaScript tweaks (i.e. tweaks.js) one can use twkLoadCss('../js/moretweaks.css');.

/***************************************/
// Function to load additional css files
// from custom-ui/js/
/***************************************/
function twkLoadCssFromJs(filename)
{ new Asset.css(twkURL.replace("tweaks.js", filename));
};

Add your own text or content to the FastMail screen Edit

These code snippets need to be in the domready wrapper.

The following code allows you to add a link to the left of the existing stuff on the right end of the top line (the example uses a link to the Keyboard shortcut list in the wiki but anything can be put there by replacing the value assigned to twkAddedString.

window.addEvent('domready', function() {
  twkAddedString = "<a target='_blank' href=http://wiki.fastmail.fm/index.php?title=NewInterface#Keyboard_shortcuts> keyboard shortcuts </a>" ;
  $('admin').innerHTML = twkAddedString + "<b> | </b>" + $('admin').innerHTML ;
});

This following snippet changes the username (email address) displayed at the top of the screen to whatever you want. It can also be done in the Options/Account Preferences screen's Display section, so you'd probably need this code only if you want to do it dynamically (based on some conditions).

window.addEvent('domready', function() {
  $('usernameDisplay').innerHTML= "Whatever I want goes here" + "<b> | </b>" ;
});

Warning: using .innerHTML removes all of the event handlers FM has registered. As such, if it is attached to any page elements which uses javascript (such as the sidebar or the message list), those elements will not retain their JS functionality. For example, the folder search will cease to work if attached to the sidebar.

The following can be used to safely add content to other elements, such as the sidebar, without interfering with FM's JS. This example embeds a simple Google Calendar at the bottom of the sidebar. It must be in a domready wrapper.

window.addEvent('domready', function() {
  new IFrame({
    'class': 'gcal',
    src: 'https://www.google.com/calendar/m',
    width: '100%',
    height: '300',
    scrolling: 'no',
    style: { border: '1px solid #000' }
  }).inject('sidebar');
});

Show (limited set of) keyboard shortcuts Edit

The following snippets modifies the text in buttons and links to display the keyboard shortcut for that command. For example, the delete button will show "Delete [.d]" (since the shortcut is .d). It only works for a number of shortcuts. Also, as a side-effect, the action menu that appears when you press "." is slightly mangled.

Technical clarification: the code is a mix of different approaches. Some actions have a kbshortcut attribute which can be used to automatically get and add the shortcut. Other actions haven't so the shortcut is added manually. For <option> elements without the kbshortcut attribute, special treatment is required since there is no
element.
/* Show shortcuts */
window.addEvent('domready', function() {
	function appendsc(elem, shortcut) {
		elem.innerHTML=elem.innerHTML + " [" + shortcut +"]";
	}
	function addsc(selector, shortcut) {
		$$(selector).each(function (s) {
  		     	appendsc(s, shortcut);
	   	});
	}
	function addscToOptionElem(selector, optionValue, shortcut) {
		$$(selector).each(function (s) {
			if(s.value==optionValue) appendsc(s, shortcut);
	   	});
	}
	function scanAndAddShortcuts(selector) {
		$$(selector).each(function (s) {
			var sc = s.getAttribute('kbshortcut');
			if(sc!=null) appendsc(s, sc);
		});
	}

	// manually
	addsc('.actionDelete div', ".d");
	addsc('.actionSpam div', ".s");
	addsc('.actionRefresh div', "u");
	addsc('#sbms h2', "/");
	addsc('.actionReply div', "r");
	addsc('.actionReplyAll div', "a");
	addsc('.actionForward div', "w");
	addsc('.backToMailbox a', "u");
	addsc('.msgPrevious', "k");
	addsc('.msgNext', "j");
	addsc('.viewToggle', "F");
	addsc('.viewImages', "I");

	// auto add shortcuts based on kbshortcut attribute
	scanAndAddShortcuts('#moreActions option'); 	// for mailbox screen
	scanAndAddShortcuts('.moreJumpPoints option');	// for message screen

	// for limited actions on message screen, since no kbshortcut attribute to scan for :-(
	addscToOptionElem('.lessJumpPoints option', 'MarkUnsn', "u");
	addscToOptionElem('.lessJumpPoints option', 'MarkFlag', "f");
	addscToOptionElem('.lessJumpPoints option', 'Redirect', "r");
	addscToOptionElem('.lessJumpPoints option', 'DelPerm', "%");
});

Extension 1: create a class for the shortcut

To further customize the shortcut appearance, you could wrap the shortcut in a span element which you then control via tweaks.css. For example, you could change the appendsc() function definition above as follows:

	function appendsc(elem, shortcut) {
		elem.innerHTML=elem.innerHTML + " <span class='shct'>[" + shortcut +"]</span>";
	}

Extension 2: Adding a show/hide toggle button

And, going even further, you could add a link in the header (the 'admin' area) to toggle the display of the shortcuts. It uses a cookie to remember the state across sessions (see the mootools documentation for more info). The following assumes you have used the above snippet to append the shct class to all shortcuts. Note: it doesn't work for the shortcuts displayed in the more actions combo box, these shortcuts remain shown.

There are 3 things to add/change:

1) Add the following at the end of the general "show shortcuts" snippet above (i.e. after all the addscToOptionElem calls):

	// Read cookie and show or hide shortcuts
	var showSC = Cookie.read('showShortcuts');
	if(showSC=='0') {
		showKBShortcuts('0');
	} else {
		showKBShortcuts('1');
	}

2) Then, add the following before the general window.addEvent call:

function showKBShortcuts(show) {
	if(show=='0') {
		$$('.shct').each(function(s) {
				s.addClass('hidden');
			});
		Cookie.write('showShortcuts', '0', {duration: 30}); // 1 month
	} else if(show=='1') {
		$$('.shct').each(function(s) {
				s.removeClass('hidden');
			});
		Cookie.write('showShortcuts', '1', {duration: 30}); // 1 month
	}
}

function toggleKBshortcutDisplay() {
	var showSC = Cookie.read('showShortcuts');
	if(showSC=='0') {
		showKBShortcuts('1');
	} else {
		showKBShortcuts('0');
	}
}

3) Finally, add a clickable link at the header calling the toggle function

... code from above ( Add to beta number a link to wiki bug page ) goes here (except for last line)...
toggleLink="<a onClick=toggleKBshortcutDisplay()> Show/Hide </a>";
twkbetar.innerHTML = toggleLink + twktemp + "</a>" + poststr ;

Show a random quote Edit

This code will show a random quote when you login, and on any page when you press Shift+Space.
First you will need to download quotes.txt and upload it to the /custom-ui/ folder.
Then add this code to /custom-ui/js/tweaks.js

window.addEvent('domready', function() {
   if(window.location.href.search(/MSignal\=MB\-\*$/)!=-1){getQuote();}
});

window.addEvent('keyup', function(event) {
    if(event.shift == true && event.key == 'space'){getQuote();}
});

var qR;
function getQuote()
{
    if(qR == undefined || qR.running == false){qR=new Request({url:$$('script[src$=tweaks.js]')[0].src.replace("tweaks.js", '../quotes.txt'), onSuccess:quoteSuccess, onFailure:quoteFail});qR.send();
    if($('randomQuote')){$('randomQuote').set('html', 'Getting random quote...');}
    else{new Element('div',{'class':'statusMessage','html':'Getting random quote...','id':'randomQuote'}).injectAfter($('pageHeader'));}}
}

function quoteSuccess(a)
{
    var s = a.split("%");
    var pickedQuote = s[$random(0,s.length-1)];
    while(pickedQuote.length>200){pickedQuote = s[$random(0,s.length-1)];}
    pickedQuote = pickedQuote.replace(/</g,'<').replace(/>/g,'>').replace(/^\s*|\s*$/g,'').replace(/\n/g,'<br />');
    $('randomQuote').set('html',pickedQuote);
}
function quoteFail(){$('randomQuote').set('html',"Couldn't retrieve random quote");}

Hide the long message box on the sieve edit screen Edit

This code snippet can be used to hide the FastMail message box on any screen. the example hides it in the Sieve edit screens (that returns a very long message box). Adapt it to any other screen by changing the tested condition or element to be hidden.

window.addEvent('domready', function () {
   $$('.description').each(function(e) {
     if(e.textContent.indexOf("We use 'sieve' as our filtering engine.")!=-1)
         e.style.display='none';
   });
 }); // domready

If you include some of the functions from the #Helper functions section this code can be slightly shortened to:

whenDOMready( function () {
   $$('.description').each(function(e) {
     if(containsSubstring(e.textContent,"We use 'sieve' as our filtering engine."))
         e.style.display='none';
   });
}); // domready

Here is an example of adapting the previous code to limit the height of the error message that appears when trying to save a Sieve script that contains a syntax error and making it scrollable instead (works on Firefox. IE would require somewhat different styles. Also it can really be done with just css. It's here only as an example how to adapt the previous code):

whenDOMready( function () {
   $$('.errorMessage').each(function(e) {
     if(containsSubstring(e.textContent,"Problem saving advanced rules:"))
         e.style.maxHeight='2in';
         e.style.overflow='scroll';
   });
});

Hide status messages by hitting ctrl keyEdit

Simple script to hide any status messages when you hit the ctrl key.

document.addEvent('keydown', function (e) {
  e = e || window.event;
  if (e.control) {
    $$('div.statusMessage').destroy();
  }
});

Mailbox Screen Edit

Apply Custom Styles to Messages Edit

This example shows how to apply custom styles to mailbox messages depending on their "From", "Subject", and/or "Date" values.

Add code like the following to your custom-ui/js/tweaks.js file:

//----------------------------------------------------------
// Some mailbox utility functions:
//----------------------------------------------------------

// Loop over all messages in the mailbox and execute a function
// on each of them:
//
function foreachMailboxMessage (callback)
{
    $$('#mailbox .contentTable tr.message').each(callback);
}

// Get the "From" value of a mailbox message.
//
// Note: This is the actual value, like "John Doe <jdoe@example.com>",
// not the displayed value, which might just be "John Doe".
//
function getMailboxMessageFrom (msg)
{
    var f = msg.getElement('td.from span');
    if (f) f = f.get('title');
    if (typeof f == 'undefined') f = '';    // just in case...
    return f;
}

// Get the displayed "Subject" value of a mailbox message.
//
function getMailboxMessageSubject (msg)
{
    var s = msg.getElement('td.subject a');
    if (s) s = s.innerHTML;
    if (typeof s == 'undefined') s = '';    // just in case...
    return s;
}

// Get the displayed "Date" value of a mailbox message.
//
function getMailboxMessageDate (msg)
{
    var d = msg.getElement('td.date span');
    if (d) d = d.innerHTML;
    if (typeof d == 'undefined') d = '';    // just in case...
    return d;
}

//----------------------------------------------------------

function containsString (string, substring)
{
    return string.indexOf(substring) != -1;
}

//----------------------------------------------------------

// Here is where the real work happens:

window.addEvent('domready', function () {

    if ($('mailbox')) {

        foreachMailboxMessage(function (msg) {

            var msgFrom    = getMailboxMessageFrom(msg);
            var msgSubject = getMailboxMessageSubject(msg);
            var msgDate    = getMailboxMessageDate(msg);

            if (msgFrom == 'voice-noreply@google.com') {
                msg.addClass('message_from_voicemail');
            }
            else if (containsString(msgFrom, '@mywork.com')) {
                msg.addClass('message_from_work');
            }
            else if (containsString(msgSubject, '[Catalyst] ')) {
                msg.addClass('message_from_catalyst');
            }

            if (msgDate.match(/[AP]M/)) {
                msg.addClass('message_is_recent');
            }
        });
    }

});

The CSS styling classes should be added to your custom-ui/css/tweaks.css file, for example:

tr.message_from_work {
    color: #336699;
}
tr.message_from_work a {
    color: #336699;
}

Custom buttons from the "More Actions..." menu Edit

This code enables you to add your own buttons to the mailbox and message view interface, with actions from the "More Actions..." menu.

Add the following code to custom-ui/js/tweaks.js

var customMailboxButtons = [['Mark as unread email','MarkUnsn'],['Mark as read','MarkSeen']];

//Custom mailbox buttons
var globalSelect;
window.addEvent('domready', function(){
    if($('mailbox') || $('message')) {
        globalSelect = ($$('select[name=FMB-ACT]')[0])?$$('select[name=FMB-ACT]')[0]:$$('select[name=FMR-LAT]')[0];
        twkAddMailboxButtons(customMailboxButtons);
    }
});

function twkAddMailboxButtons(arr)
{
    if(arr.length>5){alert("You have too many custom buttons defined.\nPlease use no more than 5.");return;}
    arr.each(function(button) {
        var optionIndex = twkGetOptionIndex(button[1]);
        if(optionIndex == false) {return;};
        var twkCustomButton = new Element('button', {'type':'button','text':button[0],'value':button[1],'name':optionIndex,'class':'customBtn_'+button[1]});
        twkCustomButton.style.padding="3px";twkCustomButton.style.paddingTop="2px";
        twkCustomButton.addEvent('click', function(event) {
            globalSelect.selectedIndex = this.name;
            globalSelect.fireEvent('change');
            $E('span.secondaryActions button.msgDo').sendEvent('click'); // Add message view auto-do
        });
        twkCustomButton.injectAfter($$(".actionSpam")[0]);
    });
}

function twkGetOptionIndex(value)
{
    var actOptions = globalSelect.getElements('option');
    for(var i = 0; i < actOptions.length; i++)
    {
        if(actOptions[i].value == value)
        return i;
    }
    return false;
}

To customize it, all you have to change is var customMailboxButtons = [] with the values below:

* value = "More Action..." text
*
* MarkSeen = Mark read
* MarkUnsn = Mark unread //Will also work in Message view
* MarkFlag = Flag //Will also work in Message view
* MarkUnfg = Remove flag
* MarkUnsm = Report non-spam
* Redirect = Redirect //Will also work in Message view
* Forward = Forward as attachments
* Archive = Download Zip
* Detach = Detach attached messages
* Bounce = Bounce
* DelPerm = Delete permanently //Will also work in Message view
* SaveAll = All
* SaveFrom = Sender
* SaveToCc = Recipient
* SelDis = Select displayed
* SelAll = Select all in folder
* UnselDis = Deselect displayed
* UnselAll = Deselect all in folder
* Pop:0 = <All> (POP)
* ImapPurge = Purge IMAP deleted
* ImapUndel = Mark IMAP undeleted

For example if you want "IMAP Purge deleted", "Check all POP" and "Redirect"; you would do this: var customMailboxButtons = [['YOUR_TEXT_HERE','ImapPurge'],['YOUR_TEXT_HERE','Pop:0'],['YOUR_TEXT_HERE','Redirect']]

The 'customBtn_' class will add the values specified in 'customMailboxButtons' as new CSS classes for styling. Any value containing parameters, such as Pop:0, will not produce a valid class, but will still render a button. If you'd like to style those parametrized buttons, change the class value from 'customBtn_'+button[1] to 'customBtn_'+optionIndex to append numerical values to the class instead. If you have an existing class for styling buttons, you may also append that class to the value if you would like to apply both classes (e.g. 'class':'yourButtonClass customBtn_'+button[1]).

Customise date format on mailbox screen Edit

The date column on the mailbox screen has an epoch="..." attribute which is the unix GMT time of that column. So you can do this to extend the existing date with hours + minutes.

$$('#mailbox .contentTable td.date span').each(function (s) {
  var d = new Date(s.get('epoch')*1000);
  s.innerHTML = s.innerHTML + " " + d.getHours() + ":" + d.getMinutes();
});

If you want more control over the date format, you could include a date formatting library such as [1] in your tweaks.css, or explicitly construct the date using the JS date methods [2]

Here's an example (contributed by a user; see history of this page) where the date/time has been customised according to the following rules ("AP" = "AM" or "PM"; "Day" = 3-letter day of week; "Mon" = 3-letter month; "DD" = 2-digit day of month; "YY" = year, etc.):

1) If the date is today, use the default format (time only):

H:MM AP

2) If the date is not today but is within the last week:

Day Mon DD HH:MM AP

3) If the date is a week or more old but within the same calendar year:

Mon DD HH:MM AP

4) If the date is from a previous calendar year:

Mon DD 'YY HH:MM AP
$$('#mailbox .contentTable td.date span').each(function (s) {
  var d = new Date(s.get('epoch')*1000)
  var today = new Date()
  var ds = d.toDateString()	  //date as string "DAY MON DD YYYY" e.g. "Mon Dec 08 2008"
  if (ds != today.toDateString())	//if today, leave default string, else...
	{
	var show = ""		//build up date/time string to show
	// determine whether within 1 week:
	// first convert to UTC to calculate day, otherwise 
	// times near end of day are calcluated as wrong day
	var adjustFactor = today.getTimezoneOffset()*60000
	today_adjusted=new Date(today - adjustFactor)
	d_adjusted=new Date(d - adjustFactor)
	// Divide by number of MS/day to drop HH:MM:SS:MS:
	if (Math.floor(today_adjusted / 86400000) - Math.floor(d_adjusted / 86400000) < 7)
	   {show = ds.substring(0,4)}	//show day of week,space
	show += ds.substring(4,10)	//always show 3-letter month,2-digit day
	if (d.getFullYear() != today.getFullYear()) //if different year, show year
	   {show += " '" + ds.substring(13,15)} //show ' + 2-digit year
	var ts = d.toLocaleTimeString() //time as string "HH:MM:SS AP" e.g. "06:03:00 PM"
	show += " " + ts.substring(0,5) + ts.substring(8,11) //show time without seconds
	s.innerHTML = show
	}
});

When using this script, the full date may not be visible unless a change is also made to the style sheet to allow for the longer format.

For example, place the following line in custom-ui/css/tweaks.css (see NewInterfaceCSS):
   .contentTable .date { width: 12.5em; }

Change page header color, depending on mailbox quota usage Edit

The following snippet will make your page header red if there is a resource chart with its quota over 90%. For example, if your mailbox quota is 93%, the mailbox page header will turn red.

// Change page header based on resource usage
window.addEvent('domready', function () {
    if ($('resourceUsage') && $('resourceUsage').getElements('.size75, .size80, .size85').length > 0) {
        $('pageHeader').style.backgroundColor="orange";
    }
    if ($('resourceUsage') && $('resourceUsage').getElements('.size90, .size95, .size100').length > 0) {
        $('pageHeader').style.backgroundColor="red";
    }
});

Display a pop-up to warn about high resource usage Edit

The following snippet will display a pop-up once a day when resource usage is 90% or greater. A cookie with a one-day expiration is set to prevent the pop-up from displaying on each page load.

// Display separate resource alerts for near-capacity mailbox and file storage
window.addEvent('domready',function () {
	if ($('resourceUsage') && $('resourceUsage').getElements('.size90, .size95').length > 0 && !$defined($('files')) && Cookie.read('resourceAlert') == null) {
		alert('Your mailbox is nearing capacity!');
		Cookie.write('resourceAlert', '1', {duration: 1}); // 1 day
	}
	if ($('resourceUsage') && $('resourceUsage').getElements('.size90, .size95').length > 0 && $defined($('files')) && Cookie.read('resourceAlertFiles') == null) {
		alert('Your file storage is nearing capacity!');
		Cookie.write('resourceAlertFiles', '1', {duration: 1}); // 1 day
	}
});

Simple Preview ToolTips Edit

The following code snippet will, in mailbox view, display the first lines of the message as a tooltip when hovering over the subject. You have to set the the preview of the folder to "All" (Options > Folders > Edit > Preview) in order for this to function. The original previews will be hidden.

Note:

  • The code is a quick mockup (as a proof-of-concept), and I might improve it later. E.g the preview elements are fetched twice, and it currently only works with preview set to "All"(i.e. not if preview is set to "unread only").
  • This might be very expensive on folders with lots of messages displayed (>50). If you don't turn on the preview feature of these folders, it should be OK.
window.addEvent('domready', function() {

if($('mailbox')) {

  function removebr(s) {
    return s.replace(/<br>/g,' ');
  }

  var pelem=$$('table.contentTable tbody tr.preview td');

  // only proceed if preview is turned on
  if(pelem.length>0) {
    var subelem=$$('table.contentTable tbody tr.message td.subject a');

    if(subelem.length==pelem.length) {

      // Set title attribute with content of preview
      for(i=0;i<subelem.length;i++) {
        subelem[i].set('title',removebr(pelem[i].innerHTML));
      }

      // Hide original previews
      $$('table.contentTable tbody tr.preview').each(function(s) {
        s.addClass('hidden');
      });

    }

  }

}
});

Advanced Preview ToolTips Edit

The following code snippet will, in mailbox view, display the first lines of the message as a tooltip when hovering over the subject. It will automatically turn on the Preview ToolTips for every folder you visit. You can turn it on/off by the link under the Logout link.

Save preview_tooltips.js (developer version) as preview_tooltips.js on your computer. Then upload it to custom-ui/js/. It has been tested in FireFox 3.0.6, Internet Explorer 7/8, Opera 9.63, and Safari 4.

Then add the following code to custom-ui/js/tweaks.js

var downloadDir;
window.addEvent('domready', function () {
    var previewScript = new Element('script', {
    'type': 'text/javascript'
    });
    $$('script').each(function(e){
        if(e.src.indexOf("MSignal=FilesDownload-AccessDir") != -1)
        {
            downloadDir = e.src.replace("tweaks.js", "preview_tooltips.js");
        }
    });
    previewScript.set('src',downloadDir);
    previewScript.inject($$('head')[0]);
});

Finally add the following code to custom-ui/css/tweaks.css

/*Hide previews*/
.preview {display: none;}

/*Preview Tool Tip CSS*/
.previewToolTip {
    display: none;
    position: absolute;
    background-color: #ffffff;
    padding: 3px;
    opacity: 0.9;
    filter: alpha(opacity=90);
    border: solid 1px #BBBBBB;
    font-weight: normal;
    z-index: 900;
}

Remove message checkbox tooltips Edit

To remove the tooltip that appears when you hover over the checkboxes (in the 'check' column) on the mailbox page, insert the following code:

/* Remove message checkbox tooltips*/
window.addEvent('domready', function() {

  if($('mailbox')) {
    $$('#mailbox tr.message td.checkbox input').each(function (s) {
	s.removeAttribute("title");
    });
  }
	
});

Apply scripts only to Inbox (or arbitrary folder) Edit

The actual value of 'selectedFolder' will vary for each user. To find it, view the HTML source for the mailbox screen. Search for 'selectedFolder' and place the 'value' it refers to (possibly a seven-digit number) at the end of the 'if' statement where it says NUMBER.

window.addEvent('domready', function() {
  // only run if we're in the Inbox
  if($('mailbox') && $('selectedFolder').value == 'NUMBER') {
    // insert other javascript here
  };
});


Make Refresh button the leftmost button Edit

On the Action bars above and below the message listings, this makes the Refresh button the leftmost button rather than the rightmost button. This places the Refresh button next to the other Fastmail options, reducing mouse travel if it is clicked frequently.

/* Make Refresh button the leftmost button */
window.addEvent('domready', function () {
   if ($('mailbox')) {
      $$('.actionRefresh')[0].inject($$('.primaryActions')[0],'top'); /* top bar */
      $$('.actionRefresh')[1].inject($$('.primaryActions')[1],'top'); /* bottom bar */
   }
});

Add Flag Header and modify Attachment header to enable sorting Edit

The header for Flags has no text and nothing to click. The Attachments header is too big and overlaps the next header.

/* Modify text for flag and attachment headers to enable sorting */
window.addEvent('domready', function () {
  if ($('mailbox')) {
  //Show a Flag header to allow sorting
    var f_a_Header = $$('.contentTable th.flag a')[0];
    f_a_Header.textContent = 'F';
  //Change attachment header from Att to A
    f_a_Header = $$('.contentTable th.attachment a')[0];
    f_a_Header.textContent = 'A';
  };
});

To enable either or both also requires this additions to tweaks.css

.contentTable th.flag a, .contentTable th.attachment a {
display: inline;
}

Message Screen Edit

Replace Previous/Next with Newer/Older Edit

The following snippet changes the Previous and Next text at the top and bottom of the page (when viewing a message) to Newer and Older.

/* Changes previous/next to newer/older */
window.addEvent('domready', function() {
  if($('message')) {
    $$('a.msgPrevious, a.msgDeletePrevious').each(function (s) {
      s.innerHTML="▲ Newer";
    });
    $$('a.msgNext, a.msgDeleteNext').each(function (s) {
      s.innerHTML="Older ▼";
    });
  }
});

Remove sidebar in message view Edit

The following code snippet will remove the sidebar in message view (as is the case in the old interface). There is no way to show the sidebar (i.e. there is no toggle button, or moving the mouse to the left side will not display the sidebar).

/* Remove sidebar in message view */
window.addEvent('domready', function() {
  if($('message')) {
    $('main').style.left="0px";
    $('main').style.marginLeft="0px";
    $('sidebar').style.display="none";
  }
});

Display attached images inline Edit

The following code snippet will display images attached to a message inline, instead of displaying the thumbnails. The solution provided can use some improvement, because the information provided next to the thumbnail is not visible anymore. Perhaps some link to toggle between thumbnail view and inline view would be handy as well (e.g. using the cookie solution of the shortcut display toggle above).

/*Display attached images inline */
window.addEvent('domready', function() {
  if($('message')) {
    $$('div.attachedImage span.attachmentDetails').each(function (s) {
      var a_elem=s.getElement('a');
      if(a_elem.innerHTML='View') {
        var urlStr= a_elem.get('href');
        var imgStr= "<img class=\'inlineImg\' src=\'" + urlStr + "\' />";
        var hrefStr= "<a href=\'" + urlStr + "\'>" + imgStr + "</href>";
        var attImg_elem=a_elem.parentNode.parentNode.parentNode;
        attImg_elem.innerHTML= hrefStr;
      }
    });
  }
});

Also, add this line to your tweaks.css file:

/* Inline attached images (through javascript) */
.inlineImg{max-width:100%;}

The following code instead of loading the images inline just adds a link that says "show here" that loads the full image in the message screen when clicked. It works almost correctly, and is usable.

/*******************************************************/
// Add link"show here" to show image in message screen
/*******************************************************/
whenDOMready(function(){
   $$('.attachedImage').each(function(e){
      var url;
      url = e.getElement('a:contains("View")').href;
      var link = new Element( 'a',
             { 'href' : '#'
             , 'text' : 'Show here'
             ,  events:
                 { click: function(event){
                          event.preventDefault(); // Do not follow link
                          var img = new Element("img");
                          img.src = url;
                          img.setStyle('max-width','100%');
                          img.inject(e,'after');
/*  The next two lines don't work as expected:
                          e.getElement('a:contains("Show here")').destroy();
                          e.getElement('.shadow').destroy();
*/
                          }
                 }
             }
      );
      link.inject(e.getElement('.attachmentDetails'));         
   });
});

The two lines that are disabled in the code were meant to get rid of the extra link and the thumbnail that are unneeded after the full image is there but they didn't do their job (and so one can keep clicking "show here" and get multiple copies of the full image: not useful but harmless). Another approach that is only partially working and loads the full image in place of the thumbnail is here:

/* Attempt to show images inline (by clicking on thumbnail): */
whenDOMready(function(){
   $$('.attachedImage').each(function(e){
      var url;
      url = e.getElement('a:contains("View")').href;
      e.getElement('.shadow img').addEvent('click',function(){
         if ( this.src != url ) {
             this.setStyle('max-width','100%');
             this.src = url ;
         }
      });
   });
});

Spam and Next Edit

Add links to mark current message as spam and go to previous/next/mailbox. Can easily be adapted to mark unread etc.

// MarkUnsm = non-spam
// MarkUnsn = unread
// MarkFlag
// MarkSpam
window.addEvent('domready', function () {
    var upArrow = "\u25b2";
    var downArrow = "\u25bc";
    var leftArrow = "\u00ab";
    var doOnce;
    $$('.nextPrev').each(function(nextPrev) {
	var prevurl = $('msgPreviousLink').href;
	var nexturl = $('msgNextLink').href;
	nextPrev.appendChild(document.createTextNode(' - '));
	var markUnsnNextPrev = document.createElement('SPAN');
	    markUnsnNextPrev.className = 'markUnsnNextPrev';
	    markUnsnNextPrev.appendChild(document.createTextNode('Spam '));
	var msgMarkSpamMailboxLink = document.createElement('A');
	    msgMarkSpamMailboxLink.href = prevurl
		.replace('Keep','MarkSpam')
		.replace(/\*Prev\*\*[a-z0-9]*/,'*MB');
	    msgMarkSpamMailboxLink.className = 'msgMarkSpamMailbox';
	    msgMarkSpamMailboxLink.textContent = leftArrow;
	var msgMarkSpamPreviousLink = document.createElement('A');
	    msgMarkSpamPreviousLink.href = prevurl.replace('Keep','MarkSpam');
	    msgMarkSpamPreviousLink.className = 'msgMarkSpamPrevious';
	    msgMarkSpamPreviousLink.textContent = upArrow;
	var msgMarkSpamNextLink = document.createElement('A');
	    msgMarkSpamNextLink.href = nexturl.replace('Keep','MarkSpam');
	    msgMarkSpamNextLink.className = 'msgMarkSpamNext';
	    msgMarkSpamNextLink.textContent = downArrow;
	markUnsnNextPrev.appendChild(msgMarkSpamMailboxLink);
	markUnsnNextPrev.appendChild(document.createTextNode(' '));
	markUnsnNextPrev.appendChild(msgMarkSpamPreviousLink);
	markUnsnNextPrev.appendChild(document.createTextNode(' '));
	markUnsnNextPrev.appendChild(msgMarkSpamNextLink);
	nextPrev.appendChild(markUnsnNextPrev);
	if(doOnce = 0) { // set ids
	    msgMarkSpamMailboxLink.id = 'msgMarkSpamMailboxLink';
	    msgMarkSpamPreviousLink.id = 'msgMarkSpamPreviousLink';
	    msgMarkSpamNextLink.id = 'msgMarkSpamNextLink';
	} doOnce++;
    }); // .nextPrev

    // for compactness, "Del v ^"
    $$('.deleteNextPrev').each(function(x) {
	var n = x.childNodes;
	for(var i=0;i<n.length;i++) {
	    if(n.nodeType == 3 && /\s*Delete\s*and:\s*/.test(n[i].nodeValue)) // Text "Delete and: "
		txtNode.nodeValue = "Del ";
	    if(/\bmsgDeletePrevious\b/.test(n.className))
		n.textContent = '\u25b2';
	    if(/\bmsgDeleteNext\b/.test(n.className))
		n.textContent = '\u25bc';
	}
    }); // .deleteNextPrev
}); // domready

Display math formulas with ASCIIMathML Edit

ASCIIMathML is a client-side mathematical markup language for displaying mathematical expressions in web browsers. It consists of a single lightweight file containing Javascript code that translate easy to use and user tolerant markup to MathML that can be displayed in compatible browsers that is licensed with LGPL. ASCIIMathML was created bu Peter Jipsen.

The following code uploads the file to the message screen so if any ASCIIMathML formulas are there they are parsed and displayed as math (see screenshot. Compare with the same without math rendering).

First you need to download the latest version of ASCIIMathML. Then upload the file ASCIIMathML.js to your {My Files}/custom-ui/js folder. Add the code to load an external script and after it the following lines to your tweaks.js file:

// load ASCIIMathML only if this is the message screen
window.addEvent('domready', function(){
  if($('message')||$('composepreview')){twkLoadScript("ASCIIMathML.js")};
});

This works quite well but is not perfect. If you don't see the resulting formula when composing then you might get something different from what you meant it to be (formula "with brackets" in the screenshot) but you can see it in the message preview screen of the Compose screen (accessed form the drop-down menu in the text actions toolbar above the message). A better solution would have something dynamic like in the ASCIIMathML demo in the Compose screen so that formulas are seen as they are typed. Another solution would be a toggle button between parsed/unparsed formulas or one that renders selected text in a floating box (Perhaps MooTools can help in this). Yet another approach for toggling between parsed/unparsed formilas can be seen in ASciencePad. Double click the MathExamples there and this shows an editor where formulas are rendered and clicking on a formula shows the source ready to be edited. That would be the best way to do it but it requires much more work than just importing an existing file. Then there's html composition. The the editor in ASciencePad is HTMLarea with an ASCIIMathML plugin. There is also a plugin for Xinha but no plugin for FCKeditor as far as I know. So two possible solutions are to either do an "html editor transplant" and stick HTMLarea with the plugin from the ASciencePad distribution in place of FCKeditor. This can be useful for other purposes too or create a plugin for FCKeditor.

To sum it up: reading emails with ASCIIMathML requires a simple tweak. Writing formulas requires much more work if it to be done in the webmail client so right now the easiest way is to use the editor on ASCIIMathML site (or a local copy of it, perhaps tweak a link to it from the Compose screen) and paste into the composed message.

And then there's of course another small problem: whoever you correspond with should be able to use the same technology! So this solves the viewing problem for a FastMail user. Messages sent out this way should contain a link to somewhere where the text can be pasted and parsed like the editor on ASCIIMathML site. This of course is easily solved with the "quick notes" function in the compose screen but can also be automated somehow.

Some other issues: it might happen that text is rendered as math that should not have been rendered. The above code provides no direct solution for this (other than either commenting out the code in the tweaks file or using "reply" and reading the quoted message). Also ASCIIMathML has a mode for rendering text with formulas in it (by surrounding it with amath ... endamath). This can be used to render text with informal math such as "The formula of a circle is (x-a)^2+(y-b)^2=R^2 and you should have used it" by inserting amath ... endamath around the whole message body. The current tweak does not do it and would only render the formula in this example if it were written as "The formula of a circle is `(x-a)^2+(y-b)^2=R^2` and you should have used it" (with backquotes around the formula). Doing this would require some method of allowing the user to opt out of rendering formulas in a message if rendering fails (or to only render formulas when the user asks for them).

Compose Screen Edit

The Compose screen is a perhaps the best candidate for customization, allowing one to embed into it tools to be used in composing email. It is quite complex in having two working modes (plain text composition and HTML composition) and many components, some interchangeable (like the personality menu that can be interchanged with the custom "From" field).

Remove Discard Confirmation Edit

This removes the confirmation box "Discard current message and any saved drafts?" when the Discard button is pressed. Upon pressing the Discard button, the in-progress message will be discarded immediately and not saved in the Drafts folder.

if ($defined($('compose'))) {
      /* Remove confirmation upon discard */
      $$('.actionDiscard')[0].removeProperty('confirm');
      $$('.actionDiscard')[1].removeProperty('confirm');
}


To further speed discarding in-progress messages, the following code makes the top Discard button the leftmost button, making it more accessible:

if ($defined($('compose'))) {
      /* Make top Discard button the leftmost button */
      $$('.secondaryActions')[0].inject($$('.actions')[0],'top');
      $$('.secondaryActions')[0].setProperty('style', 'float: left');
}

Moving Options above the editing area Edit

Some people modify the compose options a lot more than most, and would like the options above the editing area.

if ($defined($('compose'))) {
	$E('fieldset.messageHeaders').grab($('messageOptions'), 'after');
	$E('fieldset.messageHeaders').grab($('showMessageOptions'), 'after');
}

For a cleaner appearance, add the following lines to the bottom of the above if({}) snippet above to merge the compose options with the message headers at the top of the page.

	$E('fieldset#messageOptions>div>#toggleWrapText').setStyles({float:'left',marginLeft:'18em',marginTop:'-2.8em'});
	$E('fieldset#messageOptions label[for~="toggleWrapText"]').setStyles({float:'left',marginLeft:'22em',marginTop:'-3.2em'});

Then, add the following to your tweaks.css file:

/* Move message options above edit area */
#messageOptions { margin: 1em 2em -5em 1.5em; padding: 0; font-size: 80% }
#messageOptions label { width: auto; margin:0; padding:0; position: relative; left: -.25em}
#showMessageOptions { display:none }

If someone can determine the proper DOM elements to add to tweaks.css, the secondary modification to tweaks.js could be eliminated, and would be semantically more appealing. [Mystakill]

HTML Composition Screen Edit

FastMail uses FCKeditor to allow composition of messages formatted with HTML. This is a JavaScript based html editor and being based on JavaScript allows lots of customization.

Advanced HTML Composer Toolbar Edit

This code shows all of the commands for the Rich Text Editor used to Compose messages.

Some useful commands that are there: "Paste as plain text", "Find and Replace", "Remove Format", "Style", "Format". Some less useful ones: "Show blocks", "Insert Smiley".

Add the following code to custom-ui/js/tweaks.js

window.addEvent('domready', function () {
    if($('FMC-MessageBodyHTMLEditor___Frame')) {
        $('FMC-MessageBodyHTMLEditor___Frame').src=$('FMC-MessageBodyHTMLEditor___Frame').src.replace("Email","Default");
    }
});

Toggle Button for HTML Composer Toolbar Edit

The following code adds a button to toggle between the standard FastMail html editing toolbar and the full FCKeditor toolbar.

The placement of the button can use some improvement (to line it up with the rest of them and stick it on the right before the "make plain text" button).

//Toggles toolbar
function twktogltoolbar()
{         var twkfr=$('FMC-MessageBodyHTMLEditor___Frame');
          twkfr.src=(twkfr.src.indexOf('Email')!=-1)?twkfr.src.replace("Email","Default"):twkfr.src.replace("Default","Email");
}

//Toggles toggle button
function twktogltoglbutton()
{
   var twkb=$('twktogl');
   twkb.setStyle('display',(twkb.getStyle('display')=='none')?'inline':'none');
}

//Makes and adds toggle button
function twkaddtogl()
{
     var twktoglbutton = new Element('button', {'type':'button','text':'Toggle Toolbar','id':'twktogl'});
     twktoglbutton.addEvent('click', twktogltoolbar);
     twktoglbutton.style.padding="3px";twktoglbutton.style.paddingTop="2px";
     twktoglbutton.inject($$('.secondaryActions')[1], 'top' );
}

//Dom ready
window.addEvent('domready', function(){
   if($('FMC-MessageBodyHTMLEditor___Frame'))
    {
     $$('.actionPlain')[0].addEvent('click',function(){twktogltoglbutton();});
     twkaddtogl();
   }
   else if($$('.actionRich')[0]) {
     $$('.actionRich')[0].addEvent('click',function(){twktogltoglbutton();});
     twkaddtogl();
   }
});

Custom Composer Toolbar Edit

And now for a completely custom toolbar (based on work by Fast_Mail_Er). The following code creates a custom toolbar that is always loaded when the compose window is loaded to edit an html message. I separated most of the code in a separate file that is only loaded in this screen.

In the file tweaks.js I put the code:

// load FCKeditor tweaks only if this is the html compose screen
window.addEvent('domready', function(){
if($('FMC-MessageBodyHTMLEditor___Frame')){twkLoadScript("fck.js")};
});

The rest of the code for this example is in a separate file named fck.js and is loaded after the domready event so we needn't worry about this detail. Here it is (well, actually I also have the above toggle example code in that file):

/************************************/
// Load custom toolbar
/************************************/
var customToolbar =
           [
             ['Cut','Copy','Paste','PasteText','PasteWord']
           , ['Undo','Redo','-','Find','Replace','-','RemoveFormat']
           , ['Bold','Italic','Underline','StrikeThrough','-','Subscript','Superscript']
           , ['TextColor','BGColor']
           ,    '/'
           , ['OrderedList','UnorderedList','-','Outdent','Indent','Blockquote']
           , ['Link','Unlink','Anchor']
           ,  ['JustifyLeft','JustifyCenter','JustifyRight']
           ,  ['LeftToRight','RightToLeft']
           ,    '/'
           , ['Style','FontFormat','FontName','FontSize']
           , ['Table','Image','Rule','SpecialChar','-','Link','Unlink']
           , ['ShowBlocks','Source','About']
           ] ;

function FCK_LoadToolbar(toolbar_array)
{
    var win = $('FMC-MessageBodyHTMLEditor___Frame').contentWindow;
    win.FCKConfig.ToolbarSets["Custom"] = toolbar_array;
    var oToolbarSet =  win.FCKToolbarSet_Create();
    oToolbarSet.Load("Custom");
};

$('FMC-MessageBodyHTMLEditor___Frame').addEvent('load',function(){
    FCK_LoadToolbar(customToolbar);
});

The toolbar definition in this code sample is by not optimal for anyone. Remove what you don't need. Add what you need. All the available options are listed in the documentation. Also this code has some slight flaws. For instance it doesn't load the custom toolbar if one uses the button "Make Rich Text" on mail originally composed in plain text mode.

Changing HTML Composer Configuration settings Edit

Other than the toolbar there are other configurations in FCKeditor that may be changed from what FastMail have configured, such as the list of styles in the styles drop down menu, the fonts menu, some options disabled in some dialog boxes etc. Some of them are easily changed by adding one line to the custom toolbar code right after the line that begins with var win. Here are some examples:

    var win = $('FMC-MessageBodyHTMLEditor___Frame').contentWindow; //this line is already in the code above
    win.FCKConfig.EnterMode = 'p' ;   // Enter key makes paragraph instead of div element
    win.FCKConfig.BrowserContextMenuOnCtrl = true ; // make browser context menu available
    win.FCKConfig.FirefoxSpellChecker = true     // previous lines makes it also usable
    win.FCKConfig.AutoDetectLanguage = false ;   // Use English interface in hotels abroad
    win.FCKConfig.ImageDlgHideLink = false ;     // To set link target on images

The configuration options are listed in FCKeditor's developr guide. One needs to remember that whereever the developer guide uses FCKConfig we would need to use the above technique that references win.FCKConfig!

FCKeditor uses the concept of "styles" and these are set in an XML file that's easy to configure. To provide the link to alternate styles file if you put one in the file stoarge area you would need to build the URL like it is done here. I found the styles file used by FastMail here by looking at the JavaScript code generated for the HTML compose screen (on FireFox using the Web Developer extension with Information->view Javascript). There is also a way to set custom styles in the "styles" menu using JavaScript.

FCKeditor provides a tool to use templates. The template configuration file that FastMail loads seems to be the one that came with the default distribution that has three quite generic sample templates. Configuring custom templates is quite simple and they can be very powerful if customized for specific email use (perhaps they can be used to create HTML signatures?)

FCKeditor can use plugins loaded from any folder (the guide says that the plugins must be placed in the same folder where the FCKeditor files are stored, but then the guide also says how to load them from a different folder by merely providing that folder's location so I guess this means we can load plugins from our file storage directories). One can find plugins on the plugins page in SourceForge. The guide has some instructions on how to write plugins.

FCKeditor's JavaScript API documentation provides information on how to interact with the editor using JavaScript (from a plugin or directly from Javascript running in the web page).

Notes Screen Edit

Move "Create New Note" button to the top of the page Edit

This code adds the "Create New Note" next to the "Save Note" button on the top.

Add the following code to custom-ui/js/tweaks.js

window.addEvent('domready', function () {
   if ($('notepad')) {
      ($$('li.listButton')[0]).inject($$('.collapseContainer')[0],'top');
   }
});

Prevent Notepad from expanding Edit

This code prevents the notepad from growing over 450px

Add the following code to custom-ui/js/tweaks.js

//Limit Notepad height
window.addEvent('domready',function() {
    if($('notepad')) {
        $$('textarea')[0].setStyle('overflow','scroll').setStyle('height',450);
        setTimeout(function() {
            $$('.textAreaGhost')[0].dispose();
        }, 0);
    }
});

Create Encrypt/Decrypt buttons Edit

For storing semi-sensitive things, passwords, etc. This tweak adds client-side encryption and decryption for notes. Text can be written and encrypted client-side (using a password), and then saved encrypted on Fastmail's servers. Later the encrypted note is fetched, decrypted (using the same password), and can be read.

History: In 2010 the original AES code at the link was changed to be incompatible with the original tweak, and the original author of this tweak is no longer around. The code below was created to work with the 2012 AES code, and is confirmed to work in both the AJAX and classic Fastmail interfaces as of March 16, 2013. Browsers tested include Apple iOS Safari (iPad) and these Windows 7 browsers: Firefox, Internet Explorer, Opera, Chrome, and Safari.

Detailed instructions to install this Notepad encryption/decryption tweak:

  • In the online Fastmail interface, click Files at the top of the screen.
  • If you don't have it already, create the top-level folder named exactly (including case): {My Files}/custom-ui
    • To create the new folder, first click {My Files} in the folders list at the left
    • Then open (click) the More actions tool at the top and choose Create subfolder
    • In the new entry box (after called), enter custom-ui then click Do
  • If you don't have them already, create subfolders of that folder named exactly (including case): {My Files}/custom-ui/css and {My Files}/custom-ui/js. The changes below don't use the css folder, but that will be where you place any tweaks.css file you might create.
    • To create these new subfolders, click custom-ui in the folder list at the left
    • Then create the css and js subfolders in a similar manner to the earlier instructions
  • Copy the AES javascript from this site to your computer, then upload it and name it exactly aes.js to the {My Files}/custom-ui/js folder. I did this by copying/pasting into Windows Notepad on my PC, then saving the file on my PC, then uploading it and changing the name during the upload process. You could also upload any small text file to that Fastmail Files folder, then click the (details) link for that file to edit it, then paste the javascript, then save your edit.
  • In a similar fashion, create the tweaks.js file in the {My Files}/custom-ui/js folder using the following code:
/* Notepad AES encryption -- Bill n5bb 2011.05.22 */
/* http://www.emaildiscussions.com/showpost.php?p=523682&postcount=19 */
/* http://www.emaildiscussions.com/showthread.php?p=553618#post553618 */
function twkLoadScript(filename){  // function to load additional javascript file from custom-ui/js/
   window.addEvent('domready',function(){
       new Asset.javascript($$('script[src*=tweaks.js]')[0].src.replace("tweaks.js", filename));
})}
var password = new String;
window.addEvent('domready', function() {
  if (document.body.id != 'notepad') return;
  twkLoadScript("aes.js");    // Load aes.js encryption/decryption code from local folder
  var textarea = $E('textarea');
  new Element('button', {
    html: '<div>Encrypt</div>',
    type: 'button',
    style: 'margin-top:6px',
    class: 'actionSpell',
    events: {
      click: function() {
        password = window.prompt('Enter passphrase', password);
        textarea.value = Aes.Ctr.encrypt(textarea.value, password, 256);
      }
    }
  }).inject($E('div.actions'));
  new Element('button', {
    html: '<div>Decrypt</div>',
    type: 'button',
    style: 'margin-top:6px',
    class: 'actionSpell',
    events: {
      click: function() {
        password = window.prompt('Enter passphrase', password);
        textarea.value = Aes.Ctr.decrypt(textarea.value, password, 256);
      }
    }
  }).inject($E('div.actions'));
});
  • Click on (details) for both the aes.js and tweaks.js files and if needed change the Mime Type to either Auto (application/javascript) or manually to * application/javascript.
  • In the Options>Account Preferences screen, check the Custom styles box in the Display section. This will enable both tweaks.css in your {My Files}/custom-ui/css folder and tweaks.js in your {My Files}/custom-ui/js folder to be automatically loaded and executed.

After all of these changes, in your Notepad screen you should see that new Encrypt and Decrypt buttons have been added at the top. Here is how to use these new features in the Notepad:

  • Anytime you see plain text in the Notepad editing area (when reading an existing note or when creating a new note) the Encrypt button will bring up a popup window asking for a passphrase. Enter any string of letters, numbers, spaces, and symbols, then click OK.
    • All text in that particular note (excluding the Title) will then be immediately encrypted using the passphrase you entered.
    • But the existing note is not changed unless you click the Save note button. This will save the encrypted string, which is longer than the original string.
    • If you wish, you can encrypt the encrypted text multiple times, and later decrypt it multiple times back to the original.
  • While viewing an encrypted note, the Decrypt button will bring up a popup window asking for a passphrase. Enter any string of letters, numbers, spaces, and symbols, then click OK.
    • The original note contents will be visible.
    • But the encrypted note is not changed unless you click the Save note button. This will save the unencrypted (original) string back as a plaintext note.
  • You can copy/paste from/to the note by clicking inside the note text body and using CTL-A to select all text, CTL-C to copy, CTL-X to cut and copy, and CTL-V to paste.
  • If you lose the passphrase, there is no way to recover your original note. This is AES encryption and is very secure.

Files Screen Edit

Add a Rich Text Editor to the details screen Edit

This code adds a Rich Text Editor to the details screen when you edit a html file.

Add the following code to custom-ui/js/tweaks.js

window.addEvent('domready', function() {
    var detailsEditor = $$('textarea[name=FFP-Contents]')[0];
    if(detailsEditor && $$('option[selected]')[0].value == "text/html")
    {
        detailsEditor.set('id','FFP-Contents');
        new Asset.javascript('https://www.fastmail.fm/3pp/fck-latest/fckeditor.js');
        setTimeout(function() {
            var oFCKeditor = new FCKeditor('FFP-Contents') ;
            oFCKeditor.BasePath = "/3pp/fck-latest/";
            oFCKeditor.ReplaceTextarea();
        }, 200);
    }
});

Requests Edit

Below you can put request for javascript tweaks that you think are feasable for javascript tweaking, but for which you don't have the knowledge or time to do it yourself. Perhaps somebody reading this will implement it someday and post the solution above...

  • Tweak FCKeditor - Tweaking more internal things like the list of characters in the "insert character" button, lists of fonts/styles and custom templates. Also adding plugins from the file storage should be possible.

  • Focus on custom "From" field (input #messageFrom) when the Compose screen loads with that field (helps not to forget to edit the sending address). Also: Trigger change to "custom from" when some personalities are selected ("template personalities" such as those used with subdomain addresses where some part of the address needs to be edited).


  • Idea: the 'quote of day' tweak might do something on the line of what's being decribed here.

  • From the NewInterfaceBugsWontFix section. Detail info of files in File Storage is unavailable when using long filenames in the name column. The next column will "cover" the details command. Can we put the detail info anywhere else or change columns positions? I don't know if this is fixable with a tweak. Rob M. said he can't/won't do it. Rob - I don't really want to add a separate column, and it's not possible to make this work with CSS for arbitrary width nicely, so I'm going to leave as is for now. Make your browser window wider or filename shorter
    • To make this more concrete: one solution might be to move the "details" link to the left of the filename (by switching the positions of the <a> and <small> nodes inside <td class="fileODD name"> node. A more elegant tweak would be to mimick the functionality from the mailbox of what happens when the sender's name/address is clicked (it opens a menu with options). An ugly solution: use <marquee>.

Around Wikia's network

Random Wiki