<commit_before>import moment from 'moment';
import { markdown } from 'markdown';

function nextDates(event) {
  const start = moment(event.start);
  const yesterday = moment().subtract(1, 'day');

  const dates = [];
  if (event.dates && event.dates.length > 0) {
    // distinguish multiple days in the same week from the same day across weeks
    if (moment(event.dates[0]).day() === start.day()) {
      // find the next date
      let nextDate;
      event.dates.forEach((date) => {
        date = moment(date);
        if (!nextDate ||
          (nextDate.isBefore(yesterday) && date.isAfter(nextDate)) ||
          (date.isAfter(yesterday) && date.isBefore(nextDate))) {
          nextDate = date;
        }
      });
      if (nextDate) {
        dates.push(nextDate); // .format('MMMM Do');
      }
    } else {
      dates.push(start);
      const end = moment(event.dates[event.dates.length - 1]);
      dates.push(end);
      // result = `${start.format('MMMM Do')} - ${end.format('MMMM Do')}`;
    }
  } else {
    dates.push(start); // .format('MMMM Do YYYY');
  }

  const times = [];
  if (dates.length > 0) {
    times.push(start); // .format(' @ h:mm a');
    if (event.times && event.times.length > 0) {
      event.times.forEach((time) => {
        times.push(moment(time.start));
        // result += ` & ${moment(time.start).format('h:mm a')}`;
      });
    }
  }
  return { dates, times };
}

function markupEvent(event, urlBase) {
  const { nextDates: { dates, times } } = event;
  const url = `${urlBase}/events/${event.path || event._id}`;
  let image = '';
  if (event.image) {
    image = `
<a href="${url}"><img style="max-width: 432px; padding-top: 24px;"
src="${event.image.data}" /></a>
  `;
  }
  let at = dates.map(d => d.format('MMMM Do')).join(' - ');
  if (dates.length <= 1) {
    at += ` @ ${times.map(t => t.format('h:mm a')).join(' & ')}`;
  }
  at = `<div style="padding-bottom: 6px;">${at}</div>`;
  let location = '';
  if (event.location) {
    location = `<div style="color: #999999;">${event.location}</div>`;
  }
  const address = '';
  if (event.address) {
    location = `<div style="color: #999999;">${event.address}</div>`;
  }
  let text;
  if (event.text) {
    text = `<div>${markdown.toHTML(event.text)}</div>`;
  } else {
    text = '<div style="padding-bottom: 24px;"></div>';
  }
  return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
  ${image}
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${event.name}</a>
  <div>${at}</div>
  ${location}
  ${address}
  ${text}
</a>
  `;
}

function markupMessage(label, message, urlBase) {
  const url = `${urlBase}/messages/${message.path || message._id}`;
  let verses = '';
  if (message.verses) {
    verses = `<div style="padding-top: 6px; color: #999999">${message.verses}</div>`;
  }
  let author = '';
  if (message.author) {
    author = `<div style="padding-top: 6px; color: #999999">${message.author}</div>`;
  }
  return `
<div>
  <h2 style="font-weight: 100;">${label}</h2>
  <a style="font-size: 18px; font-weight: 600;" href="${url}">${message.name}</a>
  ${verses}
  ${author}
</div>
  `;
}

function markupPage(page, section, urlBase) {
  const url = `${urlBase}/${page.path || `pages/${page._id}`}`;
  let image = '';
  if (section.backgroundImage) {
    image = `
<a href="${url}"><img style="max-width: 432px; padding-top: 24px;"
src="${section.backgroundImage.data}" /></a>
  `;
  }
  return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
  ${image}
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${page.name}</a>
</a>
  `;
}

function markupFile(file, section, urlBase) {
  const url = `${urlBase}/file/${file._id}/${file.name}`;
  return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${file.name}</a>
</a>
  `;
}

export function render(newsletter, urlBase) {
  const sections = (newsletter.sections || []).map((section) => {
    switch (section.type) {
      case 'text':
        return markdown.toHTML(section.text);

      case 'image':
        return `<img style="max-width: 432px;" src="${section.image.data}" />`;

      case 'event': {
        const event = section.eventId;
        return markupEvent({ ...event.toObject(), nextDates: nextDates(event) }, urlBase);
      }

      case 'library': {
        const nextMessageMarkup = section.nextMessage ?
          markupMessage('This week', section.nextMessage, urlBase) : '';
        const previousMessageMarkup = section.previousMessage ?
          markupMessage('Last week', section.previousMessage, urlBase) : '';
        return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
${nextMessageMarkup}
${previousMessageMarkup}
</div>
        `;
      }
<commit_msg>Allow coloring the background of text sections in newsletters.<commit_after>import moment from 'moment';
import { markdown } from 'markdown';

function markupText(text, section) {
  let contents = markdown.toHTML(text);
  if (section.color) {
    contents = `
<div style="padding: 1px 24px; margin: 24px 0px; background-color: ${section.color}">
${contents}
</div>
    `;
  }
  return contents;
}

