<commit_before>/*
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
*/

//-----------------------------------------------------------------------------
// communication from extension settings to here
function extEditorSettingsObserver() {
    this.id = "extEditorSettingsObserver";
    this.service = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
    this.register();
}
extEditorSettingsObserver.prototype = {
    observe: function (subject, topic, prefString) {

        var data = AFreadPref(prefString);
        switch (prefString) {
            case 'exteditor.default.editor': {
                initButton(data);
                prefNotifierExe = data;
                break;
            }
            case 'exteditor.editor.unicode': prefEditorUnicode = data; break;
            case 'exteditor.editor.83filename': prefEditor83Filename = data; break;
            case 'exteditor.headers.edit': prefEditHeaders[exteditor_GLOBAL] = data; break;
            case 'exteditor.headers.edit.subject': prefEditHeaders[exteditor_SUBJECT] = data; break;
            case 'exteditor.headers.edit.to': prefEditHeaders[exteditor_TO] = data; break;
            case 'exteditor.headers.edit.cc': prefEditHeaders[exteditor_CC] = data; break;
            case 'exteditor.headers.edit.bcc': prefEditHeaders[exteditor_BCC] = data; break;
            case 'exteditor.headers.edit.replyto': prefEditHeaders[exteditor_REPLY_TO] = data; break;
            case 'exteditor.headers.edit.newsgroup': prefEditHeaders[exteditor_NEWSGROUP] = data; break;
            default: extEditorError(getLocaleString("UnexpectedPrefString") + ": " + prefString); break;
        }
    },
    register: function () {
        this.service.addObserver(this, this.id, false);
    },
    unregister: function () {
        this.service.removeObserver(this, this.id);
    }
}

//-----------------------------------------------------------------------------
// communication from the asynchronous editor process to update the TB editor window once the external editor returned
function extEditorObserver() {
    this.id = "extEditorObserver";
    this.service = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
    this.register();
}
extEditorObserver.prototype = {
    observe: function (subject, topic, prefString) {
        updateEditor();
    },
    register: function () {
        this.service.addObserver(this, this.id, false);
    },
    unregister: function () {
        this.service.removeObserver(this, this.id);
    }
}

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

var settingsObserver;
var dirSeparator;
var pathSeparator;
var osType;
var newLine;
var prefNotifierExe;
var prefEditHeaders = new Array;     // hash[headername] = bool
var headersEnd; // to be initialized in initExteditor() with os specific newline
var prefEditorUnicode;  // bool
var prefEditor83Filename;  // bool
var file;
var isEditorDisabled = false;

const exteditor_GLOBAL = "Global";  // just to store the global headers preference
var exteditor_SUBJECT = getLocaleString("Subject");
var exteditor_TO = getLocaleString("To");
var exteditor_CC = getLocaleString("Cc");
var exteditor_BCC = getLocaleString("Bcc");
var exteditor_REPLY_TO = getLocaleString("Reply-To");
var exteditor_NEWSGROUP = getLocaleString("Newsgroup");

function launchExtEditor() {

    // doAction(); return; // debug only, to be removed

    if (prefNotifierExe == "") {
        extEditorError(getLocaleString("PleaseFirstDefineYourEditor"));
        return;
    }

    var subject = document.getElementById('msgSubject').value;
    var content = "";

    if (prefEditHeaders[exteditor_GLOBAL]) {
        // Get the headers
        var msgCompFields = gMsgCompose.compFields;
        Recipients2CompFields(msgCompFields);

        if (prefEditHeaders[exteditor_SUBJECT]) content += exteditor_SUBJECT + ":   " + subject + newLine;
        if (prefEditHeaders[exteditor_TO]) content += exteditor_TO + ":        " + msgCompFields.to + newLine;
        if (prefEditHeaders[exteditor_CC]) content += exteditor_CC + ":        " + msgCompFields.cc + newLine;
        if (prefEditHeaders[exteditor_BCC]) content += exteditor_BCC + ":       " + msgCompFields.bcc + newLine;
        if (prefEditHeaders[exteditor_REPLY_TO]) content += exteditor_REPLY_TO + ":  " + msgCompFields.replyTo + newLine;
        if (prefEditHeaders[exteditor_NEWSGROUP]) content += exteditor_NEWSGROUP + ": " + msgCompFields.newsgroups + newLine;
        content += headersEnd;
    }

    if (isEditAsHtml()) {
        // params in http://mxr.mozilla.org/aviarybranch/source/content/base/public/nsIDocumentEncoder.h  (old)
        //    or in  http://mxr.mozilla.org/comm-central/source/mozilla/content/base/public/nsIDocumentEncoder.idl
        content += GetCurrentEditor().outputToString("text/html", 2);
    } else {
        content += GetCurrentEditor().outputToString("text/plain", 0);
    }

    setEditorDisabled(true);
    file = tmpFilename(subject, prefEditor83Filename);
    extEditorWriteFile(content, prefEditorUnicode, file);

    var params = new Array(file);
    editorObserver = new extEditorObserver();
    const launched = extEditorRunProgram(prefNotifierExe, params, editorObserver); // non blocking call

    if (!launched) {
        setEditorDisabled(false);
    }
}


