| /* |
| 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; |
| } |