<commit_before>import { Shutdownable } from "./../lib/shutdownable.js";

/*
 * The main purpose of this is to allow to work on other windows besides compose, like
 * conversations compose extension.
 *
 *
 * Interface of any compose_window implementation:
 *
 *   // type: can be "TO", "CC" or "BCC"
 *   // returns an array with the recipients
 *   recipients( type )
 *
 *   // It registers the listeners for:
 *   //
 *   //    start and stop, when windows loses focus, etc.
 *   //
 *   //    for dictionary change
 *   //
 *   //    for recipients changed
 *   //
 *   setListeners( ad_object )
 *
 *   // Shows a message to the user by some seconds.
 *   setLabel( message )
 **/

/**
 *  params are:
 *    ad: automatic_dictionary instance
 *    name: plugin name
 *    logo_url: url of the plugin logo
 *    notification_time: time to show notifications in ms
 *
 **/
export const ComposeWindow = (function (params) {
  this.ad = params.ad;
  this.params = params;
  this.shutdown_chain = [];
  this.logger = params.logger;
  this.window = this.ad.window;
});

ComposeWindow.canManageWindow = function (window) {
  const regex = /chrome.*messengercompose\.(xul|xhtml)/
  return regex.test(window.document.location);
};

const waitAnd = function (fn) {
  setTimeout(fn, 800);
}

const getTabId = async function (self) {
  if (self.tabId) return self.tabId
  var window = await browser.windows.get(self.window.id, { populate: true });
  self.tabId = window.tabs[0].id;
  return self.tabId;
}

Object.assign(
  ComposeWindow.prototype,
  Shutdownable);
Object.assign(ComposeWindow.prototype, {

  name: "CompoWindow",
  logger: null,

  setListeners: function () {
    var _this = this;
    //capture language change event
    this.addListener(browser.compose_ext.onLanguageChange, async function (tabId, language) {
      try {
        if (tabId != await getTabId(_this)) {
          _this.logger.debug("Ignoring tab id, mine is " + (await getTabId(_this)));
          return;
        }
        _this.ad.languageChanged();
      } catch (e) {
        _this.logger.error(e);
      }
    });
    this.addListener(browser.compose_ext.onRecipientsChange, async function (tabId) {
      if (tabId != await getTabId(_this)) {
        _this.logger.debug("Ignoring tab id, mine is " + (await getTabId(_this)));
        return;
      }
      waitAnd(function () {
        _this.ad.deduceLanguage();
      });
    });

    this.addListener(browser.windows.onFocusChanged, function (windowId) {
      if (_this.window.id != windowId) {
        _this.logger.debug("Ignoring onFocusChanged because different windowId");
        return
      }

      waitAnd(function () {
        _this.ad.deduceLanguage();
      });
    });

    window.automatic_dictionary_initialized = true;
    this.shutdown_chain.push(function () {
      window.automatic_dictionary_initialized = false;
    });
    this.logger.debug("events registered");
    this.ad.addEventListener('shutdown', function () {
      _this.logger.debug("shutting down compose window");
      _this.shutdown();
    });
  },

  getCurrentLang: async function () {
    var lang = await browser.compose_ext.getCurrentLanguage(await getTabId(this));
    this.logger.debug("Current lang is " + lang);
    return lang;
  },

  recipients: async function (recipientType) {
    recipientType = recipientType || "to";
    var tabId = await getTabId(this);
    var details = await browser.compose.getComposeDetails(tabId);
    var recipients = details[recipientType];
    if (typeof (recipients) == "string") {
      // Cast to array
      recipients = [recipients]
    }
    // Array of String or ComposeRecipients
    if (recipients.length > 0 && typeof (recipients[0]) == "string") {
      // Normalize recipients only when its raw string.
      recipients = this.normalizeRecipients(recipients);
    } else {
      // ComposeRecipients
      recipients = await this.getEmailFromRecipients(recipients);
    }
    return recipients;
  },

  // Remove everything but the email because its the canonical part.
  normalizeRecipients: function (list) {
    var out = [];
    for (var i = 0, size = list.length; i < size; i++) {
      var item = list[i];
      try {
        out.push(this.extractEmails(item)[0]);
      } catch (e) {
        // Normalization could not find an email, lets use it as is.
        out.push(item);
      }
    }
    return out;
  },
  extractEmails: function (text) {
    return text.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi);
  },

  getEmailFromRecipients: async function (recipients) {
    var out = [];
    for (var i = 0, size = recipients.length; i < size; i++) {
      var recipient = recipients[i];
      switch (recipient.type) {
        case "contact":
          var contact = await browser.contacts.get(recipient.id);
          out.push(contact.properties.PrimaryEmail);
          break;
        case "mailingList":
          var list = await browser.mailingLists.get(recipient.id);
          // Mailing list does not have an email so we try to get its name.
          out.push(list.properties.name);
          break;
      }
    }
    return out;
  },
  changeLabel: async function (str) {
    this.logger.info("Changing label to: " + str);
    browser.compose_ext.showNotification(
      await getTabId(this),
      str,
      {
        logo_url: this.params.logo_url,
        notification_time: this.params.notification_time
      }
    );
  },
  changeLanguage: async function (lang) {
    browser.compose_ext.setSpellCheckerLanguage(await getTabId(this), lang);
  },
  canSpellCheck: async function () {
    return await browser.compose_ext.canSpellCheck(await getTabId(this));
  }
});<commit_msg>Make all ComposeWindow methods private as possible<commit_after>import { Shutdownable } from "./../lib/shutdownable.js";