//-----------------------------------------------------------------------------
function updateEditor() {
    var content = extEditorReadFile(file, prefEditorUnicode);
    extEditorDeleteFile(file);

    if (content == null) {
        setEditorDisabled(false);
        return;
    }

    try {
        var list = content.split(headersEnd);
        var messageText;

        if (prefEditHeaders[exteditor_GLOBAL]) {
            // if headers edition is activated
            if (list.length == 1) {
                messageText = content;
            } else {
                messageText = list[1];
                var headersLines = list[0].split(newLine);
                var headerHash = new Array;
                var headerType = "unknown"; // should never be used
                for (var i = 0; i < headersLines.length; i++) {
                    var whichHeader = headersLines[i].split(":");
                    if (whichHeader.length >= 2) {
                        headerType = whichHeader.shift().replace(/\s+/g, "").toLowerCase();
                        // if the subject contains ":", the array has more than 2 members...
                        var headerContent = whichHeader.join(":").replace(/^\s+/, "");
                        if (headerHash[headerType] === undefined) {
                            headerHash[headerType] = headerContent;
                        } else {
                            headerHash[headerType] += "," + headerContent;
                        }
                    } else {
                        // if not only spaces or empty line
                        if (/\w/.test(headersLines[i])) {
                            headerHash[headerType] += "," + headersLines[i];
                        }
                    }
                }

                var subject = headerHash[exteditor_SUBJECT.toLowerCase()];
                if (subject !== undefined && subject !== GetMsgSubjectElement().value) {
                    document.getElementById('msgSubject').value = subject;
                    gMsgCompose.compFields.subject = subject;
                    SetComposeWindowTitle();
                }

                var msgCompFields = gMsgCompose.compFields;

                Recipients2CompFields(msgCompFields);

                if (prefEditHeaders[exteditor_TO]) msgCompFields.to = headerHash[exteditor_TO.toLowerCase()];
                if (prefEditHeaders[exteditor_CC]) msgCompFields.cc = headerHash[exteditor_CC.toLowerCase()];
                if (prefEditHeaders[exteditor_BCC]) msgCompFields.bcc = headerHash[exteditor_BCC.toLowerCase()];
                if (prefEditHeaders[exteditor_REPLY_TO]) msgCompFields.replyTo = headerHash[exteditor_REPLY_TO.toLowerCase()];
                if (prefEditHeaders[exteditor_NEWSGROUP]) msgCompFields.newsgroups = headerHash[exteditor_NEWSGROUP.toLowerCase()];

                extEditorCompFields2Recipients(msgCompFields);
            }
        } else {
            // No headers edition here
            messageText = content;
        }
        // Replace \r\n by \n
        if (osType == "win") {
            messageText = messageText.replace(/\r\n/g, "\n");
        }
    } catch (e) {
        // A message could be displayed her, but I don't wan't to bother with localizations...
        console.log("exteditor.updateEditor()", e)
    } finally {
        setEditorDisabled(false);
    }

    var editor = GetCurrentEditor();

    if (isEditAsHtml()) {
        editor.rebuildDocumentFromSource("");   // is there a Clear() method somewhere ?
        editor.insertHTML(messageText);
    } else {
        // Don't use rebuildDocumentFromSource() here: it turns the editor in a
        // html mode in  which multiple spaces disapear.
        var wholeDocRange = editor.document.createRange();
        var rootNode = editor.rootElement;
        wholeDocRange.selectNodeContents(rootNode);
        editor.selection.addRange(wholeDocRange);
        try {
            editor.selection.deleteFromDocument();
        } catch (e) {
            // The selection did not exist yet. Everything should be fine
        }
        if (messageText) {
            editor.insertText(messageText);
        }
    }
}

