Merge remote-tracking branch 'origin/master' into user-icons

v1.18.x
Julian Lam 10 years ago
commit 730a62519f

@ -40,7 +40,7 @@
"mmmagic": "^0.4.0",
"morgan": "^1.3.2",
"nconf": "~0.7.1",
"nodebb-plugin-composer-default": "1.0.15",
"nodebb-plugin-composer-default": "1.0.16",
"nodebb-plugin-dbsearch": "0.2.17",
"nodebb-plugin-emoji-extended": "0.4.13",
"nodebb-plugin-markdown": "4.0.6",
@ -48,9 +48,9 @@
"nodebb-plugin-soundpack-default": "0.1.4",
"nodebb-plugin-spam-be-gone": "0.4.2",
"nodebb-rewards-essentials": "0.0.5",
"nodebb-theme-lavender": "2.0.2",
"nodebb-theme-persona": "3.0.22",
"nodebb-theme-vanilla": "4.0.14",
"nodebb-theme-lavender": "2.0.4",
"nodebb-theme-persona": "3.0.23",
"nodebb-theme-vanilla": "4.0.15",
"nodebb-widget-essentials": "2.0.1",
"npm": "^2.1.4",
"passport": "^0.3.0",

@ -1,10 +1,33 @@
#navigation {
ul {
list-style-type: none;
padding: 0;
li {
#main-nav {
.active {
background-color: #eee;
}
li a {
cursor: move;
}
}
#available {
.drag-item {
cursor: move;
margin-right: 10px;
}
}
}
#enabled {
.iconPicker i {
cursor: pointer;
}
}
ul {
list-style-type: none;
padding: 0;
}
}

@ -32,6 +32,8 @@ define('admin/general/dashboard', ['semver'], function(semver) {
intervals.rooms = null;
intervals.graphs = null;
graphData.rooms = null;
graphData.traffic = null;
usedTopicColors.length = 0;
});

