GMail Align to Right Right-To-Left Scripts (languages)

By daniel Rozenberg Last update Apr 18, 2006 — Installed 2,711 times. Daily Installs: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0
// ==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);
    
}