+ {(o.isReply ? <>{postInfo}{fileBlock}> : <>{fileBlock}{postInfo}>)}
+
{commentHTML}
+
+ >;
+
+ const container = $.el('div', {
+ className: `postContainer ${postClass}Container`,
+ id: `pc${ID}`
+ });
+ $.extend(container, wholePost);
+
+ // Fix quotelinks
+ for (var quote of $$('.quotelink', container)) {
+ var href = quote.getAttribute('href');
+ if (href[0] === '#') {
+ if (!this.sameThread(boardID, threadID)) {
+ quote.href = this.threadURL(boardID, threadID) + href;
+ }
+ } else {
+ var match;
+ if ((match = quote.href.match(SWYotsuba.regexp.quotelink)) && (this.sameThread(match[1], match[2]))) {
+ quote.href = href.match(/(#[^#]*)?$/)[0] || '#';
+ }
+ }
+ }
+
+ return container;
+ },
+
+ summaryText(status, posts, files) {
+ let text = '';
+ if (status) { text += `${status} `; }
+ text += `${posts} post${posts > 1 ? 's' : ''}`;
+ if (+files) { text += ` and ${files} image repl${files > 1 ? 'ies' : 'y'}`; }
+ return text += ` ${status === '-' ? 'shown' : 'omitted'}.`;
+ },
+
+ summary(boardID, threadID, posts, files) {
+ return $.el('a', {
+ className: 'summary',
+ textContent: this.summaryText('', posts, files),
+ href: `/${boardID}/thread/${threadID}`
+ }
+ );
+ },
+
+ thread(thread, data, withReplies) {
+ let root;
+ if (root = thread.nodes.root) {
+ $.rmAll(root);
+ } else {
+ thread.nodes.root = (root = $.el('div', {
+ className: 'thread',
+ id: `t${data.no}`
+ }
+ ));
+ }
+ if (this.hat) { $.add(root, this.hat.cloneNode(false)); }
+ $.add(root, thread.OP.nodes.root);
+ if (data.omitted_posts || (!withReplies && data.replies)) {
+ const [posts, files] = Array.from(withReplies ?
+ // XXX data.omitted_images is not accurate.
+ [data.omitted_posts, data.images - data.last_replies.filter(data => !!data.ext).length]
+ :
+ [data.replies, data.images]);
+ const summary = this.summary(thread.board.ID, data.no, posts, files);
+ $.add(root, summary);
+ }
+ return root;
+ },
+
+ catalogThread(thread, data, pageCount) {
+ let cssText, imgClass, src;
+ const { staticPath, gifIcon } = this;
+ const { tn_w, tn_h } = data;
+
+ if (data.spoiler && !Conf['Reveal Spoiler Thumbnails']) {
+ let spoilerRange;
+ src = `${staticPath}spoiler`;
+ if (spoilerRange = this.spoilerRange[thread.board]) {
+ // Randomize the spoiler image.
+ src += (`-${thread.board}`) + Math.floor(1 + (spoilerRange * Math.random()));
+ }
+ src += '.png';
+ imgClass = 'spoiler-file';
+ cssText = "--tn-w: 100; --tn-h: 100;";
+ } else if (data.filedeleted) {
+ src = `${staticPath}filedeleted-res${gifIcon}`;
+ imgClass = 'deleted-file';
+ } else if (thread.OP.file) {
+ src = thread.OP.file.thumbURL;
+ const ratio = 250 / Math.max(tn_w, tn_h);
+ cssText = `--tn-w: ${tn_w * ratio}; --tn-h: ${tn_h * ratio};`;
+ } else {
+ src = `${staticPath}nofile.png`;
+ imgClass = 'no-file';
+ }
+
+ const postCount = data.replies + 1;
+ const fileCount = data.images + !!data.ext;
+
+ const container = $.el(
+ 'div',
+ generateCatalogThreadHtml(thread, src, imgClass, data, postCount, fileCount, pageCount, staticPath, gifIcon)
+ );
+ $.before(thread.OP.nodes.info, [...Array.from(container.childNodes)]);
+
+ for (var br of $$('br', thread.OP.nodes.comment)) {
+ if (br.previousSibling && (br.previousSibling.nodeName === 'BR')) {
+ $.addClass(br, 'extra-linebreak');
+ }
+ }
+
+ const root = $.el('div', {
+ className: 'thread catalog-thread',
+ id: `t${thread}`
+ }
+ );
+ if (thread.OP.highlights) { $.addClass(root, ...Array.from(thread.OP.highlights)); }
+ if (!thread.OP.file) { $.addClass(root, 'noFile'); }
+ root.style.cssText = cssText || '';
+
+ return root;
+ },
+
+ catalogReply(thread, data) {
+ let excerpt = '';
+ if (data.com) {
+ excerpt = this.parseCommentDisplay(data.com).replace(/>>\d+/g, '').trim().replace(/\n+/g, ' // ');
+ }
+ if (data.ext) {
+ if (!excerpt) { excerpt = `${$.unescape(data.filename)}${data.ext}`; }
+ }
+ if (data.com) {
+ if (!excerpt) { excerpt = $.unescape(data.com.replace(/