function extEditorCompFields2Recipients(msgCompFields) {
    // Copied and tweaked from addressingWidgetOverlay.js to keep functionalities as of Thunderbird 60

    let listbox = document.getElementById("addressingWidget");
    let newListBoxNode = listbox.cloneNode(false);
    let listBoxColsClone = _menulistFriendlyClone(listbox.itemChildren[0]);
    newListBoxNode.appendChild(listBoxColsClone);
    let templateNode = _menulistFriendlyClone(listbox.itemChildren[0]);
    listbox.parentNode.replaceChild(newListBoxNode, listbox);

    top.MAX_RECIPIENTS = 0;
    let msgReplyTo = msgCompFields.replyTo;
    let msgTo = msgCompFields.to;
    let msgCC = msgCompFields.cc;
    let msgBCC = msgCompFields.bcc;
    let msgNewsgroups = msgCompFields.newsgroups;
    let msgFollowupTo = msgCompFields.followupTo;
    let havePrimaryRecipient = false;
    if (msgReplyTo) {
        awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgReplyTo, false, {}),
            "addr_reply", newListBoxNode, templateNode);
    }
    if (msgTo) {
        let rcp = msgCompFields.splitRecipients(msgTo, false, {});
        if (rcp.length) {
            awSetInputAndPopupFromArray(rcp, "addr_to", newListBoxNode, templateNode);
            havePrimaryRecipient = true;
        }
    }
    if (msgCC) {
        awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgCC, false, {}),
            "addr_cc", newListBoxNode, templateNode);
    }
    if (msgBCC) {
        awSetInputAndPopupFromArray(msgCompFields.splitRecipients(msgBCC, false, {}),
            "addr_bcc", newListBoxNode, templateNode);
    }
    if (msgNewsgroups) {
        awSetInputAndPopup(msgNewsgroups, "addr_newsgroups", newListBoxNode, templateNode);
        havePrimaryRecipient = true;
    }
    if (msgFollowupTo) {
        awSetInputAndPopup(msgFollowupTo, "addr_followup", newListBoxNode, templateNode);
    }
    // If it's a new message, we need to add an extra empty recipient.
    if (!havePrimaryRecipient) {
        _awSetInputAndPopup("", "addr_to", newListBoxNode, templateNode);
    }

    // remove the kept first dummy row
    awRemoveRow(1);
    // spellcheck configuration on the original function is omitted since it's not purpose here
}

//-----------------------------------------------------------------------------<commit_msg>Try another method to update address fields<commit_after>/*
This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0.
If a copy of the MPL was not distributed with this file,
You can obtain one at https://mozilla.org/MPL/2.0/.
*/

//-----------------------------------------------------------------------------
// communication from extension settings to here
function extEditorSettingsObserver() {
    this.id = "extEditorSettingsObserver";
    this.service = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
    this.register();
}
extEditorSettingsObserver.prototype = {
    observe: function (subject, topic, prefString) {

        var data = AFreadPref(prefString);
        switch (prefString) {
            case 'exteditor.default.editor': {
                initButton(data);
                prefNotifierExe = data;
                break;
            }
            case 'exteditor.editor.unicode': prefEditorUnicode = data; break;
            case 'exteditor.editor.83filename': prefEditor83Filename = data; break;
            case 'exteditor.headers.edit': prefEditHeaders[exteditor_GLOBAL] = data; break;
            case 'exteditor.headers.edit.subject': prefEditHeaders[exteditor_SUBJECT] = data; break;
            case 'exteditor.headers.edit.to': prefEditHeaders[exteditor_TO] = data; break;
            case 'exteditor.headers.edit.cc': prefEditHeaders[exteditor_CC] = data; break;
            case 'exteditor.headers.edit.bcc': prefEditHeaders[exteditor_BCC] = data; break;
            case 'exteditor.headers.edit.replyto': prefEditHeaders[exteditor_REPLY_TO] = data; break;
            case 'exteditor.headers.edit.newsgroup': prefEditHeaders[exteditor_NEWSGROUP] = data; break;
            default: extEditorError(getLocaleString("UnexpectedPrefString") + ": " + prefString); break;
        }
    },
    register: function () {
        this.service.addObserver(this, this.id, false);
    },
    unregister: function () {
        this.service.removeObserver(this, this.id);
    }
}

