webui/js/source/listview-threaded.js (162 lines of code) (raw):

/* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to You under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ function calc_email_width() { // Get email width; used for calculating reply nesting offsets let body = document.body; let html = document.documentElement; return Math.max(body.scrollWidth, body.offsetWidth, html.clientWidth, html.scrollWidth, html.offsetWidth); } function listview_threaded(json, start) { let list = document.getElementById('emails'); list.innerHTML = ""; let s = start || 0; if (json.thread_struct && json.thread_struct.length) { for (let n = s; n < (s + G_current_per_page); n++) { let z = json.thread_struct.length - n - 1; // reverse order by default if (json.thread_struct[z]) { let item = listview_threaded_element(json.thread_struct[z], z); list.inject(item); // Hidden placeholder for expanding email(s) let placeholder = new HTML('div', { class: G_chatty_layout ? 'email_placeholder_chatty' : 'email_placeholder', id: 'email_%u'.format(z) }); list.inject(placeholder); } } } else { list.inject(txt("No emails found...")); } } function find_email(id) { if (!G_current_json.emails) return null; for (let email of G_current_json.emails) { if (email.id == id) return email; } return null; } function count_replies(thread) { let reps = 0; if (isArray(thread.children)) { for (let child of thread.children) { if (child.tid == thread.tid) reps--; reps++; reps += count_replies(child); } } return reps; } function count_people(thread, hash) { let ppl = hash || {}; let eml = find_email(thread.tid); if (eml) ppl[eml.from] = true; if (isArray(thread.children)) { for (let child of thread.children) { count_people(child, ppl); } } let n = 0; for (let _ in ppl) n++; return n; } function last_email(thread) { let newest = thread.epoch; if (isArray(thread.children)) { for (let child of thread.children) { newest = Math.max(newest, last_email(child)); } } return newest; } function listview_threaded_element(thread, idx) { let eml = find_email(thread.tid); if (!eml) { return; } let link_wrapper = new HTML('a', { href: 'thread/%s'.format(eml.id), onclick: 'return(expand_email_threaded(%u));'.format(idx) }); let element = new HTML('div', { class: G_current_listmode_compact ? "listview_email_compact" : "listview_email_flat" }, " "); let date = new Date(eml.epoch * 1000.0); let now = new Date(); // Add gravatar let gravatar = new HTML('img', { class: "gravatar", src: GRAVATAR_URL.format(eml.gravatar) }); element.inject(gravatar); // Add author let authorName = eml.from.replace(/\s*<.+>/, "").replace(/"/g, ''); let authorEmail = eml.from.match(/\s*<(.+@.+)>\s*/); if (authorName.length == 0) authorName = authorEmail ? authorEmail[1] : "(No author?)"; let author = new HTML('span', { class: "listview_email_author" }, authorName); element.inject(author); // reasons to show the list name let showList = G_current_domain == 'inbox' || G_current_list == '*' || G_current_domain == '*'; // If space and needed, inject ML name if (!G_current_listmode_compact && showList) { author.style.lineHeight = '16px'; author.inject(new HTML('br')); author.inject(new HTML('span', { class: "label label-primary", style: "font-style: italic; font-size: 1rem;" }, eml.list_raw.replace(/[<>]/g, '').replace('.', '@', 1))); } // Combined space for subject + body teaser let as = new HTML('div', { class: 'listview_email_as' }); let suba = new HTML('a', {}, eml.subject === '' ? '(No subject)' : eml.subject); if (G_current_listmode_compact && showList) { let kbd = new HTML('kbd', { class: 'listview_kbd' }, eml.list_raw.replace(/[<>]/g, '').replace('.', '@', 1)) suba = [kbd, suba]; } let subject = new HTML('div', { class: 'listview_email_subject email_unread' }, suba); as.inject(subject); if (!G_current_listmode_compact) { // No body teaser in compact mode let body = new HTML('div', { class: 'listview_email_body' }, eml.body); as.inject(body); } element.inject(as); // Labels let labels = new HTML('div', { class: 'listview_email_labels' }); // Participants let ppl = count_people(thread); let ptitle = (ppl == 1) ? "one participant" : "%u participants".format(ppl); let people = new HTML('span', { class: 'label label-default', title: ptitle }, [ new HTML('span', { class: 'glyphicon glyphicon-user' }, ' '), " %u".format(ppl) ]); labels.inject(people); // Replies let reps = count_replies(thread); let rtitle = (reps == 1) ? "one reply" : "%u replies".format(reps); let repl = new HTML('span', { class: 'label label-default', title: rtitle }, [ new HTML('span', { class: 'glyphicon glyphicon-envelope' }, ' '), " %u".format(reps) ]); labels.inject(repl); // Date date = new Date(last_email(thread) * 1000.0); let dl = new HTML('span', { class: 'label label-default' }, date.ISOBare()); if (now - date < 86400000) { dl.setAttribute("class", "label label-primary"); } labels.inject(dl); element.inject(labels); link_wrapper.inject(element); return link_wrapper; }