@okjavis/nodebb-theme-javis 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/scss/_topic.scss +730 -184
- package/static/lib/theme.js +215 -0
- package/templates/partials/topic/sidebar.tpl +48 -0
- package/templates/topic.tpl +133 -0
package/static/lib/theme.js
CHANGED
|
@@ -6,14 +6,186 @@
|
|
|
6
6
|
(function() {
|
|
7
7
|
'use strict';
|
|
8
8
|
|
|
9
|
+
var carouselCounter = 0;
|
|
10
|
+
|
|
9
11
|
// Theme initialization
|
|
10
12
|
$(document).ready(function() {
|
|
11
13
|
console.log('JAVIS Community Theme initialized');
|
|
12
14
|
|
|
13
15
|
// Ensure sidebar toggle works (reinitialize if Harmony's handler isn't loaded)
|
|
14
16
|
initSidebarToggle();
|
|
17
|
+
|
|
18
|
+
// Initialize image carousels for posts with multiple images
|
|
19
|
+
initPostImageCarousels();
|
|
20
|
+
|
|
21
|
+
// Fix bookmark alert - only show when bookmark is meaningfully ahead
|
|
22
|
+
fixBookmarkAlert();
|
|
23
|
+
|
|
24
|
+
// Re-initialize carousels when new posts are loaded (infinite scroll, etc.)
|
|
25
|
+
// Also handle post edits by clearing the processed flag
|
|
26
|
+
$(window).on('action:posts.loaded action:topic.loaded action:ajaxify.end', function() {
|
|
27
|
+
initPostImageCarousels();
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
// Handle post edits - need to re-process the edited post
|
|
31
|
+
$(window).on('action:posts.edited', function(ev, data) {
|
|
32
|
+
if (data && data.post && data.post.pid) {
|
|
33
|
+
// Find the edited post and remove the processed flag so it gets re-scanned
|
|
34
|
+
var $post = $('[data-pid="' + data.post.pid + '"]');
|
|
35
|
+
var $content = $post.find('[component="post/content"]');
|
|
36
|
+
$content.removeAttr('data-carousel-processed');
|
|
37
|
+
// Remove any existing carousel in this post
|
|
38
|
+
$content.find('.post-image-carousel').remove();
|
|
39
|
+
// Re-initialize
|
|
40
|
+
initPostImageCarousels();
|
|
41
|
+
}
|
|
42
|
+
});
|
|
15
43
|
});
|
|
16
44
|
|
|
45
|
+
/**
|
|
46
|
+
* Convert multiple images in post content to Bootstrap carousels
|
|
47
|
+
*/
|
|
48
|
+
function initPostImageCarousels() {
|
|
49
|
+
// Find all post content areas that haven't been processed
|
|
50
|
+
$('[component="post/content"]:not([data-carousel-processed])').each(function() {
|
|
51
|
+
var $content = $(this);
|
|
52
|
+
$content.attr('data-carousel-processed', 'true');
|
|
53
|
+
|
|
54
|
+
// Find all images in post content - including those in links (lightbox) and paragraphs
|
|
55
|
+
var $images = $content.find('img').filter(function() {
|
|
56
|
+
var $img = $(this);
|
|
57
|
+
|
|
58
|
+
// Exclude images already in a carousel
|
|
59
|
+
if ($img.closest('.carousel, .post-image-carousel').length) {
|
|
60
|
+
return false;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Exclude emojis and small icons by class
|
|
64
|
+
if ($img.hasClass('emoji') || $img.hasClass('emoji-img') || $img.hasClass('icon') || $img.hasClass('not-responsive')) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Check the image source to determine if it's a content image
|
|
69
|
+
var src = $img.attr('src') || '';
|
|
70
|
+
|
|
71
|
+
// Include images from uploads folder or with image extensions
|
|
72
|
+
var isContentImage = src.indexOf('/assets/uploads/') !== -1 ||
|
|
73
|
+
src.indexOf('/files/') !== -1 ||
|
|
74
|
+
src.match(/\.(jpg|jpeg|png|gif|webp)(\?|$)/i);
|
|
75
|
+
|
|
76
|
+
if (!isContentImage) {
|
|
77
|
+
return false;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Exclude tiny images by checking width/height attributes if present
|
|
81
|
+
var width = $img.attr('width');
|
|
82
|
+
var height = $img.attr('height');
|
|
83
|
+
if ((width && parseInt(width, 10) < 50) || (height && parseInt(height, 10) < 50)) {
|
|
84
|
+
return false;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return true;
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Only create carousel if 2+ images
|
|
91
|
+
if ($images.length >= 2) {
|
|
92
|
+
createCarousel($content, $images);
|
|
93
|
+
console.log('JAVIS: Found ' + $images.length + ' images, creating carousel');
|
|
94
|
+
} else if ($images.length > 0) {
|
|
95
|
+
console.log('JAVIS: Found ' + $images.length + ' image(s), not enough for carousel');
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Create a Bootstrap 5 carousel from a set of images
|
|
102
|
+
*/
|
|
103
|
+
function createCarousel($content, $images) {
|
|
104
|
+
var carouselId = 'post-carousel-' + (++carouselCounter);
|
|
105
|
+
|
|
106
|
+
// Build carousel HTML
|
|
107
|
+
var carouselHtml = '<div id="' + carouselId + '" class="carousel slide post-image-carousel" data-bs-ride="false">';
|
|
108
|
+
|
|
109
|
+
// Indicators (dots)
|
|
110
|
+
carouselHtml += '<div class="carousel-indicators">';
|
|
111
|
+
$images.each(function(index) {
|
|
112
|
+
var activeClass = index === 0 ? 'active' : '';
|
|
113
|
+
var ariaCurrent = index === 0 ? 'aria-current="true"' : '';
|
|
114
|
+
carouselHtml += '<button type="button" data-bs-target="#' + carouselId + '" data-bs-slide-to="' + index + '" class="' + activeClass + '" ' + ariaCurrent + ' aria-label="Slide ' + (index + 1) + '"></button>';
|
|
115
|
+
});
|
|
116
|
+
carouselHtml += '</div>';
|
|
117
|
+
|
|
118
|
+
// Carousel inner (slides)
|
|
119
|
+
carouselHtml += '<div class="carousel-inner">';
|
|
120
|
+
$images.each(function(index) {
|
|
121
|
+
var $img = $(this);
|
|
122
|
+
var src = $img.attr('src');
|
|
123
|
+
var alt = $img.attr('alt') || 'Image ' + (index + 1);
|
|
124
|
+
var activeClass = index === 0 ? 'active' : '';
|
|
125
|
+
carouselHtml += '<div class="carousel-item ' + activeClass + '">';
|
|
126
|
+
carouselHtml += '<img src="' + src + '" class="d-block w-100" alt="' + alt + '" loading="lazy">';
|
|
127
|
+
carouselHtml += '</div>';
|
|
128
|
+
});
|
|
129
|
+
carouselHtml += '</div>';
|
|
130
|
+
|
|
131
|
+
// Navigation arrows
|
|
132
|
+
carouselHtml += '<button class="carousel-control-prev" type="button" data-bs-target="#' + carouselId + '" data-bs-slide="prev">';
|
|
133
|
+
carouselHtml += '<span class="carousel-control-prev-icon" aria-hidden="true"></span>';
|
|
134
|
+
carouselHtml += '<span class="visually-hidden">Previous</span>';
|
|
135
|
+
carouselHtml += '</button>';
|
|
136
|
+
carouselHtml += '<button class="carousel-control-next" type="button" data-bs-target="#' + carouselId + '" data-bs-slide="next">';
|
|
137
|
+
carouselHtml += '<span class="carousel-control-next-icon" aria-hidden="true"></span>';
|
|
138
|
+
carouselHtml += '<span class="visually-hidden">Next</span>';
|
|
139
|
+
carouselHtml += '</button>';
|
|
140
|
+
|
|
141
|
+
carouselHtml += '</div>';
|
|
142
|
+
|
|
143
|
+
// Find the topmost element to insert carousel before
|
|
144
|
+
// Images can be in: <p><img></p>, <p><a><img></a></p>, <a><img></a>, or just <img>
|
|
145
|
+
var $firstImg = $images.first();
|
|
146
|
+
var $insertBefore = $firstImg;
|
|
147
|
+
|
|
148
|
+
// Walk up to find the direct child of $content
|
|
149
|
+
while ($insertBefore.parent().length && !$insertBefore.parent().is($content)) {
|
|
150
|
+
$insertBefore = $insertBefore.parent();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// Insert carousel before the first image's container
|
|
154
|
+
$insertBefore.before(carouselHtml);
|
|
155
|
+
|
|
156
|
+
// Collect elements to remove (images and their empty containers)
|
|
157
|
+
var elementsToRemove = [];
|
|
158
|
+
$images.each(function() {
|
|
159
|
+
var $img = $(this);
|
|
160
|
+
var $element = $img;
|
|
161
|
+
|
|
162
|
+
// Walk up to find the direct child of $content
|
|
163
|
+
while ($element.parent().length && !$element.parent().is($content)) {
|
|
164
|
+
$element = $element.parent();
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Mark for removal if not already marked
|
|
168
|
+
if (elementsToRemove.indexOf($element[0]) === -1) {
|
|
169
|
+
elementsToRemove.push($element[0]);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Remove the original image containers
|
|
174
|
+
$(elementsToRemove).remove();
|
|
175
|
+
|
|
176
|
+
// Initialize Bootstrap carousel
|
|
177
|
+
var carouselEl = document.getElementById(carouselId);
|
|
178
|
+
if (carouselEl && typeof bootstrap !== 'undefined') {
|
|
179
|
+
new bootstrap.Carousel(carouselEl, {
|
|
180
|
+
interval: false, // Don't auto-slide
|
|
181
|
+
touch: true,
|
|
182
|
+
wrap: true
|
|
183
|
+
});
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
console.log('JAVIS: Created carousel with ' + $images.length + ' images');
|
|
187
|
+
}
|
|
188
|
+
|
|
17
189
|
function initSidebarToggle() {
|
|
18
190
|
// Check if the toggle element exists
|
|
19
191
|
var toggleEl = $('[component="sidebar/toggle"]');
|
|
@@ -51,4 +223,47 @@
|
|
|
51
223
|
console.log('JAVIS: Sidebar toggle initialized');
|
|
52
224
|
}
|
|
53
225
|
|
|
226
|
+
/**
|
|
227
|
+
* Fix the bookmark alert bug - NodeBB shows "Click here to return to last read post"
|
|
228
|
+
* even when the bookmark position isn't meaningfully ahead of the current position.
|
|
229
|
+
*
|
|
230
|
+
* The bug: NodeBB checks if bookmark exists and postcount > threshold, but doesn't
|
|
231
|
+
* check if the bookmark is actually ahead of where the user currently is.
|
|
232
|
+
*
|
|
233
|
+
* This fix removes the bookmark alert if:
|
|
234
|
+
* 1. The bookmark is at position 1 or 2 (meaningless to "return" to the start)
|
|
235
|
+
* 2. The bookmark is at or behind the current post index
|
|
236
|
+
*/
|
|
237
|
+
function fixBookmarkAlert() {
|
|
238
|
+
$(window).on('action:topic.loaded', function() {
|
|
239
|
+
// Small delay to let NodeBB's handleBookmark run first
|
|
240
|
+
setTimeout(function() {
|
|
241
|
+
if (typeof ajaxify === 'undefined' || !ajaxify.data || !ajaxify.data.template || !ajaxify.data.template.topic) {
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
require(['storage', 'alerts'], function(storage, alerts) {
|
|
246
|
+
var tid = ajaxify.data.tid;
|
|
247
|
+
var bookmark = ajaxify.data.bookmark || storage.getItem('topic:' + tid + ':bookmark');
|
|
248
|
+
var postIndex = ajaxify.data.postIndex || 1;
|
|
249
|
+
var bookmarkInt = parseInt(bookmark, 10) || 0;
|
|
250
|
+
var postIndexInt = parseInt(postIndex, 10) || 1;
|
|
251
|
+
|
|
252
|
+
// Remove bookmark alert if:
|
|
253
|
+
// 1. No meaningful bookmark (position 1 or 2 - essentially the start)
|
|
254
|
+
// 2. Bookmark is at or behind current position (nothing to "return" to)
|
|
255
|
+
// 3. Bookmark is only 1-2 posts ahead (not worth showing notification)
|
|
256
|
+
var shouldRemoveAlert = bookmarkInt <= 2 ||
|
|
257
|
+
bookmarkInt <= postIndexInt ||
|
|
258
|
+
(bookmarkInt - postIndexInt) <= 2;
|
|
259
|
+
|
|
260
|
+
if (shouldRemoveAlert) {
|
|
261
|
+
alerts.remove('bookmark');
|
|
262
|
+
console.log('JAVIS: Removed unnecessary bookmark alert (bookmark: ' + bookmarkInt + ', current: ' + postIndexInt + ')');
|
|
263
|
+
}
|
|
264
|
+
});
|
|
265
|
+
}, 100);
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
54
269
|
})();
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
<div id="topic-sidebar" class="topic-sidebar">
|
|
2
|
+
<div class="d-flex flex-column gap-2">
|
|
3
|
+
<div class="topic-sidebar-actions d-flex flex-column gap-2 mb-3">
|
|
4
|
+
<!-- Reply Button -->
|
|
5
|
+
<!-- IMPORT partials/topic/reply-button.tpl -->
|
|
6
|
+
|
|
7
|
+
<!-- Mark Unread Button -->
|
|
8
|
+
{{{ if loggedIn }}}
|
|
9
|
+
<button component="topic/mark-unread" class="btn btn-ghost btn-sm d-flex gap-2 align-items-center w-100 justify-content-start">
|
|
10
|
+
<i class="fa fa-fw fa-inbox"></i>
|
|
11
|
+
<span class="fw-semibold text-nowrap">[[topic:mark-unread]]</span>
|
|
12
|
+
</button>
|
|
13
|
+
{{{ end }}}
|
|
14
|
+
|
|
15
|
+
<!-- Watching/Not Watching -->
|
|
16
|
+
<!-- IMPORT partials/topic/watch.tpl -->
|
|
17
|
+
|
|
18
|
+
<!-- Sort Options -->
|
|
19
|
+
<!-- IMPORT partials/topic/sort.tpl -->
|
|
20
|
+
|
|
21
|
+
<!-- Topic Tools (for mods/admins) -->
|
|
22
|
+
<!-- IMPORT partials/topic/tools.tpl -->
|
|
23
|
+
</div>
|
|
24
|
+
|
|
25
|
+
<!-- Post Navigator / Timeline -->
|
|
26
|
+
<div class="pagination-block flex-grow-1">
|
|
27
|
+
<div class="scroller-content d-flex gap-2 flex-column align-items-start">
|
|
28
|
+
<button class="btn btn-ghost btn-sm d-flex gap-2 align-items-center pagetop w-100 justify-content-start">
|
|
29
|
+
<i class="fa fa-fw fa-angle-up"></i>
|
|
30
|
+
<span class="timeago text-xs text-muted text-nowrap" title="{./timestampISO}"></span>
|
|
31
|
+
</button>
|
|
32
|
+
<div class="scroller-container position-relative w-100">
|
|
33
|
+
<div class="scroller-thumb d-flex gap-2 text-nowrap position-relative" style="height: 40px;">
|
|
34
|
+
<div class="scroller-thumb-icon rounded d-inline-block" style="width: 4px; height: 40px;"></div>
|
|
35
|
+
<div class="d-flex flex-column">
|
|
36
|
+
<span class="thumb-text small fw-semibold mb-0"></span>
|
|
37
|
+
<span class="thumb-timestamp timeago text-xs text-muted mb-0"></span>
|
|
38
|
+
</div>
|
|
39
|
+
</div>
|
|
40
|
+
</div>
|
|
41
|
+
<button class="btn btn-ghost btn-sm d-flex gap-2 align-items-center pagebottom w-100 justify-content-start">
|
|
42
|
+
<i class="fa fa-fw fa-angle-down"></i>
|
|
43
|
+
<span class="timeago text-xs text-muted text-nowrap" title="{./lastposttimeISO}"></span>
|
|
44
|
+
</button>
|
|
45
|
+
</div>
|
|
46
|
+
</div>
|
|
47
|
+
</div>
|
|
48
|
+
</div>
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
<!-- IMPORT partials/breadcrumbs-json-ld.tpl -->
|
|
2
|
+
{{{ if config.theme.enableBreadcrumbs }}}
|
|
3
|
+
<!-- IMPORT partials/breadcrumbs.tpl -->
|
|
4
|
+
{{{ end }}}
|
|
5
|
+
{{{ if widgets.header.length }}}
|
|
6
|
+
<div data-widget-area="header">
|
|
7
|
+
{{{each widgets.header}}}
|
|
8
|
+
{{widgets.header.html}}
|
|
9
|
+
{{{end}}}
|
|
10
|
+
</div>
|
|
11
|
+
{{{ end }}}
|
|
12
|
+
|
|
13
|
+
<div itemid="{url}" itemscope itemtype="https://schema.org/DiscussionForumPosting">
|
|
14
|
+
<meta itemprop="headline" content="{escape(titleRaw)}">
|
|
15
|
+
<meta itemprop="text" content="{escape(titleRaw)}">
|
|
16
|
+
<meta itemprop="url" content="{url}">
|
|
17
|
+
<meta itemprop="datePublished" content="{timestampISO}">
|
|
18
|
+
<meta itemprop="dateModified" content="{lastposttimeISO}">
|
|
19
|
+
<div itemprop="author" itemscope itemtype="https://schema.org/Person">
|
|
20
|
+
<meta itemprop="name" content="{author.username}">
|
|
21
|
+
{{{ if author.userslug }}}<meta itemprop="url" content="{config.relative_path}/user/{author.userslug}">{{{ end }}}
|
|
22
|
+
</div>
|
|
23
|
+
|
|
24
|
+
<div class="d-flex flex-column gap-3">
|
|
25
|
+
<div class="d-flex flex-wrap">
|
|
26
|
+
<div class="d-flex flex-column gap-3 flex-grow-1">
|
|
27
|
+
<div class="topic-header d-flex align-items-start gap-3">
|
|
28
|
+
<a href="#" onclick="history.back(); return false;" class="topic-back-btn" title="Go back">
|
|
29
|
+
<i class="fa fa-arrow-left"></i>
|
|
30
|
+
</a>
|
|
31
|
+
<h1 component="post/header" class="tracking-tight fw-semibold fs-3 mb-0 text-break {{{ if config.theme.centerHeaderElements }}}text-center{{{ end }}}">
|
|
32
|
+
<span class="topic-title" component="topic/title">{title}</span>
|
|
33
|
+
</h1>
|
|
34
|
+
</div>
|
|
35
|
+
|
|
36
|
+
<div class="topic-info d-flex gap-2 align-items-center flex-wrap {{{ if config.theme.centerHeaderElements }}}justify-content-center{{{ end }}}">
|
|
37
|
+
<span component="topic/labels" class="d-flex gap-2 {{{ if (!scheduled && (!pinned && (!locked && (!oldCid && !icons.length)))) }}}hidden{{{ end }}}">
|
|
38
|
+
<span component="topic/scheduled" class="badge badge border border-gray-300 text-body {{{ if !scheduled }}}hidden{{{ end }}}">
|
|
39
|
+
<i class="fa fa-clock-o"></i> [[topic:scheduled]]
|
|
40
|
+
</span>
|
|
41
|
+
<span component="topic/pinned" class="badge badge border border-gray-300 text-body {{{ if (scheduled || !pinned) }}}hidden{{{ end }}}">
|
|
42
|
+
<i class="fa fa-thumb-tack"></i> {{{ if !pinExpiry }}}[[topic:pinned]]{{{ else }}}[[topic:pinned-with-expiry, {isoTimeToLocaleString(./pinExpiryISO, config.userLang)}]]{{{ end }}}
|
|
43
|
+
</span>
|
|
44
|
+
<span component="topic/locked" class="badge badge border border-gray-300 text-body {{{ if !locked }}}hidden{{{ end }}}">
|
|
45
|
+
<i class="fa fa-lock"></i> [[topic:locked]]
|
|
46
|
+
</span>
|
|
47
|
+
<a component="topic/moved" href="{config.relative_path}/category/{oldCid}" class="badge badge border border-gray-300 text-body text-decoration-none {{{ if !oldCid }}}hidden{{{ end }}}">
|
|
48
|
+
<i class="fa fa-arrow-circle-right"></i> {{{ if privileges.isAdminOrMod }}}[[topic:moved-from, {oldCategory.name}]]{{{ else }}}[[topic:moved]]{{{ end }}}
|
|
49
|
+
</a>
|
|
50
|
+
{{{each icons}}}<span class="lh-1">{@value}</span>{{{end}}}
|
|
51
|
+
</span>
|
|
52
|
+
{function.buildCategoryLabel, category, "a", "border"}
|
|
53
|
+
<div data-tid="{./tid}" component="topic/tags" class="lh-1 tags tag-list d-flex flex-wrap hidden-xs hidden-empty gap-2"><!-- IMPORT partials/topic/tags.tpl --></div>
|
|
54
|
+
<div class="d-flex hidden-xs gap-2"><!-- IMPORT partials/topic/stats.tpl --></div>
|
|
55
|
+
</div>
|
|
56
|
+
</div>
|
|
57
|
+
<div class="d-flex gap-2 align-items-center mt-2 hidden-empty" component="topic/thumb/list"><!-- IMPORT partials/topic/thumbs.tpl --></div>
|
|
58
|
+
</div>
|
|
59
|
+
|
|
60
|
+
<div class="topic-layout d-flex gap-4 mb-4 mb-lg-0">
|
|
61
|
+
<!-- Main Content Area -->
|
|
62
|
+
<div class="topic flex-grow-1" style="min-width: 0;">
|
|
63
|
+
{{{ if merger }}}
|
|
64
|
+
<!-- IMPORT partials/topic/merged-message.tpl -->
|
|
65
|
+
{{{ end }}}
|
|
66
|
+
{{{ if forker }}}
|
|
67
|
+
<!-- IMPORT partials/topic/forked-message.tpl -->
|
|
68
|
+
{{{ end }}}
|
|
69
|
+
{{{ if !scheduled }}}
|
|
70
|
+
<!-- IMPORT partials/topic/deleted-message.tpl -->
|
|
71
|
+
{{{ end }}}
|
|
72
|
+
|
|
73
|
+
<div class="posts-container" style="min-width: 0;">
|
|
74
|
+
<ul component="topic" class="posts timeline list-unstyled mt-sm-2 p-0 py-3" style="min-width: 0;" data-tid="{tid}" data-cid="{cid}">
|
|
75
|
+
{{{ each posts }}}
|
|
76
|
+
<li component="post" class="pt-4 {{{ if posts.deleted }}}deleted{{{ end }}} {{{ if posts.selfPost }}}self-post{{{ end }}} {{{ if posts.topicOwnerPost }}}topic-owner-post{{{ end }}}" <!-- IMPORT partials/data/topic.tpl -->>
|
|
77
|
+
<a component="post/anchor" data-index="{./index}" id="{increment(./index, "1")}"></a>
|
|
78
|
+
<meta itemprop="datePublished" content="{./timestampISO}">
|
|
79
|
+
{{{ if ./editedISO }}}
|
|
80
|
+
<meta itemprop="dateModified" content="{./editedISO}">
|
|
81
|
+
{{{ end }}}
|
|
82
|
+
|
|
83
|
+
<!-- IMPORT partials/topic/post.tpl -->
|
|
84
|
+
</li>
|
|
85
|
+
{{{ if (config.topicPostSort != "most_votes") }}}
|
|
86
|
+
{{{ each ./events}}}<!-- IMPORT partials/topic/event.tpl -->{{{ end }}}
|
|
87
|
+
{{{ end }}}
|
|
88
|
+
{{{ end }}}
|
|
89
|
+
</ul>
|
|
90
|
+
{{{ if browsingUsers }}}
|
|
91
|
+
<div class="visible-xs">
|
|
92
|
+
<!-- IMPORT partials/topic/browsing-users.tpl -->
|
|
93
|
+
<hr/>
|
|
94
|
+
</div>
|
|
95
|
+
{{{ end }}}
|
|
96
|
+
{{{ if config.theme.enableQuickReply }}}
|
|
97
|
+
<!-- IMPORT partials/topic/quickreply.tpl -->
|
|
98
|
+
{{{ end }}}
|
|
99
|
+
</div>
|
|
100
|
+
|
|
101
|
+
{{{ if config.usePagination }}}
|
|
102
|
+
<!-- IMPORT partials/paginator.tpl -->
|
|
103
|
+
{{{ end }}}
|
|
104
|
+
</div>
|
|
105
|
+
|
|
106
|
+
<!-- Right Sidebar with Topic Actions -->
|
|
107
|
+
<div class="topic-sidebar-container d-none d-lg-block" style="width: 200px; flex-shrink: 0;">
|
|
108
|
+
<!-- IMPORT partials/topic/sidebar.tpl -->
|
|
109
|
+
|
|
110
|
+
<!-- Additional Widgets (if any) -->
|
|
111
|
+
{{{ if widgets.sidebar.length }}}
|
|
112
|
+
<div data-widget-area="sidebar" class="mt-4">
|
|
113
|
+
{{{each widgets.sidebar}}}
|
|
114
|
+
{{widgets.sidebar.html}}
|
|
115
|
+
{{{end}}}
|
|
116
|
+
</div>
|
|
117
|
+
{{{ end }}}
|
|
118
|
+
</div>
|
|
119
|
+
</div>
|
|
120
|
+
</div>
|
|
121
|
+
</div>
|
|
122
|
+
|
|
123
|
+
<div data-widget-area="footer">
|
|
124
|
+
{{{each widgets.footer}}}
|
|
125
|
+
{{widgets.footer.html}}
|
|
126
|
+
{{{end}}}
|
|
127
|
+
</div>
|
|
128
|
+
|
|
129
|
+
{{{ if !config.usePagination }}}
|
|
130
|
+
<noscript>
|
|
131
|
+
<!-- IMPORT partials/paginator.tpl -->
|
|
132
|
+
</noscript>
|
|
133
|
+
{{{ end }}}
|