//-----------------------------------------------------------------------------
// communication from the asynchronous editor process to update the TB editor window once the external editor returned
function extEditorObserver() {
    this.id = "extEditorObserver";
    this.service = Components.classes["@mozilla.org/observer-service;1"].getService(Components.interfaces.nsIObserverService);
    this.register();
}
extEditorObserver.prototype = {
    observe: function (subject, topic, prefString) {
        updateEditor();
    },
    register: function () {
        this.service.addObserver(this, this.id, false);
    },
    unregister: function () {
        this.service.removeObserver(this, this.id);
    }
}

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

var settingsObserver;
var dirSeparator;
var pathSeparator;
var osType;
var newLine;
var prefNotifierExe;
var prefEditHeaders = new Array;     // hash[headername] = bool
var headersEnd; // to be initialized in initExteditor() with os specific newline
var prefEditorUnicode;  // bool
var prefEditor83Filename;  // bool
var file;
var isEditorDisabled = false;

const exteditor_GLOBAL = "Global";  // just to store the global headers preference
var exteditor_SUBJECT = getLocaleString("Subject");
var exteditor_TO = getLocaleString("To");
var exteditor_CC = getLocaleString("Cc");
var exteditor_BCC = getLocaleString("Bcc");
var exteditor_REPLY_TO = getLocaleString("Reply-To");
var exteditor_NEWSGROUP = getLocaleString("Newsgroup");

function launchExtEditor() {

    // doAction(); return; // debug only, to be removed

    if (prefNotifierExe == "") {
        extEditorError(getLocaleString("PleaseFirstDefineYourEditor"));
        return;
    }

    var subject = document.getElementById('msgSubject').value;
    var content = "";

    if (prefEditHeaders[exteditor_GLOBAL]) {
        // Get the headers
        var msgCompFields = gMsgCompose.compFields;
        Recipients2CompFields(msgCompFields);

        if (prefEditHeaders[exteditor_SUBJECT]) content += exteditor_SUBJECT + ":   " + subject + newLine;
        if (prefEditHeaders[exteditor_TO]) content += exteditor_TO + ":        " + msgCompFields.to + newLine;
        if (prefEditHeaders[exteditor_CC]) content += exteditor_CC + ":        " + msgCompFields.cc + newLine;
        if (prefEditHeaders[exteditor_BCC]) content += exteditor_BCC + ":       " + msgCompFields.bcc + newLine;
        if (prefEditHeaders[exteditor_REPLY_TO]) content += exteditor_REPLY_TO + ":  " + msgCompFields.replyTo + newLine;
        if (prefEditHeaders[exteditor_NEWSGROUP]) content += exteditor_NEWSGROUP + ": " + msgCompFields.newsgroups + newLine;
        content += headersEnd;
    }

    if (isEditAsHtml()) {
        // params in http://mxr.mozilla.org/aviarybranch/source/content/base/public/nsIDocumentEncoder.h  (old)
        //    or in  http://mxr.mozilla.org/comm-central/source/mozilla/content/base/public/nsIDocumentEncoder.idl
        content += GetCurrentEditor().outputToString("text/html", 2);
    } else {
        content += GetCurrentEditor().outputToString("text/plain", 0);
    }

    setEditorDisabled(true);
    file = tmpFilename(subject, prefEditor83Filename);
    extEditorWriteFile(content, prefEditorUnicode, file);

    var params = new Array(file);
    editorObserver = new extEditorObserver();
    const launched = extEditorRunProgram(prefNotifierExe, params, editorObserver); // non blocking call

    if (!launched) {
        setEditorDisabled(false);
    }
}


