diff --git a/public/css/style.less b/public/css/style.less
index e9743a8533..96232cbb47 100644
--- a/public/css/style.less
+++ b/public/css/style.less
@@ -827,6 +827,7 @@ body .navbar .nodebb-inline-block {
display: none;
-moz-opacity: 0.5;
opacity: 0.5;
+ margin-top: 0;
-webkit-transition: opacity 250ms ease-in;
-moz-transition: opacity 250ms ease-in;
-ms-transition: opacity 250ms ease-in;
@@ -862,18 +863,31 @@ body .navbar .nodebb-inline-block {
.post-window {
position: fixed;
bottom: 45px;
- height: 30%;
+ height: 450px;
+ display: none;
- div {
+ > div {
height: 100%;
- background: rgba(64, 64, 64, 0.2);
+ background: rgba(64, 64, 64, 0.6);
+
+ .btn-toolbar {
+ width: 90%;
+ margin: 0 auto;
+
+ span {
+ color: white;
+ }
+ }
input {
width: 100%;
text-align: center;
- background: none;
+ background: rgba(255, 255, 255, 0.9);
border: none;
padding: 0.5em 0;
+ -webkit-border-radius: 0px;
+ -moz-border-radius: 0px;
+ border-radius: 0px;
}
textarea {
@@ -885,6 +899,13 @@ body .navbar .nodebb-inline-block {
margin: 1em auto;
resize: none;
color: white;
+ height: 330px;
}
}
+}
+
+@media (max-width: 979px) {
+ .post-window {
+ position: static;
+ }
}
\ No newline at end of file
diff --git a/public/src/forum/category.js b/public/src/forum/category.js
index 582366f119..620ba2eae9 100644
--- a/public/src/forum/category.js
+++ b/public/src/forum/category.js
@@ -6,7 +6,9 @@
var new_post = document.getElementById('new_post');
new_post.onclick = function() {
- app.open_post_window('topic', cid);
+ require(['composer'], function(cmp) {
+ cmp.push(0, cid);
+ });
}
ajaxify.register_events([
diff --git a/public/src/forum/topic.js b/public/src/forum/topic.js
index c11128eb86..c180adfa66 100644
--- a/public/src/forum/topic.js
+++ b/public/src/forum/topic.js
@@ -430,16 +430,23 @@
else div = '#' + div;
jQuery(div + ' .post_reply').click(function() {
- if (thread_state.locked !== '1') app.open_post_window('reply', tid, topic_name);
+ if (thread_state.locked !== '1') {
+ require(['composer'], function(cmp) {
+ cmp.push(tid);
+ });
+ }
});
jQuery(div + ' .quote').click(function() {
- if (thread_state.locked !== '1') app.open_post_window('quote', tid, topic_name);
-
- var pid = $(this).parents('li').attr('data-pid');
-
- $('#post_content').val('> ' + $('#content_' + pid).html() + '\n');
- console.log("POST ID "+pid);
+ if (thread_state.locked !== '1') {
+ var pid = $(this).parents('li').attr('data-pid');
+
+ require(['composer'], function(cmp) {
+ cmp.push(tid);
+ console.log($('.post-window textarea'), $('#content_' + pid));
+ $('.post-window textarea').val('> ' + $('#content_' + pid).html() + '\n');
+ });
+ }
});
jQuery(div + ' .edit, ' + div + ' .delete').each(function() {
diff --git a/public/src/modules/composer.js b/public/src/modules/composer.js
index 0713134035..c6ae89ae7d 100644
--- a/public/src/modules/composer.js
+++ b/public/src/modules/composer.js
@@ -1,7 +1,8 @@
define(function() {
var composer = {
initialized: false,
- posts: [],
+ active: 0,
+ posts: {},
btnContainer: undefined,
postContainer: undefined,
listEl: undefined
@@ -9,7 +10,7 @@ define(function() {
composer.init = function() {
// Create the fixed bottom bar
- var contentEl = document.getElementById('content');
+ var footerEl = document.getElementById('footer');
composer.btnContainer = document.createElement('div');
composer.btnContainer.innerHTML = '
';
@@ -17,21 +18,122 @@ define(function() {
composer.postContainer = document.createElement('div');
composer.postContainer.className = 'post-window row-fluid';
- composer.postContainer.innerHTML = '';
+ composer.postContainer.innerHTML = '' +
+ '
' +
+ '
' +
+ '
' +
+ '
';
composer.listEl = composer.btnContainer.querySelector('ul');
- document.body.insertBefore(composer.btnContainer, contentEl);
+ document.body.insertBefore(composer.btnContainer, footerEl);
document.body.insertBefore(composer.postContainer, composer.btnContainer);
socket.on('api:composer.push', function(threadData) {
- console.log(threadData);
var uuid = utils.generateUUID(),
btnEl = document.createElement('li');
btnEl.innerHTML = '
' + threadData.title + '';
btnEl.setAttribute('data-uuid', uuid);
composer.listEl.appendChild(btnEl);
- composer.posts.push(uuid);
+ composer.posts[uuid] = {
+ tid: threadData.tid,
+ cid: threadData.cid,
+ title: threadData.tid ? threadData.title : '',
+ body: ''
+ };
+ composer.active++;
composer.update();
+ composer.load(uuid);
+ });
+
+ // Posts bar events
+ $(composer.btnContainer).on('click', 'li', function() {
+ var uuid = this.getAttribute('data-uuid');
+ composer.load(uuid);
+ });
+
+ // Post Window events
+ var jPostContainer = $(composer.postContainer),
+ postContentEl = composer.postContainer.querySelector('textarea')
+ jPostContainer.on('change', 'input, textarea', function() {
+ var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
+ if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
+ else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
+ });
+ jPostContainer.on('click', '.action-bar button', function() {
+ var action = this.getAttribute('data-action'),
+ uuid = $(this).parents('.post-window').attr('data-uuid');
+ switch(action) {
+ case 'post': composer.post(uuid); break;
+ case 'minimize': composer.minimize(); break;
+ case 'discard': composer.discard(uuid); break;
+ }
+ });
+ jPostContainer.on('click', '.formatting-bar span', function() {
+ var iconClass = this.querySelector('i').className,
+ cursorEnd = postContentEl.value.length,
+ selectionStart = postContentEl.selectionStart,
+ selectionEnd = postContentEl.selectionEnd,
+ selectionLength = selectionEnd - selectionStart;
+
+ switch(iconClass) {
+ case 'icon-bold':
+ if (selectionStart === selectionEnd) {
+ // Nothing selected
+ postContentEl.value = postContentEl.value + '**bolded text**';
+ postContentEl.selectionStart = cursorEnd+2;
+ postContentEl.selectionEnd = postContentEl.value.length - 2;
+ } else {
+ // Text selected
+ postContentEl.value = postContentEl.value.slice(0, selectionStart) + '**' + postContentEl.value.slice(selectionStart, selectionEnd) + '**' + postContentEl.value.slice(selectionEnd);
+ postContentEl.selectionStart = selectionStart + 2;
+ postContentEl.selectionEnd = selectionEnd + 2;
+ }
+ break;
+ case 'icon-italic':
+ if (selectionStart === selectionEnd) {
+ // Nothing selected
+ postContentEl.value = postContentEl.value + '*italicised text*';
+ postContentEl.selectionStart = cursorEnd+1;
+ postContentEl.selectionEnd = postContentEl.value.length - 1;
+ } else {
+ // Text selected
+ postContentEl.value = postContentEl.value.slice(0, selectionStart) + '*' + postContentEl.value.slice(selectionStart, selectionEnd) + '*' + postContentEl.value.slice(selectionEnd);
+ postContentEl.selectionStart = selectionStart + 1;
+ postContentEl.selectionEnd = selectionEnd + 1;
+ }
+ break;
+ case 'icon-list':
+ // Nothing selected
+ postContentEl.value = postContentEl.value + "\n\n* list item";
+ postContentEl.selectionStart = cursorEnd+4;
+ postContentEl.selectionEnd = postContentEl.value.length;
+ break;
+ case 'icon-link':
+ if (selectionStart === selectionEnd) {
+ // Nothing selected
+ postContentEl.value = postContentEl.value + '[link text](link url)';
+ postContentEl.selectionStart = cursorEnd+12;
+ postContentEl.selectionEnd = postContentEl.value.length - 1;
+ } else {
+ // Text selected
+ postContentEl.value = postContentEl.value.slice(0, selectionStart) + '[' + postContentEl.value.slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.value.slice(selectionEnd);
+ postContentEl.selectionStart = selectionStart + selectionLength + 3;
+ postContentEl.selectionEnd = selectionEnd + 11;
+ }
+ break;
+ }
});
composer.initialized = true;
@@ -39,7 +141,7 @@ define(function() {
composer.update = function() {
if (composer.initialized) {
- if (composer.posts.length > 0) {
+ if (composer.active > 0) {
composer.btnContainer.setAttribute('data-active', '1');
} else {
composer.btnContainer.removeAttribute('data-active');
@@ -47,13 +149,87 @@ define(function() {
}
}
- composer.push = function(tid) {
- socket.emit('api:composer.push', tid);
+ composer.push = function(tid, cid) {
+ socket.emit('api:composer.push', {
+ tid: tid,
+ cid: cid
+ });
+ }
+
+ composer.load = function(post_uuid) {
+ var post_data = composer.posts[post_uuid],
+ titleEl = composer.postContainer.querySelector('input'),
+ bodyEl = composer.postContainer.querySelector('textarea');
+
+ composer.postContainer.style.display = 'block';
+ composer.postContainer.style.bottom = composer.btnContainer.offsetHeight + "px";
+ composer.postContainer.setAttribute('data-uuid', post_uuid);
+ if (post_data.tid > 0) {
+ titleEl.value = 'Replying to: ' + post_data.title;
+ titleEl.readonly = true;
+ } else {
+ titleEl.value = post_data.title;
+ }
+ bodyEl.value = post_data.body
+
+ // Highlight the button
+ $('.posts-bar li').removeClass('active');
+ composer.btnContainer.querySelector('[data-uuid="' + post_uuid + '"]').className += ' active';
+ }
+
+ composer.post = function(post_uuid) {
+ // Check title and post length
+ var postData = composer.posts[post_uuid],
+ titleEl = composer.postContainer.querySelector('input'),
+ bodyEl = composer.postContainer.querySelector('textarea');
+ if (titleEl.value.length <= 3) {
+ return app.alert({
+ type: 'error',
+ timeout: 5000,
+ title: 'Title too short',
+ message: "Please enter a longer title.",
+ alert_id: 'post_error'
+ });
+ }
+
+ // Still here? Let's post.
+ if (postData.tid === 0 || postData.cid) {
+ socket.emit('api:topics.post', {
+ 'title' : titleEl.value,
+ 'content' : bodyEl.value,
+ 'category_id' : postData.cid
+ });
+ } else if (parseInt(postData.tid) > 0) {
+ socket.emit('api:posts.reply', {
+ 'topic_id' : postData.tid,
+ 'content' : bodyEl.value
+ });
+ }
+
+ composer.discard(post_uuid);
+ }
+
+ composer.discard = function(post_uuid) {
+ if (composer.posts[post_uuid]) {
+ // Commit
+ var btnEl = composer.btnContainer.querySelector('[data-uuid="' + post_uuid + '"]');
+ delete composer.posts[post_uuid];
+ composer.active--;
+ btnEl.parentNode.removeChild(btnEl);
+ composer.minimize();
+ composer.update();
+ }
+ }
+
+ composer.minimize = function() {
+ composer.postContainer.style.display = 'none';
+ $('.posts-bar li').removeClass('active');
}
composer.init();
return {
- push: composer.push
+ push: composer.push,
+ load: composer.load
};
});
\ No newline at end of file
diff --git a/src/websockets.js b/src/websockets.js
index 09cea3505d..9e00d73889 100644
--- a/src/websockets.js
+++ b/src/websockets.js
@@ -305,10 +305,22 @@ var SocketIO = require('socket.io').listen(global.server, { log:false }),
});
});
- socket.on('api:composer.push', function(tid) {
- topics.get_topic(tid, uid, function(topicData) {
- socket.emit('api:composer.push', topicData);
- });
+ socket.on('api:composer.push', function(data) {
+ if (data.tid > 0) {
+ topics.get_topic(data.tid, uid, function(topicData) {
+ topicData.tid = data.tid;
+ socket.emit('api:composer.push', topicData);
+ });
+ } else {
+ user.getUserField(uid, 'username', function(username) {
+ socket.emit('api:composer.push', {
+ tid: 0,
+ cid: data.cid,
+ username: username,
+ title: 'New Topic'
+ });
+ });
+ }
});
});