// ==UserScript==
// @name GMail Align to Right Right-To-Left Scripts (languages)
// @namespace grease1 DOT daniboy AT antichef DOT com
// @description Aligns to the right those messages with Right-To-Left written scripts (Such as Hebrew and Arabic)
// @include http://mail.google.com/mail/*
// @include https://mail.google.com/mail/*
// @date 2006-04-19
// @version 0.1
// @GM_version 0.6.4
// ==/UserScript==
/* The actual main function begins in line 105, if you really want to follow :) */
const DELAYED_MAX_EXECUTION_TIMES = 250;
// Creates the Direction Changer line at the top of the message
// if dir is true, the message is aligned to the right. Clicking the Direction Changer will align it to the left
function addDirectionChanger(node, dir) {
var newDirectionChanger = document.createElement('span');
newDirectionChanger.appendChild(document.createTextNode('- Align this message to ' + (dir ? 'Left -' : 'Right -')));
newDirectionChanger.className = 'directionChanger';
newDirectionChanger.style.color = 'rgb(92, 110, 252)';
newDirectionChanger.style.fontSize = '9px';
newDirectionChanger.style.cursor = 'pointer';
// Attaches the Event Listener to dispatch when clicked, according to the dir
// --> function clickedLTRDirectionChanger(); or clickedRTLDirectionChanger();
newDirectionChanger.addEventListener('mousedown', dir ? clickedLTRDirectionChanger : clickedRTLDirectionChanger, false);
// Appends the new Direction Changer before the text's node
node.parentNode.insertBefore(newDirectionChanger, node);
}
// This function will change the direction of the text, and replace the Direction Changer line its opposite
// if dir is true, the message will be aligned to the right
function clickedDirectionChanger(node, dir) {
node.style.direction = dir ? 'RTL' : 'LTR';
node.parentNode.removeChild(node.previousSibling);
addDirectionChanger(node, dir);
}
// This function is attached to opening messages. It checks if the message has loaded by looking the length of the text
// If the length is zero, the message is assumed as not loaded, and the function calls itself after 10ms,
// up to a preset number of times (as to not cause a javascript-infinite-loop-alert).
// If the length is > 0, the checkForRTL(node); function is called
function delayedCheckForRTL(msgNum, timesRan) {
var node = window.document.getElementById('mb_' + msgNum);
if (node.textContent.length > 0) {
checkForRTL(node);
} else if (timesRan < DELAYED_MAX_EXECUTION_TIMES) {
window.setTimeout(delayedCheckForRTL, 10, msgNum, timesRan+1);
}
}
// The purpose of this user script - this function checks if the message contains RTL language scripts
// This is done by checking that the message has a "word" of at least 3 "letters" (can also be punctuation) of these scripts
function checkForRTL(node) {
var regexpRTL = /[\u0590-\u07bf]{3,}/;
/* Unicode Sub-SubRanges explained. Kinda.
\u0590-\u05ff - Hebrew Characters
\u0600-\u06ff - Arabic Characters
\u0700-\u074f - Syriac Characters
\u0750-\u077f - Arabic Supplement (Any idea what's here?)
\u0780-\u07bf - Thaana Characters (RTL according to wikipedia. True?) */
if (regexpRTL.test(node.textContent)) {
// If the text contains a word in RTL language scripts, change the message's direction to RTL
// and add a Direction Changer line (remove if one already exists. Needed because there are many ways to open messages)
node.style.direction = 'RTL';
var existingDirectionChanger = node.previousSibling;
if (existingDirectionChanger) {
existingDirectionChanger.parentNode.removeChild(existingDirectionChanger);
}
addDirectionChanger(node, true);
}
}
// This function checks if the Right control pane (New Window, Print and Expand All) is loaded.
// If it isn't, the function calls itself after 10ms, up to a preset number of times (as to not cause a javascript-infinite-loop-alert).
// If the pane is loaded, check that there is an "Expand All" button (there might not be, if this is a single-message conversation)
// and attach an Event Listener to it
// --> function expandAllChecks();
function delayedCheckForExpandAll(timesRan) {
var controlsNode = window.document.getElementById('ap');
if (controlsNode.childNodes.length > 0) {
var expandAllContainer = window.document.getElementById('ec');
if (expandAllContainer) {
expandAllContainer.addEventListener('mousedown', expandAllChecks, false);
}
} else if (timesRan < DELAYED_MAX_EXECUTION_TIMES) {
window.setTimeout(delayedCheckForExpandAll, 10, timesRan+1);
}
}
// Main run
(function() {
// First check that we are in the conversation view by finding the id='msgs' node
var divMsgs = window.document.getElementById('msgs');
if (divMsgs) {
var moreMessages = true;
var messageNumber = 0;
var messageNode;
// Pass through every message in the conversation to check if it is open (textContent length > 0)
// This is needed because a Starred message is open by default
do {
// The messages' text is under an id='mb_0', id='mb_1', id='mb_2' (etc.) node, but not directly under
messageNode = window.document.getElementById('mb_' + messageNumber);
// Check that there is such a node as mb_i (where i is a number)
if (messageNode) {
if (messageNode.textContent.length > 0) {
// If the message has text, check for RTL language scripts
checkForRTL(messageNode);
} else {
// If the message doesn't have text, it is a closed message
// Attach an Event Listener to check when it is being opened
// --> function newMessageOpened();
messageNode.parentNode.parentNode.addEventListener('mousedown', newMessageOpened, false);
}
// If there is no such node as mb_i, exit the loop
} else {
moreMessages = false;
}
messageNumber++;
} while (moreMessages);
// This line will eventually attach an Event Listener to the "Expand All" button
// This is time delayed because GMail might take time to load the pane with this button
// --> function delayedCheckForExpandAll(timesRan);
window.setTimeout(delayedCheckForExpandAll, 150, 0);
}
})();
// The various Event Listeners are here
// The Event Listener dispatched when the user clicks to change the message's direction to left
function clickedLTRDirectionChanger(e) {
// Call the function to handle clicking on direction changers
// --> function clickedDirectionChanger(node, dir);
clickedDirectionChanger(e.target.nextSibling, false);
}
// The Event Listener dispatched when the user clicks to change the message's direction to the right
function clickedRTLDirectionChanger(e) {
// Call the function to handle clicking on direction changers
// --> function clickedDirectionChanger(node, dir);
clickedDirectionChanger(e.target.nextSibling, true);
}
// The Event Listener dispatched when the user opens a single closed message
function newMessageOpened(e) {
// The ancestor node to check for the message's number was found after a lot of testings ;)
var node = e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode;
var regexpMatchMessageNumber = /mh_(\d+)/;
var messageNumber = regexpMatchMessageNumber.exec(node.getAttribute('id'))[1];
// This line will eventually check for the message's RTL status
// This is time delayed bacause GMail will probably take a few moments to load the message
// --> function delayedCheckForRTL(messageNumber, timesRan);
window.setTimeout(delayedCheckForRTL, 150, messageNumber, 0);
}
// The Event Listener dispatched when the user clicks the "Expand All" button
function expandAllChecks() {
var moreMessages = true;
var messageNumber = 0;
var messageNode;
// Traverse all messages, and call the
do {
// The messages' text is under an id='mb_0', id='mb_1', id='mb_2' (etc.) node, but not directly under
messageNode = window.document.getElementById('mb_' + messageNumber);
// Check that there is such a node as mb_i (where i is a number)
if (messageNode) {
// Calls the delayedCheckForRTL(messageNumber, timesRan); function on each message
delayedCheckForRTL(messageNumber, 0);
// If there is no such node as mb_i, exit the loop
} else {
moreMessages = false;
}
messageNumber++;
} while (moreMessages);
}