function nextDates(event) {
  const start = moment(event.start);
  const yesterday = moment().subtract(1, 'day');

  const dates = [];
  if (event.dates && event.dates.length > 0) {
    // distinguish multiple days in the same week from the same day across weeks
    if (moment(event.dates[0]).day() === start.day()) {
      // find the next date
      let nextDate;
      event.dates.forEach((date) => {
        date = moment(date);
        if (!nextDate ||
          (nextDate.isBefore(yesterday) && date.isAfter(nextDate)) ||
          (date.isAfter(yesterday) && date.isBefore(nextDate))) {
          nextDate = date;
        }
      });
      if (nextDate) {
        dates.push(nextDate); // .format('MMMM Do');
      }
    } else {
      dates.push(start);
      const end = moment(event.dates[event.dates.length - 1]);
      dates.push(end);
      // result = `${start.format('MMMM Do')} - ${end.format('MMMM Do')}`;
    }
  } else {
    dates.push(start); // .format('MMMM Do YYYY');
  }

  const times = [];
  if (dates.length > 0) {
    times.push(start); // .format(' @ h:mm a');
    if (event.times && event.times.length > 0) {
      event.times.forEach((time) => {
        times.push(moment(time.start));
        // result += ` & ${moment(time.start).format('h:mm a')}`;
      });
    }
  }
  return { dates, times };
}

function markupEvent(event, urlBase) {
  const { nextDates: { dates, times } } = event;
  const url = `${urlBase}/events/${event.path || event._id}`;
  let image = '';
  if (event.image) {
    image = `
<a href="${url}"><img style="max-width: 432px; padding-top: 24px;"
src="${event.image.data}" /></a>
  `;
  }
  let at = dates.map(d => d.format('MMMM Do')).join(' - ');
  if (dates.length <= 1) {
    at += ` @ ${times.map(t => t.format('h:mm a')).join(' & ')}`;
  }
  at = `<div style="padding-bottom: 6px;">${at}</div>`;
  let location = '';
  if (event.location) {
    location = `<div style="color: #999999;">${event.location}</div>`;
  }
  const address = '';
  if (event.address) {
    location = `<div style="color: #999999;">${event.address}</div>`;
  }
  let text;
  if (event.text) {
    text = `<div>${markdown.toHTML(event.text)}</div>`;
  } else {
    text = '<div style="padding-bottom: 24px;"></div>';
  }
  return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
  ${image}
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${event.name}</a>
  <div>${at}</div>
  ${location}
  ${address}
  ${text}
</a>
  `;
}

function markupMessage(label, message, urlBase) {
  const url = `${urlBase}/messages/${message.path || message._id}`;
  let verses = '';
  if (message.verses) {
    verses = `<div style="padding-top: 6px; color: #999999">${message.verses}</div>`;
  }
  let author = '';
  if (message.author) {
    author = `<div style="padding-top: 6px; color: #999999">${message.author}</div>`;
  }
  return `
<div>
  <h2 style="font-weight: 100;">${label}</h2>
  <a style="font-size: 18px; font-weight: 600;" href="${url}">${message.name}</a>
  ${verses}
  ${author}
</div>
  `;
}

function markupPage(page, section, urlBase) {
  const url = `${urlBase}/${page.path || `pages/${page._id}`}`;
  let image = '';
  if (section.backgroundImage) {
    image = `
<a href="${url}"><img style="max-width: 432px; padding-top: 24px;"
src="${section.backgroundImage.data}" /></a>
  `;
  }
  return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
  ${image}
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${page.name}</a>
</a>
  `;
}

function markupFile(file, section, urlBase) {
  const url = `${urlBase}/file/${file._id}/${file.name}`;
  return `
<div style="margin-bottom: 24px; border-top: solid 1px #cccccc;">
  <a style="display: block; padding-top: 24px; padding-bottom: 24px;
  font-size: 18px; font-weight: 600;" href="${url}">${file.name}</a>
</a>
  `;
}

export function render(newsletter, urlBase) {
  const sections = (newsletter.sections || []).map((section) => {
    switch (section.type) {
      case 'text':
        return markupText(section.text, section);

      case 'image':
        return `<img style="max-width: 432px;" src="${section.image.data}" />`;

      case 'event': {
        const event = section.eventId;
        return markupEvent({ ...event.toObject(), nextDates: nextDates(event) }, urlBase);
      }

      case 'library': {
        const nextMessageMarkup = section.nextMessage ?
          markupMessage('This week', section.nextMessage, urlBase) : '';
        const previousMessageMarkup = section.previousMessage ?
          markupMessage('Last week', section.previousMessage, urlBase) : '';
        return `
<div style="padding-bottom: 24px; border-top: solid 1px #cccccc;">
${nextMessageMarkup}
${previousMessageMarkup}
</div>
        `;
      }

{'fraction_non_alphanumeric': 0.20498400077571996, 'fraction_numerical': 0.01958692911858819, 'mean_word_length': 3.2235872235872236, 'pattern_counts': {'":': 0, '<': 93, '<?xml version=': 0, '>': 107, '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': '2726231', 'n_tokens_mistral': 3762, 'n_tokens_neox': 3566, 'n_words': 761}