//-----------------------------------------------------------------------------
function updateEditor() {
    var content = extEditorReadFile(file, prefEditorUnicode);
    extEditorDeleteFile(file);

    if (content == null) {
        setEditorDisabled(false);
        return;
    }

    try {
        var list = content.split(headersEnd);
        var messageText;

        if (prefEditHeaders[exteditor_GLOBAL]) {
            // if headers edition is activated
            if (list.length == 1) {
                messageText = content;
            } else {
                messageText = list[1];
                var headersLines = list[0].split(newLine);
                var headerHash = new Array;
                var headerType = "unknown"; // should never be used
                for (var i = 0; i < headersLines.length; i++) {
                    var whichHeader = headersLines[i].split(":");
                    if (whichHeader.length >= 2) {
                        headerType = whichHeader.shift().replace(/\s+/g, "").toLowerCase();
                        // if the subject contains ":", the array has more than 2 members...
                        var headerContent = whichHeader.join(":").replace(/^\s+/, "");
                        if (headerHash[headerType] === undefined) {
                            headerHash[headerType] = headerContent;
                        } else {
                            headerHash[headerType] += "," + headerContent;
                        }
                    } else {
                        // if not only spaces or empty line
                        if (/\w/.test(headersLines[i])) {
                            headerHash[headerType] += "," + headersLines[i];
                        }
                    }
                }

                var subject = headerHash[exteditor_SUBJECT.toLowerCase()];
                if (subject !== undefined && subject !== GetMsgSubjectElement().value) {
                    GetMsgSubjectElement().value = subject;
                    SetComposeWindowTitle();
                }

                var msgCompFields = gMsgCompose.compFields;

                Recipients2CompFields(msgCompFields);

                let knownTypes = new Set();
                let hasChange = true;
                exteditorEachAddressType((recipientType, compFieldKey, headerKey) => {
                    knownTypes.add(recipientType);

                    if (headerKey && !hasChange) {
                        let edited = msgCompFields.splitRecipients(headerHash[headerKey], false, {}).join(",");
                        let original = msgCompFields.splitRecipients(msgCompFields[compFieldKey], false, {}).join(",");

                        hasChange = edited !== original;
                    }
                })

                // Rewrite all fields if any of the field is changed
                if (hasChange) {
                    exteditorClearRecipientOfType(knownTypes);
                    awCleanupRows();

                    exteditorEachAddressType((recipientType, compFieldKey, headerKey) => {
                        let addresses = headerKey ? headerHash[headerKey] : msgCompFields[compFieldKey];
                        awAddRecipients(msgCompFields, recipientType, addresses);
                    });

                    awCleanupRows();
                }
            }
        } else {
            // No headers edition here
            messageText = content;
        }
        // Replace \r\n by \n
        if (osType == "win") {
            messageText = messageText.replace(/\r\n/g, "\n");
        }
    } catch (e) {
        // A message could be displayed her, but I don't wan't to bother with localizations...
        console.log("exteditor.updateEditor()", e)
    } finally {
        setEditorDisabled(false);
    }

    var editor = GetCurrentEditor();

    if (isEditAsHtml()) {
        editor.rebuildDocumentFromSource("");   // is there a Clear() method somewhere ?
        editor.insertHTML(messageText);
    } else {
        // Don't use rebuildDocumentFromSource() here: it turns the editor in a
        // html mode in  which multiple spaces disapear.
        var wholeDocRange = editor.document.createRange();
        var rootNode = editor.rootElement;
        wholeDocRange.selectNodeContents(rootNode);
        editor.selection.addRange(wholeDocRange);
        try {
            editor.selection.deleteFromDocument();
        } catch (e) {
            // The selection did not exist yet. Everything should be fine
        }
        if (messageText) {
            editor.insertText(messageText);
        }
    }
}

/**
 * Enumerates known address field types along with corresponding keys.
 *
 * @param cb {(recipientType: string, compFieldKey: string, headerKey?: string) => void}
 */
function exteditorEachAddressType(cb) {
    cb("addr_to", "to", prefEditHeaders[exteditor_TO] ? exteditor_TO.toLowerCase() : undefined);
    cb("addr_cc", "cc", prefEditHeaders[exteditor_CC] ? exteditor_CC.toLowerCase() : undefined);
    cb("addr_bcc", "bcc", prefEditHeaders[exteditor_BCC] ? exteditor_BCC.toLowerCase() : undefined);
    cb("addr_reply", "replyTo", prefEditHeaders[exteditor_REPLY_TO] ? exteditor_REPLY_TO.toLowerCase() : undefined);
    cb("addr_newsgroups", "newsgroups", prefEditHeaders[exteditor_NEWSGROUP] ? exteditor_NEWSGROUP.toLowerCase() : undefined);
}

/**
 * Clears address fields which have specified types.
 *
 * @param {Set<string>} recipientTypeSet
 */
function exteditorClearRecipientOfType(recipientTypeSet) {
    for (let row = 1, n = awGetNumberOfRecipients(); row <= n; row++) {
        let popup = awGetPopupElement(row);
        if (recipientTypeSet.has(popup.value)) {
            let input = awGetInputElement(row);
            awSetInputAndPopupValue(input, "", popup, "addr_to", -1);
        }
    }
}

//-----------------------------------------------------------------------------
{'fraction_non_alphanumeric': 0.13846934890214493, 'fraction_numerical': 0.002453780090536024, 'mean_word_length': 2.244749485243651, 'pattern_counts': {'":': 20, '<': 7, '<?xml version=': 0, '>': 9, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '745032', 'n_tokens_mistral': 6358, 'n_tokens_neox': 6233, 'n_words': 1384}