@ -1,7 +1,7 @@
"use strict";
/* global define, app, ajaxify, socket, templates, bootbox */
define('admin/general/navigation', ['translator'], function(translator) {
define('admin/general/navigation', ['translator', 'iconSelect'], function(translator, iconSelect) {
var navigation = {},
available;
@ -9,46 +9,87 @@ define('admin/general/navigation', ['translator'], function(translator) {
available = ajaxify.data.available;
$('#enabled').html(translator.unescape($('#enabled').html()));
$('#main-nav').html(translator.unescape($('#main-nav').html()));
translator.translate(translator.unescape($('#available').html()), function(html) {
$('#available').html(html)
.find('li').draggable({
connectToSortable: '#enabled',
.find('li .drag-item').draggable({
connectToSortable: '#main-nav',
helper: 'clone',
distance: 10,
stop: drop
});
});
$('#enabled')
.on('click', '.delete', remove)
.on('click', '.toggle', toggle)
.sortable()
.droppable({
accept: $('#available li')
$('#main-nav').sortable().droppable({
accept: $('#available li .drag-item')
});
$('#enabled').on('click', '.iconPicker', function() {
var iconEl = $(this).find('i');
iconSelect.init(iconEl, function(el) {
var newIconClass = el.attr('value');
var index = iconEl.parents('[data-index]').attr('data-index');
$('#main-nav [data-index="' + index + '"] i').attr('class', 'fa fa-fw ' + newIconClass);
iconEl.siblings('[name="iconClass"]').val(newIconClass);
});
});
$('#main-nav').on('click', 'li', onSelect);
$('#enabled')
.on('click', '.delete', remove)
.on('click', '.toggle', toggle);
$('#save').on('click', save);
};
function onSelect() {
var clickedIndex = $(this).attr('data-index');
$('#main-nav li').removeClass('active');
$(this).addClass('active');
var detailsForm = $('#enabled').children('[data-index="' + clickedIndex + '"]');
$('#enabled li').addClass('hidden');
if (detailsForm.length) {
detailsForm.removeClass('hidden');
}
return false;
}
function drop(ev, ui) {
var id = ui.helper.attr('data-id'),
el = $('#enabled [data-id="' + id + '"]'),
data = id === 'custom' ? {} : available[id];
el = $('#main-nav [data-id="' + id + '"]'),
data = id === 'custom' ? {iconClass: 'fa-navicon'} : available[id];
data.enabled = false;
data.index = parseInt($('#enabled').children().last().attr('data-index'), 10) + 1;
templates.parse('admin/general/navigation', 'enabled', {enabled: [data]}, function(li) {
templates.parse('admin/general/navigation', 'navigation', {navigation: [data]}, function(li) {
li = $(translator.unescape(li));
el.after(li);
el.remove();
});
templates.parse('admin/general/navigation', 'enabled', {enabled: [data]}, function(li) {
li = $(translator.unescape(li));
$('#enabled').append(li);
});
}
function save() {
var nav = [];
$('#enabled li').each(function() {
var form = $(this).find('form').serializeArray(),
var indices = [];
$('#main-nav li').each(function() {
indices.push($(this).attr('data-index'));
});
indices.forEach(function(index) {
var el = $('#enabled').children('[data-index="' + index + '"]');
var form = el.find('form').serializeArray(),
data = {},
properties = {};
@ -81,7 +122,9 @@ define('admin/general/navigation', ['translator'], function(translator) {
}
function remove() {
$(this).parents('li').remove();
var index = $(this).parents('[data-index]').attr('data-index');
$('#main-nav [data-index="' + index + '"]').remove();
$('#enabled [data-index="' + index + '"]').remove();
return false;
}

@ -10,6 +10,7 @@ define('forum/category', [
'sort',
'components',
'translator'
], function(pagination, infinitescroll, share, navigator, categoryTools, sort, components, translator) {
var Category = {};
@ -172,7 +173,7 @@ define('forum/category', [
function enableInfiniteLoadingOrPagination() {
if (!config.usePagination) {
infinitescroll.init(Category.loadMoreTopics);
infinitescroll.init($('[component="category"]'), Category.loadMoreTopics);
} else {
navigator.hide();
pagination.init(ajaxify.data.currentPage, ajaxify.data.pageCount);
@ -245,94 +246,16 @@ define('forum/category', [
});
}
Category.onTopicsLoaded = function(data, callback) {
if(!data || !data.topics.length) {
return;
}
function removeAlreadyAddedTopics(topics) {
return topics.filter(function(topic) {
return components.get('category/topic', 'tid', topic.tid).length === 0;
});
}
var after = null,
before = null;
function findInsertionPoint() {
var topics = components.get('category/topic');
if (!topics.length) {
return;
}
var last = topics.last(),
lastIndex = last.attr('data-index'),
firstIndex = data.topics[data.topics.length - 1].index;
if (firstIndex > lastIndex) {
after = last;
} else {
before = topics.first();
}
}
data.topics = removeAlreadyAddedTopics(data.topics);
if(!data.topics.length) {
return;
}
data.showSelect = data.privileges.editable;
findInsertionPoint();
templates.parse('category', 'topics', data, function(html) {
translator.translate(html, function(translatedHTML) {
var container = $('[component="category"]'),
html = $(translatedHTML);
$('[component="category"]').removeClass('hidden');
$('.category-sidebar').removeClass('hidden');
$('#category-no-topics').remove();
if(config.usePagination) {
container.empty().append(html);
} else {
if(after) {
html.insertAfter(after);
} else if(before) {
html.insertBefore(before);
} else {
container.append(html);
}
}
if (typeof callback === 'function') {
callback();
}
html.find('.timeago').timeago();
app.createUserTooltips();
utils.makeNumbersHumanReadable(html.find('.human-readable-number'));
});
});
};
Category.loadMoreTopics = function(direction) {
if (!$('[component="category"]').length || !$('[component="category"]').children().length) {
return;
}
var topics = components.get('category/topic');
var topics = $('[component="category/topic"]');
var afterEl = direction > 0 ? topics.last() : topics.first();
var after = parseInt(afterEl.attr('data-index'), 10) || 0;
var offset = $('#header-menu').height();
loadTopicsAfter(after, direction, function() {
if (direction < 0 && afterEl.length) {
Category.scrollToTopic(afterEl.attr('data-index'), null, 0, offset);
}
});
loadTopicsAfter(after, direction);
};
function loadTopicsAfter(after, direction, callback) {
@ -348,18 +271,71 @@ define('forum/category', [
author: utils.params().author
}, function (data, done) {
if (data.topics && data.topics.length) {
Category.onTopicsLoaded(data, function() {
done();
callback();
});
Category.onTopicsLoaded(data, direction, done);
} else {
done();
}
$('[component="category"]').attr('data-nextstart', data.nextStart);
$(window).trigger('action:categories.loaded');
});
}
Category.onTopicsLoaded = function(data, direction, callback) {
if (!data || !data.topics.length) {
return callback();
}
function removeAlreadyAddedTopics(topics) {
return topics.filter(function(topic) {
return components.get('category/topic', 'tid', topic.tid).length === 0;
});
}
data.topics = removeAlreadyAddedTopics(data.topics);
if (!data.topics.length) {
return callback();
}
data.showSelect = data.privileges.editable;
var after, before;
var topics = $('[component="category/topic"]');
if (direction > 0 && topics.length) {
after = topics.last();
} else if (direction < 0 && topics.length) {
before = topics.first();
}
infinitescroll.parseAndTranslate('category', 'topics', data, function(html) {
$('[component="category"]').removeClass('hidden');
$('.category-sidebar').removeClass('hidden');
$('#category-no-topics').remove();
if (after) {
html.insertAfter(after);
} else if (before) {
var height = $(document).height(),
scrollTop = $(window).scrollTop();
html.insertBefore(before);
$(window).scrollTop(scrollTop + ($(document).height() - height));
} else {
$('[component="category"]').append(html);
}
infinitescroll.removeExtra($('[component="category/topic"]'), direction, 60);
html.find('.timeago').timeago();
app.createUserTooltips();
utils.makeNumbersHumanReadable(html.find('.human-readable-number'));
callback();
});
};
return Category;
});

@ -8,24 +8,39 @@ define('forum/infinitescroll', ['translator'], function(translator) {
var callback;
var previousScrollTop = 0;
var loadingMore = false;
var topOffset = 0;
var container;
scroll.init = function(el, cb) {
if (typeof el === 'function') {
callback = el;
container = $(document);
} else {
callback = cb;
container = el || $(document);
}
scroll.init = function(cb, _topOffest) {
callback = cb;
topOffset = _topOffest || 0;
$(window).off('scroll', onScroll).on('scroll', onScroll);
};
function onScroll() {
var top = $(window).height() * 0.3 + topOffset,
bottom = ($(document).height() - $(window).height()) * 0.85,
currentScrollTop = $(window).scrollTop();
if (currentScrollTop < top && currentScrollTop < previousScrollTop) {
callback(-1);
} else if (currentScrollTop > bottom && currentScrollTop > previousScrollTop) {
callback(1);
var currentScrollTop = $(window).scrollTop();
var wh = $(window).height();
var viewportHeight = container.height() - wh;
var offsetTop = container.offset() ? container.offset().top : 0;
var scrollPercent = 100 * (currentScrollTop - offsetTop) / (viewportHeight <= 0 ? wh : viewportHeight);
var top = 20, bottom = 80;
var direction = currentScrollTop > previousScrollTop ? 1 : -1;
if (scrollPercent < top && currentScrollTop < previousScrollTop) {
callback(direction);
} else if (scrollPercent > bottom && currentScrollTop > previousScrollTop) {
callback(direction);
} else if (scrollPercent < 0 && direction > 0 && viewportHeight < 0) {
callback(direction);
}
previousScrollTop = currentScrollTop;
}
@ -53,5 +68,23 @@ define('forum/infinitescroll', ['translator'], function(translator) {
});
};
scroll.removeExtra = function(els, direction, count) {
if (els.length <= count) {
return;
}
var removeCount = els.length - count;
if (direction > 0) {
var height = $(document).height(),
scrollTop = $(window).scrollTop();
els.slice(0, removeCount).remove();
$(window).scrollTop(scrollTop + ($(document).height() - height));
} else {
els.slice(els.length - removeCount).remove();
}
};
return scroll;
});

@ -87,12 +87,14 @@ define('forum/topic', [
}
function onKeyDown(ev) {
if (ev.which === 36) { // home key
Topic.toTop();
return false;
} else if (ev.which === 35) { // end key
Topic.toBottom();
return false;
if (ev.target.nodeName === 'BODY') {
if (ev.which === 36) { // home key
Topic.toTop();
return false;
} else if (ev.which === 35) { // end key
Topic.toBottom();
return false;
}
}
}
@ -192,7 +194,7 @@ define('forum/topic', [
function enableInfiniteLoadingOrPagination() {
if (!config.usePagination) {
infinitescroll.init(posts.loadMorePosts);
infinitescroll.init($('[component="topic"]'), posts.loadMorePosts);
} else {
navigator.hide();

@ -246,7 +246,9 @@ define('forum/topic/postTools', ['share', 'navigator', 'components', 'translator
function getUserName(button) {
var username = '',
post = button.parents('[data-pid]');
if (button.attr('component') === 'topic/reply') {
return username;
}
if (post.length) {
username = post.attr('data-username').replace(/\s/g, '-');
}

@ -106,10 +106,11 @@ define('forum/topic/posts', [
data.posts.forEach(function(post) {
var p = components.get('post', 'pid', post.pid);
if (p.hasClass('new')) {
p.remove()
p.remove();
}
});
}
data.posts = data.posts.filter(function(post) {
return components.get('post', 'pid', post.pid).length === 0;
});
@ -123,9 +124,9 @@ define('forum/topic/posts', [
var after, before;
if (direction === 1 && repliesSelector.length) {
if (direction > 0 && repliesSelector.length) {
after = repliesSelector.last();
} else if (direction === -1 && repliesSelector.length) {
} else if (direction < 0 && repliesSelector.length) {
before = repliesSelector.first();
}
@ -141,17 +142,18 @@ define('forum/topic/posts', [
} else if (before) {
// Save document height and position for future reference (about 5 lines down)
var height = $(document).height(),
scrollTop = $(document).scrollTop();
scrollTop = $(window).scrollTop();
// Insert the new post
html.insertBefore(before);
// Now restore the relative position the user was on prior to new post insertion
$(document).scrollTop(scrollTop + ($(document).height() - height));
$(window).scrollTop(scrollTop + ($(document).height() - height));
} else {
components.get('topic').append(html);
}
infinitescroll.removeExtra(components.get('posts'), direction, 40);
html.hide().fadeIn('slow');
var pids = [];
@ -258,7 +260,7 @@ define('forum/topic/posts', [
};
function showBottomPostBar() {
if(components.get('post').length > 1 || !components.get('post', 'index', 0).length) {
if (components.get('post').length > 1 || !components.get('post', 'index', 0).length) {
$('.bottom-post-bar').removeClass('hidden');
}
}

@ -4,7 +4,7 @@ define('components', function() {
components.core = {
'post': function(name, value) {
return $('[data-' + name + '="' + value + '"]');
return $('[component="post"][data-' + name + '="' + value + '"]');
},
'post/content': function(pid) {
return components.core.post('pid', pid).find('[component="post/content"]');

@ -73,6 +73,9 @@
};
helpers.generateCategoryBackground = function(category) {
if (!category) {
return '';
}
var style = [];
if (category.bgColor) {

@ -8,6 +8,14 @@ navigationController.get = function(req, res, next) {
return next(err);
}
data.enabled.forEach(function(enabled, index) {
enabled.index = index;
enabled.selected = index === 0;
});
data.navigation = data.enabled.slice();
res.render('admin/general/navigation', data);
});
};

@ -301,16 +301,18 @@ module.exports = function(privileges) {
};
privileges.categories.give = function(privileges, cid, groupName, callback) {
async.each(privileges, function(privilege, next) {
groups.join('cid:' + cid + ':privileges:groups:' + privilege, groupName, next);
}, callback);
giveOrRescind(groups.join, privileges, cid, groupName, callback);
};
privileges.categories.rescind = function(privileges, cid, groupName, callback) {
giveOrRescind(groups.leave, privileges, cid, groupName, callback);
};
function giveOrRescind(method, privileges, cid, groupName, callback) {
async.each(privileges, function(privilege, next) {
groups.leave('cid:' + cid + ':privileges:groups:' + privilege, groupName, next);
method('cid:' + cid + ':privileges:groups:' + privilege, groupName, next);
}, callback);
};
}
privileges.categories.canMoveAllTopics = function(currentCid, targetCid, uid, callback) {
async.parallel({

@ -188,6 +188,10 @@ module.exports = function(privileges) {
], callback);
};
privileges.topics.canEdit = function(tid, uid, callback) {
privileges.topics.isOwnerOrAdminOrMod(tid, uid, callback);
};
privileges.topics.isOwnerOrAdminOrMod = function(tid, uid, callback) {
helpers.some([
function(next) {

@ -350,19 +350,15 @@ function sortPosts(posts, data) {
});
}
} else {
if (data.sortDirection === 'desc') {
posts.sort(function(p1, p2) {
if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return -1;
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return 1;
return 0;
});
} else {
posts.sort(function(p1, p2) {
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) return -1;
if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) return 1;
return 0;
});
}
var direction = data.sortDirection === 'desc' ? 1 : -1;
posts.sort(function(p1, p2) {
if (p1[fields[0]][fields[1]] > p2[fields[0]][fields[1]]) {
return direction;
} else if (p1[fields[0]][fields[1]] < p2[fields[0]][fields[1]]) {
return -direction;
}
return 0;
});
}
}

@ -34,19 +34,23 @@ module.exports = function(SocketTopics) {
}
var set = 'tid:' + data.tid + ':posts';
if (results.settings.topicPostSort === 'most_votes') {
set = 'tid:' + data.tid + ':posts:votes';
}
var reverse = results.settings.topicPostSort === 'newest_to_oldest' || results.settings.topicPostSort === 'most_votes';
var start = Math.max(0, parseInt(data.after, 10));
var infScrollPostsPerPage = 10;
if (data.direction === -1) {
start = start - (reverse ? -infScrollPostsPerPage : infScrollPostsPerPage);
}
if (reverse) {
start = results.topic.postcount - 1 - start;
if (results.settings.topicPostSort === 'most_votes') {
set = 'tid:' + data.tid + ':posts:votes';
if (data.direction > 0) {
if (reverse) {
start = results.topic.postcount - start;
}
} else {
if (reverse) {
start = results.topic.postcount - start - infScrollPostsPerPage - 1;
} else {
start = start - infScrollPostsPerPage - 1;
}
}

@ -3,32 +3,68 @@
<div class="panel panel-default">
<div class="panel-heading">Active Navigation</div>
<div class="panel-body">
<div class="clearfix">
<ul id="main-nav" class="nav navbar-nav">
<!-- BEGIN navigation -->
<li data-index="{navigation.index}" class="{navigation.class} <!-- IF navigation.selected --> active <!-- ENDIF navigation.selected -->">
<a href="#" title="{navigation.route}" id="{navigation.id}">
<!-- IF navigation.iconClass -->
<i class="fa fa-fw {navigation.iconClass}"></i>
<!-- ENDIF navigation.iconClass -->
<!-- IF navigation.text -->
<span class="{navigation.textClass}">{navigation.text}</span>
<!-- ENDIF navigation.text -->
</a>
</li>
<!-- END navigation -->
</ul>
</div>
<hr/>
<ul id="enabled">
<!-- BEGIN enabled -->
<li class="well">
<li data-index="{enabled.index}" class="well <!-- IF !enabled.selected -->hidden<!-- ENDIF !enabled.selected -->">
<form>
<div class="row">
<div class="col-sm-6">
<label>ID: <small>optional</small>
<input class="form-control" type="text" name="id" value="{enabled.id}" />
</label>
<label>Title: <small>shown upon mouseover</small>
<div class="from-group">
<label>Icon Class:</label>
<br/>
<span class="iconPicker"><i class="fa fa-2x {enabled.iconClass}"></i>
<input class="form-control" type="hidden" name="iconClass" value="{enabled.iconClass}" />
</span>
</div>
<div class="from-group">
<label>Route: <small>ex. /unread</small></label>
<input class="form-control" type="text" name="route" value="{enabled.route}" />
</div>
<div class="form-group">
<label>Title: <small>shown upon mouseover</small></label>
<input class="form-control" type="text" name="title" value="{enabled.title}" />
</label>
<label>Icon Class: <small><a href="http://fortawesome.github.io/Font-Awesome/cheatsheet/" target="_blank">pick one</a></small>
<input class="form-control" type="text" name="iconClass" value="{enabled.iconClass}" />
</label>
</div>
</div>
<div class="col-sm-6">
<label>Route: <small>ex. /unread</small>
<input class="form-control" type="text" name="route" value="{enabled.route}" />
</label>
<label>Text:
<div class="from-group">
<label>Text:</label>
<input class="form-control" type="text" name="text" value="{enabled.text}" />
</label>
<label>Text Class: <small>optional</small>
</div>
<div class="from-group">
<label>Text Class: <small>optional</small></label>
<input class="form-control" type="text" name="textClass" value="{enabled.textClass}" />
</label>
</div>
<div class="form-group">
<label>ID: <small>optional</small></label>
<input class="form-control" type="text" name="id" value="{enabled.id}" />
</div>
</div>
</div>
@ -73,13 +109,21 @@
<div class="panel-heading">Available Menu Items</div>
<div class="panel-body">
<ul id="available">
<li data-id="custom" class="alert alert-warning">
<li data-id="custom" class="clearfix">
<div data-id="custom" class="drag-item alert alert-warning pull-left">
<i class="fa fa-fw fa-navicon"></i>
</div>
<strong>Custom Route</strong>
</li>
<!-- BEGIN available -->
<li data-id="@index" class="alert <!-- IF available.core -->alert-info<!-- ELSE -->alert-success<!-- ENDIF available.core -->">
<strong>{available.text}</strong> {available.route}
<span class="pull-right badge"><!-- IF available.core -->core<!-- ELSE -->plugin<!-- ENDIF available.core --></span>
<li data-id="@index" class="clearfix">
<div data-id="@index" class="drag-item alert <!-- IF available.core -->alert-info<!-- ELSE -->alert-success<!-- ENDIF available.core --> pull-left">
<i class="fa fa-fw <!-- IF available.iconClass -->{available.iconClass}<!-- ELSE -->fa-navicon<!-- ENDIF available.iconClass -->"></i>
</div>
<p>
<strong>{available.text}</strong> {available.route} <br/>
<!-- IF available.core --> core <!-- ELSE --> plugin <!-- ENDIF available.core -->
</p>
</li>
<!-- END available -->
</ul>

@ -1 +1 @@
data-index="{posts.index}" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.user.username}" data-userslug="{posts.user.userslug}" data-timestamp="{posts.timestamp}" data-votes="{posts.votes}" itemscope itemtype="http://schema.org/Comment"
data-index="{posts.index}" data-pid="{posts.pid}" data-uid="{posts.uid}" data-username="{posts.user.username}" data-userslug="{posts.user.userslug}" itemscope itemtype="http://schema.org/Comment"
Loading…
Cancel
Save