/*
 * The main purpose is to allow to work on other windows besides compose, like
 * conversations compose extension.
 **/

/**
 *  params are:
 *    ad: automatic_dictionary instance
 *    name: plugin name
 *    logo_url: url of the plugin logo
 *    notification_time: time to show notifications in ms
 *
 **/
export const ComposeWindow = (function (params) {
  this.ad = params.ad;
  this.params = params;
  this.shutdown_chain = [];
  this.logger = params.logger;
  this.window = this.ad.window;
});

ComposeWindow.canManageWindow = function (window) {
  const regex = /chrome.*messengercompose\.(xul|xhtml)/
  return regex.test(window.document.location);
};

const waitAnd = function (fn) {
  setTimeout(fn, 800);
}

const getTabId = async function (self) {
  if (self.tabId) return self.tabId
  var window = await browser.windows.get(self.window.id, { populate: true });
  self.tabId = window.tabs[0].id;
  return self.tabId;
}

function normalizeRecipients(list) {
  var out = [];
  for (var i = 0, size = list.length; i < size; i++) {
    var item = list[i];
    try {
      out.push(extractEmail(item));
    } catch (e) {
      // Normalization could not find an email, lets use it as is.
      out.push(item);
    }
  }
  return out;
}

function extractEmail(text) {
  return text.match(/\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/gi)[0];
}

async function getEmailFromRecipients(recipients) {
  var out = [];
  for (var i = 0, size = recipients.length; i < size; i++) {
    var recipient = recipients[i];
    switch (recipient.type) {
      case "contact":
        var contact = await browser.contacts.get(recipient.id);
        out.push(contact.properties.PrimaryEmail);
        break;
      case "mailingList":
        var list = await browser.mailingLists.get(recipient.id);
        // Mailing list does not have an email so we try to get its name.
        out.push(list.properties.name);
        break;
    }
  }
  return out;
}

Object.assign(ComposeWindow.prototype, Shutdownable);

Object.assign(ComposeWindow.prototype, {

  name: "CompoWindow",
  logger: null,

  setListeners: function () {
    var _this = this;
    //capture language change event
    this.addListener(browser.compose_ext.onLanguageChange, async function (tabId, language) {
      try {
        if (tabId != await getTabId(_this)) {
          _this.logger.debug("Ignoring tab id, mine is " + (await getTabId(_this)));
          return;
        }
        _this.ad.languageChanged();
      } catch (e) {
        _this.logger.error(e);
      }
    });
    this.addListener(browser.compose_ext.onRecipientsChange, async function (tabId) {
      if (tabId != await getTabId(_this)) {
        _this.logger.debug("Ignoring tab id, mine is " + (await getTabId(_this)));
        return;
      }
      waitAnd(function () {
        _this.ad.deduceLanguage();
      });
    });

    this.addListener(browser.windows.onFocusChanged, function (windowId) {
      if (_this.window.id != windowId) {
        _this.logger.debug("Ignoring onFocusChanged because different windowId");
        return
      }

      waitAnd(function () {
        _this.ad.deduceLanguage();
      });
    });

    window.automatic_dictionary_initialized = true;
    this.shutdown_chain.push(function () {
      window.automatic_dictionary_initialized = false;
    });
    this.logger.debug("events registered");
    this.ad.addEventListener('shutdown', function () {
      _this.logger.debug("shutting down compose window");
      _this.shutdown();
    });
  },

  getCurrentLang: async function () {
    var lang = await browser.compose_ext.getCurrentLanguage(await getTabId(this));
    this.logger.debug("Current lang is " + lang);
    return lang;
  },

  recipients: async function (recipientType) {
    recipientType = recipientType || "to";
    var tabId = await getTabId(this);
    var details = await browser.compose.getComposeDetails(tabId);
    var recipients = details[recipientType];
    if (typeof (recipients) == "string") {
      // Cast to array
      recipients = [recipients]
    }
    // Array of String or ComposeRecipients
    if (recipients.length > 0 && typeof (recipients[0]) == "string") {
      // Normalize recipients only when its raw string.
      recipients = normalizeRecipients(recipients);
    } else {
      // ComposeRecipients
      recipients = await getEmailFromRecipients(recipients);
    }
    return recipients;
  },

  changeLabel: async function (str) {
    this.logger.info("Changing label to: " + str);
    browser.compose_ext.showNotification(
      await getTabId(this),
      str,
      {
        logo_url: this.params.logo_url,
        notification_time: this.params.notification_time
      }
    );
  },
  changeLanguage: async function (lang) {
    browser.compose_ext.setSpellCheckerLanguage(await getTabId(this), lang);
  },
  canSpellCheck: async function () {
    return await browser.compose_ext.canSpellCheck(await getTabId(this));
  }
});

{'fraction_non_alphanumeric': 0.12773077995723714, 'fraction_numerical': 0.002602956214557962, 'mean_word_length': 2.8054474708171204, 'pattern_counts': {'":': 4, '<': 7, '<?xml version=': 0, '>': 5, 'https://': 0, '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': '327256', 'n_tokens_mistral': 3381, 'n_tokens_neox': 3301, 'n_words': 794}