Compare commits

..

552 Commits

Author SHA1 Message Date
Julian Lam
48a7c48f7b 0.0.7 2013-09-30 17:05:28 -04:00
Julian Lam
7a919fbac4 dropping down to 0.0.6 for re-release 2013-09-30 17:05:22 -04:00
Julian Lam
181220621e fixed issue with server crashing on post 2013-09-30 17:01:39 -04:00
Julian Lam
249c45dfe2 0.0.7 2013-09-30 16:30:11 -04:00
Julian Lam
b19d84f1a7 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-30 16:28:50 -04:00
Julian Lam
865edb70c2 added meta description to topics, closes #362 2013-09-30 16:28:37 -04:00
Baris Usakli
e78369f0fa use User.isAdmin instead 2013-09-30 16:24:00 -04:00
Baris Usakli
d40a6a5c3f added adminitstrator value to userData for admin users page 2013-09-30 16:19:20 -04:00
Baris Usakli
cb7768d095 cant ban yourself 2013-09-30 16:04:54 -04:00
Baris Usakli
4290516d29 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-30 16:03:06 -04:00
Baris Usakli
6a7a2301ee removed admin and delete buttons from template 2013-09-30 16:03:00 -04:00
Julian Lam
9a79c58e33 closed #361 - groups admin modal had an extra button that did nothing 2013-09-30 16:02:38 -04:00
Julian Lam
4c39c1ec30 fixed issue with install script erroring out if redis host is not defined 2013-09-30 15:52:10 -04:00
psychobunny
571cb8b44f readded back admin css for users 2013-09-30 15:31:29 -04:00
Baris Usakli
4207792ffd added app alert if template data cant be loaded 2013-09-30 15:12:14 -04:00
Baris Usakli
9f67282a79 admin site will display reconnecting notification when server is offline closes #346 2013-09-30 13:04:03 -04:00
Julian Lam
ee71c1cf0d Merge branch 'iamcardinal-merge' 2013-09-30 11:23:41 -04:00
Julian Lam
06f2284bcd added output to show bound address on instance start 2013-09-30 11:23:11 -04:00
Julian Lam
8cbe79655a emptying Meta.config on init (Just to be safe) 2013-09-30 11:18:02 -04:00
Julian Lam
a4c1d733b7 fixed issue where setOnEmpty would fail in install.js as Meta.config was
not defined (as it had not been initted yet)
2013-09-30 11:17:12 -04:00
Julian Lam
839649d42f Merge branch 'master' of https://github.com/iamcardinal/NodeBB-enhancement into iamcardinal-merge 2013-09-30 11:12:38 -04:00
Quinton Marchi
504e2aac4a Adds: regex for ports, mini for public/src/forum
+ Regular expression for port during install
+ Minification for public/src/forum JS files
+ Minification for public/src/forum/admin JS files
2013-09-30 02:42:34 -04:00
Minami
eafb41602c Edited to simplify install
Removed the two steps and made it default to 0.0.0.0, edited the
webserver.js to reflect the changes.
2013-09-29 21:03:29 -05:00
Baris Soner Usakli
2dcc4172c4 closes #357 2013-09-29 21:29:43 -04:00
Baris Soner Usakli
f96a711298 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-29 21:03:19 -04:00
Baris Soner Usakli
ad28b9b339 fixed crash in meta 2013-09-29 21:03:16 -04:00
Julian Lam
8dd8536f6b closes #317 - hitting discard on a post with content will now throw a
bootbox confirmation modal
2013-09-29 20:38:45 -04:00
Julian Lam
7bf5b2ec57 changed fireHook to be error-first, closes #319 2013-09-29 20:33:35 -04:00
Julian Lam
aa731aa894 Merge branch 'middleware_refactor' 2013-09-29 20:28:50 -04:00
Julian Lam
c58cb257dc closes #349 - loading middlewares using async instead of crazy
middleware-stack modifying shenanigans
2013-09-29 20:27:52 -04:00
Baris Soner Usakli
06f59cf853 closes #310 2013-09-29 15:08:00 -04:00
Baris Soner Usakli
83b4b5434f fixed username button title 2013-09-29 14:27:31 -04:00
Baris Soner Usakli
9de5214a2f closes #353 2013-09-29 14:18:43 -04:00
Julian Lam
f08067bab2 fixes #356 - issue where accessing a NodeBB using a different machine name
from one that it was set up with causing template data to not load, as it
was being accessed via absolute url, instead of relative url
2013-09-29 13:47:27 -04:00
Julian Lam
1160f39cb0 revamped reconnection notification, closes #354 2013-09-28 22:05:10 -04:00
Julian Lam
d633ecd160 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-28 17:45:27 -04:00
Julian Lam
4cea313060 updated getPostsByPids method to resolve premature return issue, and
refactored it to use async.map instead of eachSeries
2013-09-28 17:44:18 -04:00
Baris Soner Usakli
9b7f8076eb removed console.log 2013-09-28 16:18:55 -04:00
Quinton Marchi
571259f241 Adds hostname support, fixes #343
Works from previous testing but I haven't tried this personal revision
of the code. Take it with a grain of salt.
2013-09-28 03:55:06 -04:00
psychobunny
607ee8bbc1 added outgoing.less, fixed modern.less theme, fixed #316 2013-09-27 13:11:01 -04:00
psychobunny
36d2f74887 fixed unread category view 2013-09-27 12:59:36 -04:00
Julian Lam
cdaf409a99 monkey-patching getPostsByPids so that it returns ISOString instead of relativeTime 2013-09-27 11:53:25 -04:00
Julian Lam
fba487d8a2 removing extra comma in package.json 2013-09-27 11:48:01 -04:00
Julian Lam
3ab7306199 linting, minor bugfix, moving hiredis to optional deps 2013-09-27 11:47:16 -04:00
Julian Lam
2e4e94d5f8 Merge branch 'redis-optimizations' of https://github.com/adarqui/NodeBB into adarqui-redis-optimizations 2013-09-27 11:44:40 -04:00
Baris Usakli
3348ed3524 fixed email key 2013-09-26 13:56:36 -04:00
Baris Usakli
942a21b4be fixed email meta changes 2013-09-26 13:31:38 -04:00
Baris Usakli
9c14618d55 dont redirect back to reset password after login 2013-09-26 12:50:24 -04:00
Baris Usakli
7d50551392 remove throw in email send 2013-09-26 12:29:35 -04:00
Baris Usakli
f2c1a92513 closes #215 2013-09-25 13:08:11 -04:00
Baris Usakli
fe6595cd2a Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-25 12:30:17 -04:00
Baris Usakli
c2aac916aa fixed topics not appearing after a post in category view 2013-09-25 12:30:07 -04:00
psychobunny
e161b5387b fixed untranslated language on dynamic new posts and infinite loading 2013-09-25 12:16:06 -04:00
Baris Usakli
585e5cd88f closes #341 2013-09-25 12:11:32 -04:00
psychobunny
1637ffc5dc re-adding teaser 2013-09-25 11:40:05 -04:00
psychobunny
a28797ee03 fix for recent view teasers 2013-09-25 11:26:28 -04:00
Julian Lam
49e28f9d1e fixing bug where outgoing links didn't actually let you leave... oops! 2013-09-25 11:25:48 -04:00
Baris Usakli
4a7cd664fd fixed a bug in template parsing 2013-09-24 16:46:45 -04:00
Baris Usakli
253e11d55b cleaned up websockets.js 2013-09-24 16:02:14 -04:00
Baris Usakli
eb1c1c78d4 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-24 15:59:51 -04:00
Baris Usakli
0b0d64b52e logout/login changes, updateHeader changes 2013-09-24 15:59:08 -04:00
Julian Lam
1ae51ef5ea redis export function (commented out for now) 2013-09-24 15:36:17 -04:00
psychobunny
de1e3230f5 fixed topics and admin views, broken after merge 2013-09-24 15:23:16 -04:00
psychobunny
1fbc038e64 fixed conflicts, added new language key for users/online 2013-09-24 15:15:39 -04:00
psychobunny
cd63dd429b finalizing todo's before merging 2013-09-24 14:59:13 -04:00
Baris Usakli
fbfdf561fc Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-24 14:57:01 -04:00
Baris Usakli
83d5a84edd login redirects back 2013-09-24 14:56:51 -04:00
psychobunny
5abf02c6d1 default language, en 2013-09-24 14:55:46 -04:00
psychobunny
b8037845d6 registration.tpl; translator variable system 2013-09-24 14:50:38 -04:00
Julian Lam
c80e2552b2 express-namespaceing routes 2013-09-24 14:18:41 -04:00
psychobunny
1a1fea535b header language 2013-09-24 14:17:36 -04:00
psychobunny
5c7da4b686 fixed serverside templating; footer, logout internationalization; updated more global lang keys 2013-09-24 14:14:26 -04:00
Julian Lam
b6ee89a6d8 fixed typo in plugins page 2013-09-24 14:12:26 -04:00
psychobunny
2d8e6bd980 category language 2013-09-24 13:31:44 -04:00
Julian Lam
a173d61464 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-24 13:31:10 -04:00
Baris Usakli
8f04a136c8 fixed whitespace 2013-09-24 13:31:02 -04:00
Julian Lam
b7675e1ec7 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-24 13:30:59 -04:00
Julian Lam
964fbfe2bb fixes to get nodebb to at least load on IE 2013-09-24 13:30:54 -04:00
Baris Usakli
8fb8956c0d closes #331 2013-09-24 13:30:09 -04:00
psychobunny
c11920ac08 internationalization: 404 and 403 2013-09-24 13:22:10 -04:00
Baris Usakli
9a4eb26246 closes #332 2013-09-24 13:06:14 -04:00
Julian Lam
0d9958afe7 HOTFIX for scripts 2013-09-24 13:01:18 -04:00
Julian Lam
d6dd74b50a removed extra debug log 2013-09-24 12:34:54 -04:00
Julian Lam
d5437ca8fa Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-24 12:34:17 -04:00
Julian Lam
14720057c2 serving unminified libraries in development mode, minified otherwise 2013-09-24 12:33:51 -04:00
Baris Usakli
170ed8cc01 fixed conflict 2013-09-24 12:31:28 -04:00
Baris Usakli
25576eb35a closes #329 2013-09-24 12:29:51 -04:00
Julian Lam
66cb1fb6ad removed console.log from socket connection success handler 2013-09-24 11:02:06 -04:00
Julian Lam
bcc65fd879 closed #328 2013-09-24 08:38:19 -04:00
Julian Lam
99440585e6 Merge branch 'master' into optimize-22-09-2013
Conflicts:
	package.json
2013-09-23 20:04:09 -04:00
Baris Usakli
290d69d14a remember where the topic was left off 2013-09-23 17:41:54 -04:00
Baris Usakli
4fce06677f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-23 16:49:11 -04:00
Baris Usakli
f53cd8b5b6 category view fix 2013-09-23 16:49:01 -04:00
Julian Lam
da59f47624 adding missing files 2013-09-23 15:46:40 -04:00
Julian Lam
521586f08f Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-23 15:45:23 -04:00
Julian Lam
f39a7ada7c skipping minification if the minFile is already up-to-date - issue #322 2013-09-23 15:45:03 -04:00
Baris Usakli
bae9f46d8b small fixes to search 2013-09-23 15:31:52 -04:00
Julian Lam
d7ea24e218 issue #322 - minifying css file and added jquery to minified compilation 2013-09-23 15:27:46 -04:00
Julian Lam
e621d7e601 closed #322 - autominifying client-side assets on load 2013-09-23 15:21:34 -04:00
Baris Usakli
aa6eff4c54 one more itme 2013-09-23 14:58:06 -04:00
Baris Usakli
f36583e676 possible fix for users/online duplication 2013-09-23 14:55:34 -04:00
Baris Usakli
0a0a91d4b2 remove listener 2013-09-23 14:51:59 -04:00
Baris Usakli
6c70f9e308 only collapse header if its expanded 2013-09-23 14:51:16 -04:00
Baris Usakli
b25c3d8b67 closes #324 2013-09-23 14:40:31 -04:00
Baris Usakli
bade99d069 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-23 13:43:25 -04:00
Baris Usakli
776b51fef7 closes #323 2013-09-23 13:43:15 -04:00
Julian Lam
da2d014f00 added IRC link to wiki 2013-09-23 12:55:21 -04:00
Julian Lam
7fdf83089d linting 2013-09-23 12:50:27 -04:00
Julian Lam
043aafd7b7 closed #315 2013-09-22 20:40:10 -04:00
Julian Lam
3254979568 re: #321, removed cheerio dep from package.json 2013-09-22 15:28:22 -04:00
Julian Lam
6fd202fe36 closed #321, removed cheerio use altogether, and made changes to the
client side to ajaxify the user to /outgoing when an external link is
clicked
2013-09-22 15:27:10 -04:00
Julian Lam
5f00f1e18e renamed "toHTML" method to "parse" 2013-09-22 13:54:21 -04:00
Julian Lam
c940ce3329 camelCased get_latest_undeleted_pid method 2013-09-22 13:33:05 -04:00
Julian Lam
2366e2b209 refactored get_latest_undeleted_pid to not blindly call getPostsByTid in
order to determine post deletion. (issue #320)
2013-09-22 13:28:10 -04:00
Julian Lam
2b7fd3c9d1 interim commit 2013-09-22 12:55:03 -04:00
Andrew Darqui
d3e37d1716 slight variable name changes & remove unused var from getPostsByPids, removed underscore dependency 2013-09-22 00:36:56 -04:00
Andrew Darqui
d2b858c997 slight variable name changes & remove unused var from getPostsByPids, removed underscore dependency 2013-09-22 00:36:40 -04:00
Baris Soner Usakli
de34c7580f closes #314 2013-09-22 00:29:31 -04:00
Baris Soner Usakli
bcb492751c closes #313, also fixed progressbar 2013-09-22 00:01:31 -04:00
adarqui
e26cc79819 redis multi pipeline used for getPostsByPids 2013-09-21 22:47:27 -04:00
adarqui
98cb2d4c17 a little logic to allow nodebb to use a unix domain socket rather than tcp/ip. unix dom sock has less overhead/latency. 2013-09-21 15:42:54 -04:00
adarqui
2fc569715c hiredis & underscore packages 2013-09-21 15:26:52 -04:00
Julian Lam
3eb594df43 fixing timestamps in category view (topics listing) for noscript 2013-09-20 20:48:14 -04:00
Julian Lam
8243019a60 closed #312 2013-09-20 20:43:02 -04:00
psychobunny
5a10f7fcfe init variables in translator 2013-09-20 17:31:53 -04:00
psychobunny
5e89caf358 internationalization: topic.json 2013-09-20 17:16:01 -04:00
psychobunny
35b40ef650 internationalization: unread.tpl 2013-09-20 17:03:30 -04:00
psychobunny
56b7d6cb7c internationalization: users 2013-09-20 17:00:33 -04:00
psychobunny
8b8a890ac9 removed unnecessary warning message to anonymous users attempting to unfavourite posts 2013-09-20 16:57:25 -04:00
psychobunny
5645bcee2d topic.json en lang; tested serverside internationalization in favourites; fixed some keys in login 2013-09-20 16:43:50 -04:00
psychobunny
a631707db4 app.js + todo 2013-09-20 16:02:25 -04:00
psychobunny
451ffafb9e finished initial client side & server side language parsing methods; integrated preloading into ajaxify and server app.js 2013-09-20 16:01:52 -04:00
psychobunny
b5274a0d91 init 2013-09-20 14:35:32 -04:00
Baris Soner Usakli
db4dc03abc added timeago to unread page 2013-09-19 20:49:52 -04:00
Baris Soner Usakli
6be5bcc4c8 closes #308 2013-09-19 20:43:56 -04:00
Baris Usakli
21efda4a84 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-19 16:40:11 -04:00
Baris Usakli
082375f129 link fade outs 2013-09-19 16:40:02 -04:00
Julian Lam
1204859263 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-19 16:38:20 -04:00
Julian Lam
c8b5d1561e minor CSS fixes to post timeago text in topic view 2013-09-19 16:38:04 -04:00
Baris Usakli
6912e77d25 removed data-pid from buttons 2013-09-19 16:25:51 -04:00
Baris Usakli
d84d096e7e added post links 2013-09-19 16:24:30 -04:00
Baris Usakli
29c17fd32e fixed user infinite loading 2013-09-19 15:50:28 -04:00
Baris Usakli
d721320af5 added jquery.timeago.js 2013-09-19 15:10:03 -04:00
Baris Usakli
efef9c864c closes #306 2013-09-19 15:02:35 -04:00
Baris Usakli
27cb837b08 closes #303 2013-09-19 12:29:25 -04:00
psychobunny
047fcbe63f style revert for cerulean 2013-09-18 17:06:32 -04:00
psychobunny
dbe0b1551b creating a separate theme for my metro ui shenanigans, follow up revert on cerulean incoming 2013-09-18 17:02:26 -04:00
psychobunny
8f7b047b7a fixed padding in category-box 2013-09-18 16:58:12 -04:00
psychobunny
a38ace0df9 moved metro slide animation out of vanilla into cerulean, and rejigged the home page a bit 2013-09-18 16:56:34 -04:00
psychobunny
0b0b06e8b9 fixed users.tpl from fail merge earlier 2013-09-18 16:31:02 -04:00
psychobunny
f83b61903a screwing around with animations in cerulean; started modernui look and feel 2013-09-18 16:25:32 -04:00
Julian Lam
881fa41bb7 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-18 16:25:23 -04:00
Baris Usakli
f9442db96f closes #284 2013-09-18 16:23:08 -04:00
Julian Lam
64521494ce removed outdated socket.io client libs (socket.io package adds these routes anyway) 2013-09-18 16:22:35 -04:00
Julian Lam
e75e3399ab added link to upgrade instructions 2013-09-18 16:06:51 -04:00
Baris Usakli
e5ef498164 merged users.tpl 2013-09-18 16:05:01 -04:00
Baris Usakli
24e4be77d8 closes #304 2013-09-18 16:01:54 -04:00
psychobunny
0d55f2ef32 cerulean: switched to lucida grande / tahoma 2013-09-18 14:47:38 -04:00
psychobunny
8ec3371139 cerulean theme: new users view 2013-09-18 12:45:31 -04:00
Julian Lam
ccca4d2914 fixed issue where meta was called before nconf loaded 2013-09-18 12:15:29 -04:00
psychobunny
28704a6164 hinting 2013-09-18 10:50:02 -04:00
psychobunny
cf4ba9d1d3 format/jshint favourites.js 2013-09-18 10:44:54 -04:00
Julian Lam
c69e30c146 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-18 07:23:50 -04:00
psychobunny
fd32d75d3b jshinted categories.js 2013-09-17 15:54:08 -04:00
psychobunny
afa228fd91 jshint: temporarily setting unused flag to false, low priority right now compared to the rest of our issues 2013-09-17 15:49:07 -04:00
psychobunny
a01a7ae5fc fixed following.tpl and followers.tpl layout (broken from BS3 merge) 2013-09-17 15:46:22 -04:00
psychobunny
64b071f277 nconf and redis wrapper no longer global objects. jshinted app.js. updated sockets.io to latest 2013-09-17 15:38:57 -04:00
Julian Lam
af246ad0d7 Merge remote-tracking branch 'origin' 2013-09-17 15:14:15 -04:00
Julian Lam
56a87329ec removing extra line 2013-09-17 15:14:11 -04:00
Baris Usakli
eddddc694d closes #302 2013-09-17 14:37:01 -04:00
Baris Usakli
ce61138351 fixed conflicts 2013-09-17 13:42:07 -04:00
Baris Usakli
564662ee00 closes #294 2013-09-17 13:37:03 -04:00
psychobunny
678db837fd refactored post-block 2013-09-17 13:29:45 -04:00
psychobunny
c789f4230d formatted app.js, updated jshintrc to include node 2013-09-17 13:15:36 -04:00
psychobunny
1b9e451a6f formatting - server side admin 2013-09-17 13:10:58 -04:00
psychobunny
1780b343b4 formatting - routes 2013-09-17 13:10:14 -04:00
psychobunny
aea3181d27 formatting - server side core 2013-09-17 13:09:37 -04:00
psychobunny
c44461e33f formatting - template js admin side 2013-09-17 13:07:30 -04:00
psychobunny
221b9bc149 formatting - template associated js 2013-09-17 13:05:54 -04:00
psychobunny
dc41c6bc0d formatting - core client side 2013-09-17 13:04:40 -04:00
psychobunny
828d937dec .jsbeautify 2013-09-17 13:03:53 -04:00
psychobunny
cf889cfe25 .jshintrc, massive dehinting adventure to follow 2013-09-17 12:59:04 -04:00
psychobunny
6bab2523d5 fixes #276 popup alert appearing under admin content 2013-09-17 12:55:20 -04:00
psychobunny
d714b5c592 editorconfig end_of_line 2013-09-17 12:54:03 -04:00
psychobunny
08316496cd fixes #289 recent page not responsive 2013-09-16 13:53:11 -04:00
psychobunny
d5fd985f0d applied previous commit to default theme instead 2013-09-16 13:50:14 -04:00
psychobunny
eab7839195 fixes #298 footer flash onload 2013-09-16 13:48:32 -04:00
Julian Lam
18717dd6f5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-15 20:52:05 -04:00
Julian Lam
d249f411f8 closed #300 - issue where web socket connections were not working properly
with passworded redis instances
2013-09-15 20:51:36 -04:00
psychobunny
b85e2545d6 fixes #299 2013-09-15 16:32:30 -04:00
Julian Lam
79280b195e changed get_uid_by_userslug to be error-first 2013-09-15 10:15:23 -04:00
Julian Lam
4a18728e19 closed #297 2013-09-15 10:11:29 -04:00
Julian Lam
b7498416fa fixing redisstore sessions with passworded redis stores 2013-09-15 09:23:48 -04:00
Julian Lam
49b201db0f added heroku support for dynamic port reading via process.env 2013-09-15 09:09:44 -04:00
Julian Lam
be4d6761b1 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-15 07:58:07 -04:00
Julian Lam
c01ba1a3cb closed #296 2013-09-15 07:58:02 -04:00
Baris Soner Usakli
af59ff3209 closes #286, closes #287 2013-09-14 22:28:50 -04:00
Baris Soner Usakli
b0a37c6ac5 closes #293 2013-09-14 22:04:03 -04:00
Julian Lam
8b3fd9d1e9 fixed bug where deleted threads caused thread tools to disappear (js
error)
2013-09-14 11:44:06 -04:00
Julian Lam
95b16690e0 altering api route to use next() instead of echoing 404... 2013-09-14 11:01:28 -04:00
Julian Lam
f6b865a052 closed #292 2013-09-14 10:58:50 -04:00
Julian Lam
d1c756306a updates to allow dynamic addition of static directories provided by plugins 2013-09-13 11:10:17 -04:00
Julian Lam
ad5af8e123 updated screenshots for 0.0.6 2013-09-12 17:15:14 -04:00
psychobunny
ba89285c74 editorconfig; whitespace from favourites 2013-09-12 14:19:46 -04:00
psychobunny
43c0c2ec2a fix for favouriting 2013-09-12 14:16:13 -04:00
psychobunny
a277104ad5 dehinted favourites.js 2013-09-12 14:13:00 -04:00
psychobunny
84afffc761 categories.js jshinted 2013-09-12 14:07:29 -04:00
Julian Lam
5e5680fd13 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-12 13:55:45 -04:00
Julian Lam
727da38fb6 fixing issue where upgrade path only applies db fixes to default redis host and port 2013-09-12 13:55:38 -04:00
Baris Soner Usakli
79096cfdce online count fix for anon users 2013-09-12 13:10:14 -04:00
Baris Soner Usakli
a848f82b8f fix for upgrade script 2013-09-12 12:44:18 -04:00
Julian Lam
71bdd4608b removed defunct installation routes 2013-09-12 12:38:46 -04:00
Julian Lam
f04a948152 Merge pull request #282 from vincentbriglia/master
User search in non-admin removes search header/input
2013-09-12 09:17:33 -07:00
Vincent Briglia
15be3e52ae Merge remote-tracking branch 'upstream/master' 2013-09-12 16:05:43 +00:00
Vincent Briglia
b1003e954e user search shouldn't replace the whole top-level userbox 2013-09-12 16:04:04 +00:00
Julian Lam
f2b9a7ff0c Merge pull request #279 from vincentbriglia/master
isEmailAvailable returns inverted value
2013-09-12 06:39:53 -07:00
Vincent Briglia
19ad9ab224 inverted value required, double exclamation mark just casts as boolean 2013-09-12 13:37:03 +00:00
Vincent Briglia
8eae8a4fb5 !undefined = true
!!undefined = false
!!!undefined = true

!false = true
!!false = false
!!!false = true

!true = false
!!true = true
!!!true = false

when using isEmailAvailable (positive) in correlation with exists (positive) make sure that the outcome is a positive return (and not an inverted)
2013-09-12 13:07:00 +00:00
Baris Usakli
85fbe38e71 fixed to icon selection in admin/categories 2013-09-11 16:44:40 -04:00
Baris Usakli
e896fd1fd6 show created category right away 2013-09-11 16:20:00 -04:00
Baris Usakli
916150de01 closes #228 2013-09-11 16:04:34 -04:00
Julian Lam
732204f11b Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-11 15:41:48 -04:00
Julian Lam
1df50ff855 updated topic viewing so that markAsRead marks any relevant notifications read (issue #219) 2013-09-11 15:41:20 -04:00
psychobunny
11042858b8 Merge pull request #277 from designcreateplay/theme/cerulean
theme/cerulean
2013-09-11 12:31:00 -07:00
Baris Usakli
89ca2319f5 removed console.log 2013-09-11 15:28:20 -04:00
psychobunny
2f4fd310bd moved everything into the header class 2013-09-11 15:27:46 -04:00
Baris Usakli
6d7919ad85 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-11 15:24:15 -04:00
Baris Usakli
59467c906d misc fixes 2013-09-11 15:24:01 -04:00
psychobunny
def871ac89 fixed overlapping text in topic text-title 2013-09-11 15:21:23 -04:00
psychobunny
6fe5a93a73 header updates 2013-09-11 15:08:08 -04:00
psychobunny
7796c9269c cleaned up unread.tpl 2013-09-11 14:57:59 -04:00
psychobunny
ec150d4a4c removed unnecessary markup from users 2013-09-11 14:53:31 -04:00
psychobunny
49abff1609 moved footer style into cerulean 2013-09-11 14:49:31 -04:00
psychobunny
be59c16aad updated footer to follow standards 2013-09-11 14:47:58 -04:00
Julian Lam
f1144f3a7e Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-11 14:41:30 -04:00
Julian Lam
9ebff816dc fixing error where sometimes the admin panel js was not loaded 2013-09-11 14:41:08 -04:00
psychobunny
4c3eacd745 fixed footer markup 2013-09-11 14:37:43 -04:00
Baris Usakli
325e402d0f Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-11 13:53:51 -04:00
Baris Usakli
251587cb86 return err form updateProfile 2013-09-11 13:53:42 -04:00
Julian Lam
9fb1f8acf8 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-11 13:49:46 -04:00
Julian Lam
cffbc76da1 closed #275, refactored configs to use new method "list", which returns error first 2013-09-11 13:49:17 -04:00
Baris Usakli
be51025048 the online count on the footer uses the websockets now, it should also instantly update when people come online or go offline, #273 2013-09-11 13:24:16 -04:00
Baris Usakli
ae6f9fc87c changed notifications to use relative path 2013-09-11 13:04:04 -04:00
Baris Usakli
e61a7bff65 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-11 13:03:12 -04:00
Baris Usakli
bb14881b63 change getUserData to return err 2013-09-11 13:02:55 -04:00
Julian Lam
a7f1bfa7c9 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-11 12:55:34 -04:00
Julian Lam
7b3384a6be fixing some bad logic in app starting 2013-09-11 12:55:29 -04:00
Baris Usakli
efa8717fc5 getMultipleUserFields returns err now 2013-09-11 12:49:54 -04:00
psychobunny
9e42cee87c modules/taskbar.less, more cleanup of style 2013-09-11 12:34:56 -04:00
psychobunny
9fe30b905b modules.less, moved postWindow out. clean up unused css in style 2013-09-11 12:24:10 -04:00
Julian Lam
2fc9afa4f4 updated default category descriptions 2013-09-11 11:22:22 -04:00
psychobunny
732c8b1f7f header, footer,.. started cleaning up style (the big 'un) 2013-09-10 17:12:07 -04:00
psychobunny
19be3bc55c serious clean up of topic.less 2013-09-10 16:36:47 -04:00
Julian Lam
8a7e2577aa Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-10 16:26:38 -04:00
Julian Lam
e32494879c closed #270 2013-09-10 16:26:26 -04:00
psychobunny
ac4803961a topic cleanup and begin cerulean port 2013-09-10 15:50:21 -04:00
psychobunny
de8d6d3c8c category, removing deprecated thread-rating 2013-09-10 15:13:59 -04:00
psychobunny
392b80fe14 home.less 2013-09-10 15:10:51 -04:00
psychobunny
9fa75a8b45 init - cerulean theme 2013-09-10 15:02:03 -04:00
psychobunny
b19097ab8f moved .less files into the vanilla theme folder 2013-09-10 14:23:46 -04:00
psychobunny
31624b32e9 footer no longer overlaps composer; fixes 272 2013-09-10 13:16:10 -04:00
Julian Lam
b8b6558f53 deleting and restoring posts and topics now update the corresponding RSS feeds
fixed #257
2013-09-10 12:34:48 -04:00
Julian Lam
a53e75aeaa closed #271 2013-09-10 11:25:19 -04:00
Baris Usakli
7798004568 fixed active_users not deleting over 10 2013-09-09 16:27:16 -04:00
Baris Usakli
63873575a5 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-09 15:09:15 -04:00
Baris Usakli
9845df4d1b another fix to path in image upload 2013-09-09 15:09:05 -04:00
psychobunny
462d5f5d05 spacing between pin/lock icons in cat view 2013-09-09 14:34:39 -04:00
psychobunny
1d860923d0 booleanifying the isAdmin return... now we're admins again! :) 2013-09-09 14:31:22 -04:00
Baris Usakli
c9a3fac654 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-09 14:29:50 -04:00
Baris Usakli
a1c9685b49 fixed image upload, delete temp file if there is error 2013-09-09 14:29:40 -04:00
psychobunny
c16c4aac44 console.log 2013-09-09 14:17:11 -04:00
psychobunny
e80ff9c551 moved RSS icon away from the sharing buttons (category view); added back RSS icon to topic view. closes #259 2013-09-09 14:08:30 -04:00
psychobunny
3176ccc6e3 fixed footer flickering 2013-09-09 14:00:11 -04:00
Baris Usakli
b24196be36 closes #269 2013-09-09 13:37:50 -04:00
Baris Soner Usakli
52365a9755 upgrade user checks for undefined data 2013-09-07 19:12:49 -04:00
Julian Lam
41bea9f50c adding upgradeAdmin method to upgrade script -- pending fix to upgradeUsers 2013-09-07 19:01:17 -04:00
Julian Lam
3416c7bb3c Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-07 15:49:35 -04:00
Julian Lam
ce126b11fb fixes to administrator methods 2013-09-07 15:49:23 -04:00
Baris Soner Usakli
99db8fc3fe fixed admin/users page events and buttons 2013-09-07 15:48:38 -04:00
Julian Lam
780cec2596 missing alert-warning class in plugins admin page 2013-09-07 11:55:35 -04:00
Julian Lam
d3b4cb71c0 resolved race condition in plugin auto-enabling (not much of a race... I'd
always lose!!)
2013-09-07 11:53:55 -04:00
Julian Lam
1758c3e3f2 fix for admin templates 2013-09-07 11:51:37 -04:00
Julian Lam
995fa7d6fd interim commit -- enabling default plugins on setup 2013-09-07 11:45:04 -04:00
Julian Lam
87abe426d8 fixing 'use_port' to save into config as boolean 2013-09-07 11:29:26 -04:00
Julian Lam
bec0b46a2c refactoring installation scripts to use node prompt module, lots of other fixes
fixed #263, fixed #264, fixed #265
2013-09-07 10:40:20 -04:00
Baris Soner Usakli
a88ddc2a4d fixed image upload 2013-09-05 14:12:39 -04:00
Baris Soner Usakli
1f60578a63 closes #261 2013-09-05 12:18:51 -04:00
psychobunny
2f70489e22 outgoing links still BS2, fixed. 2013-09-05 23:55:57 +08:00
psychobunny
3b75734672 footer.less, added stats cards (ala soundsz) and replaced old footer 2013-09-05 23:28:15 +08:00
Julian Lam
ff4b35d6f1 minor bug with feed posttime if it was never edited 2013-09-04 14:16:11 -04:00
Julian Lam
e5b26fdad0 removing node-rss dep 2013-09-04 14:08:24 -04:00
Julian Lam
bcbcf40eae Merge branch 'feed_refactor' 2013-09-04 13:54:44 -04:00
Julian Lam
661fdfb43e bugfixing and allowing feeds to be generated on request (as opposed to just updated on posting 2013-09-04 13:52:28 -04:00
Julian Lam
31f08c49e7 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-04 13:36:33 -04:00
Julian Lam
2999d61ac9 minor fix... turning plugin libraries array into object 2013-09-04 13:36:25 -04:00
Baris Usakli
ca72a4b0fc fixed comma to semicolon 2013-09-04 12:13:36 -04:00
Julian Lam
164977972e totally derped commit 2013-09-03 23:42:26 -04:00
Julian Lam
b6fbfcc814 interim commit for new feed refactor 2013-09-03 23:18:25 -04:00
Julian Lam
418700ce3f changing info to warn when config file not found 2013-09-03 22:21:56 -04:00
Julian Lam
1d52557562 removing hook ordering message from non-development runs 2013-09-03 22:03:04 -04:00
psychobunny
45e24c54ce pagination scroll block fix + cleanup 2013-09-04 06:01:23 +08:00
psychobunny
abd909d23b pagination - scroll to top / bottom arrows 2013-09-04 04:50:51 +08:00
Julian Lam
f39932ece7 group join leave, and updating 2013-09-03 16:05:01 -04:00
Julian Lam
03fb649274 Merge branch 'master' into user_groups 2013-09-03 14:09:18 -04:00
Julian Lam
54e9e95076 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-03 13:20:49 -04:00
Julian Lam
201fb4d73e fixed #256 - where saving and retrieval of RSS feeds wasn't working on some instances due to a relative path being passed to read/writeFile 2013-09-03 13:20:19 -04:00
Baris Usakli
e0cc35ba66 creating topics shows up in user profile too issue #220 2013-09-03 13:19:51 -04:00
Baris Usakli
d3818e888e Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-03 13:11:44 -04:00
Baris Usakli
a2af4a3e91 closes #220 2013-09-03 13:11:34 -04:00
Julian Lam
02a02fa64c updated getTopicWithPosts to use a start and end option, and fixed issue with RSS feed saving (issue #256) 2013-09-03 13:11:21 -04:00
Julian Lam
9fae0d2505 removing dupe main post from posts array in topic noscript 2013-09-03 12:36:48 -04:00
Julian Lam
e23198bbcc Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-03 12:21:53 -04:00
Julian Lam
0a8c43901d more fixes to noscript 2013-09-03 12:21:48 -04:00
Baris Usakli
e9054301d1 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-09-03 12:18:38 -04:00
Baris Usakli
caaede7e84 added loading spinner to topic.tpl 2013-09-03 12:18:26 -04:00
Julian Lam
3ed9e1dd51 closed #253, closed #252 2013-09-03 12:17:55 -04:00
Julian Lam
207eccc505 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-03 11:48:39 -04:00
Julian Lam
45d5524df7 fixing minor errors in css in composer 2013-09-03 11:48:34 -04:00
Baris Usakli
1f08e407d1 closes #248 2013-09-03 11:45:15 -04:00
Baris Usakli
2c215afe33 closes #249 2013-09-03 11:29:56 -04:00
Julian Lam
d1d2b03dfd Merge branch 'master' into user_groups 2013-09-03 11:06:22 -04:00
Julian Lam
c1a41c6605 added hook priority to plugin hook registration 2013-09-02 21:53:15 -04:00
Julian Lam
d1a17b39ea allowing npm update of socket.io to newer version 2013-09-02 17:14:56 -04:00
Julian Lam
bf365bedfd pushing fix to issue where htmlfile transport for socket.io was causing
NodeBB to not work at all
2013-09-02 17:00:33 -04:00
Julian Lam
6a1ab47a08 hardcoding the exclusion of 'htmlfile' from web sockets, since it seems to
cause NodeBB to fail
2013-09-02 16:53:15 -04:00
Julian Lam
5fdaa6b0ee Merge branch 'master' into user_groups 2013-09-01 23:19:29 -04:00
Julian Lam
a6b0c2638a user list on group edit modal 2013-09-01 23:19:16 -04:00
Julian Lam
a671d08f0b updated readme - nginx not required 2013-09-01 19:17:40 -04:00
Julian Lam
5e869a5e5c Merge branch 'master' into user_groups 2013-09-01 16:14:24 -04:00
Julian Lam
0e6109ff2b a better conditional, just to please baris 2013-09-01 16:01:15 -04:00
Julian Lam
0bff6ee504 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-09-01 15:27:51 -04:00
Julian Lam
7cbe429be9 fixing issue with plugins trying to load 0 plugins and crashing 2013-09-01 15:27:45 -04:00
Baris Soner Usakli
09b578522f added prefic to less middleware 2013-09-01 15:21:28 -04:00
psychobunny
40897d0be6 themed reset.tpl and reset_code.tpl to BS3, closes #239 2013-08-31 03:46:48 +08:00
Baris Usakli
f49bc088fc Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-30 15:08:12 -04:00
Baris Usakli
7795a9ead2 fixed a crash 2013-08-30 15:07:58 -04:00
psychobunny
2c238f49e0 added search button, hiding the form by default. also fixes issue #241
notes: mobile needs a dedicated notification screen, imo, rather than
using the same popup. and /search needs a form on the page itself.
2013-08-31 02:59:50 +08:00
Baris Usakli
599789634a closes #227 2013-08-30 14:47:31 -04:00
Baris Usakli
77359f7b83 added space between user name and colon 2013-08-30 14:27:54 -04:00
Baris Usakli
7cff55a160 closes #209 2013-08-30 14:25:59 -04:00
Julian Lam
8d26eb4e07 minor stuff and such 2013-08-30 13:32:44 -04:00
Baris Usakli
ad5e3ebce3 closes #236 2013-08-30 12:36:06 -04:00
Baris Usakli
2deb0e1708 closes #238 2013-08-30 12:04:35 -04:00
Baris Usakli
b2f383f095 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-30 11:47:37 -04:00
Baris Usakli
22ababd87a closes #240 2013-08-30 11:47:26 -04:00
Julian Lam
167c1fa348 fixing topic CSS so the h3 styles only apply to the topic title 2013-08-30 11:43:46 -04:00
Baris Usakli
3b1bf67436 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-30 11:40:32 -04:00
Baris Usakli
d2eb73df96 can add and remove images from posts 2013-08-30 11:40:20 -04:00
Julian Lam
4d075efcd3 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-30 11:19:06 -04:00
Julian Lam
8a53c56a46 updated install script to only populate "socket" object in client config with "address", which takes "use_port" into account.
This closes #234
2013-08-30 11:18:15 -04:00
Julian Lam
73d4152d1d updated readme to add nginx version requirement 2013-08-30 11:07:44 -04:00
Julian Lam
c0a90bd677 interim commit 2013-08-30 07:43:47 -04:00
Baris Soner Usakli
106157a951 removed commented out line 2013-08-29 21:19:35 -04:00
Baris Soner Usakli
ab9cf6d036 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-29 21:18:34 -04:00
Baris Soner Usakli
54ba6efc93 can edit and add an image to a post, still cant remove previously added images #issue #233 2013-08-29 21:18:23 -04:00
Julian Lam
85ac4aac94 group creation and deletion 2013-08-29 14:55:30 -04:00
Baris Usakli
691b6611d0 user search uses reds now 2013-08-29 13:40:04 -04:00
Baris Usakli
1b33b4425b removed active from pill, #issue 231 2013-08-29 11:49:53 -04:00
Baris Usakli
41defbcf9c added delays to home page animations 2013-08-29 11:41:27 -04:00
Baris Soner Usakli
928594fc7c Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-28 22:08:54 -04:00
Baris Soner Usakli
cc1f668308 changed chat online offline messages 2013-08-28 22:08:46 -04:00
Julian Lam
f2b8813fb9 brain dumped groups lib 2013-08-28 22:06:55 -04:00
Julian Lam
5ed7c31278 removing console logs 2013-08-28 21:35:00 -04:00
Julian Lam
13b456bffd fixing up bs3 integration for themes page 2013-08-28 21:33:17 -04:00
Julian Lam
938dee481b Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-28 21:30:22 -04:00
Baris Soner Usakli
a69140faa9 show error message on failed login 2013-08-28 17:22:48 -04:00
psychobunny
6a5ebdb1ef fixed edit/delete/favourite buttons not working on subposts / topics 2013-08-29 05:06:22 +08:00
Julian Lam
ece4d083a5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-28 16:33:09 -04:00
Julian Lam
96688a8ffe changing method to retrieve themes to use /node_modules 2013-08-28 16:32:59 -04:00
Baris Soner Usakli
e9b0aa537f center speech arrow 2013-08-28 16:32:45 -04:00
psychobunny
4052c3e817 Merge pull request #226 from designcreateplay/bootstrap3
Bootstrap3
2013-08-28 13:20:23 -07:00
psychobunny
4a6e838b18 updated new settings markup to bs3 2013-08-29 04:15:45 +08:00
psychobunny
3773f6b44f merge conflicts + updated search.tpl to BS3 2013-08-29 04:12:36 +08:00
psychobunny
a19e836d63 fixed taskbar, tabs were stacking vertically instead of horizontally 2013-08-29 04:03:36 +08:00
psychobunny
aa5e5f9cc0 fixed bootbox3 issue in categories 2013-08-29 04:00:43 +08:00
psychobunny
6647e75c77 updated bootbox to BS3 2013-08-29 03:45:15 +08:00
psychobunny
f85a514ee3 fixed a bug in ACP where you could accidentally delete or ban a user even if you clicked on cancel. 2013-08-29 03:43:14 +08:00
psychobunny
619dd84d81 help block text in picture uploader 2013-08-29 03:26:00 +08:00
psychobunny
f9fdfab19e accountedit.tpl -> modal updates for picture selector and uploader 2013-08-29 03:23:45 +08:00
psychobunny
ca46d0f8e1 fixed disconnect modal; fixed modal overlay BS3 issue 2013-08-29 03:14:43 +08:00
psychobunny
bb8f75b4be merged.. conflicted up the ass. registration looks gud again 2013-08-29 03:01:58 +08:00
Baris Soner Usakli
0db599a478 closes #205 2013-08-28 14:46:18 -04:00
Baris Soner Usakli
902e60fab2 added password settings to admin 2013-08-28 13:53:20 -04:00
Baris Soner Usakli
87f48e2cc9 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-28 13:48:05 -04:00
Baris Soner Usakli
cc55073107 issue #224 2013-08-28 13:47:52 -04:00
Julian Lam
96c43b4607 fixed #221 - issue where replies would not automatically refresh a post after editing 2013-08-28 12:29:15 -04:00
Julian Lam
f1b4367168 closed #180, closed #223 2013-08-28 11:00:34 -04:00
Julian Lam
4416f8530d matching connect-redis session with cookie ttl 2013-08-28 10:18:17 -04:00
Baris Soner Usakli
4e48ab2363 changed max age to milliseconds 2013-08-27 23:40:38 -04:00
psychobunny
b5a26696f8 fixed the chat modal 2013-08-28 04:36:14 +08:00
psychobunny
4307229ae0 added profile link and chat button fn to both user dropdowns 2013-08-28 04:09:53 +08:00
psychobunny
b681f01be9 fixed profile link in topic user dropdown menu 2013-08-28 03:56:34 +08:00
Baris Usakli
c262027728 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-27 15:55:48 -04:00
Baris Usakli
6a08fedf18 closes #210 2013-08-27 15:55:44 -04:00
psychobunny
e471b4edf5 category + recent replies layout rejigged 2013-08-28 03:43:05 +08:00
psychobunny
dd86812822 some BS2 buttons managed to escape my grasp.. until now. 2013-08-28 03:11:08 +08:00
psychobunny
ac311ab75d fixed derpy profile image size in /search 2013-08-28 03:08:05 +08:00
psychobunny
c9387313e2 ACP header and all admin pages BS3'd 2013-08-28 03:04:57 +08:00
Julian Lam
97641dbcda fixing teasers, that I broke in the last commit 2013-08-27 15:00:35 -04:00
Julian Lam
54fdbcd947 fixing broken category 2013-08-27 14:57:21 -04:00
psychobunny
7636e09284 admin hero-unit to jumbotron and updated welcome message to be slightly less embarassing 2013-08-28 02:35:07 +08:00
psychobunny
7666ed3010 fixed two bugs in admin, meta.buildTitle errors out because no notification icon is present; pagination element missing
& final less cleanup for today
2013-08-28 02:32:38 +08:00
psychobunny
e82a9ec37a more less cleanup 2013-08-28 02:25:17 +08:00
psychobunny
770d9e5b7c removed mobile menu markup + css, more cleanup 2013-08-28 02:19:59 +08:00
Julian Lam
5f0e0c993e adding localhost as default for email server 2013-08-27 14:16:56 -04:00
psychobunny
7be69b0b54 more cleanup, account.less, removed mobile sidebar functionality completely 2013-08-28 02:11:13 +08:00
psychobunny
251beb7f9b updated less to use .pointer mixin 2013-08-28 02:05:50 +08:00
Baris Usakli
1c27cbd90a Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-27 14:04:25 -04:00
Baris Usakli
4b4b26651a derp 2013-08-27 14:04:22 -04:00
Julian Lam
1a6ba8c230 fixed derpiness I introduced last commit 2013-08-27 14:04:09 -04:00
psychobunny
06904506d4 header.less, got rid of some unused css classes related to the original post window 2013-08-28 02:02:35 +08:00
psychobunny
62a28825f8 more css cleanup 2013-08-28 02:00:10 +08:00
psychobunny
c6c693acd2 some cleanup of topics.less 2013-08-28 01:58:56 +08:00
Julian Lam
8d04454457 closed #218 2013-08-27 13:52:30 -04:00
Baris Usakli
aeb831eeff closes #217 2013-08-27 13:47:19 -04:00
psychobunny
67db78ad66 styled topic's user dropdown 2013-08-28 01:44:41 +08:00
psychobunny
06809ab5fc got rid of a lot of junk markup and unused css classes; standardized the pulling of pid/uid across post.js code 2013-08-28 01:41:32 +08:00
Baris Usakli
08ba911738 closes #216 2013-08-27 13:38:09 -04:00
Baris Usakli
59f4b6788a Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-27 13:32:54 -04:00
Baris Usakli
a317a4d689 closes #206 2013-08-27 13:32:43 -04:00
psychobunny
6e164e61aa removed some unnecessary CSS 2013-08-28 01:30:43 +08:00
psychobunny
2098bf84a1 finished pagination for topic view 2013-08-28 01:23:19 +08:00
Julian Lam
860a83ba90 closed #212 2013-08-27 13:04:18 -04:00
Julian Lam
abce5fd120 refactored postTools.toHTML to fire post.parse hook, and removed auto-markdowning
closed #214
2013-08-27 12:50:15 -04:00
Julian Lam
0243e9c5be issue #214, preparing for addition of post.parse hook by renaming markdownToHTML to just "toHTML", and making it asynchronous. 2013-08-27 12:14:27 -04:00
Baris Usakli
8a4a0154f6 closes #211, closes #213 2013-08-27 12:10:23 -04:00
psychobunny
469a5221ed added postcount to topics return; started scrollspy behaviour for "postid / postcount" in topics. 2013-08-27 06:36:49 +08:00
psychobunny
f251b9c6c5 Merge branch 'master' into bootstrap3 2013-08-27 06:03:05 +08:00
Baris Usakli
c4228be86c bold active section in account page 2013-08-26 17:50:31 -04:00
psychobunny
94e565aa00 account pages done; 403; 404 2013-08-27 05:42:09 +08:00
psychobunny
cc6e028b1d fixed alert-notify (and blanks) to alert-warning 2013-08-27 05:16:49 +08:00
Julian Lam
d4d4c3cc92 0.0.6 2013-08-26 17:13:14 -04:00
psychobunny
33a69abece mark all as read button was unstyled 2013-08-27 05:11:39 +08:00
Julian Lam
3a12ba177a possibly fixing issue #202 again 2013-08-26 17:10:41 -04:00
psychobunny
6a4947533f fixed BS3 issue, navbar now centered; fixed topic view sub-post moderator tools layout 2013-08-27 05:07:52 +08:00
Julian Lam
28e1538fdb issue #203 2013-08-26 16:54:15 -04:00
psychobunny
879a31f51e had to remove hidden-xs from the footer - BS3's !important takes precedence over the fading animation and looks ugly (flex) 2013-08-27 04:51:29 +08:00
Julian Lam
eed66c099b fixing double notif error on new notif 2013-08-26 16:42:29 -04:00
psychobunny
49fc87e295 removed unnecessary css definitions (previous fixes for BS2), and fixed category width on mobile screens 2013-08-27 04:42:04 +08:00
Julian Lam
ece2edf579 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-26 16:37:37 -04:00
Julian Lam
1961f01cab possibly fixing issue #203 2013-08-26 16:37:22 -04:00
psychobunny
fe89f1f096 added a .hidden-mobile class for inline block elements. also removed button text on jumbotron for mobile devices 2013-08-27 04:37:16 +08:00
psychobunny
346681ba27 removed mobile menu until it can be pluginified. refactored topics UX a hell of a lot, looks much better on both desktop+mobile now. 2013-08-27 04:30:00 +08:00
Baris Usakli
4a214b6ef0 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-26 16:04:37 -04:00
Baris Usakli
dda429ab5d unread count shows total 2013-08-26 16:04:31 -04:00
Julian Lam
15feaafd68 attempting fix for #179, @barisusakli can you pull and test if this is still present? 2013-08-26 15:20:06 -04:00
Julian Lam
14e7907e06 closed #201 2013-08-26 14:56:00 -04:00
Baris Usakli
b4b483b35a removed console.log 2013-08-26 13:21:14 -04:00
Baris Usakli
79c9bdba7f removed console.log 2013-08-26 13:20:10 -04:00
Baris Usakli
a6837a7869 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-26 13:18:29 -04:00
Baris Usakli
0a485a7ff6 closes #109 2013-08-26 13:18:20 -04:00
Julian Lam
bca1602474 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-26 12:24:16 -04:00
Julian Lam
96ee0a2017 closed #184 2013-08-26 12:22:55 -04:00
Baris Usakli
25550e18d0 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-26 11:59:02 -04:00
Baris Usakli
154d0160bc closes #200 2013-08-26 11:58:52 -04:00
Julian Lam
aefa56221b closed #202 2013-08-26 11:56:28 -04:00
Baris Soner Usakli
dd40cbc139 check if topicData is valid 2013-08-24 18:51:24 -04:00
Baris Soner Usakli
23db2e5c9e better fix for #198 2013-08-24 18:47:10 -04:00
Baris Soner Usakli
eff1b174c0 closes #198 2013-08-24 18:43:06 -04:00
Baris Soner Usakli
545069b069 closes #199 2013-08-24 18:19:01 -04:00
psychobunny
13e13cd5a8 some changes to topic view + BS3 upgrade 2013-08-24 04:34:13 +08:00
Julian Lam
f767535ce5 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-23 16:25:22 -04:00
Julian Lam
aeebd069e2 fixing nconf issue where redis js was requiring redis configs before they were defined 2013-08-23 16:25:16 -04:00
psychobunny
0f8aad52b2 updated all alert-error to alert-danger (wtf did BS3 need to change that?) 2013-08-24 03:36:44 +08:00
psychobunny
b6a5cbf956 trim whitespace mode on 2013-08-24 03:30:34 +08:00
Julian Lam
d2aa2a9a29 derpiness 2013-08-23 15:22:26 -04:00
psychobunny
a482d4db5f fixed /recent, /unread 2013-08-24 03:10:15 +08:00
Julian Lam
d7eb30ccbd fuck git pt 2 2013-08-23 14:57:33 -04:00
Julian Lam
9bc12f28b4 fuck git 2013-08-23 14:55:25 -04:00
Julian Lam
4d11fba20a auto-focusing input on login screen 2013-08-23 14:30:58 -04:00
psychobunny
df15dceaef merging master to pull baris' latest fixes 2013-08-24 01:59:35 +08:00
Baris Usakli
cb6f587f24 closes #197 2013-08-23 13:45:57 -04:00
psychobunny
9f2e192993 jumbotron updates. I'll come back to fixing the button layout on mobile later 2013-08-24 01:27:12 +08:00
Baris Usakli
c647793512 meta config changes, refactors 2013-08-23 13:14:36 -04:00
psychobunny
d62f36c6a0 now /users looks as it should. made some tweaks to user search to improve UX as well 2013-08-24 01:11:50 +08:00
psychobunny
2427724868 navbar changes + logout screen + replaced hardcoded sitename on logout screen with breadcrumbs
note: perhaps breadcrumbs should be dynamically created in future
instead of copypasted everywhere.
2013-08-24 00:50:16 +08:00
psychobunny
9b35d8f8e1 migrating the rest of row-fluids to row 2013-08-24 00:44:27 +08:00
psychobunny
a859f4524c container-fluid to container 2013-08-24 00:43:38 +08:00
psychobunny
8be896aebb label-important to label-danger 2013-08-24 00:43:03 +08:00
psychobunny
a008cf971d register and login -> BS3 2013-08-24 00:38:40 +08:00
Julian Lam
caa057ff4d Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-22 14:42:13 -04:00
Julian Lam
43152bdaf9 closed #194 - refactoring of post creation so that, normalizing post
objects

also fixes julianlam/nodebb-plugin-mentions#1
2013-08-22 14:40:28 -04:00
Baris Soner Usakli
4cdb7ff32b closes #192 2013-08-22 13:47:07 -04:00
Julian Lam
7cbb01a78a making RDB available to plugins without require 2013-08-22 11:42:49 -04:00
Julian Lam
555eddff83 Merge branch 'master' of github.com:designcreateplay/NodeBB 2013-08-22 10:48:28 -04:00
Julian Lam
a3cab53b73 added username mentions plugin to default, and tweaked admin panel to show
plugins installed via npm
2013-08-22 10:47:24 -04:00
Baris Soner Usakli
454d5827fd overflow hidden on users recent posts 2013-08-22 10:18:17 -04:00
Baris Soner Usakli
186c426691 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-22 10:14:15 -04:00
Baris Soner Usakli
5e1e1ecf6f moved refreshTitle into load_template callback 2013-08-22 10:14:07 -04:00
Julian Lam
2d3d0f688a a couple minor 'tweaks' to the plugin system so that it works with npm installed plugins 2013-08-22 09:50:29 -04:00
Julian Lam
7975844e4d renamed two hooks and added filter:post.get hook 2013-08-21 22:40:47 -04:00
Baris Soner Usakli
f2b138510e removed console.log 2013-08-21 19:58:45 -04:00
Baris Soner Usakli
1c95ef4060 fix for infinite scroll crashing, issue #187 2013-08-21 19:57:11 -04:00
Baris Soner Usakli
b43e12a42a closes #187, closes #190 2013-08-21 19:21:49 -04:00
psychobunny
316077fffb category view starting to look like it should 2013-08-22 05:04:58 +08:00
psychobunny
4373037071 fixed composer window having 0 width; recent replies looks gud again 2013-08-22 04:05:48 +08:00
psychobunny
24e81c873c small user img-thumbnails changed to img-rounded 2013-08-22 03:55:31 +08:00
psychobunny
9e4a3905a0 moved most span12 and span9 over to BS3; img-polaroid to img-thumbnail 2013-08-22 03:39:43 +08:00
psychobunny
b54b4a6580 breadcrumbs don't need a container element anymore 2013-08-22 03:28:27 +08:00
psychobunny
f33a9c3941 updated hidden/visible device flags to BS3 2013-08-22 03:25:17 +08:00
psychobunny
d792798963 breadcrumbs 2013-08-22 03:22:33 +08:00
psychobunny
e5af4f6299 BS3 source files, damn you windows github 2013-08-22 03:18:03 +08:00
psychobunny
ef5bd9dc61 moved home categories into its own less file 2013-08-22 03:17:52 +08:00
psychobunny
8834feac65 BS3 sourcefiles + modified category row to new format. now mobile has two columns of categories, looks much neater. 2013-08-22 03:13:49 +08:00
psychobunny
2f70dd732d BS3 basic integration; btn-large to btn-lg (inc FA); navbar fixed 2013-08-22 02:57:18 +08:00
Baris Soner Usakli
3f793ae902 changed document.body.offsetHeight to .height() 2013-08-21 14:33:05 -04:00
Baris Soner Usakli
ac56f3a30a change document.body.scrollTop to .scrollTop() 2013-08-21 13:17:16 -04:00
psychobunny
e2ffac74bc added css classes to hide/show elements based on logged in status; hid unread category to anonymous users
added some general classes .nodebb-loggedin and .nodebb-loggedout for
toggling display based on user status
2013-08-22 00:40:37 +08:00
psychobunny
1c08ca54c5 added subtle glowing css animation to unread notification icon 2013-08-22 00:22:05 +08:00
psychobunny
f1547a7b1f added badge to header for unread topics count 2013-08-22 00:08:11 +08:00
psychobunny
746fa93c80 added API call for total unread topics; moved unread notification parsing to client side
also fixed a bug where the new notification icon glow would disappear on
page refresh even if there were existing notifications.
2013-08-21 23:34:35 +08:00
psychobunny
5ab1758d28 simple toaster popup on new notification 2013-08-21 23:08:51 +08:00
Julian Lam
2e4e1df3d9 closed #186 - infinite scroller not working in firefox 2013-08-20 22:36:46 -04:00
psychobunny
eded61d66e typo 2013-08-21 03:40:23 +08:00
psychobunny
d9360da9a5 default motd: removed version number and button text on mobile layouts 2013-08-21 03:37:48 +08:00
Baris Usakli
2b7a1b7515 changed the recursion to load posts to a while loop 2013-08-20 12:31:08 -04:00
Baris Usakli
1e66116e1d closes #181 2013-08-20 12:11:17 -04:00
Baris Soner Usakli
a95582b382 closes #183, closes #182 2013-08-20 00:50:59 -04:00
Baris Soner Usakli
7860cfdec3 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-20 00:26:58 -04:00
Baris Soner Usakli
30bbea9c74 closes #175 2013-08-20 00:26:50 -04:00
Baris Usakli
481105d6be reverted that back, it would take other users to the topic too 2013-08-19 14:54:11 -04:00
Baris Usakli
f939a632a6 take to topic after creation 2013-08-19 14:43:37 -04:00
Baris Usakli
c05f56d28c fix for category view when its empty 2013-08-19 14:28:51 -04:00
Baris Usakli
b844251587 Merge branch 'master' of https://github.com/designcreateplay/NodeBB 2013-08-19 14:03:31 -04:00
Baris Usakli
b9bd907a6b speech bubbles 2013-08-19 14:03:20 -04:00
psychobunny
17d86a2a35 force vertical scrollbar to prevent centering jumps during navigation 2013-08-20 01:50:19 +08:00
Baris Usakli
c70c67394a closes #171 2013-08-19 13:31:04 -04:00
Baris Usakli
92d3559146 closes #176 2013-08-19 11:56:40 -04:00
Baris Usakli
0e9a3c3a9f hide load more button if not topics are left 2013-08-19 11:02:31 -04:00
Baris Usakli
41263f0332 closes #178 2013-08-19 10:58:02 -04:00
Julian Lam
3747427538 updated readme to remove indiegogo info, and to add 0.0.5 screenshots 2013-08-19 10:13:18 -04:00
Baris Soner Usakli
b65554ca15 removed console.log 2013-08-16 17:04:41 -04:00
Baris Soner Usakli
00cb15d3c8 check if there is follow element 2013-08-16 17:04:08 -04:00
Baris Soner Usakli
6690f49c4e added profile views to users, little cleanup to use app.addCommasToNumbers 2013-08-16 13:03:56 -04:00
Baris Soner Usakli
ff805a704d closes #175 2013-08-15 17:03:43 -04:00
Baris Soner Usakli
f83be710a0 closes #172, #173, #174 2013-08-15 16:50:00 -04:00
Julian Lam
3933549659 committing fixes to composer window 2013-08-15 14:04:12 -04:00
Julian Lam
4993b74c23 Merge remote-tracking branch 'origin' 2013-08-15 11:40:53 -04:00
Julian Lam
76e7a98c88 style fixes to image-drop list in composer window 2013-08-15 11:40:41 -04:00
Baris Soner Usakli
999e98e43d add label to banned users, dont hide their posts 2013-08-14 17:42:36 -04:00
Baris Usakli
74af205426 banned users cant login, show error messages on failed logins 2013-08-14 15:49:56 -04:00
Baris Usakli
9ad82f4907 dont filter banned when searching if user is admin 2013-08-14 14:26:09 -04:00
Baris Usakli
2e6b37e018 wrapped search form in li 2013-08-14 13:56:06 -04:00
Baris Usakli
67070e335e removed console.log 2013-08-14 13:44:18 -04:00
Baris Usakli
22536e694c fixes wrong topics getting loaded into wrong categories 2013-08-14 13:43:42 -04:00
Baris Usakli
929282a2f7 ban users, closes #125, banning a user hides all posts topics of a user and their profile page becomes inaccessible 2013-08-14 13:32:07 -04:00
Baris Soner Usakli
b0092b68c6 closes #166 2013-08-13 17:29:16 -04:00
Baris Usakli
91446378bd require winston 2013-08-13 16:02:21 -04:00
Baris Usakli
dceec0ce46 more winston, issue #62 2013-08-13 16:00:24 -04:00
Baris Usakli
1856e394f3 more winston 2013-08-13 15:25:40 -04:00
Baris Usakli
8dc7a0dbbf changed favouriting to wait for socket call to end before changing star class 2013-08-13 15:05:35 -04:00
Baris Usakli
6e17ff7981 added winston, added wrapper for winston.error until they fix it, issue #62 2013-08-13 14:45:28 -04:00
204 changed files with 34074 additions and 17624 deletions

4
.editorconfig Normal file
View File

@@ -0,0 +1,4 @@
root = true
[*.js, *.css, *.tpl]
indent_style = tab

4
.gitignore vendored
View File

@@ -6,9 +6,13 @@ npm-debug.log
node_modules/ node_modules/
sftp-config.json sftp-config.json
config.json config.json
public/src/nodebb.min.js
public/config.json public/config.json
public/css/*.css public/css/*.css
public/themes/* public/themes/*
!/public/themes/vanilla
!/public/themes/cerulean
!/public/themes/modern
*.sublime-project *.sublime-project
*.sublime-workspace *.sublime-workspace
plugins/* plugins/*

17
.jsbeautifyrc Normal file
View File

@@ -0,0 +1,17 @@
{
"indent_size": 4,
"indent_char": " ",
"indent_level": 0,
"indent_with_tabs": true,
"preserve_newlines": true,
"max_preserve_newlines": 10,
"jslint_happy": true,
"brace_style": "collapse",
"keep_array_indentation": false,
"keep_function_indentation": false,
"space_before_conditional": true,
"break_chained_methods": false,
"eval_code": false,
"unescape_strings": false,
"wrap_line_length": 0
}

86
.jshintrc Normal file
View File

@@ -0,0 +1,86 @@
{
// JSHint Default Configuration File (as on JSHint website)
// See http://jshint.com/docs/ for more details
"maxerr" : 50, // {int} Maximum error before stopping
// Enforcing
"bitwise" : true, // true: Prohibit bitwise operators (&, |, ^, etc.)
"camelcase" : false, // true: Identifiers must be in camelCase
"curly" : true, // true: Require {} for every new block or scope
"eqeqeq" : true, // true: Require triple equals (===) for comparison
"forin" : true, // true: Require filtering for..in loops with obj.hasOwnProperty()
"immed" : false, // true: Require immediate invocations to be wrapped in parens e.g. `(function () { } ());`
"indent" : 4, // {int} Number of spaces to use for indentation
"latedef" : false, // true: Require variables/functions to be defined before being used
"newcap" : false, // true: Require capitalization of all constructor functions e.g. `new F()`
"noarg" : true, // true: Prohibit use of `arguments.caller` and `arguments.callee`
"noempty" : true, // true: Prohibit use of empty blocks
"nonew" : false, // true: Prohibit use of constructors for side-effects (without assignment)
"plusplus" : false, // true: Prohibit use of `++` & `--`
"quotmark" : false, // Quotation mark consistency:
// false : do nothing (default)
// true : ensure whatever is used is consistent
// "single" : require single quotes
// "double" : require double quotes
"undef" : true, // true: Require all non-global variables to be declared (prevents global leaks)
"unused" : false, // true: Require all defined variables be used TODO: Set this to true, update codebase.
"strict" : true, // true: Requires all functions run in ES5 Strict Mode
"trailing" : false, // true: Prohibit trailing whitespaces
"maxparams" : false, // {int} Max number of formal params allowed per function
"maxdepth" : false, // {int} Max depth of nested blocks (within functions)
"maxstatements" : false, // {int} Max number statements per function
"maxcomplexity" : false, // {int} Max cyclomatic complexity per function
"maxlen" : false, // {int} Max number of characters per line
// Relaxing
"asi" : false, // true: Tolerate Automatic Semicolon Insertion (no semicolons)
"boss" : false, // true: Tolerate assignments where comparisons would be expected
"debug" : false, // true: Allow debugger statements e.g. browser breakpoints.
"eqnull" : false, // true: Tolerate use of `== null`
"es5" : false, // true: Allow ES5 syntax (ex: getters and setters)
"esnext" : false, // true: Allow ES.next (ES6) syntax (ex: `const`)
"moz" : false, // true: Allow Mozilla specific syntax (extends and overrides esnext features)
// (ex: `for each`, multiple try/catch, function expression…)
"evil" : false, // true: Tolerate use of `eval` and `new Function()`
"expr" : false, // true: Tolerate `ExpressionStatement` as Programs
"funcscope" : false, // true: Tolerate defining variables inside control statements"
"globalstrict" : false, // true: Allow global "use strict" (also enables 'strict')
"iterator" : false, // true: Tolerate using the `__iterator__` property
"lastsemic" : false, // true: Tolerate omitting a semicolon for the last statement of a 1-line block
"laxbreak" : false, // true: Tolerate possibly unsafe line breakings
"laxcomma" : false, // true: Tolerate comma-first style coding
"loopfunc" : false, // true: Tolerate functions being defined in loops
"multistr" : false, // true: Tolerate multi-line strings
"proto" : false, // true: Tolerate using the `__proto__` property
"scripturl" : false, // true: Tolerate script-targeted URLs
"smarttabs" : false, // true: Tolerate mixed tabs/spaces when used for alignment
"shadow" : false, // true: Allows re-define variables later in code e.g. `var x=1; x=2;`
"sub" : false, // true: Tolerate using `[]` notation when it can still be expressed in dot notation
"supernew" : false, // true: Tolerate `new function () { ... };` and `new Object;`
"validthis" : false, // true: Tolerate using this in a non-constructor function
// Environments
"browser" : true, // Web Browser (window, document, etc)
"couch" : false, // CouchDB
"devel" : true, // Development/debugging (alert, confirm, etc)
"dojo" : false, // Dojo Toolkit
"jquery" : true, // jQuery
"mootools" : false, // MooTools
"node" : true, // Node.js
"nonstandard" : false, // Widely adopted globals (escape, unescape, etc)
"prototypejs" : false, // Prototype and Scriptaculous
"rhino" : false, // Rhino
"worker" : false, // Web Workers
"wsh" : false, // Windows Scripting Host
"yui" : false, // Yahoo User Interface
// Legacy
"nomen" : false, // true: Prohibit dangling `_` in variables
"onevar" : false, // true: Allow only one `var` statement per function
"passfail" : false, // true: Stop on first error
"white" : false, // true: Check against strict whitespace and indentation rules
// Custom Globals
"globals" : {} // additional predefined global variables
}

View File

@@ -1,23 +1,29 @@
Please support NodeBB development! Check out our IndieGoGo campaign and like, share, and follow us :)
[NodeBB Homepage](http://www.nodebb.org/ "NodeBB") # [IndieGoGo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/ "IndieGoGo") # [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") # [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
# NodeBB # NodeBB
**NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8. **NodeBB** is a robust Node.js driven forum built on a redis database. It is powered by web sockets, and is compatible down to IE8.
![NodeBB Main Category Listing](http://i.imgur.com/N8SLkcDh.png) * [NodeBB Homepage](http://www.nodebb.org/ "NodeBB")
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
![NodeBB Topic Page](http://i.imgur.com/RBkFzdkh.png) ![NodeBB Main Category Listing](http://i.imgur.com/ZBrHqLr.png)
![NodeBB Topic Page](http://i.imgur.com/YSBA6Vr.png)
## How can I follow along/contribute? ## How can I follow along/contribute?
* Our [Indiegogo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/) is accepting contributions until August 17th, 2013
* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/designcreateplay/NodeBB/wiki/Version-History-%26-Roadmap) * Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/designcreateplay/NodeBB/wiki/Version-History-%26-Roadmap)
* If you are a developer, feel free to check out the source and submit pull requests. * If you are a developer, feel free to check out the source and submit pull requests.
* If you are a designer, NodeBB needs themes! NodeBB will accept any LESS or CSS file and use it in place of the default Twitter Bootstrap theme. Consider extending Bootstrap themes by extending the base bootstrap LESS file. * If you are a designer, NodeBB needs themes! NodeBB will accept any LESS or CSS file and use it in place of the default Twitter Bootstrap theme. Consider extending Bootstrap themes by extending the base bootstrap LESS file.
## Requirements ## Requirements
NodeBB requires a version of Node.js at least 0.8 or greater, and a Redis version 2.6 or greater. NodeBB requires the following software to be installed:
* A version of Node.js at least 0.8 or greater
* Redis, version 2.6 or greater.
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
## Installation ## Installation
@@ -55,3 +61,7 @@ NodeBB can also be started with helper programs, such as `supervisor` and `forev
*(Optional)* Some server configurations may install the node binary as `nodejs` instead of `node`. You can re-map it (so as to not break compatibility with `node-supervisor`) by running the following command: *(Optional)* Some server configurations may install the node binary as `nodejs` instead of `node`. You can re-map it (so as to not break compatibility with `node-supervisor`) by running the following command:
# update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10 # update-alternatives --install /usr/bin/node node /usr/bin/nodejs 10
## Upgrading NodeBB
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)

178
app.js
View File

@@ -16,127 +16,125 @@
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
var fs = require('fs'), (function () {
nconf = require('nconf'), "use strict";
// Configuration setup
var nconf = require('nconf');
nconf.argv().env();
var fs = require('fs'),
async = require('async'),
winston = require('winston'),
pkg = require('./package.json'), pkg = require('./package.json'),
url = require('url'); path = require('path'),
meta;
// Runtime environment // Runtime environment
global.env = process.env.NODE_ENV || 'production', global.env = process.env.NODE_ENV || 'production';
// Configuration setup winston.remove(winston.transports.Console);
nconf.argv().file({ file: __dirname + '/config.json'}); winston.add(winston.transports.Console, {
colorize: true
});
// Log GNU copyright info along with server info winston.add(winston.transports.File, {
console.log('Info: NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.'); filename: 'error.log',
console.log('Info: This program comes with ABSOLUTELY NO WARRANTY.'); level: 'error'
console.log('Info: This is free software, and you are welcome to redistribute it under certain conditions.'); });
console.log('Info: ===');
if(nconf.get('upgrade')) { // TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
require('./src/upgrade').upgrade(); winston.err = function (err) {
} else if (!nconf.get('setup') && nconf.get('base_url')) { winston.error(err.stack);
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/');
nconf.set('upload_url', nconf.get('url') + 'uploads/');
global.nconf = nconf;
console.log('Info: Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
console.log('Info: Base Configuration OK.');
// TODO: Replace this with nconf-redis
var meta = require('./src/meta.js');
global.config = {};
meta.config.get(function(config) {
for(c in config) {
if (config.hasOwnProperty(c)) {
global.config[c] = config[c];
}
}
var categories = require('./src/categories.js'),
RDB = require('./src/redis.js'),
templates = require('./public/src/templates.js'),
webserver = require('./src/webserver.js'),
websockets = require('./src/websockets.js'),
plugins = require('./src/plugins'),
admin = {
'categories': require('./src/admin/categories.js')
}; };
DEVELOPMENT = true; // Log GNU copyright info along with server info
winston.info('NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
winston.info('This program comes with ABSOLUTELY NO WARRANTY.');
winston.info('This is free software, and you are welcome to redistribute it under certain conditions.');
winston.info('');
if (fs.existsSync(__dirname + '/config.json') && (!nconf.get('setup') && !nconf.get('upgrade'))) {
// Load server-side configs
nconf.file({
file: __dirname + '/config.json'
});
meta = require('./src/meta.js');
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + '/');
nconf.set('upload_url', nconf.get('url') + 'uploads/');
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
winston.info('NodeBB instance bound to: ' + (nconf.get('bind_address') || 'Any address'));
if (process.env.NODE_ENV === 'development') {
winston.info('Base Configuration OK.');
}
meta.configs.init(function () {
// Initial setup for Redis & Reds
var reds = require('reds'),
RDB = require('./src/redis.js');
reds.createClient = function () {
return reds.client || (reds.client = RDB);
};
var templates = require('./public/src/templates.js'),
translator = require('./public/src/translator.js'),
webserver = require('./src/webserver.js'),
SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket']}),
websockets = require('./src/websockets.js'),
plugins = require('./src/plugins'); // Don't remove this - plugins initializes itself
websockets.init(SocketIO);
global.configuration = {};
global.templates = {}; global.templates = {};
global.translator = translator;
(function(config) { translator.loadServer();
config['ROOT_DIRECTORY'] = __dirname;
// todo: replace below with read directory code, derp.
templates.init([ templates.init([
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index', 'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext', 'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis', 'emails/header', 'emails/footer',
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic' 'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
]); ]);
templates.ready(webserver.init); templates.ready(webserver.init);
//setup scripts to be moved outside of the app in future.
function setup_categories() {
console.log('Info: Checking categories...');
categories.getAllCategories(function(data) {
if (data.categories.length === 0) {
console.log('Info: Setting up default categories...');
fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
default_categories = JSON.parse(default_categories);
for (var category in default_categories) {
admin.categories.create(default_categories[category]);
}
}); });
} else if (nconf.get('upgrade')) {
meta = require('./src/meta.js');
console.log('Info: Hardcoding uid 1 as an admin'); meta.configs.init(function () {
var user = require('./src/user.js'); require('./src/upgrade').upgrade();
user.makeAdministrator(1); });
} else { } else {
console.log('Info: Categories OK. Found ' + data.categories.length + ' categories.');
}
});
}
setup_categories();
}(global.configuration));
});
} else {
// New install, ask setup questions // New install, ask setup questions
if (nconf.get('setup')) console.log('Info: NodeBB Setup Triggered via Command Line'); if (nconf.get('setup')) {
else console.log('Info: Configuration not found, starting NodeBB setup'); winston.info('NodeBB Setup Triggered via Command Line');
} else {
winston.warn('Configuration not found, starting NodeBB setup');
}
var install = require('./src/install'); var install = require('./src/install');
process.stdout.write( winston.info('Welcome to NodeBB!');
"\nWelcome to NodeBB!\nThis looks like a new installation, so you'll have to answer a " + winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.');
"few questions about your environment before we can proceed.\n\n" + winston.info('Press enter to accept the default setting (shown in brackets).');
"Press enter to accept the default setting (shown in brackets).\n\n\n"
);
install.setup(function(err) { install.setup(function (err) {
if (err) { if (err) {
console.log('Error: There was a problem completing NodeBB setup: ', err.message); winston.error('There was a problem completing NodeBB setup: ', err.message);
} else { } else {
if (!nconf.get('setup')) { winston.info('NodeBB Setup Completed.');
process.stdout.write(
"Please start NodeBB again and register a new user. This user will automatically become an administrator.\n\n"
);
}
} }
process.exit(); process.exit();
}); });
} }
}());

View File

@@ -1,7 +1,7 @@
[ [
{ {
"name": "Announcements", "name": "Announcements",
"description": "A place to talk about whateeeever you want", "description": "Announcements regarding our community",
"blockclass": "category-blue", "blockclass": "category-blue",
"icon" : "icon-bullhorn" "icon" : "icon-bullhorn"
}, },
@@ -13,61 +13,61 @@
}, },
{ {
"name": "NodeBB Development", "name": "NodeBB Development",
"description": "Bugs? Dont worry, we dont read this thread, so post them here.", "description": "NodeBB development news and announcements",
"blockclass": "category-blue", "blockclass": "category-blue",
"icon" : "icon-github" "icon" : "icon-github"
}, },
{ {
"name": "Blogs", "name": "Blogs",
"description": "In future an example of how a hidden category should look like.", "description": "Blog posts from individual members",
"blockclass": "category-blue", "blockclass": "category-blue",
"icon" : "icon-pencil" "icon" : "icon-pencil"
}, },
{ {
"name": "Feature Requests", "name": "Feature Requests",
"description": "In future an example of how a hidden category should look like.", "description": "Got a feature request you'd like to see? Give us a shout here.",
"blockclass": "category-purple", "blockclass": "category-purple",
"icon" : "icon-lightbulb" "icon" : "icon-lightbulb"
}, },
{ {
"name": "Bug Reports", "name": "Bug Reports",
"description": "In future an example of how a hidden category should look like.", "description": "Having trouble with NodeBB? Let us know...",
"blockclass": "category-purple", "blockclass": "category-purple",
"icon" : "icon-cogs" "icon" : "icon-cogs"
}, },
{ {
"name": "NodeBB Addons", "name": "NodeBB Plugins",
"description": "In future an example of how a hidden category should look like.", "description": "Enhance your NodeBB with plugins!",
"blockclass": "category-purple", "blockclass": "category-purple",
"icon" : "icon-plus-sign" "icon" : "icon-plus-sign"
}, },
{ {
"name": "NodeBB Link Exchange", "name": "NodeBB Link Exchange",
"description": "In future an example of how a hidden category should look like.", "description": "Link exchange",
"blockclass": "category-purple", "blockclass": "category-purple",
"icon" : "icon-exchange" "icon" : "icon-exchange"
}, },
{ {
"name": "News", "name": "News",
"description": "In future an example of how a hidden category should look like.", "description": "News from around the world",
"blockclass": "category-darkblue", "blockclass": "category-darkblue",
"icon" : "icon-globe" "icon" : "icon-globe"
}, },
{ {
"name": "Movies", "name": "Movies",
"description": "In future an example of how a hidden category should look like.", "description": "Discuss the latest movies here",
"blockclass": "category-darkblue", "blockclass": "category-darkblue",
"icon" : "icon-film" "icon" : "icon-film"
}, },
{ {
"name": "Games", "name": "Games",
"description": "In future an example of how a hidden category should look like.", "description": "Discuss the latest games here",
"blockclass": "category-darkblue", "blockclass": "category-darkblue",
"icon" : "icon-screenshot" "icon" : "icon-screenshot"
}, },
{ {
"name": "Random", "name": "Random",
"description": "In future an example of how a hidden category should look like.", "description": "Anything and (almost) everything welcome!",
"blockclass": "category-darkblue", "blockclass": "category-darkblue",
"icon" : "icon-beer" "icon" : "icon-beer"
} }

View File

@@ -2,7 +2,7 @@
"name": "nodebb", "name": "nodebb",
"license": "GPLv3 or later", "license": "GPLv3 or later",
"description": "NodeBB Forum", "description": "NodeBB Forum",
"version": "0.0.5", "version": "0.0.7",
"homepage": "http://www.nodebb.org", "homepage": "http://www.nodebb.org",
"repository": { "repository": {
"type": "git", "type": "git",
@@ -10,10 +10,10 @@
}, },
"main": "app.js", "main": "app.js",
"dependencies": { "dependencies": {
"socket.io": "0.9.14", "socket.io": "~0.9.16",
"redis": "0.8.3", "redis": "0.8.3",
"express": "3.2.0", "express": "3.2.0",
"express-namespace": "0.1.1", "express-namespace": "~0.1.1",
"emailjs": "0.3.4", "emailjs": "0.3.4",
"cookie": "0.0.6", "cookie": "0.0.6",
"connect-redis": "1.4.5", "connect-redis": "1.4.5",
@@ -27,13 +27,22 @@
"bcrypt": "0.7.5", "bcrypt": "0.7.5",
"async": "0.2.8", "async": "0.2.8",
"node-imagemagick": "0.1.8", "node-imagemagick": "0.1.8",
"node-rss": "1.0.1",
"gravatar": "1.0.6", "gravatar": "1.0.6",
"nconf": "~0.6.7", "nconf": "~0.6.7",
"sitemap": "~0.6.0", "sitemap": "~0.6.0",
"cheerio": "~0.12.0", "cheerio": "~0.12.0",
"request": "~2.25.0", "request": "~2.25.0",
"reds": "~0.2.4" "reds": "~0.2.4",
"winston": "~0.7.2",
"nodebb-plugin-mentions": "~0.1.0",
"nodebb-plugin-markdown": "~0.1.0",
"rss": "~0.2.0",
"prompt": "~0.2.11",
"uglify-js": "~2.4.0",
"validator": "~1.5.1"
},
"optionalDependencies": {
"hiredis": "~0.1.15"
}, },
"bugs": { "bugs": {
"url": "https://github.com/designcreateplay/NodeBB/issues" "url": "https://github.com/designcreateplay/NodeBB/issues"

View File

@@ -1,5 +1 @@
@import "style"; @import "../themes/cerulean/cerulean.less";
@import "topic";
@import "category";
@import "noscript";

View File

@@ -1,846 +0,0 @@
@import "mixins";
body {
/*background: #fdfdfd;*/ // port to default theme when it is implemented.
-webkit-transition: margin-bottom 250ms ease;
-moz-transition: margin-bottom 250ms ease;
-ms-transition: margin-bottom 250ms ease;
-o-transition: margin-bottom 250ms ease;
transition: margin-bottom 250ms ease;
&.composing {
margin-bottom: 350px;
}
@media (min-width: 979px)
{
padding-top: 70px;
}
@media (max-width: 979px)
{
padding-bottom: 50px;
}
}
button, a {
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
.none {
display: none !important;
}
.block, .show {
display: block;
}
.badge {
vertical-align: 17%;
}
.nav .badge {
vertical-align: 10%;
}
#alert_window {
position: fixed;
right: 20px;
top: 60px;
width: 300px;
height: 0px;
}
.toaster-alert {
cursor: pointer;
}
footer.footer {
color: #555;
text-align: center;
a {
color: #222;
}
}
#post_window {
width: 100%;
position: fixed;
height: 350px;
left: 0px;
bottom: 0px;
background: white;
z-index: 1500;
input {
width: 100%;
height: 30px;
padding: 5px;
}
textarea {
width: 100%;
background: #222;
height: 220px;
resize: none;
border-radius: 0;
border: 1px solid #111;
font-size: 16px;
color: #bebebe;
outline: 0;
&:focus {
outline: 0;
border:none !important;
box-shadow:none !important;
}
}
.post-title-container {
opacity: 0.8;
height: 50px;
}
.post-content-container {
opacity: 0.8;
background: #000;
width: 100%;
height: 300px;
}
}
#user_label { //belongs in header.less
img {
border: 1px solid #454;
margin-right: 8px;
margin-top: -2px;
float: left;
width:24px;
height:24px;
}
span {
font-size: 14px;
font-weight: 400;
color: #ded;
}
}
#reply_title {
font-size: 17px;
padding-top: 14px;
font-weight: 600;
}
.alt-logins {
margin: 0 0 0 1em;
padding: 0;
li {
vertical-align: top;
background: transparent;
display: none;
margin: 0.25em;
.pointer;
&.active {
.inline-block;
}
i {
-webkit-transition: color 100ms linear;
-moz-transition: color 100ms linear;
-ms-transition: color 100ms linear;
-o-transition: color 100ms linear;
transition: color 100ms linear;
&.icon-twitter-sign:hover {
color: #4099FF;
}
&.icon-facebook-sign:hover {
color: #3b5999;
}
&.icon-google-plus-sign:hover {
color: #d34836;
}
}
}
}
#thread_active_users {
float: right;
color: rgb(153,153,153);
strong {
color: rgb(100,100,100);
font-weight: 600;
cursor: pointer;
}
}
.account-username-box{
border-bottom:1px solid #e3e3e3;
margin-bottom:10px;
}
.account-sub-links a{
margin-left:10px;
}
.account-username{
font-size:20px;
font-weight:bold;
}
.account-picture-block{
display:inline-block;
vertical-align:top;
}
.account-online-status {
.icon-circle-blank {
color:red;
}
.icon-circle {
color:green;
}
}
.user-profile-picture {
width:128px;
margin-bottom:10px;
}
.user-picture-label {
font-size:20px;
}
.account-bio-block{
display:inline-block;
vertical-align:top;
}
.account-bio-label{
display:inline-block;
width:100px;
}
.user-recent-posts {
div {
color: #333;
margin-bottom: 10px;
cursor: pointer;
p {
color: #333;
}
}
span {
padding-top: 10px;
}
}
.category-icon {
width: 100%;
height: 90px;
text-align: center;
border-radius: 0px;
margin: 0;
padding-top:25px;
cursor: pointer;
margin-bottom: 20px;
border-radius: 5px;
overflow:hidden;
}
.category-row h4 {
font-weight: 700;
text-align: left;
/*color: #555;*/ // NOTE: color for cat/topic header links should be grey in the default theme when we get around to it.
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.category-row a {
text-decoration: none;
border: 0;
}
.category-purple {
@color: #ab1290;
background: @color;
color: white;
&:hover {
background: lighten(@color, 10%);
}
}
.category-darkblue {
@color: #004C66;
background: @color;
color: white;
&:hover {
background: lighten(@color, 10%);
}
}
.category-blue {
@color: #0059B2;
background: @color;
color: white;
&:hover {
background: lighten(@color, 10%);
}
}
.category-darkgreen {
@color: #004000;
background: @color;
color: white;
&:hover {
background: lighten(@color, 10%);
}
}
.category-orange {
@color: #FF7A4D;
color: white;
background: @color;
&:hover {
background: lighten(@color, 10%);
}
}
.category-list {
li {
.inline-block;
.pointer;
padding: 0.5em 0;
text-align: center;
margin: 0.5em;
-webkit-border-radius: 5px;
-moz-border-radius: 5px;
border-radius: 5px;
padding: 0.5em;
}
}
.hero-unit {
background: #56BCDA;
color: white;
padding: 30px;
word-wrap: break-word;
}
.users-box{
display: inline-block;
margin-top: 20px;
text-align: center;
vertical-align: top;
max-width: 104px;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
img {
width:80px;
height:80px;
}
a {
margin:5px;
}
}
a:hover, .btn-link:hover, .btn-link:active, .btn-link:focus {
text-decoration:none;
}
.formatting-bar {
.no-select;
span:focus {
outline: none;
}
}
.breadcrumb {
li {
max-width: 35%;
white-space: nowrap;
text-overflow:ellipsis;
overflow: hidden;
}
}
body .navbar .nodebb-inline-block {
display:inline-block;
}
#right-menu{
float:right;
}
#admin-redis-info {
span {
display:inline-block;
width:220px;
}
}
.post-signature {
color: #666;
font-size: 12px;
border-top: 1px solid #ddd;
display: inline-block;
img {
max-width:200px;
max-height:60px;
}
}
.username-field {
.icon-circle {
font-size: 12px;
color: green;
margin-right:3px;
}
.icon-circle-blank {
font-size: 12px;
color: red;
margin-right:3px;
}
}
#chat-content {
width:95%;
height:200px;
resize:none;
}
#chat-message-input {
width:95%;
}
#content{
padding-bottom:20px;
}
.dropdown-toggle {
i {
font-size: 12px;
&.active {
color: #558;
text-shadow: 0 0 1em #aaf, 0 0 1em #aaf, 0 0 1em #aaf;
}
}
}
#notif-list {
li {
font-size: 12px;
width: 300px;
text-align: left;
a {
white-space: normal;
}
&.unread {
background: #eceff5;
}
}
}
.taskbar {
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;
-o-transition: opacity 250ms ease-in;
transition: opacity 250ms ease-in;
&[data-active="1"] {
display: block;
}
&:hover {
-moz-opacity: 1;
opacity: 1;
}
li {
a > span {
.inline-block;
max-width: 200px;
text-overflow: ellipsis;
white-space: nowrap;
overflow: hidden;
}
img {
max-width: 24px;
max-height: 24px;
margin-right: 1em;
}
&.pulse {
-webkit-animation: pulsate 2500ms linear;
-webkit-animation-iteration-count: infinite;
}
@-webkit-keyframes pulsate {
0% { background: none; }
50% { background: #e5e5e5; }
100% { background: none; }
}
}
}
.post-window {
position: fixed;
display: none;
height: 350px;
visibility: hidden;
> div {
position: absolute;
height: 100%;
background: rgba(64, 64, 64, 0.6);
visibility: visible;
.btn-toolbar {
width: 90%;
margin: 0 auto;
span {
color: white;
&:hover {
text-decoration: none;
}
}
}
input {
width: 98%;
text-align: center;
border: none;
padding: 0.5em 0;
-webkit-border-radius: 0px;
-moz-border-radius: 0px;
border-radius: 0px;
margin: 1% 1% 2% 1%;
}
textarea {
background: rgba(64, 64, 64, 0.95);
border: none;
padding: 0.5em;
display: block;
width: 90%;
margin: 0.5em auto;
resize: none;
color: white;
height: 200px;
}
#imagedrop {
text-align:center;
color:white;
position: absolute;
top: 0px;
left: 0px;
width: 100%;
height:230px;
line-height:230px;
font-size:20px;
vertical-align: middle;
}
#imagelist {
position: absolute;
bottom: 5px;
left: 0px;
padding-left:2em;
div {
margin-right:5px;
}
span {
line-height:20px;
float:left;
}
button {
padding-left:5px;
}
}
}
}
@media (max-width: 979px) {
.post-window {
position: relative;
bottom: 0px !important;
> div {
position: static;
width: 100% !important;
}
}
}
#mobile-menu {
position: fixed;
bottom: 0px;
height: 50px;
background: #333;
width: 100%;
left: 0px;
}
#mobile-menu, #mobile-menu-overlay {
z-index: 999;
@media (min-width: 979px)
{
display: none !important;
}
}
.btn-none,
.btn-none:active,
.btn-none[disabled] {
background-color: transparent;
background-image: none;
-webkit-box-shadow: none;
-moz-box-shadow: none;
box-shadow: none;
}
.btn-none {
cursor: pointer;
border-color: transparent;
-webkit-border-radius: 0;
-moz-border-radius: 0;
border-radius: 0;
}
.btn-none:hover,
.btn-none:focus {
text-decoration: none;
background-color: transparent;
}
.btn-none[disabled]:hover,
.btn-none[disabled]:focus {
text-decoration: none;
}
.btn-none {
.icon-white {
color: white;
}
}
#mobile-menu {
button {
color: #eee;
padding: 10px;
text-shadow: none;
-webkit-tap-highlight-color: rgba(0,0,0,0);
}
}
#mobile-menu-overlay {
background: rgba(0, 0, 0, 0.85);
position: fixed;
top: 0px;
left: 0px;
height: 100%;
width: 100%;
//margin-top: 50px;
padding-top: 20px;
opacity: 0;
-webkit-transition: opacity 150ms ease;
-moz-transition: opacity 150ms ease;
-ms-transition: opacity 150ms ease;
-o-transition: opacity 150ms ease;
transition: opacity 150ms ease;
z-index: -1;
&.menu-visible {
z-index: 99;
opacity: 1;
}
.mobile-menu-icon {
color: white;
width: 20%;
min-width: 85px;
height: auto;
text-align: center;
padding: 10px;
display: inline-block;
cursor: pointer;
-webkit-tap-highlight-color: rgba(0,0,0,0);
-webkit-transition: margin-top 250ms ease;
-moz-transition: margin-top 250ms ease;
-ms-transition: margin-top 250ms ease;
-o-transition: margin-top 250ms ease;
transition: margin-top 250ms ease;
margin-top: 20%;
&.menu-visible {
margin-top: 0%;
}
i {
width: 100%;
}
}
}
#mobile-sidebar {
height: 100%;
position: absolute;
left: 100%;
top: 0px;
overflow: hidden;
margin-top: 60px;
}
.category-box {
height:90px;
.post-preview {
padding-left:10px;
padding-right:10px;
text-align:left;
img {
width:60px;
height:60px;
padding-right:5px;
}
p {
overflow: hidden;
text-overflow:ellipsis;
height:60px;
}
}
}
#mark-allread-btn {
margin-bottom:15px;
}
@-webkit-keyframes scroll-2 /* Safari and Chrome */
{
0% {top: 0px;}
25% {top: -90px;}
50% {top: -180px;}
75% {top: -270px;}
100% {top: -360px;}
}
@keyframes scroll-2
{
0% {top: 0px;}
25% {top: -90px;}
50% {top: -180px;}
75% {top: -270px;}
100% {top: -360px;}
}
@-webkit-keyframes scroll-1 /* Safari and Chrome */
{
0% {top: 0px;}
33% {top: -90px;}
66% {top: -180px;}
100% {top: -270px;}
}
@keyframes scroll-1
{
0% {top: 0px;}
33% {top: -90px;}
66% {top: -180px;}
100% {top: -270px;}
}
@-webkit-keyframes scroll-0 /* Safari and Chrome */
{
0% {top: 0px;}
50% {top: -90px;}
100% {top: -180px;}
}
@keyframes scroll-0
{
0% {top: 0px;}
50% {top: -90px;}
100% {top: -180px;}
}
.category-slider-2:hover {
position:relative;
-webkit-animation: scroll-2 10s ease 0.5s infinite normal;
animation: scroll-2 10s ease 0.5s infinite normal;/* Safari and Chrome: */
}
.category-slider-1:hover {
position:relative;
-webkit-animation: scroll-1 8s ease 0.5s infinite normal;
animation: scroll-1 8s ease 0.5s infinite normal;/* Safari and Chrome: */
}
.category-slider-0:hover {
position:relative;
-webkit-animation: scroll-0 6s ease 0.5s infinite normal;
animation: scroll-0 6s ease 0.5s infinite normal;/* Safari and Chrome: */
}
.form-search {
float: left;
margin-top: 5px;
margin-bottom:0px;
}
.search-result-post {
width: 100%;
line-height: 16px;
padding: 5px;
overflow:hidden;
img {
display: block;
float: left;
width:48px;
height:48px;
padding-right:10px;
}
}

View File

@@ -1,193 +0,0 @@
.post-container {
list-style-type: none;
padding: 0;
margin: 0;
clear: both;
.profile-image-block {
display: inline-block;
text-align: center;
font-size: 12px;
.stats {
clear: both;
}
}
li {
padding-bottom: 15px;
&.deleted {
-moz-opacity: 0.30;
opacity: 0.30;
height:30px;
overflow-y:hidden;
}
&.deleted-expanded {
height:100%;
overflow-y:default;
}
}
.profile-block, .post-block {
border: 1px solid rgba(0, 0, 0, 0.06);
border-radius: 5px;
padding: 10px;
p {
line-height: 1.75em;
}
}
.profile-block {
background: rgba(0, 0, 0, 0.02);
margin-right: -11px;
margin-left: -11px;
margin-bottom: -11px;
margin-top: 15px;
border-radius: 0 0 5px 5px;
font-size: 10px;
line-height: 18px;
padding: 5px;
padding-left: 10px;
img.hidden-desktop {
max-width: 10px;
max-height: 10px;
padding-top: 5px;
margin-right: 5px;
}
}
.post-content {
min-height: 50px;
padding: 2px 5px 0 5px;
word-wrap: break-word;
}
.post-images{
padding: 2px 5px 0 5px;
}
.post-block {
.post-buttons {
font-size: 12px;
float: right;
margin-right: 5px;
button, a {
display: inline-block;
padding: 0px 15px;
border: none;
border-left: 1px solid rgba(0, 0, 0, 0.06);
cursor: pointer;
background: none;
font-size: 12px;
height: 20px;
&:last-child {
padding-right: 5px;
}
}
.icon-star {
//theme this to make it yellow eventually
}
}
}
&.deleted {
-moz-opacity: 0.30;
opacity: 0.30;
}
/*http://stackoverflow.com/questions/11037517/bootstrap-making-responsive-changes-to-layout*/
@media (max-width: 979px) {
.span12-tablet {
width: 100% !important;
margin-left:0px;
*width: 100% !important;
}
}
.main-post {
h3 {
margin: 0;
.topic-title {
width: auto;
white-space: nowrap;
text-overflow:ellipsis;
overflow: hidden;
margin: 0;
padding: 0;
padding-top: 5px;
margin-bottom: -5px;
padding-bottom: 5px;
}
}
.main-avatar {
color: white;
position: relative;
float: left;
margin-right: 25px;
margin-bottom: 0px;
padding-bottom: 0px;
text-align: center;
width:80px;
@media (max-width: 767px) {
display: none;
}
}
.main-avatar:hover .hover-overlay {
opacity: 0.75;
}
.hover-overlay {
margin: 5px;
position: absolute;
bottom: 0px;
height: 35px;
padding-top: 2px;
width: 80px;
font-size: 13px;
line-height: 16px;
background: #000;
opacity: 0;
transition: opacity 0.3s;
}
.post-content {
min-height: 80px;
}
hr {
margin-top: 0;
margin-right: 10px;
margin-bottom: 0;
}
.post-block {
.post-buttons {
div {
border: 0;
}
}
}
.favourite {
cursor: pointer;
}
.btn {
display: inline-block;
}
.topic-buttons {
margin-top: 8px;
}
}
}

3
public/language/TODO Normal file
View File

@@ -0,0 +1,3 @@
NPM INSTALL
For now, language packs will be stored here. Eventually, will be moved to server side to allow for npm install-ability.
When that happens, server code will generate compressed JSON language files in this folder.

View File

@@ -0,0 +1,7 @@
{
"new_topic_button": "New Topic",
"no_topics": "<strong>There are no topics in this category.</strong><br />Why don't you try posting one?",
"sidebar.recent_replies": "Recent Replies",
"sidebar.active_participants": "Active Participants",
"sidebar.moderators": "Moderators"
}

View File

@@ -0,0 +1,9 @@
{
"chat.chatting_with": "Chat with <span id='chat-with-name'></span>",
"chat.placeholder": "type chat message, here press enter to send",
"chat.send": "Send",
"stats.online": "Online",
"stats.users": "Users",
"stats.topics": "Topics",
"stats.posts": "Posts"
}

View File

@@ -0,0 +1,17 @@
{
"home": "Home",
"search": "Search",
"buttons.close": "Close",
"403.title": "Access Denied",
"403.message": "You seem to have stumbled upon a page that you do not have access to. Perhaps you should <a href='/login'>try logging in</a>?",
"404.title": "Not Found",
"404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='/''>home page</a>.",
"logout": "Logout",
"logout.title": "You are now logged out.",
"logout.message": "You have successfully logged out of NodeBB",
"header.recent": "Recent",
"header.unread": "Unread",
"header.users": "Users",
"header.search": "Search",
"notifications.loading": "Loading Notifications"
}

View File

@@ -0,0 +1,10 @@
{
"login": "Login",
"username": "Username",
"password": "Password",
"remember_me": "Remember Me?",
"forgot_password": "Forgot Password?",
"alternative_logins": "Alternative Logins",
"failed_login_attempt": "Failed login attempt, please try again.",
"login_successful": "You have successfully logged in!"
}

View File

@@ -0,0 +1,16 @@
{
"register": "Register",
"help.email": "By default, your email will be hidden from the public.",
"help.username_restrictions": "A unique username between %1 and %2 characters. Others can mention you with @<span id='yourUsername'>username</span>.",
"help.minimum_password_length": "Your password's length must be at least %1 characters.",
"email_address": "Email Address",
"email_address_placeholder": "Enter Email Address",
"username": "Username",
"username_placeholder": "Enter Username",
"password": "Password",
"password_placeholder": "Enter Password",
"confirm_password": "Confirm Password",
"confirm_password_placeholder": "Confirm Password",
"register_now_button": "Register Now",
"alternative_registration": "Alternative Registration"
}

View File

@@ -0,0 +1,22 @@
{
"profile": "Profile",
"posted_by": "Posted by",
"chat": "Chat",
"notify_me": "Be notified of new replies in this topic",
"favourite": "Favourite",
"quote": "Quote",
"reply": "Reply",
"edit": "Edit",
"delete": "Delete",
"banned": "banned",
"link": "Link",
"thread_tools.title": "Thread Tools",
"thread_tools.pin": "Pin Thread",
"thread_tools.lock": "Lock Thread",
"thread_tools.move": "Move Thread",
"thread_tools.delete": "Delete Thread",
"load_categories": "Loading Categories",
"confirm_move": "Move",
"favourites.not_logged_in.title": "Not Logged In",
"favourites.not_logged_in.message": "Please log in in order to favourite this post"
}

View File

@@ -0,0 +1,5 @@
{
"no_unread_topics": "There are no unread topics.",
"mark_all_read": "Mark all as Read",
"load_more": "Load More"
}

View File

@@ -0,0 +1,9 @@
{
"latest_users": "Latest Users",
"top_posters": "Top Posters",
"most_reputation": "Most Reputation",
"online": "Online",
"search": "Search",
"enter_username": "Enter a username to search",
"load_more": "Load More"
}

Binary file not shown.

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,7 +1,8 @@
var ajaxify = {}; var ajaxify = {};
(function($) { (function ($) {
/*global app, templates, utils*/
var location = document.location || window.location, var location = document.location || window.location,
rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''), rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''),
@@ -11,8 +12,8 @@ var ajaxify = {};
var executed = {}; var executed = {};
var events = []; var events = [];
ajaxify.register_events = function(new_page_events) { ajaxify.register_events = function (new_page_events) {
for (var i = 0, ii = events.length; i<ii; i++) { for (var i = 0, ii = events.length; i < ii; i++) {
socket.removeAllListeners(events[i]); // optimize this to user removeListener(event, listener) instead. socket.removeAllListeners(events[i]); // optimize this to user removeListener(event, listener) instead.
} }
@@ -20,27 +21,36 @@ var ajaxify = {};
}; };
window.onpopstate = function(event) { window.onpopstate = function (event) {
// "quiet": If set to true, will not call pushState // "quiet": If set to true, will not call pushState
if (event !== null && event.state && event.state.url !== undefined) ajaxify.go(event.state.url, null, null, true); if (event !== null && event.state && event.state.url !== undefined) ajaxify.go(event.state.url, null, null, true);
}; };
ajaxify.go = function(url, callback, template, quiet) { var pagination;
// leave room and join global
ajaxify.go = function (url, callback, template, quiet) {
// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
$(window).off('scroll');
app.enter_room('global'); app.enter_room('global');
var url = url.replace(/\/$/, ""); pagination = pagination || document.getElementById('pagination');
if (pagination) pagination.parentNode.style.display = 'none';
window.onscroll = null;
// end
// Remove trailing slash
url = url.replace(/\/$/, "");
var hash = window.location.hash; var hash = window.location.hash;
if(url.indexOf(RELATIVE_PATH.slice(1)) !== -1) { if (url.indexOf(RELATIVE_PATH.slice(1)) !== -1) {
url = url.slice(RELATIVE_PATH.length); url = url.slice(RELATIVE_PATH.length);
} }
var tpl_url = templates.get_custom_map(url.split('?')[0]); var tpl_url = templates.get_custom_map(url.split('?')[0]);
if (tpl_url == false && !templates[url]) { if (tpl_url == false && !templates[url]) {
if(url === '' || url === '/') { if (url === '' || url === '/') {
tpl_url = 'home'; tpl_url = 'home';
} else { } else {
tpl_url = url.split('/')[0].split('?')[0]; tpl_url = url.split('/')[0].split('?')[0];
@@ -52,75 +62,87 @@ var ajaxify = {};
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) { if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
if (quiet !== true) { if (quiet !== true) {
if (window.history && window.history.pushState) {
window.history.pushState({ window.history.pushState({
"url": url "url": url
}, url, RELATIVE_PATH + "/" + url); }, url, RELATIVE_PATH + "/" + url);
} }
}
translator.load(tpl_url);
jQuery('#footer, #content').fadeOut(100); jQuery('#footer, #content').fadeOut(100);
templates.flush(); templates.flush();
templates.load_template(function() { templates.load_template(function () {
exec_body_scripts(content); exec_body_scripts(content);
if (callback) { if (callback) {
callback(); callback();
} }
app.process_page(); jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
jQuery('#content, #footer').stop(true, true).fadeIn(200, function() { app.process_page();
if(window.location.hash) if (window.location.hash)
hash = window.location.hash; hash = window.location.hash;
if(hash) if (hash)
app.scrollToPost(hash.substr(1)); app.scrollToPost(hash.substr(1));
}); });
}, url, template); utils.refreshTitle(url);
socket.emit('api:meta.buildTitle', url, function(title) { }, url, template);
document.title = title;
});
return true; return true;
} }
return false; return false;
} };
$('document').ready(function() { $('document').ready(function () {
if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers
content = content || document.getElementById('content'); content = content || document.getElementById('content');
// Enhancing all anchors to ajaxify... // Enhancing all anchors to ajaxify...
$(document.body).on('click', 'a', function(e) { $(document.body).on('click', 'a', function (e) {
if (this.href == window.location.href + "#") return; function hrefEmpty(href) {
if(this.href.slice(-1) === "#") return; return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
}
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
return;
var url = this.href.replace(rootUrl +'/', ''); app.previousUrl = window.location.href;
if (this.target !== '') return;
if (!e.ctrlKey && e.which === 1) { if (!e.ctrlKey && e.which === 1) {
if (this.host === window.location.host) {
// Internal link
var url = this.href.replace(rootUrl + '/', '');
if (ajaxify.go(url)) { if (ajaxify.go(url)) {
e.preventDefault();
}
} else if (window.location.pathname !== '/outgoing') {
// External Link
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
e.preventDefault(); e.preventDefault();
} }
} }
}); });
}); });
function exec_body_scripts(body_el) { function exec_body_scripts(body_el) {
// modified from http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml // modified from http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
function nodeName(elem, name) { function nodeName(elem, name) {
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase(); return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
}; }
function evalScript(elem) { function evalScript(elem) {
var data = (elem.text || elem.textContent || elem.innerHTML || "" ), var data = (elem.text || elem.textContent || elem.innerHTML || ""),
head = document.getElementsByTagName("head")[0] || head = document.getElementsByTagName("head")[0] ||
document.documentElement, document.documentElement,
script = document.createElement("script"); script = document.createElement("script");
@@ -128,7 +150,7 @@ var ajaxify = {};
script.type = "text/javascript"; script.type = "text/javascript";
try { try {
script.appendChild(document.createTextNode(data)); script.appendChild(document.createTextNode(data));
} catch(e) { } catch (e) {
script.text = data; script.text = data;
} }
@@ -139,17 +161,17 @@ var ajaxify = {};
head.insertBefore(script, head.firstChild); head.insertBefore(script, head.firstChild);
//TODO: remove from head before inserting?, doing this breaks scripts in safari so commented out for now //TODO: remove from head before inserting?, doing this breaks scripts in safari so commented out for now
//head.removeChild(script); //head.removeChild(script);
}; }
var scripts = [], var scripts = [],
script, script,
children_nodes = body_el.childNodes, children_nodes = $(body_el).children(),
child, child,
i; i;
for (i = 0; children_nodes[i]; i++) { for (i = 0; children_nodes[i]; i++) {
child = children_nodes[i]; child = children_nodes[i];
if (nodeName(child, "script" ) && if (nodeName(child, "script") &&
(!child.type || child.type.toLowerCase() === "text/javascript")) { (!child.type || child.type.toLowerCase() === "text/javascript")) {
scripts.push(child); scripts.push(child);
} }
@@ -162,6 +184,6 @@ var ajaxify = {};
} }
evalScript(scripts[i]); evalScript(scripts[i]);
} }
}; }
}(jQuery)); }(jQuery));

View File

@@ -1,88 +1,68 @@
var socket, var socket,
config, config,
app = {}, app = {};
API_URL = null;
(function () {
(function() {
var showWelcomeMessage = false; var showWelcomeMessage = false;
function loadConfig() { app.loadConfig = function() {
$.ajax({ $.ajax({
url: RELATIVE_PATH + '/api/config', url: RELATIVE_PATH + '/api/config',
success: function(data) { success: function (data) {
API_URL = data.api_url;
config = data; config = data;
socket = io.connect(config.socket.address + (config.socket.port ? ':' + config.socket.port : '')); if(socket) {
socket.disconnect();
setTimeout(function() {
socket.socket.connect();
}, 200);
} else {
socket = io.connect(config.socket.address);
var reconnecting = false; var reconnecting = false,
var reconnectTries = 0; reconnectEl, reconnectTimer;
socket.on('event:connect', function(data) { socket.on('event:connect', function (data) {
console.log('connected to nodebb socket: ', data);
app.username = data.username; app.username = data.username;
app.showLoginMessage(); app.showLoginMessage();
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
}); });
socket.on('event:alert', function(data) { socket.on('event:alert', function (data) {
app.alert(data); app.alert(data);
}); });
socket.on('event:consolelog', function(data) { socket.on('connect', function (data) {
console.log(data); if (reconnecting) {
}); reconnectEl.html('<i class="icon-ok"></i> Connected!');
socket.on('connect', function(data){
if(reconnecting) {
setTimeout(function(){
app.alert({
alert_id: 'connection_alert',
title: 'Connected',
message: 'Connection successful.',
type: 'success',
timeout: 5000
});
}, 1000);
reconnecting = false; reconnecting = false;
reconnectTries = 0;
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
}
});
socket.on('reconnecting', function(data) { setTimeout(function() {
function showDisconnectModal() { reconnectEl.removeClass('active');
$('#disconnect-modal').modal({ }, 3000);
backdrop:'static',
show:true
});
$('#reload-button').on('click',function(){
$('#disconnect-modal').modal('hide');
window.location.reload();
});
} }
socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
});
socket.on('event:disconnect', function() {
socket.socket.connect();
});
socket.on('reconnecting', function (data) {
if (!reconnectEl) reconnectEl = $('#reconnect');
reconnecting = true; reconnecting = true;
reconnectTries++;
if(reconnectTries > 4) { reconnectEl.addClass('active');
showDisconnectModal(); reconnectEl.html('<i class="icon-spinner icon-spin"></i> Reconnecting...');
return;
}
app.alert({
alert_id: 'connection_alert',
title: 'Reconnecting',
message: 'You have disconnected from NodeBB, we will try to reconnect you. <br/><i class="icon-refresh icon-spin"></i>',
type: 'notify',
timeout: 5000
});
}); });
socket.on('api:user.get_online_users', function(users) { socket.on('api:user.get_online_users', function (users) {
jQuery('.username-field').each(function() { jQuery('a.username-field').each(function () {
if (this.processed === true) if (this.processed === true)
return; return;
@@ -97,25 +77,40 @@ var socket,
el.prepend('<i class="icon-circle-blank"></i>'); el.prepend('<i class="icon-circle-blank"></i>');
} }
el.processed = true;
});
jQuery('button .username-field').each(function () {
//DRY FAIL
if (this.processed === true)
return;
var el = jQuery(this),
uid = el.parents('li').attr('data-uid');
if (uid && jQuery.inArray(uid, users) !== -1) {
el.parent().addClass('btn-success');
} else {
el.parent().addClass('btn-danger');
}
el.processed = true; el.processed = true;
}); });
}); });
app.enter_room('global'); app.enter_room('global');
}
}, },
async: false async: false
}); });
} }
// takes a string like 1000 and returns 1,000 // takes a string like 1000 and returns 1,000
app.addCommas = function(text) { app.addCommas = function (text) {
return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"); return text.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,");
} }
// Willingly stolen from: http://phpjs.org/functions/strip_tags/ // Willingly stolen from: http://phpjs.org/functions/strip_tags/
app.strip_tags = function(input, allowed) { app.strip_tags = function (input, allowed) {
allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>) allowed = (((allowed || "") + "").toLowerCase().match(/<[a-z][a-z0-9]*>/g) || []).join(''); // making sure the allowed arg is a string containing only tags in lowercase (<a><b><c>)
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
@@ -131,14 +126,14 @@ var socket,
// message = alert message content // message = alert message content
// timeout default = permanent // timeout default = permanent
// location : alert_window (default) or content // location : alert_window (default) or content
app.alert = function(params) { app.alert = function (params) {
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime()); var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
var alert = $('#'+alert_id); var alert = $('#' + alert_id);
function startTimeout(div, timeout) { function startTimeout(div, timeout) {
var timeoutId = setTimeout(function() { var timeoutId = setTimeout(function () {
$(div).fadeOut(1000, function() { $(div).fadeOut(1000, function () {
$(this).remove(); $(this).remove();
}); });
}, timeout); }, timeout);
@@ -146,15 +141,14 @@ var socket,
$(div).attr('timeoutId', timeoutId); $(div).attr('timeoutId', timeoutId);
} }
if(alert.length > 0) { if (alert.length > 0) {
alert.find('strong').html(params.title); alert.find('strong').html(params.title);
alert.find('p').html(params.message); alert.find('p').html(params.message);
alert.attr('class', "alert toaster-alert " + ((params.type=='warning') ? '' : "alert-" + params.type)); alert.attr('class', "alert toaster-alert " + "alert-" + params.type);
clearTimeout(alert.attr('timeoutId')); clearTimeout(alert.attr('timeoutId'));
startTimeout(alert, params.timeout); startTimeout(alert, params.timeout);
} } else {
else {
var div = document.createElement('div'), var div = document.createElement('div'),
button = document.createElement('button'), button = document.createElement('button'),
strong = document.createElement('strong'), strong = document.createElement('strong'),
@@ -163,7 +157,7 @@ var socket,
p.innerHTML = params.message; p.innerHTML = params.message;
strong.innerHTML = params.title; strong.innerHTML = params.title;
div.className = "alert toaster-alert " + ((params.type=='warning') ? '' : "alert-" + params.type); div.className = "alert toaster-alert " + "alert-" + params.type;
div.setAttribute('id', alert_id); div.setAttribute('id', alert_id);
div.appendChild(button); div.appendChild(button);
@@ -172,23 +166,23 @@ var socket,
button.className = 'close'; button.className = 'close';
button.innerHTML = '&times;'; button.innerHTML = '&times;';
button.onclick = function(ev) { button.onclick = function (ev) {
div.parentNode.removeChild(div); div.parentNode.removeChild(div);
} }
if (params.location == null) if (params.location == null)
params.location = 'alert_window'; params.location = 'alert_window';
jQuery('#'+params.location).prepend(jQuery(div).fadeIn('100')); jQuery('#' + params.location).prepend(jQuery(div).fadeIn('100'));
if (params.timeout) { if (params.timeout) {
startTimeout(div, params.timeout); startTimeout(div, params.timeout);
} }
if (params.clickfn) { if (params.clickfn) {
div.onclick = function() { div.onclick = function () {
params.clickfn(); params.clickfn();
jQuery(div).fadeOut(500, function() { jQuery(div).fadeOut(500, function () {
this.remove(); this.remove();
}); });
} }
@@ -196,8 +190,8 @@ var socket,
} }
} }
app.alertSuccess = function(message, timeout) { app.alertSuccess = function (message, timeout) {
if(!timeout) if (!timeout)
timeout = 2000; timeout = 2000;
app.alert({ app.alert({
@@ -208,22 +202,21 @@ var socket,
}); });
} }
app.alertError = function(message, timeout) { app.alertError = function (message, timeout) {
if(!timeout) if (!timeout)
timeout = 2000; timeout = 2000;
app.alert({ app.alert({
title: 'Error', title: 'Error',
message: message, message: message,
type: 'error', type: 'danger',
timeout: timeout timeout: timeout
}); });
} }
app.current_room = null; app.current_room = null;
app.enter_room = function(room) { app.enter_room = function (room) {
if (socket) {
if(socket) {
if (app.current_room === room) if (app.current_room === room)
return; return;
@@ -236,20 +229,20 @@ var socket,
} }
}; };
app.populate_online_users = function() { app.populate_online_users = function () {
var uids = []; var uids = [];
jQuery('.post-row').each(function() { jQuery('.post-row').each(function () {
uids.push(this.getAttribute('data-uid')); uids.push(this.getAttribute('data-uid'));
}); });
socket.emit('api:user.get_online_users', uids); socket.emit('api:user.get_online_users', uids);
} }
app.process_page = function() { app.process_page = function () {
// here is where all modules' onNavigate should be called, I think. // here is where all modules' onNavigate should be called, I think.
require(['mobileMenu'], function(mobileMenu) { require(['mobileMenu'], function (mobileMenu) {
mobileMenu.onNavigate(); mobileMenu.onNavigate();
}); });
@@ -257,13 +250,13 @@ var socket,
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
active = parts[parts.length-1]; active = parts[parts.length - 1];
jQuery('#main-nav li').removeClass('active'); jQuery('#main-nav li').removeClass('active');
if(active) { if (active) {
jQuery('#main-nav li a').each(function() { jQuery('#main-nav li a').each(function () {
var href = this.getAttribute('href'); var href = this.getAttribute('href');
if(active.match(/^users/)) if (active == "sort-posts" || active == "sort-reputation" || active == "search" || active == "latest" || active == "online")
active = 'users'; active = 'users';
if (href && href.match(active)) { if (href && href.match(active)) {
jQuery(this.parentNode).addClass('active'); jQuery(this.parentNode).addClass('active');
@@ -272,12 +265,15 @@ var socket,
}); });
} }
setTimeout(function() { $('span.timeago').timeago();
setTimeout(function () {
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes. window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
}, 100); }, 100);
} }
app.showLoginMessage = function() { app.showLoginMessage = function () {
function showAlert() { function showAlert() {
app.alert({ app.alert({
type: 'success', type: 'success',
@@ -287,9 +283,9 @@ var socket,
}); });
} }
if(showWelcomeMessage) { if (showWelcomeMessage) {
showWelcomeMessage = false; showWelcomeMessage = false;
if(document.readyState !== 'complete') { if (document.readyState !== 'complete') {
$(document).ready(showAlert); $(document).ready(showAlert);
} else { } else {
showAlert(); showAlert();
@@ -297,23 +293,36 @@ var socket,
} }
} }
app.addCommasToNumbers = function() { app.addCommasToNumbers = function () {
$('.formatted-number').each(function(index, element) { $('.formatted-number').each(function (index, element) {
$(element).html(app.addCommas($(element).html())); $(element).html(app.addCommas($(element).html()));
}); });
} }
app.createNewPosts = function(data) { app.openChat = function (username, touid) {
require(['chat'], function (chat) {
var chatModal;
if (!chat.modalExists(touid)) {
chatModal = chat.createModal(username, touid);
} else {
chatModal = chat.getModal(touid);
}
chat.load(chatModal.attr('UUID'));
});
}
app.createNewPosts = function (data) {
data.posts[0].display_moderator_tools = 'none'; data.posts[0].display_moderator_tools = 'none';
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data), var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
uniqueid = new Date().getTime(), translator.translate(html, function(translatedHTML) {
var uniqueid = new Date().getTime(),
tempContainer = jQuery('<div id="' + uniqueid + '"></div>') tempContainer = jQuery('<div id="' + uniqueid + '"></div>')
.appendTo("#post-container") .appendTo("#post-container")
.hide() .hide()
.append(html) .append(translatedHTML)
.fadeIn('slow'); .fadeIn('slow');
for(var x=0,numPosts=data.posts.length;x<numPosts;x++) { for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) {
socket.emit('api:post.privileges', data.posts[x].pid); socket.emit('api:post.privileges', data.posts[x].pid);
} }
@@ -322,112 +331,97 @@ var socket,
app.populate_online_users(); app.populate_online_users();
app.addCommasToNumbers(); app.addCommasToNumbers();
$('span.timeago').timeago();
});
} }
app.infiniteLoaderActive = false; app.infiniteLoaderActive = false;
app.loadMorePosts = function(tid, callback) { app.loadMorePosts = function (tid, callback) {
if(app.infiniteLoaderActive) if (app.infiniteLoaderActive)
return; return;
app.infiniteLoaderActive = true; app.infiniteLoaderActive = true;
if ($('#loading-indicator').attr('done') === '0')
$('#loading-indicator').removeClass('hide');
socket.emit('api:topic.loadMore', { socket.emit('api:topic.loadMore', {
tid: tid, tid: tid,
after: document.querySelectorAll('#post-container li[data-pid]').length after: document.querySelectorAll('#post-container li[data-pid]').length
}, function(data) { }, function (data) {
app.infiniteLoaderActive = false; app.infiniteLoaderActive = false;
if(data.posts.length) { if (data.posts.length) {
$('#loading-indicator').attr('done', '0');
app.createNewPosts(data); app.createNewPosts(data);
if(callback) } else {
callback(); $('#loading-indicator').attr('done', '1');
} }
$('#loading-indicator').addClass('hide');
if (callback)
callback(data.posts);
}); });
} }
app.scrollToPost = function(pid) { app.scrollToTop = function () {
if(!pid) $('body,html').animate({
scrollTop: 0
});
};
app.scrollToBottom = function () {
$('body,html').animate({
scrollTop: $('html').height() - 100
});
}
app.scrollToPost = function (pid) {
if (!pid)
return; return;
var container = $(document.body), var container = $(document.body),
scrollTo = $('#post_anchor_' + pid),
tid = $('#post-container').attr('data-tid');
function animateScroll() {
$('body,html').animate({
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
}, 400);
//$('body,html').scrollTop(scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height());
}
if (!scrollTo.length && tid) {
var intervalID = setInterval(function () {
app.loadMorePosts(tid, function (posts) {
scrollTo = $('#post_anchor_' + pid); scrollTo = $('#post_anchor_' + pid);
if(!scrollTo.length) { if (tid && scrollTo.length) {
var tid = $('#post-container').attr('data-tid'); animateScroll();
app.loadMorePosts(tid, function() {
app.scrollToPost(pid);
});
return;
} }
container.animate({ if (!posts.length || scrollTo.length)
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height() clearInterval(intervalID);
}); });
}, 100);
} else if (tid) {
animateScroll();
} }
jQuery('document').ready(function() { }
addTouchEvents();
$('#search-form').on('submit', function() { jQuery('document').ready(function () {
$('#search-form').on('submit', function () {
var input = $(this).find('input'); var input = $(this).find('input');
ajaxify.go("search/"+input.val(), null, "search"); ajaxify.go("search/" + input.val(), null, "search");
input.val(''); input.val('');
return false; return false;
}) });
}); });
showWelcomeMessage = location.href.indexOf('loggedin') !== -1; showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
loadConfig(); app.loadConfig();
function addTouchEvents() {
return; // later.
// click simulation just for testing/sanity purposes.
var el = jQuery("#content"),
sidebar = jQuery('#mobile-sidebar'),
width = el.width();
function onTouchMove(ev) {
var coordinates = window.event ? window.event.touches[0] : ev.touches[0];
el.css({
marginLeft: -parseInt(width - coordinates.pageX) + 'px',
paddingRight: parseInt(width - coordinates.pageX) + 'px'});
sidebar.css({
marginLeft: -parseInt(width - coordinates.pageX) + 'px',
width: parseInt(width - coordinates.pageX) + 'px'
});
}
function onMouseMove(ev) {
ev.touches = [{pageX: ev.pageX, pageY: ev.pageY}];
onTouchMove(ev);
}
function onTouchEnd() {
el.css({
marginLeft: '0px',
paddingRight: '0px'
});
sidebar.css({
marginLeft: '0px',
width: '0px'
});
}
el.on('touchmove', onTouchMove);
el.on('mousedown', function() {
el.on('mousemove', onMouseMove);
});
el.on('touchend', onTouchEnd);
el.on('mouseup', function() {
el.off('mousemove');
onTouchEnd();
});
}
}()); }());

View File

@@ -4,30 +4,32 @@
isFollowing = templates.get('isFollowing'); isFollowing = templates.get('isFollowing');
$(document).ready(function() { $(document).ready(function() {
var username = $('.account-username a').html();
app.enter_room('user/' + theirid);
var rep = $('#reputation'); app.addCommasToNumbers();
rep.html(app.addCommas(rep.html()));
var postcount = $('#postcount');
postcount.html(app.addCommas(postcount.html()));
var followBtn = $('#follow-btn'); var followBtn = $('#follow-btn');
var unfollowBtn = $('#unfollow-btn'); var unfollowBtn = $('#unfollow-btn');
if(yourid !== theirid) { if (yourid !== theirid) {
if(isFollowing) { if (isFollowing) {
followBtn.hide(); followBtn.hide();
unfollowBtn.show(); unfollowBtn.show();
} else { } else {
followBtn.show(); followBtn.show();
unfollowBtn.hide(); unfollowBtn.hide();
} }
} else {
followBtn.hide();
unfollowBtn.hide();
} }
followBtn.on('click', function() { followBtn.on('click', function() {
socket.emit('api:user.follow', {uid: theirid}, function(success) { socket.emit('api:user.follow', {
var username = $('.account-username a').html(); uid: theirid
if(success) { }, function(success) {
if (success) {
followBtn.hide(); followBtn.hide();
unfollowBtn.show(); unfollowBtn.show();
app.alertSuccess('You are now following ' + username + '!'); app.alertSuccess('You are now following ' + username + '!');
@@ -39,9 +41,10 @@
}); });
unfollowBtn.on('click', function() { unfollowBtn.on('click', function() {
socket.emit('api:user.unfollow', {uid: theirid}, function(success) { socket.emit('api:user.unfollow', {
var username = $('.account-username a').html(); uid: theirid
if(success) { }, function(success) {
if (success) {
followBtn.show(); followBtn.show();
unfollowBtn.hide(); unfollowBtn.hide();
app.alertSuccess('You are no longer following ' + username + '!'); app.alertSuccess('You are no longer following ' + username + '!');
@@ -58,17 +61,24 @@
var onlineStatus = $('.account-online-status'); var onlineStatus = $('.account-online-status');
socket.on('api:user.isOnline', function(online) { function handleUserOnline(data) {
if(online) { if (data.online) {
onlineStatus.find('span span').text('online'); onlineStatus.find('span span').text('online');
onlineStatus.find('i').attr('class', 'icon-circle'); onlineStatus.find('i').attr('class', 'icon-circle');
} else { } else {
onlineStatus.find('span span').text('offline'); onlineStatus.find('span span').text('offline');
onlineStatus.find('i').attr('class', 'icon-circle-blank'); onlineStatus.find('i').attr('class', 'icon-circle-blank');
} }
}); }
socket.emit('api:user.isOnline', theirid); socket.on('api:user.isOnline', handleUserOnline);
socket.emit('api:user.isOnline', theirid, handleUserOnline);
socket.on('event:new_post', function(data) {
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
$('.user-recent-posts').prepend(html);
});
}); });

View File

@@ -1,5 +1,3 @@
var gravatarPicture = templates.get('gravatarpicture'); var gravatarPicture = templates.get('gravatarpicture');
var uploadedPicture = templates.get('uploadedpicture'); var uploadedPicture = templates.get('uploadedpicture');
@@ -12,8 +10,9 @@ $(document).ready(function() {
$('#upload-progress-bar').css('width', '0%'); $('#upload-progress-bar').css('width', '0%');
$('#upload-progress-box').show(); $('#upload-progress-box').show();
$('#upload-progress-box').removeClass('hide');
if(!$('#userPhotoInput').val()) { if (!$('#userPhotoInput').val()) {
error('select an image to upload!'); error('select an image to upload!');
return false; return false;
} }
@@ -27,13 +26,14 @@ $(document).ready(function() {
error('Error: ' + xhr.status); error('Error: ' + xhr.status);
}, },
uploadProgress : function(event, position, total, percent) { uploadProgress: function(event, position, total, percent) {
$('#upload-progress-bar').css('width', percent+'%'); console.log(percent);
$('#upload-progress-bar').css('width', percent + '%');
}, },
success: function(response) { success: function(response) {
if(response.error) { if (response.error) {
error(response.error); error(response.error);
return; return;
} }
@@ -50,7 +50,9 @@ $(document).ready(function() {
$('#upload-picture-modal').modal('hide'); $('#upload-picture-modal').modal('hide');
}, 750); }, 750);
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] }); socket.emit('api:updateHeader', {
fields: ['username', 'picture', 'userslug']
});
success('File uploaded successfully!'); success('File uploaded successfully!');
} }
}); });
@@ -59,25 +61,25 @@ $(document).ready(function() {
}); });
function hideAlerts() { function hideAlerts() {
$('#alert-status').hide(); $('#alert-status').addClass('hide');
$('#alert-success').hide(); $('#alert-success').addClass('hide');
$('#alert-error').hide(); $('#alert-error').addClass('hide');
$('#upload-progress-box').hide(); $('#upload-progress-box').addClass('hide');
} }
function status(message) { function status(message) {
hideAlerts(); hideAlerts();
$('#alert-status').text(message).show(); $('#alert-status').text(message).removeClass('hide');
} }
function success(message) { function success(message) {
hideAlerts(); hideAlerts();
$('#alert-success').text(message).show(); $('#alert-success').text(message).removeClass('hide');
} }
function error(message) { function error(message) {
hideAlerts(); hideAlerts();
$('#alert-error').text(message).show(); $('#alert-error').text(message).removeClass('hide');
} }
function changeUserPicture(type) { function changeUserPicture(type) {
@@ -86,7 +88,7 @@ $(document).ready(function() {
}; };
socket.emit('api:user.changePicture', userData, function(success) { socket.emit('api:user.changePicture', userData, function(success) {
if(!success) { if (!success) {
app.alertError('There was an error changing picture!'); app.alertError('There was an error changing picture!');
} }
}); });
@@ -94,31 +96,31 @@ $(document).ready(function() {
var selectedImageType = ''; var selectedImageType = '';
$('#submitBtn').on('click',function(){ $('#submitBtn').on('click', function() {
var userData = { var userData = {
uid:$('#inputUID').val(), uid: $('#inputUID').val(),
email:$('#inputEmail').val(), email: $('#inputEmail').val(),
fullname:$('#inputFullname').val(), fullname: $('#inputFullname').val(),
website:$('#inputWebsite').val(), website: $('#inputWebsite').val(),
birthday:$('#inputBirthday').val(), birthday: $('#inputBirthday').val(),
location:$('#inputLocation').val(), location: $('#inputLocation').val(),
signature:$('#inputSignature').val() signature: $('#inputSignature').val()
}; };
socket.emit('api:user.updateProfile', userData, function(data) { socket.emit('api:user.updateProfile', userData, function(err, data) {
if(data.success) { if (data.success) {
app.alertSuccess('Your profile has been updated successfully!'); app.alertSuccess('Your profile has been updated successfully!');
if(data.picture) { if (data.picture) {
$('#user-current-picture').attr('src', data.picture); $('#user-current-picture').attr('src', data.picture);
$('#user_label img').attr('src', data.picture); $('#user_label img').attr('src', data.picture);
} }
if(data.gravatarpicture) { if (data.gravatarpicture) {
$('#user-gravatar-picture').attr('src', data.gravatarpicture); $('#user-gravatar-picture').attr('src', data.gravatarpicture);
gravatarPicture = data.gravatarpicture; gravatarPicture = data.gravatarpicture;
} }
} else { } else {
app.alertError('There was an error updating your profile!'); app.alertError('There was an error updating your profile! ' + err.error);
} }
}); });
return false; return false;
@@ -127,27 +129,25 @@ $(document).ready(function() {
function updateImages() { function updateImages() {
var currentPicture = $('#user-current-picture').attr('src'); var currentPicture = $('#user-current-picture').attr('src');
if(gravatarPicture) { if (gravatarPicture) {
$('#user-gravatar-picture').attr('src', gravatarPicture); $('#user-gravatar-picture').attr('src', gravatarPicture);
$('#gravatar-box').show(); $('#gravatar-box').show();
} } else
else
$('#gravatar-box').hide(); $('#gravatar-box').hide();
if(uploadedPicture) { if (uploadedPicture) {
$('#user-uploaded-picture').attr('src', uploadedPicture); $('#user-uploaded-picture').attr('src', uploadedPicture);
$('#uploaded-box').show(); $('#uploaded-box').show();
} } else
else
$('#uploaded-box').hide(); $('#uploaded-box').hide();
if(currentPicture == gravatarPicture) if (currentPicture == gravatarPicture)
$('#gravatar-box .icon-ok').show(); $('#gravatar-box .icon-ok').show();
else else
$('#gravatar-box .icon-ok').hide(); $('#gravatar-box .icon-ok').hide();
if(currentPicture == uploadedPicture) if (currentPicture == uploadedPicture)
$('#uploaded-box .icon-ok').show(); $('#uploaded-box .icon-ok').show();
else else
$('#uploaded-box .icon-ok').hide(); $('#uploaded-box .icon-ok').hide();
@@ -159,17 +159,18 @@ $(document).ready(function() {
updateImages(); updateImages();
$('#change-picture-modal').modal('show'); $('#change-picture-modal').modal('show');
$('#change-picture-modal').removeClass('hide');
return false; return false;
}); });
$('#gravatar-box').on('click', function(){ $('#gravatar-box').on('click', function() {
$('#gravatar-box .icon-ok').show(); $('#gravatar-box .icon-ok').show();
$('#uploaded-box .icon-ok').hide(); $('#uploaded-box .icon-ok').hide();
selectedImageType = 'gravatar'; selectedImageType = 'gravatar';
}); });
$('#uploaded-box').on('click', function(){ $('#uploaded-box').on('click', function() {
$('#gravatar-box .icon-ok').hide(); $('#gravatar-box .icon-ok').hide();
$('#uploaded-box .icon-ok').show(); $('#uploaded-box .icon-ok').show();
selectedImageType = 'uploaded'; selectedImageType = 'uploaded';
@@ -178,12 +179,12 @@ $(document).ready(function() {
$('#savePictureChangesBtn').on('click', function() { $('#savePictureChangesBtn').on('click', function() {
$('#change-picture-modal').modal('hide'); $('#change-picture-modal').modal('hide');
if(selectedImageType) { if (selectedImageType) {
changeUserPicture(selectedImageType); changeUserPicture(selectedImageType);
if(selectedImageType == 'gravatar') if (selectedImageType == 'gravatar')
$('#user-current-picture').attr('src', gravatarPicture); $('#user-current-picture').attr('src', gravatarPicture);
else if(selectedImageType == 'uploaded') else if (selectedImageType == 'uploaded')
$('#user-current-picture').attr('src', uploadedPicture); $('#user-current-picture').attr('src', uploadedPicture);
} }
@@ -193,10 +194,11 @@ $(document).ready(function() {
$('#userPhotoInput').val(''); $('#userPhotoInput').val('');
}); });
$('#uploadPictureBtn').on('click', function(){ $('#uploadPictureBtn').on('click', function() {
$('#change-picture-modal').modal('hide'); $('#change-picture-modal').modal('hide');
$('#upload-picture-modal').modal('show'); $('#upload-picture-modal').modal('show');
$('#upload-picture-modal').removeClass('hide');
hideAlerts(); hideAlerts();
@@ -219,50 +221,62 @@ $(document).ready(function() {
function onPasswordChanged() { function onPasswordChanged() {
passwordvalid = utils.isPasswordValid(password.val()); passwordvalid = utils.isPasswordValid(password.val());
if (password.val().length < 6) { if (password.val().length < config.minimumPasswordLength) {
password_notify.html('Password too short'); password_notify.html('Password too short');
password_notify.attr('class', 'label label-important'); password_notify.attr('class', 'alert alert-danger');
} else if(!passwordvalid) { password_notify.removeClass('hide');
} else if (!passwordvalid) {
password_notify.html('Invalid password'); password_notify.html('Invalid password');
password_notify.attr('class', 'label label-important'); password_notify.attr('class', 'alert alert-danger');
password_notify.removeClass('hide');
} else { } else {
password_notify.html('OK!'); password_notify.html('OK!');
password_notify.attr('class', 'label label-success'); password_notify.attr('class', 'alert alert-success');
password_notify.removeClass('hide');
} }
onPasswordConfirmChanged(); onPasswordConfirmChanged();
} }
function onPasswordConfirmChanged() { function onPasswordConfirmChanged() {
if(password.val() !== password_confirm.val()) { if (password_notify.hasClass('alert-danger') || !password_confirm.val()) {
password_confirm_notify.addClass('hide');
return;
}
if (password.val() !== password_confirm.val()) {
password_confirm_notify.html('Passwords must match!'); password_confirm_notify.html('Passwords must match!');
password_confirm_notify.attr('class', 'label label-important'); password_confirm_notify.attr('class', 'alert alert-danger');
password_confirm_notify.removeClass('hide');
passwordsmatch = false; passwordsmatch = false;
} else { } else {
password_confirm_notify.html('OK!'); password_confirm_notify.html('OK!');
password_confirm_notify.attr('class', 'label label-success'); password_confirm_notify.attr('class', 'alert alert-success');
password_confirm_notify.removeClass('hide');
passwordsmatch = true; passwordsmatch = true;
} }
} }
password.on('keyup', onPasswordChanged); password.on('blur', onPasswordChanged);
password_confirm.on('keyup', onPasswordConfirmChanged); password_confirm.on('blur', onPasswordConfirmChanged);
$('#changePasswordBtn').on('click', function() { $('#changePasswordBtn').on('click', function() {
if(passwordvalid && passwordsmatch && currentPassword.val()) { if (passwordvalid && passwordsmatch && currentPassword.val()) {
socket.emit('api:user.changePassword', {'currentPassword': currentPassword.val(),'newPassword': password.val() }, function(data) { socket.emit('api:user.changePassword', {
'currentPassword': currentPassword.val(),
'newPassword': password.val()
}, function(err) {
currentPassword.val(''); currentPassword.val('');
password.val(''); password.val('');
password_confirm.val(''); password_confirm.val('');
password_notify.html(''); password_notify.addClass('hide');
password_confirm_notify.html(''); password_confirm_notify.addClass('hide');
passwordsmatch = false; passwordsmatch = false;
passwordvalid = false; passwordvalid = false;
if(data.err) { if (err) {
app.alertError(data.err); app.alertError(err.error);
return; return;
} }

View File

@@ -2,19 +2,41 @@
var yourid = templates.get('yourid'), var yourid = templates.get('yourid'),
theirid = templates.get('theirid'); theirid = templates.get('theirid');
function createMenu() {
var userslug = $('.account-username-box').attr('data-userslug');
var links = $('<div class="account-sub-links inline-block pull-right">\
<span id="settingsLink" class="pull-right"><a href="/user/' + userslug + '/settings">settings</a></span>\
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
</div>');
$('.account-username-box').append(links);
}
$(document).ready(function() { $(document).ready(function() {
createMenu();
var editLink = $('#editLink'); var editLink = $('#editLink');
var settingsLink = $('#settingsLink'); var settingsLink = $('#settingsLink');
var favouritesLink = $('#favouritesLink');
if(yourid === "0") { if (yourid === "0" || yourid !== theirid) {
editLink.hide(); editLink.hide();
settingsLink.hide(); settingsLink.hide();
favouritesLink.hide();
} }
else if(yourid !== theirid) {
editLink.hide(); jQuery('.account-sub-links span a').removeClass('bold').each(function() {
settingsLink.hide(); var href = this.getAttribute('href');
if (window.location.href.indexOf(href) !== -1) {
jQuery(this).addClass('bold');
return false;
} }
}); });
});
}()); }());

View File

@@ -1,16 +1,13 @@
$(document).ready(function() { $(document).ready(function() {
$('#submitBtn').on('click', function() { $('#submitBtn').on('click', function() {
var settings = { var settings = {
showemail: $('#showemailCheckBox').is(':checked')?1:0 showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
}; };
socket.emit('api:user.saveSettings', settings, function(success) { socket.emit('api:user.saveSettings', settings, function(success) {
if(success) { if (success) {
app.alertSuccess('Settings saved!'); app.alertSuccess('Settings saved!');
} else { } else {
app.alertError('There was an error saving settings!'); app.alertError('There was an error saving settings!');

View File

@@ -1,11 +1,10 @@
var modified_categories = {}; var modified_categories = {};
function modified(el) { function modified(el) {
var cid = $(el).parents('li').attr('data-cid'); var cid = $(el).parents('li').attr('data-cid');
modified_categories[cid] = modified_categories[cid] || {}; modified_categories[cid] = modified_categories[cid] || {};
modified_categories[cid][el.getAttribute('data-name')] = el.value; modified_categories[cid][$(el).attr('data-name')] = $(el).val();
} }
function save() { function save() {
@@ -14,25 +13,28 @@ function save() {
} }
function select_icon(el) { function select_icon(el) {
var selected = el.className.replace(' icon-2x', ''); var selected = el.attr('class').replace(' icon-2x', '');
jQuery('#icons .selected').removeClass('selected'); jQuery('#icons .selected').removeClass('selected');
if (selected)
jQuery('#icons .' + selected).parent().addClass('selected'); jQuery('#icons .' + selected).parent().addClass('selected');
bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) { bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) {
if (confirm) { if (confirm) {
var iconClass = jQuery('.bootbox .selected').children(':first').attr('class'); var iconClass = jQuery('.bootbox .selected').children(':first').attr('class');
el.className = iconClass + ' icon icon-2x'; el.attr('class', iconClass + ' icon-2x');
el.value = iconClass; el.val(iconClass);
modified(el); modified(el);
} }
}); });
jQuery('.bootbox .span3').on('click', function() { setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into
jQuery('.bootbox .col-md-3').on('click', function() {
jQuery('.bootbox .selected').removeClass('selected'); jQuery('.bootbox .selected').removeClass('selected');
jQuery(this).addClass('selected'); jQuery(this).addClass('selected');
}); });
}, 500);
} }
@@ -48,10 +50,42 @@ jQuery('.blockclass').each(function() {
//DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap //DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap
(function() { (function() {
function showCreateCategoryModal() {
$('#new-category-modal').modal();
}
function createNewCategory() {
var category = {
name: $('#inputName').val(),
description: $('#inputDescription').val(),
icon: $('#new-category-modal i').attr('value'),
blockclass: $('#inputBlockclass').val()
};
socket.emit('api:admin.categories.create', category, function(err, data) {
if (!err) {
app.alert({
alert_id: 'category_created',
title: 'Created',
message: 'Category successfully created!',
type: 'success',
timeout: 2000
});
var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({
categories: [data]
});
$('#entry-container').append(html);
$('#new-category-modal').modal('hide');
}
});
}
jQuery('document').ready(function() { jQuery('document').ready(function() {
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
active = parts[parts.length-1]; active = parts[parts.length - 1];
jQuery('.nav-pills li').removeClass('active'); jQuery('.nav-pills li').removeClass('active');
jQuery('.nav-pills li a').each(function() { jQuery('.nav-pills li a').each(function() {
@@ -62,9 +96,11 @@ jQuery('.blockclass').each(function() {
}); });
jQuery('#save').on('click', save); jQuery('#save').on('click', save);
jQuery('#addNew').on('click', showCreateCategoryModal);
jQuery('#create-category-btn').on('click', createNewCategory);
jQuery('.icon').on('click', function(ev) { jQuery('#entry-container').on('click', '.icon', function(ev) {
select_icon(ev.target); select_icon($(this).find('i'));
}); });
jQuery('.blockclass').on('change', function(ev) { jQuery('.blockclass').on('change', function(ev) {
@@ -77,7 +113,7 @@ jQuery('.blockclass').each(function() {
jQuery('.entry-row button').each(function(index, element) { jQuery('.entry-row button').each(function(index, element) {
var disabled = $(element).attr('data-disabled'); var disabled = $(element).attr('data-disabled');
if(disabled == "0" || disabled == "") if (disabled == "0" || disabled == "")
$(element).html('Disable'); $(element).html('Disable');
else else
$(element).html('Enable'); $(element).html('Enable');
@@ -89,7 +125,7 @@ jQuery('.blockclass').each(function() {
var categoryRow = btn.parents('li'); var categoryRow = btn.parents('li');
var cid = categoryRow.attr('data-cid'); var cid = categoryRow.attr('data-cid');
var disabled = btn.html() == "Disable" ? "1":"0"; var disabled = btn.html() == "Disable" ? "1" : "0";
categoryRow.remove(); categoryRow.remove();
modified_categories[cid] = modified_categories[cid] || {}; modified_categories[cid] = modified_categories[cid] || {};
modified_categories[cid]['disabled'] = disabled; modified_categories[cid]['disabled'] = disabled;

View File

@@ -1,6 +1,3 @@
var nodebb_admin = (function(nodebb_admin) { var nodebb_admin = (function(nodebb_admin) {
nodebb_admin.config = undefined; nodebb_admin.config = undefined;
@@ -19,12 +16,12 @@ var nodebb_admin = (function(nodebb_admin) {
numFields = fields.length, numFields = fields.length,
saveBtn = document.getElementById('save'), saveBtn = document.getElementById('save'),
x, key, inputType; x, key, inputType;
for(x=0;x<numFields;x++) { for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field'); key = fields[x].getAttribute('data-field');
inputType = fields[x].getAttribute('type'); inputType = fields[x].getAttribute('type');
if (fields[x].nodeName === 'INPUT') { if (fields[x].nodeName === 'INPUT') {
if (nodebb_admin.config[key]) { if (nodebb_admin.config[key]) {
switch(inputType) { switch (inputType) {
case 'text': case 'text':
case 'textarea': case 'textarea':
case 'number': case 'number':
@@ -45,11 +42,11 @@ var nodebb_admin = (function(nodebb_admin) {
var key, value; var key, value;
e.preventDefault(); e.preventDefault();
for(x=0;x<numFields;x++) { for (x = 0; x < numFields; x++) {
key = fields[x].getAttribute('data-field'); key = fields[x].getAttribute('data-field');
if (fields[x].nodeName === 'INPUT') { if (fields[x].nodeName === 'INPUT') {
inputType = fields[x].getAttribute('type'); inputType = fields[x].getAttribute('type');
switch(inputType) { switch (inputType) {
case 'text': case 'text':
case 'number': case 'number':
value = fields[x].value; value = fields[x].value;
@@ -63,7 +60,10 @@ var nodebb_admin = (function(nodebb_admin) {
value = fields[x].value; value = fields[x].value;
} }
socket.emit('api:config.set', { key: key, value: value }); socket.emit('api:config.set', {
key: key,
value: value
});
} }
}); });
} }
@@ -82,7 +82,7 @@ var nodebb_admin = (function(nodebb_admin) {
menuEl.addEventListener('click', function(e) { menuEl.addEventListener('click', function(e) {
parentEl = e.target.parentNode; parentEl = e.target.parentNode;
if (parentEl.nodeName === 'LI') { if (parentEl.nodeName === 'LI') {
for(var x=0,numLis=liEls.length;x<numLis;x++) { for (var x = 0, numLis = liEls.length; x < numLis; x++) {
if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active'); if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active');
else jQuery(parentEl).addClass('active'); else jQuery(parentEl).addClass('active');
} }
@@ -102,7 +102,7 @@ var nodebb_admin = (function(nodebb_admin) {
alert_id: 'config_status', alert_id: 'config_status',
timeout: 2500, timeout: 2500,
title: 'Changes Saved', title: 'Changes Saved',
message: 'Your changes to the NodeBB configuration have been saved. You may have to restart NodeBB to see the changes.', message: 'Your changes to the NodeBB configuration have been saved.',
type: 'success' type: 'success'
}); });
} else { } else {
@@ -111,7 +111,7 @@ var nodebb_admin = (function(nodebb_admin) {
timeout: 2500, timeout: 2500,
title: 'Changes Not Saved', title: 'Changes Not Saved',
message: 'NodeBB encountered a problem saving your changes', message: 'NodeBB encountered a problem saving your changes',
type: 'error' type: 'danger'
}); });
} }
}); });
@@ -119,4 +119,3 @@ var nodebb_admin = (function(nodebb_admin) {
return nodebb_admin; return nodebb_admin;
}(nodebb_admin || {})); }(nodebb_admin || {}));

View File

@@ -0,0 +1,194 @@
$(document).ready(function() {
var createEl = document.getElementById('create'),
createModal = $('#create-modal'),
createSubmitBtn = document.getElementById('create-modal-go'),
createNameEl = $('#create-group-name'),
detailsModal = $('#group-details-modal'),
detailsSearch = detailsModal.find('#group-details-search'),
searchResults = detailsModal.find('#group-details-search-results'),
groupMembersEl = detailsModal.find('ul.current_members'),
detailsModalSave = detailsModal.find('.btn-primary'),
searchDelay = undefined,
listEl = $('#groups-list');
createEl.addEventListener('click', function() {
createModal.modal('show');
setTimeout(function() {
createNameEl.focus();
}, 250);
}, false);
createSubmitBtn.addEventListener('click', function() {
var submitObj = {
name: createNameEl.val(),
description: $('#create-group-desc').val()
},
errorEl = $('#create-modal-error'),
errorText;
socket.emit('api:groups.create', submitObj, function(err, data) {
if (err) {
switch (err) {
case 'group-exists':
errorText = '<strong>Please choose another name</strong><p>There seems to be a group with this name already.</p>';
break;
case 'name-too-short':
errorText = '<strong>Please specify a grou name</strong><p>A group name is required for administrative purposes.</p>';
break;
default:
errorText = '<strong>Uh-Oh</strong><p>There was a problem creating your group. Please try again later!</p>';
break;
}
errorEl.html(errorText).removeClass('hide');
} else {
createModal.modal('hide');
errorEl.addClass('hide');
createNameEl.val('');
ajaxify.go('admin/groups');
}
});
});
listEl.on('click', 'button[data-action]', function() {
var action = this.getAttribute('data-action'),
gid = $(this).parents('li[data-gid]').attr('data-gid');
switch (action) {
case 'delete':
bootbox.confirm('Are you sure you wish to delete this group?', function(confirm) {
if (confirm) {
socket.emit('api:groups.delete', gid, function(err, data) {
if (data === 'OK') ajaxify.go('admin/groups');
});
}
});
break;
case 'members':
socket.emit('api:groups.get', gid, function(err, groupObj) {
var formEl = detailsModal.find('form'),
nameEl = formEl.find('#change-group-name'),
descEl = formEl.find('#change-group-desc'),
memberIcon = document.createElement('li'),
numMembers = groupObj.members.length,
membersFrag = document.createDocumentFragment(),
memberIconImg, x;
nameEl.val(groupObj.name);
descEl.val(groupObj.description);
// Member list
memberIcon.innerHTML = '<img /><span></span>';
memberIconImg = memberIcon.querySelector('img');
memberIconLabel = memberIcon.querySelector('span');
if (numMembers > 0) {
for (x = 0; x < numMembers; x++) {
memberIconImg.src = groupObj.members[x].picture;
memberIconLabel.innerHTML = groupObj.members[x].username;
memberIcon.setAttribute('data-uid', groupObj.members[x].uid);
membersFrag.appendChild(memberIcon.cloneNode(true));
}
groupMembersEl.html('');
groupMembersEl[0].appendChild(membersFrag);
}
detailsModal.attr('data-gid', groupObj.gid);
detailsModal.modal('show');
});
break;
}
});
detailsSearch.on('keyup', function() {
var searchEl = this;
if (searchDelay) clearTimeout(searchDelay);
searchDelay = setTimeout(function() {
var searchText = searchEl.value,
resultsEl = document.getElementById('group-details-search-results'),
foundUser = document.createElement('li'),
foundUserImg, foundUserLabel;
foundUser.innerHTML = '<img /><span></span>';
foundUserImg = foundUser.getElementsByTagName('img')[0];
foundUserLabel = foundUser.getElementsByTagName('span')[0];
socket.emit('api:admin.user.search', searchText, function(err, results) {
if (!err && results && results.length > 0) {
var numResults = results.length,
resultsSlug = document.createDocumentFragment(),
x;
if (numResults > 4) numResults = 4;
for (x = 0; x < numResults; x++) {
foundUserImg.src = results[x].picture;
foundUserLabel.innerHTML = results[x].username;
foundUser.setAttribute('title', results[x].username);
foundUser.setAttribute('data-uid', results[x].uid);
resultsSlug.appendChild(foundUser.cloneNode(true));
}
resultsEl.innerHTML = '';
resultsEl.appendChild(resultsSlug);
} else resultsEl.innerHTML = '<li>No Users Found</li>';
});
}, 200);
});
searchResults.on('click', 'li[data-uid]', function() {
var userLabel = this,
uid = parseInt(this.getAttribute('data-uid')),
gid = detailsModal.attr('data-gid'),
members = [];
groupMembersEl.find('li[data-uid]').each(function() {
members.push(parseInt(this.getAttribute('data-uid')));
});
if (members.indexOf(uid) === -1) {
socket.emit('api:groups.join', {
gid: gid,
uid: uid
}, function(err, data) {
if (!err) {
groupMembersEl.append(userLabel.cloneNode(true));
}
});
}
});
groupMembersEl.on('click', 'li[data-uid]', function() {
var uid = this.getAttribute('data-uid'),
gid = detailsModal.attr('data-gid');
socket.emit('api:groups.leave', {
gid: gid,
uid: uid
}, function(err, data) {
if (!err) {
groupMembersEl.find('li[data-uid="' + uid + '"]').remove();
}
});
});
detailsModalSave.on('click', function() {
var formEl = detailsModal.find('form'),
nameEl = formEl.find('#change-group-name'),
descEl = formEl.find('#change-group-desc'),
gid = detailsModal.attr('data-gid');
socket.emit('api:groups.update', {
gid: gid,
values: {
name: nameEl.val(),
description: descEl.val()
}
}, function(err) {
if (!err) {
detailsModal.modal('hide');
ajaxify.go('admin/groups');
}
});
});
});

View File

@@ -8,7 +8,7 @@
total = 0; total = 0;
active_users.innerHTML = ''; active_users.innerHTML = '';
for(var room in data) { for (var room in data) {
if (room !== '') { if (room !== '') {
var count = data[room].length; var count = data[room].length;
total += count; total += count;

View File

@@ -21,7 +21,7 @@ var nodebb_admin = nodebb_admin || {};
alert_id: 'plugin_toggled_' + status.id, alert_id: 'plugin_toggled_' + status.id,
title: 'Plugin Enabled', title: 'Plugin Enabled',
message: 'You may need to restart NodeBB in order for these changes to be reflected.', message: 'You may need to restart NodeBB in order for these changes to be reflected.',
type: 'notify', type: 'warning',
timeout: 5000 timeout: 5000
}) })
}); });

View File

@@ -1,5 +1,3 @@
var nodebb_admin = (function(nodebb_admin) { var nodebb_admin = (function(nodebb_admin) {
var themes = {}; var themes = {};
@@ -10,7 +8,7 @@ var nodebb_admin = (function(nodebb_admin) {
themeContainer = document.querySelector('#bootstrap_themes'), themeContainer = document.querySelector('#bootstrap_themes'),
numThemes = bootswatch.themes.length; numThemes = bootswatch.themes.length;
for(var x=0;x<numThemes;x++) { for (var x = 0; x < numThemes; x++) {
var theme = bootswatch.themes[x]; var theme = bootswatch.themes[x];
themeEl.setAttribute('data-css', theme.cssMin); themeEl.setAttribute('data-css', theme.cssMin);
themeEl.setAttribute('data-theme', theme.name); themeEl.setAttribute('data-theme', theme.name);
@@ -18,7 +16,7 @@ var nodebb_admin = (function(nodebb_admin) {
'<div>' + '<div>' +
'<div class="pull-right">' + '<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' + '<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn" data-action="preview">Preview</button>' + '<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' + '</div>' +
'<h4>' + theme.name + '</h4>' + '<h4>' + theme.name + '</h4>' +
'<p>' + theme.description + '</p>' + '<p>' + theme.description + '</p>' +
@@ -39,14 +37,14 @@ var nodebb_admin = (function(nodebb_admin) {
(function() { (function() {
var scriptEl = document.createElement('script'); var scriptEl = document.createElement('script');
scriptEl.src = 'http://api.bootswatch.com/2/?callback=nodebb_admin.themes.render'; scriptEl.src = 'http://api.bootswatch.com/3/?callback=nodebb_admin.themes.render';
document.body.appendChild(scriptEl); document.body.appendChild(scriptEl);
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'), var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
installedThemeContainer = document.querySelector('#installed_themes'), installedThemeContainer = document.querySelector('#installed_themes'),
themeEvent = function(e) { themeEvent = function(e) {
if (e.target.hasAttribute('data-action')) { if (e.target.hasAttribute('data-action')) {
switch(e.target.getAttribute('data-action')) { switch (e.target.getAttribute('data-action')) {
case 'preview': case 'preview':
var cssSrc = $(e.target).parents('li').attr('data-css'), var cssSrc = $(e.target).parents('li').attr('data-css'),
cssEl = document.getElementById('base-theme'); cssEl = document.getElementById('base-theme');
@@ -58,10 +56,12 @@ var nodebb_admin = (function(nodebb_admin) {
cssSrc = parentEl.attr('data-css'), cssSrc = parentEl.attr('data-css'),
cssName = parentEl.attr('data-theme'); cssName = parentEl.attr('data-theme');
socket.emit('api:config.set', { socket.emit('api:config.set', {
key: 'theme:id', value: 'bootswatch:' + cssName key: 'theme:id',
value: 'bootswatch:' + cssName
}); });
socket.emit('api:config.set', { socket.emit('api:config.set', {
key: 'theme:src', value: cssSrc key: 'theme:src',
value: cssSrc
}); });
break; break;
} }
@@ -87,14 +87,14 @@ var nodebb_admin = (function(nodebb_admin) {
liEl = document.createElement('li'); liEl = document.createElement('li');
if (themes.length > 0) { if (themes.length > 0) {
for(var x=0,numThemes=themes.length;x<numThemes;x++) { for (var x = 0, numThemes = themes.length; x < numThemes; x++) {
liEl.setAttribute('data-theme', themes[x].id); liEl.setAttribute('data-theme', themes[x].id);
liEl.setAttribute('data-css', themes[x].src); liEl.setAttribute('data-css', themes[x].src);
liEl.innerHTML = '<img src="' + themes[x].screenshot + '" />' + liEl.innerHTML = '<img src="' + themes[x].screenshot + '" />' +
'<div>' + '<div>' +
'<div class="pull-right">' + '<div class="pull-right">' +
'<button class="btn btn-primary" data-action="use">Use</button> ' + '<button class="btn btn-primary" data-action="use">Use</button> ' +
'<button class="btn" data-action="preview">Preview</button>' + '<button class="btn btn-default" data-action="preview">Preview</button>' +
'</div>' + '</div>' +
'<h4>' + themes[x].name + '</h4>' + '<h4>' + themes[x].name + '</h4>' +
'<p>' + '<p>' +

View File

@@ -7,18 +7,30 @@ $(document).ready(function() {
action = this.getAttribute('data-action'), action = this.getAttribute('data-action'),
tid = $this.parents('[data-tid]').attr('data-tid'); tid = $this.parents('[data-tid]').attr('data-tid');
switch(action) { switch (action) {
case 'pin': case 'pin':
if (!$this.hasClass('active')) socket.emit('api:topic.pin', { tid: tid }); if (!$this.hasClass('active')) socket.emit('api:topic.pin', {
else socket.emit('api:topic.unpin', { tid: tid }); tid: tid
});
else socket.emit('api:topic.unpin', {
tid: tid
});
break; break;
case 'lock': case 'lock':
if (!$this.hasClass('active')) socket.emit('api:topic.lock', { tid: tid }); if (!$this.hasClass('active')) socket.emit('api:topic.lock', {
else socket.emit('api:topic.unlock', { tid: tid }); tid: tid
});
else socket.emit('api:topic.unlock', {
tid: tid
});
break; break;
case 'delete': case 'delete':
if (!$this.hasClass('active')) socket.emit('api:topic.delete', { tid: tid }); if (!$this.hasClass('active')) socket.emit('api:topic.delete', {
else socket.emit('api:topic.restore', { tid: tid }); tid: tid
});
else socket.emit('api:topic.restore', {
tid: tid
});
break; break;
} }
}); });
@@ -56,7 +68,7 @@ $(document).ready(function() {
// Resolve proper button state for all topics // Resolve proper button state for all topics
var topicEls = topicsListEl.querySelectorAll('li'), var topicEls = topicsListEl.querySelectorAll('li'),
numTopics = topicEls.length; numTopics = topicEls.length;
for(var x=0;x<numTopics;x++) { for (var x = 0; x < numTopics; x++) {
if (topicEls[x].getAttribute('data-pinned') === '1') topicEls[x].querySelector('[data-action="pin"]').className += ' active'; if (topicEls[x].getAttribute('data-pinned') === '1') topicEls[x].querySelector('[data-action="pin"]').className += ' active';
if (topicEls[x].getAttribute('data-locked') === '1') topicEls[x].querySelector('[data-action="lock"]').className += ' active'; if (topicEls[x].getAttribute('data-locked') === '1') topicEls[x].querySelector('[data-action="lock"]').className += ' active';
if (topicEls[x].getAttribute('data-deleted') === '1') topicEls[x].querySelector('[data-action="delete"]').className += ' active'; if (topicEls[x].getAttribute('data-deleted') === '1') topicEls[x].querySelector('[data-action="delete"]').className += ' active';

View File

@@ -1,76 +1,62 @@
(function() { (function() {
function initUsers() { var yourid = templates.get('yourid');
function isUserAdmin(element) { function isUserAdmin(element) {
var parent = $(element).parents('.users-box'); var parent = $(element).parents('.users-box');
return (parent.attr('data-admin') !== "0"); return (parent.attr('data-admin') !== "0");
} }
function isUserBanned(element) {
var parent = $(element).parents('.users-box');
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
}
function getUID(element) { function getUID(element) {
var parent = $(element).parents('.users-box'); var parent = $(element).parents('.users-box');
return parent.attr('data-uid'); return parent.attr('data-uid');
} }
jQuery('.admin-btn').each(function(index, element) { function updateUserButtons() {
var adminBtn = $(element); jQuery('.ban-btn').each(function(index, element) {
var isAdmin = isUserAdmin(adminBtn); var banBtn = $(element);
var uid = getUID(banBtn);
if(isAdmin) if (isUserAdmin(banBtn) || uid === yourid)
adminBtn.addClass('btn-success'); banBtn.addClass('disabled');
else if (isUserBanned(banBtn))
banBtn.addClass('btn-warning');
else else
adminBtn.removeClass('btn-success'); banBtn.removeClass('btn-warning');
}); });
jQuery('.delete-btn').each(function(index, element) {
var deleteBtn = $(element);
var isAdmin = isUserAdmin(deleteBtn);
if(isAdmin)
deleteBtn.addClass('disabled');
else
deleteBtn.show();
});
jQuery('.admin-btn').on('click', function() {
var adminBtn = $(this);
var isAdmin = isUserAdmin(adminBtn);
var parent = adminBtn.parents('.users-box');
var uid = getUID(adminBtn);
if(isAdmin) {
socket.emit('api:admin.user.removeAdmin', uid);
adminBtn.removeClass('btn-success');
parent.find('.delete-btn').removeClass('disabled');
parent.attr('data-admin', 0);
}
else {
bootbox.confirm('Do you really want to make "' + parent.attr('data-username') +'" an admin?', function(confirm) {
if(confirm) {
socket.emit('api:admin.user.makeAdmin', uid);
adminBtn.addClass('btn-success');
parent.find('.delete-btn').addClass('disabled');
parent.attr('data-admin', 1);
}
});
} }
return false; function initUsers() {
});
jQuery('.delete-btn').on('click', function() { updateUserButtons();
var deleteBtn = $(this);
var isAdmin = isUserAdmin(deleteBtn);
var parent = deleteBtn.parents('.users-box');
var uid = getUID(deleteBtn);
if(!isAdmin) { $('#users-container').on('click', '.ban-btn', function() {
bootbox.confirm('Do you really want to delete "' + parent.attr('data-username') +'"?', function(confirm) { var banBtn = $(this);
socket.emit('api:admin.user.deleteUser', uid); var isAdmin = isUserAdmin(banBtn);
var isBanned = isUserBanned(banBtn);
var parent = banBtn.parents('.users-box');
var uid = getUID(banBtn);
if (!isAdmin) {
if (isBanned) {
socket.emit('api:admin.user.unbanUser', uid);
banBtn.removeClass('btn-warning');
parent.attr('data-banned', 0);
} else {
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
if (confirm) {
socket.emit('api:admin.user.banUser', uid);
banBtn.addClass('btn-warning');
parent.attr('data-banned', 1);
}
}); });
} }
}
return false; return false;
}); });
@@ -79,13 +65,12 @@
jQuery('document').ready(function() { jQuery('document').ready(function() {
var yourid = templates.get('yourid'), var timeoutId = 0,
timeoutId = 0,
loadingMoreUsers = false; loadingMoreUsers = false;
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
active = parts[parts.length-1]; active = parts[parts.length - 1];
jQuery('.nav-pills li').removeClass('active'); jQuery('.nav-pills li').removeClass('active');
jQuery('.nav-pills li a').each(function() { jQuery('.nav-pills li a').each(function() {
@@ -95,8 +80,8 @@
} }
}); });
jQuery('#search-user').on('keyup', function () { jQuery('#search-user').on('keyup', function() {
if(timeoutId !== 0) { if (timeoutId !== 0) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = 0; timeoutId = 0;
} }
@@ -123,44 +108,46 @@
userListEl.innerHTML = html; userListEl.innerHTML = html;
jQuery('.icon-spinner').addClass('none'); jQuery('.icon-spinner').addClass('none');
if(data && data.length === 0) { if (data && data.length === 0) {
$('#user-notfound-notify').html('User not found!') $('#user-notfound-notify').html('User not found!')
.show() .show()
.addClass('label-important') .addClass('label-danger')
.removeClass('label-success'); .removeClass('label-success');
} } else {
else { $('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
$('#user-notfound-notify').html(data.length + ' user'+(data.length>1?'s':'') + ' found!')
.show() .show()
.addClass('label-success') .addClass('label-success')
.removeClass('label-important'); .removeClass('label-danger');
} }
initUsers(); initUsers();
}); });
function onUsersLoaded(users) { function onUsersLoaded(users) {
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ users: users }); var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
users: users
});
$('#users-container').append(html); $('#users-container').append(html);
updateUserButtons();
} }
function loadMoreUsers() { function loadMoreUsers() {
var set = ''; var set = '';
if(active === 'latest') { if (active === 'latest') {
set = 'users:joindate'; set = 'users:joindate';
} else if(active === 'sort-posts') { } else if (active === 'sort-posts') {
set = 'users:postcount'; set = 'users:postcount';
} else if(active === 'sort-reputation') { } else if (active === 'sort-reputation') {
set = 'users:reputation'; set = 'users:reputation';
} }
if(set) { if (set) {
loadingMoreUsers = true; loadingMoreUsers = true;
socket.emit('api:users.loadMore', { socket.emit('api:users.loadMore', {
set: set, set: set,
after: $('#users-container').children().length after: $('#users-container').children().length
}, function(data) { }, function(data) {
if(data.users.length) { if (data.users.length) {
onUsersLoaded(data.users); onUsersLoaded(data.users);
} }
loadingMoreUsers = false; loadingMoreUsers = false;
@@ -171,9 +158,9 @@
$('#load-more-users-btn').on('click', loadMoreUsers); $('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9; var bottom = ($(document).height() - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreUsers) { if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
loadMoreUsers(); loadMoreUsers();
} }
}); });

View File

@@ -1,4 +1,4 @@
(function() { (function () {
var cid = templates.get('category_id'), var cid = templates.get('category_id'),
room = 'category_' + cid, room = 'category_' + cid,
twitterEl = document.getElementById('twitter-intent'), twitterEl = document.getElementById('twitter-intent'),
@@ -11,22 +11,22 @@
app.enter_room(room); app.enter_room(room);
twitterEl.addEventListener('click', function() { twitterEl.addEventListener('click', function () {
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no'); window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
return false; return false;
}, false); }, false);
facebookEl.addEventListener('click', function() { facebookEl.addEventListener('click', function () {
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no'); window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
return false; return false;
}, false); }, false);
googleEl.addEventListener('click', function() { googleEl.addEventListener('click', function () {
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no'); window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
return false; return false;
}, false); }, false);
var new_post = document.getElementById('new_post'); var new_post = document.getElementById('new_post');
new_post.onclick = function() { new_post.onclick = function () {
require(['composer'], function(cmp) { require(['composer'], function (cmp) {
cmp.push(0, cid); cmp.push(0, cid);
}); });
} }
@@ -36,37 +36,38 @@
]); ]);
function onNewTopic(data) { function onNewTopic(data) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }), topics: [data]
topic = document.createElement('div'), }),
container = document.getElementById('topics-container'), topic = $(html),
topics = document.querySelectorAll('#topics-container a'), container = $('#topics-container'),
numTopics = topics.length, topics = $('#topics-container').children(),
x; numTopics = topics.length;
jQuery('#topics-container, .category-sidebar').removeClass('hidden'); jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove(); jQuery('#category-no-topics').remove();
topic.innerHTML = html;
topic = topic.querySelector('a');
if (numTopics > 0) { if (numTopics > 0) {
for(x=0;x<numTopics;x++) { for (var x = 0; x < numTopics; x++) {
if (topics[x].querySelector('.icon-pushpin')) continue; if ($(topics[x]).find('.icon-pushpin').length)
container.insertBefore(topic, topics[x]); continue;
$(topic).hide().fadeIn('slow'); topic.insertBefore(topics[x]);
topic.hide().fadeIn('slow');
break; break;
} }
} else { } else {
container.insertBefore(topic, null); container.append(topic);
$(topic).hide().fadeIn('slow'); topic.hide().fadeIn('slow');
} }
socket.emit('api:categories.getRecentReplies', cid);
$('#topics-container span.timeago').timeago();
} }
socket.on('event:new_topic', onNewTopic); socket.on('event:new_topic', onNewTopic);
socket.emit('api:categories.getRecentReplies', cid); socket.emit('api:categories.getRecentReplies', cid);
socket.on('api:categories.getRecentReplies', function(posts) { socket.on('api:categories.getRecentReplies', function (posts) {
if (!posts || posts.length === 0) { if (!posts || posts.length === 0) {
return; return;
} }
@@ -77,35 +78,39 @@
var frag = document.createDocumentFragment(), var frag = document.createDocumentFragment(),
li = document.createElement('li'); li = document.createElement('li');
for (var i=0,numPosts=posts.length; i<numPosts; i++) { for (var i = 0, numPosts = posts.length; i < numPosts; i++) {
var dateString = utils.relativeTime(posts[i].timestamp);
li.setAttribute('data-pid', posts[i].pid); li.setAttribute('data-pid', posts[i].pid);
li.innerHTML = '<a href="/users/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-polaroid" src="' + posts[i].picture + '" class="" /></a>' + li.innerHTML = '<a href="/user/' + posts[i].userslug + '"><img title="' + posts[i].username + '" style="width: 48px; height: 48px; /*temporary*/" class="img-rounded" src="' + posts[i].picture + '" class="" /></a>' +
'<a href="/topic/'+ posts[i].topicSlug + '#' + posts[i].pid + '">' + '<a href="/topic/' + posts[i].topicSlug + '#' + posts[i].pid + '">' +
'<p>' + '<p>' +
'<strong>' + posts[i].username + '</strong>: ' + posts[i].content + posts[i].content +
'</p>' + '</p>' +
'<span>posted ' + utils.relativeTime(posts[i].timestamp) + ' ago</span>' + '<p class="meta"><strong>' + posts[i].username + '</strong></span> -<span class="timeago" title="' + posts[i].relativeTime + '"></span></p>' +
'</a>'; '</a>';
frag.appendChild(li.cloneNode(true)); frag.appendChild(li.cloneNode(true));
recent_replies.appendChild(frag); recent_replies.appendChild(frag);
} }
$('#category_recent_replies span.timeago').timeago();
}); });
function onTopicsLoaded(topics) { function onTopicsLoaded(topics) {
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: topics }), var html = templates.prepare(templates['category'].blocks['topics']).parse({
topics: topics
}),
container = $('#topics-container'); container = $('#topics-container');
jQuery('#topics-container, .category-sidebar').removeClass('hidden'); jQuery('#topics-container, .category-sidebar').removeClass('hidden');
jQuery('#category-no-topics').remove(); jQuery('#category-no-topics').remove();
container.append(html); container.append(html);
}
$('#topics-container span.timeago').timeago();
}
function loadMoreTopics(cid) { function loadMoreTopics(cid) {
@@ -113,18 +118,18 @@
socket.emit('api:category.loadMore', { socket.emit('api:category.loadMore', {
cid: cid, cid: cid,
after: $('#topics-container').children().length after: $('#topics-container').children().length
}, function(data) { }, function (data) {
if(data.topics.length) { if (data.topics.length) {
onTopicsLoaded(data.topics); onTopicsLoaded(data.topics);
} }
loadingMoreTopics = false; loadingMoreTopics = false;
}); });
} }
$(window).off('scroll').on('scroll', function(ev) { $(window).off('scroll').on('scroll', function (ev) {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9; var bottom = ($(document).height() - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreTopics) { if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics(cid); loadMoreTopics(cid);
} }
}); });

View File

@@ -0,0 +1,7 @@
(function() {
$(document).ready(function() {
$('.user-favourite-posts .topic-row').on('click', function() {
ajaxify.go($(this).attr('topic-url'));
});
});
}());

View File

@@ -6,17 +6,11 @@
$(document).ready(function() { $(document).ready(function() {
if(parseInt(followersCount, 10) === 0) { if (parseInt(followersCount, 10) === 0) {
$('#no-followers-notice').show(); $('#no-followers-notice').removeClass('hide');
} }
$('.reputation').each(function(index, element) { app.addCommasToNumbers();
$(element).html(app.addCommas($(element).html()));
});
$('.postcount').each(function(index, element) {
$(element).html(app.addCommas($(element).html()));
});
}); });

View File

@@ -6,22 +6,23 @@
$(document).ready(function() { $(document).ready(function() {
if(parseInt(followingCount, 10) === 0) { if (parseInt(followingCount, 10) === 0) {
$('#no-following-notice').show(); $('#no-following-notice').removeClass('hide');
} }
if(yourid !== theirid) { if (yourid !== theirid) {
$('.unfollow-btn').hide(); $('.unfollow-btn').hide();
} } else {
else { $('.unfollow-btn').on('click', function() {
$('.unfollow-btn').on('click',function() {
var unfollowBtn = $(this); var unfollowBtn = $(this);
var followingUid = $(this).attr('followingUid'); var followingUid = $(this).attr('followingUid');
socket.emit('api:user.unfollow', {uid: followingUid}, function(success) { socket.emit('api:user.unfollow', {
uid: followingUid
}, function(success) {
var username = unfollowBtn.attr('data-username'); var username = unfollowBtn.attr('data-username');
if(success) { if (success) {
unfollowBtn.parent().remove(); unfollowBtn.parent().remove();
app.alertSuccess('You are no longer following ' + username + '!'); app.alertSuccess('You are no longer following ' + username + '!');
} else { } else {
@@ -32,14 +33,7 @@
}); });
} }
$('.reputation').each(function(index, element) { app.addCommasToNumbers();
$(element).html(app.addCommas($(element).html()));
});
$('.postcount').each(function(index, element) {
$(element).html(app.addCommas($(element).html()));
});
}); });

View File

@@ -1,85 +1,93 @@
(function() { (function() {
var num_users = document.getElementById('number_of_users'), var stats_users = document.getElementById('stats_users'),
post_stats = document.getElementById('post_stats'), stats_topics = document.getElementById('stats_topics'),
latest_user = document.getElementById('latest_user'), stats_posts = document.getElementById('stats_posts'),
active_users = document.getElementById('active_users'), stats_online = document.getElementById('stats_online'),
user_label = document.getElementById('user_label'), user_label = document.getElementById('user_label');
active_record = document.getElementById('active_record'),
right_menu = document.getElementById('right-menu');
socket.emit('user.count', {}); socket.emit('user.count', {});
socket.on('user.count', function(data) { socket.on('user.count', function(data) {
num_users.innerHTML = "We currently have <b>" + data.count + "</b> registered users."; stats_users.innerHTML = data.count;
}); });
socket.emit('post.stats'); socket.emit('post.stats');
socket.on('post.stats', function(data) { socket.on('post.stats', function(data) {
post_stats.innerHTML = "Our users have created <b>" + data.topics + "</b> topics and made <b>" + data.posts + "</b> posts."; stats_topics.innerHTML = data.topics;
}); stats_posts.innerHTML = data.posts;
socket.emit('user.latest', {});
socket.on('user.latest', function(data) {
if (data.username == '') {
latest_user.innerHTML = '';
} else {
latest_user.innerHTML = "The most recent user to register is <b><a href='/users/"+data.userslug+"'>" + data.username + "</a></b>.";
}
}); });
socket.emit('api:user.active.get'); socket.emit('api:user.active.get');
socket.on('api:user.active.get', function(data) { socket.on('api:user.active.get', function(data) {
stats_online.innerHTML = data.users;
var plural_users = parseInt(data.users) !== 1,
plural_anon = parseInt(data.anon) !== 1;
active_users.innerHTML = 'There ' + (plural_users ? 'are' : 'is') + ' <strong>' + data.users + '</strong> user' + (plural_users ? 's' : '') + ' and <strong>' + data.anon + '</strong> guest' + (plural_anon ? 's' : '') + ' online';
}); });
socket.emit('api:user.active.get_record'); socket.emit('api:updateHeader', {
socket.on('api:user.active.get_record', function(data) { fields: ['username', 'picture', 'userslug']
active_record.innerHTML = "most users ever online was <strong>" + data.record + "</strong> on <strong>" + (new Date(parseInt(data.timestamp,10))).toUTCString() + "</strong>";
}); });
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
socket.on('api:updateHeader', function(data) { socket.on('api:updateHeader', function(data) {
jQuery('#search-button').on('click', function() {
jQuery('#search-fields').removeClass('hide').show();
jQuery(this).hide();
jQuery('#search-fields input').focus()
var rightMenu = $('#right-menu'); jQuery('#search-form').on('submit', function() {
if (data.uid > 0) { jQuery('#search-fields').hide();
jQuery('#search-button').show();
});
var userLabel = rightMenu.find('#user_label'); $('#search-fields input').on('blur', function() {
if(userLabel.length) { $('#search-fields').hide();
if(data['userslug']) $('#search-button').show();
userLabel.attr('href','/users/' + data['userslug']); });
if(data['picture']) });
userLabel.find('img').attr('src',data['picture']);
if(data['username']) var loggedInMenu = $('#logged-in-menu'),
isLoggedIn = data.uid > 0;
if (isLoggedIn) {
jQuery('.nodebb-loggedin').show();
jQuery('.nodebb-loggedout').hide();
$('#logged-out-menu').addClass('hide');
$('#logged-in-menu').removeClass('hide');
$('#search-button').show();
var userLabel = loggedInMenu.find('#user_label');
if (userLabel.length) {
if (data['userslug'])
userLabel.attr('href', '/user/' + data['userslug']);
if (data['picture'])
userLabel.find('img').attr('src', data['picture']);
if (data['username'])
userLabel.find('span').html(data['username']); userLabel.find('span').html(data['username']);
}
else {
var userli = $('<li> \
<a id="user_label" href="/users/'+data['userslug']+'"> \
<img src="'+data['picture']+'"/> \
<span>'+data['username']+'</span> \
</a> \
</li>');
rightMenu.append(userli);
var logoutli = $('<li><a href="' + RELATIVE_PATH + '/logout">Log out</a></li>'); $('#logout-link').on('click', function() {
rightMenu.append(logoutli); var csrf_token = $('#csrf_token').val();
$.post(RELATIVE_PATH + '/logout', {
_csrf: csrf_token
}, function() {
window.location = RELATIVE_PATH + '/';
});
});
} }
} else { } else {
rightMenu.html(''); $('#search-button').hide();
var registerEl = document.createElement('li'), jQuery('.nodebb-loggedin').hide();
loginEl = document.createElement('li'); jQuery('.nodebb-loggedout').show();
registerEl.innerHTML = '<a href="/register">Register</a>'; $('#logged-out-menu').removeClass('hide');
loginEl.innerHTML = '<a href="/login">Login</a>'; $('#logged-in-menu').addClass('hide');
right_menu.appendChild(registerEl);
right_menu.appendChild(loginEl);
} }
$('#main-nav a,#right-menu a').off('click').on('click', function() {
if($('.navbar .navbar-collapse').hasClass('in'))
$('.navbar-header button').click();
});
}); });
// Notifications dropdown // Notifications dropdown
@@ -90,26 +98,7 @@
notifTrigger.addEventListener('click', function(e) { notifTrigger.addEventListener('click', function(e) {
e.preventDefault(); e.preventDefault();
if (notifContainer.className.indexOf('open') === -1) { if (notifContainer.className.indexOf('open') === -1) {
socket.emit('api:notifications.get'); socket.emit('api:notifications.get', null, function(data) {
socket.emit('api:notifications.mark_all_read', null, function() {
notifIcon.className = 'icon-circle-blank';
utils.refreshTitle();
});
}
});
notifList.addEventListener('click', function(e) {
var target;
switch(e.target.nodeName) {
case 'SPAN': target = e.target.parentNode.parentNode; break;
case 'A': target = e.target.parentNode; break;
case 'li': target = e.target; break;
}
if (target) {
var nid = parseInt(target.getAttribute('data-nid'));
if (nid > 0) socket.emit('api:notifications.mark_read', nid);
}
});
socket.on('api:notifications.get', function(data) {
var notifFrag = document.createDocumentFragment(), var notifFrag = document.createDocumentFragment(),
notifEl = document.createElement('li'), notifEl = document.createElement('li'),
numRead = data.read.length, numRead = data.read.length,
@@ -117,13 +106,13 @@
x; x;
notifList.innerHTML = ''; notifList.innerHTML = '';
if ((data.read.length + data.unread.length) > 0) { if ((data.read.length + data.unread.length) > 0) {
for(x=0;x<numUnread;x++) { for (x = 0; x < numUnread; x++) {
notifEl.setAttribute('data-nid', data.unread[x].nid); notifEl.setAttribute('data-nid', data.unread[x].nid);
notifEl.className = 'unread'; notifEl.className = 'unread';
notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>'; notifEl.innerHTML = '<a href="' + data.unread[x].path + '"><span class="pull-right">' + utils.relativeTime(data.unread[x].datetime, true) + '</span>' + data.unread[x].text + '</a>';
notifFrag.appendChild(notifEl.cloneNode(true)); notifFrag.appendChild(notifEl.cloneNode(true));
} }
for(x=0;x<numRead;x++) { for (x = 0; x < numRead; x++) {
notifEl.setAttribute('data-nid', data.read[x].nid); notifEl.setAttribute('data-nid', data.read[x].nid);
notifEl.className = ''; notifEl.className = '';
notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>'; notifEl.innerHTML = '<a href="' + data.read[x].path + '"><span class="pull-right">' + utils.relativeTime(data.read[x].datetime, true) + '</span>' + data.read[x].text + '</a>';
@@ -137,28 +126,62 @@
if (data.unread.length > 0) notifIcon.className = 'icon-circle active'; if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
else notifIcon.className = 'icon-circle-blank'; else notifIcon.className = 'icon-circle-blank';
socket.emit('api:notifications.mark_all_read', null, function() {
notifIcon.className = 'icon-circle-blank';
utils.refreshTitle();
}); });
});
}
});
notifList.addEventListener('click', function(e) {
var target;
switch (e.target.nodeName) {
case 'SPAN':
target = e.target.parentNode.parentNode;
break;
case 'A':
target = e.target.parentNode;
break;
case 'li':
target = e.target;
break;
}
if (target) {
var nid = parseInt(target.getAttribute('data-nid'));
if (nid > 0) socket.emit('api:notifications.mark_read', nid);
}
});
socket.on('event:new_notification', function() { socket.on('event:new_notification', function() {
document.querySelector('.notifications a i').className = 'icon-circle active'; document.querySelector('.notifications a i').className = 'icon-circle active';
app.alert({
alert_id: 'new_notif',
title: 'New notification',
message: 'You have unread notifications.',
type: 'warning',
timeout: 2000
});
utils.refreshTitle(); utils.refreshTitle();
}); });
socket.on('chatMessage', function(data) { socket.on('chatMessage', function(data) {
var username = data.username;
var fromuid = data.fromuid;
var message = data.message;
require(['chat'], function(chat) { require(['chat'], function(chat) {
var chatModal = chat.createModalIfDoesntExist(username, fromuid); var modal = null;
chatModal.show(); if (chat.modalExists(data.fromuid)) {
chat.bringModalToTop(chatModal); modal = chat.getModal(data.fromuid);
chat.appendChatMessage(modal, data.message, data.timestamp);
} else {
modal = chat.createModal(data.username, data.fromuid);
}
chat.appendChatMessage(chatModal, message); chat.load(modal.attr('UUID'));
}); });
}); });
require(['mobileMenu'], function(mobileMenu) { require(['mobileMenu'], function(mobileMenu) {
mobileMenu.init(); mobileMenu.init();
}); });

View File

@@ -1,13 +1,15 @@
(function() { (function() {
// Alternate Logins // Alternate Logins
var altLoginEl = document.querySelector('.alt-logins'); var altLoginEl = document.querySelector('.alt-logins');
altLoginEl.addEventListener('click', function(e) { altLoginEl.addEventListener('click', function(e) {
var target; var target;
switch(e.target.nodeName) { switch (e.target.nodeName) {
case 'LI': target = e.target; break; case 'LI':
case 'I': target = e.target.parentNode; break; target = e.target;
break;
case 'I':
target = e.target.parentNode;
break;
} }
if (target) { if (target) {
document.location.href = target.getAttribute('data-url'); document.location.href = target.getAttribute('data-url');
@@ -15,7 +17,6 @@
}); });
$('#login').on('click', function() { $('#login').on('click', function() {
var loginData = { var loginData = {
'username': $('#username').val(), 'username': $('#username').val(),
'password': $('#password').val(), 'password': $('#password').val(),
@@ -27,11 +28,20 @@
url: RELATIVE_PATH + '/login', url: RELATIVE_PATH + '/login',
data: loginData, data: loginData,
success: function(data, textStatus, jqXHR) { success: function(data, textStatus, jqXHR) {
if (!data.success) {
$('#login-error-notify').show();
} else {
$('#login-error-notify').hide(); $('#login-error-notify').hide();
if(app.previousUrl.indexOf('/reset/') != -1)
window.location.replace(RELATIVE_PATH + "/?loggedin"); window.location.replace(RELATIVE_PATH + "/?loggedin");
else
window.location.replace(app.previousUrl + "?loggedin");
app.loadConfig();
}
}, },
error : function(data, textStatus, jqXHR) { error: function(data, textStatus, jqXHR) {
$('#login-error-notify').show().delay(1000).fadeOut(250); $('#login-error-notify').show();
}, },
dataType: 'json', dataType: 'json',
async: true, async: true,
@@ -41,4 +51,10 @@
return false; return false;
}); });
$('#login-error-notify button').on('click', function() {
$('#login-error-notify').hide();
return false;
});
document.querySelector('#content input').focus();
}()); }());

View File

@@ -8,7 +8,8 @@
'event:new_post' 'event:new_post'
]); ]);
var newTopicCount = 0, newPostCount = 0; var newTopicCount = 0,
newPostCount = 0;
$('#new-topics-alert').on('click', function() { $('#new-topics-alert').on('click', function() {
$(this).hide(); $(this).hide();
@@ -24,16 +25,16 @@
function updateAlertText() { function updateAlertText() {
var text = ''; var text = '';
if(newTopicCount > 1) if (newTopicCount > 1)
text = 'There are ' + newTopicCount + ' new topics'; text = 'There are ' + newTopicCount + ' new topics';
else if(newTopicCount === 1) else if (newTopicCount === 1)
text = 'There is 1 new topic'; text = 'There is 1 new topic';
else else
text = 'There are no new topics'; text = 'There are no new topics';
if(newPostCount > 1) if (newPostCount > 1)
text += ' and ' + newPostCount + ' new posts.'; text += ' and ' + newPostCount + ' new posts.';
else if(newPostCount === 1) else if (newPostCount === 1)
text += ' and 1 new post.'; text += ' and 1 new post.';
else else
text += ' and no new posts.'; text += ' and no new posts.';
@@ -50,7 +51,9 @@
function onTopicsLoaded(topics) { function onTopicsLoaded(topics) {
var html = templates.prepare(templates['recent'].blocks['topics']).parse({ topics: topics }), var html = templates.prepare(templates['recent'].blocks['topics']).parse({
topics: topics
}),
container = $('#topics-container'); container = $('#topics-container');
$('#category-no-topics').remove(); $('#category-no-topics').remove();
@@ -60,8 +63,10 @@
function loadMoreTopics() { function loadMoreTopics() {
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('api:topics.loadMoreRecentTopics', {after:$('#topics-container').children().length}, function(data) { socket.emit('api:topics.loadMoreRecentTopics', {
if(data.topics && data.topics.length) { after: $('#topics-container').children().length
}, function(data) {
if (data.topics && data.topics.length) {
onTopicsLoaded(data.topics); onTopicsLoaded(data.topics);
} }
loadingMoreTopics = false; loadingMoreTopics = false;
@@ -69,9 +74,9 @@
} }
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9; var bottom = ($(document).height() - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreTopics) { if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics(); loadMoreTopics();
} }
}); });

View File

@@ -1,157 +1,154 @@
(function() { (function() {
var username = document.getElementById('username'), var username = $('#username'),
password = document.getElementById('password'), password = $('#password'),
password_confirm = document.getElementById('password-confirm'), password_confirm = $('#password-confirm'),
register = document.getElementById('register'), register = $('#register'),
emailEl = document.getElementById('email'), emailEl = $('#email'),
username_notify = document.getElementById('username-notify'), username_notify = $('#username-notify'),
email_notify = document.getElementById('email-notify'), email_notify = $('#email-notify'),
password_notify = document.getElementById('password-notify'), password_notify = $('#password-notify'),
password_confirm_notify = document.getElementById('password-confirm-notify'), password_confirm_notify = $('#password-confirm-notify'),
usernamevalid = false; validationError = false,
emailexists = false, successIcon = '<i class="icon icon-ok"></i>';
emailvalid = false,
userexists = false,
passwordsmatch = false,
passwordvalid = false;
$(username).on('keyup change', function() { $('#referrer').val(app.previousUrl);
usernamevalid = utils.isUserNameValid(username.value);
function showError(element, msg) {
element.html(msg);
element.parent()
.removeClass('alert-success')
.addClass('alert-danger');
element.show();
validationError = true;
}
if(username.value.length < 3) { function showSuccess(element, msg) {
username_notify.innerHTML = 'Username too short'; element.html(msg);
username_notify.className = 'label label-important'; element.parent()
} else if(username.value.length > 13) { .removeClass('alert-danger')
username_notify.innerHTML = 'Username too long'; .addClass('alert-success');
username_notify.className = 'label label-important'; element.show();
} else if(!usernamevalid) { }
username_notify.innerHTML = 'Invalid username';
username_notify.className = 'label label-important'; function validateEmail() {
if (!emailEl.val()) {
validationError = true;
return;
}
if (!utils.isEmailValid(emailEl.val())) {
showError(email_notify, 'Invalid email address.');
} else
socket.emit('user.email.exists', {
email: emailEl.val()
});
}
emailEl.on('blur', function() {
validateEmail();
});
function validateUsername() {
if (!username.val()) {
validationError = true;
return;
}
if (username.val().length < config.minimumUsernameLength) {
showError(username_notify, 'Username too short!');
} else if (username.val().length > config.maximumUsernameLength) {
showError(username_notify, 'Username too long!');
} else if (!utils.isUserNameValid(username.val())) {
showError(username_notify, 'Invalid username!');
} else { } else {
socket.emit('user.exists', {username: username.value}); socket.emit('user.exists', {
username: username.val()
});
} }
}
username.on('keyup', function() {
jQuery('#yourUsername').html(this.value.length > 0 ? this.value : 'username');
});
username.on('blur', function() {
validateUsername();
}); });
$(emailEl).on('keyup change', function() { function validatePassword() {
emailvalid = utils.isEmailValid(email.value); if (!password.val()) {
validationError = true;
if(!emailvalid) { return;
email_notify.innerHTML = 'Invalid email address';
email_notify.className = 'label label-important';
} }
else
socket.emit('user.email.exists', { email: emailEl.value });
});
$(password).on('keyup', function() { if (password.val().length < config.minimumPasswordLength) {
passwordvalid = utils.isPasswordValid(password.value); showError(password_notify, 'Password too short!');
if (password.value.length < 6) { } else if (!utils.isPasswordValid(password.val())) {
password_notify.innerHTML = 'Password too short'; showError(password_notify, 'Invalid password!');
password_notify.className = 'label label-important';
} else if(!passwordvalid) {
password_notify.innerHTML = 'Invalid password';
password_notify.className = 'label label-important';
} else { } else {
password_notify.innerHTML = 'OK!'; showSuccess(password_notify, successIcon);
password_notify.className = 'label label-success';
} }
if(password.value !== password_confirm.value) { if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
password_confirm_notify.innerHTML = 'Passwords must match!'; showError(password_confirm_notify, 'Passwords must match!');
password_confirm_notify.className = 'label label-important';
passwordsmatch = false;
} }
}
$(password).on('blur', function() {
validatePassword();
}); });
$(password_confirm).on('keyup', function() { function validatePasswordConfirm() {
if(password.value !== password_confirm.value) { if (!password.val() || password_notify.hasClass('alert-error')) {
password_confirm_notify.innerHTML = 'Passwords must match!'; return;
password_confirm_notify.className = 'label label-important';
passwordsmatch = false;
} }
else {
password_confirm_notify.innerHTML = 'OK!'; if (password.val() !== password_confirm.val()) {
password_confirm_notify.className = 'label label-success'; showError(password_confirm_notify, 'Passwords must match!');
passwordsmatch = true; } else {
showSuccess(password_confirm_notify, successIcon);
} }
}
$(password_confirm).on('blur', function() {
validatePasswordConfirm();
}); });
ajaxify.register_events(['user.exists', 'user.email.exists']); ajaxify.register_events(['user.exists', 'user.email.exists']);
socket.on('user.exists', function(data) { socket.on('user.exists', function(data) {
userexists = data.exists;
if (data.exists === true) { if (data.exists === true) {
username_notify.innerHTML = 'Username exists'; showError(username_notify, 'Username already taken!');
username_notify.className = 'label label-important';
} else { } else {
username_notify.innerHTML = 'OK!'; showSuccess(username_notify, successIcon);
username_notify.className = 'label label-success';
} }
}); });
socket.on('user.email.exists', function(data) { socket.on('user.email.exists', function(data) {
emailexists = data.exists;
if (data.exists === true) { if (data.exists === true) {
email_notify.innerHTML = 'Email Address exists'; showError(email_notify, 'Email address already taken!');
email_notify.className = 'label label-important'; } else {
} showSuccess(email_notify, successIcon);
else {
email_notify.innerHTML = 'OK!';
email_notify.className = 'label label-success';
} }
}); });
// Alternate Logins // Alternate Logins
var altLoginEl = document.querySelector('.alt-logins'); $('.alt-logins li').on('click', function(e) {
altLoginEl.addEventListener('click', function(e) { document.location.href = $(this).attr('data-url');
var target;
switch(e.target.nodeName) {
case 'LI': target = e.target; break;
case 'I': target = e.target.parentNode; break;
}
if (target) {
document.location.href = target.getAttribute('data-url');
}
}); });
function validateForm() { function validateForm() {
var validated = true; validationError = false;
if (username.value.length < 2 || !usernamevalid) { validateEmail();
username_notify.innerHTML = 'Invalid username'; validateUsername();
username_notify.className = 'label label-important'; validatePassword();
validated = false; validatePasswordConfirm();
return validationError;
} }
if (password.value.length < 5) { register.on('click', function(e) {
password_notify.innerHTML = 'Password too short'; if (validateForm()) e.preventDefault();
validated = false; });
}
if(password.value !== password_confirm.value) {
password_confirm_notify.innerHTML = 'Passwords must match!';
}
if (!emailvalid) {
email_notify.innerHTML = 'Invalid email address';
validated = false;
}
if(emailexists) {
email_notify.innerHTML = 'Email Address exists';
validated = false;
}
if(userexists || !passwordsmatch || !passwordvalid)
validated = false;
return validated;
}
register.addEventListener('click', function(e) {
if (!validateForm()) e.preventDefault();
}, false);
}()); }());

View File

@@ -5,7 +5,9 @@
document.getElementById('reset').onclick = function() { document.getElementById('reset').onclick = function() {
if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) { if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
socket.emit('user:reset.send', { email: inputEl.value }); socket.emit('user:reset.send', {
email: inputEl.value
});
} else { } else {
jQuery('#success').hide(); jQuery('#success').hide();
jQuery(errorEl).show(); jQuery(errorEl).show();
@@ -26,7 +28,7 @@
} else { } else {
jQuery('#success').hide(); jQuery('#success').hide();
jQuery(errorEl).show(); jQuery(errorEl).show();
switch(data.message) { switch (data.message) {
case 'invalid-email': case 'invalid-email':
errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.'; errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
break; break;

View File

@@ -10,20 +10,30 @@
if (password.value.length < 6) { if (password.value.length < 6) {
$('#error').hide(); $('#error').hide();
noticeEl.querySelector('strong').innerHTML = 'Invalid Password'; noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
noticeEl.querySelector('p').innerHTML = 'The password entered it too short, please pick a different password!'; noticeEl.querySelector('p').innerHTML = 'The password entered is too short, please pick a different password.';
noticeEl.style.display = 'block'; noticeEl.style.display = 'block';
} else if (password.value === repeat.value) { } else if (password.value !== repeat.value) {
socket.emit('user:reset.commit', { code: reset_code, password: password.value }); $('#error').hide();
noticeEl.querySelector('strong').innerHTML = 'Invalid Password';
noticeEl.querySelector('p').innerHTML = 'The two passwords you\'ve entered do not match.';
noticeEl.style.display = 'block';
} else {
socket.emit('user:reset.commit', {
code: reset_code,
password: password.value
});
} }
}, false); }, false);
// Enable the form if the code is valid // Enable the form if the code is valid
socket.emit('user:reset.valid', { code: reset_code }); socket.emit('user:reset.valid', {
code: reset_code
});
ajaxify.register_events(['user:reset.valid', 'user:reset.commit']); ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
socket.on('user:reset.valid', function(data) { socket.on('user:reset.valid', function(data) {
if (!!data.valid) resetEl.disabled = false; if ( !! data.valid) resetEl.disabled = false;
else { else {
var formEl = document.getElementById('reset-form'); var formEl = document.getElementById('reset-form');
// Show error message // Show error message

View File

@@ -6,9 +6,19 @@
$('.search-result-text').each(function() { $('.search-result-text').each(function() {
var text = $(this).html(); var text = $(this).html();
var regex = new RegExp(searchQuery, 'gi'); var regex = new RegExp(searchQuery, 'gi');
text = text.replace(regex, '<span class="label label-success">'+searchQuery+'</span>'); text = text.replace(regex, '<span class="label label-success">' + searchQuery + '</span>');
$(this).html(text); $(this).html(text);
}); });
$('#search-form input').val(searchQuery);
$('#mobile-search-form').off('submit').on('submit', function() {
var input = $(this).find('input');
ajaxify.go("search/" + input.val(), null, "search");
input.val('');
return false;
});
}); });
})(); })();

View File

@@ -26,52 +26,61 @@
if (thread_state.pinned === '1') set_pinned_state(true); if (thread_state.pinned === '1') set_pinned_state(true);
if (expose_tools === '1') { if (expose_tools === '1') {
var deleteThreadEl = document.getElementById('delete_thread'), var moveThreadModal = $('#move_thread_modal');
lockThreadEl = document.getElementById('lock_thread'),
pinThreadEl = document.getElementById('pin_thread'),
moveThreadEl = document.getElementById('move_thread'),
moveThreadModal = $('#move_thread_modal');
adminTools.style.visibility = 'inherit'; adminTools.style.visibility = 'inherit';
// Add events to the thread tools // Add events to the thread tools
deleteThreadEl.addEventListener('click', function(e) { $('#delete_thread').on('click', function(e) {
e.preventDefault();
if (thread_state.deleted !== '1') { if (thread_state.deleted !== '1') {
bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) { bootbox.confirm('Are you sure you want to delete this thread?', function(confirm) {
if (confirm) socket.emit('api:topic.delete', { tid: tid }); if (confirm) socket.emit('api:topic.delete', {
tid: tid
});
}); });
} else { } else {
bootbox.confirm('Are you sure you want to restore this thread?', function(confirm) { bootbox.confirm('Are you sure you want to restore this thread?', function(confirm) {
if (confirm) socket.emit('api:topic.restore', { tid: tid }); if (confirm) socket.emit('api:topic.restore', {
tid: tid
});
}); });
} }
}, false); return false;
});
lockThreadEl.addEventListener('click', function(e) { $('#lock_thread').on('click', function(e) {
e.preventDefault();
if (thread_state.locked !== '1') { if (thread_state.locked !== '1') {
socket.emit('api:topic.lock', { tid: tid }); socket.emit('api:topic.lock', {
tid: tid
});
} else { } else {
socket.emit('api:topic.unlock', { tid: tid }); socket.emit('api:topic.unlock', {
tid: tid
});
} }
}, false); return false;
});
pinThreadEl.addEventListener('click', function(e) { $('#pin_thread').on('click', function(e) {
e.preventDefault();
if (thread_state.pinned !== '1') { if (thread_state.pinned !== '1') {
socket.emit('api:topic.pin', { tid: tid }); socket.emit('api:topic.pin', {
tid: tid
});
} else { } else {
socket.emit('api:topic.unpin', { tid: tid }); socket.emit('api:topic.unpin', {
tid: tid
});
} }
}, false); return false;
});
moveThreadEl.addEventListener('click', function(e) { $('#move_thread').on('click', function(e) {
e.preventDefault();
moveThreadModal.modal('show'); moveThreadModal.modal('show');
}, false); return false;
});
moveThreadModal.on('shown.bs.modal', function() {
moveThreadModal.on('shown', function() {
var loadingEl = document.getElementById('categories-loading'); var loadingEl = document.getElementById('categories-loading');
if (loadingEl) { if (loadingEl) {
socket.once('api:categories.get', function(data) { socket.once('api:categories.get', function(data) {
@@ -88,7 +97,7 @@
x, info, targetCid, targetCatLabel; x, info, targetCid, targetCatLabel;
categoriesEl.className = 'category-list'; categoriesEl.className = 'category-list';
for(x=0;x<numCategories;x++) { for (x = 0; x < numCategories; x++) {
info = data.categories[x]; info = data.categories[x];
categoryEl.className = info.blockclass; categoryEl.className = info.blockclass;
categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name; categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name;
@@ -128,14 +137,17 @@
} else { } else {
app.alert({ app.alert({
'alert_id': 'thread_move', 'alert_id': 'thread_move',
type: 'error', type: 'danger',
title: 'Unable to Move Topic', title: 'Unable to Move Topic',
message: 'This topic could not be moved to ' + targetCatLabel + '.<br />Please try again later', message: 'This topic could not be moved to ' + targetCatLabel + '.<br />Please try again later',
timeout: 5000 timeout: 5000
}); });
} }
}); });
socket.emit('api:topic.move', { tid: tid, cid: targetCid }); socket.emit('api:topic.move', {
tid: tid,
cid: targetCid
});
} }
}); });
}); });
@@ -146,7 +158,7 @@
// Fix delete state for this thread's posts // Fix delete state for this thread's posts
var postEls = document.querySelectorAll('#post-container li[data-deleted]'); var postEls = document.querySelectorAll('#post-container li[data-deleted]');
for(var x=0,numPosts=postEls.length;x<numPosts;x++) { for (var x = 0, numPosts = postEls.length; x < numPosts; x++) {
if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid')); if (postEls[x].getAttribute('data-deleted') === '1') toggle_post_delete_state(postEls[x].getAttribute('data-pid'));
postEls[x].removeAttribute('data-deleted'); postEls[x].removeAttribute('data-deleted');
} }
@@ -187,7 +199,7 @@
if (data.status && data.status === 'ok') set_follow_state(data.follow); if (data.status && data.status === 'ok') set_follow_state(data.follow);
else { else {
app.alert({ app.alert({
type: 'error', type: 'danger',
alert_id: 'topic_follow', alert_id: 'topic_follow',
title: 'Please Log In', title: 'Please Log In',
message: 'Please register or log in in order to subscribe to this topic', message: 'Please register or log in in order to subscribe to this topic',
@@ -195,25 +207,37 @@
}); });
} }
}); });
socket.emit('api:topic.followCheck', tid); socket.emit('api:topic.followCheck', tid);
if (followEl[0]) {
followEl[0].addEventListener('click', function() { followEl[0].addEventListener('click', function() {
socket.emit('api:topic.follow', tid); socket.emit('api:topic.follow', tid);
}, false); }, false);
// Infinite scrolling of posts
$(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
app.loadMorePosts(tid);
} }
});
$('.post-container').on('click', '.deleted', function(ev) { enableInfiniteLoading();
var bookmark = localStorage.getItem('topic:' + tid + ':bookmark');
if(bookmark) {
app.scrollToPost(parseInt(bookmark, 10));
}
$('#post-container').on('click', '.deleted', function(ev) {
$(this).toggleClass('deleted-expanded'); $(this).toggleClass('deleted-expanded');
}); });
}); });
function enableInfiniteLoading() {
$(window).off('scroll').on('scroll', function() {
var bottom = ($(document).height() - $(window).height()) * 0.9;
if ($(window).scrollTop() > bottom && !app.infiniteLoaderActive && $('#post-container').children().length) {
app.loadMorePosts(tid);
}
});
}
var reply_fn = function() { var reply_fn = function() {
var selectionText = '', var selectionText = '',
selection = window.getSelection() || document.getSelection(); selection = window.getSelection() || document.getSelection();
@@ -229,10 +253,10 @@
}); });
} }
}; };
$('.post-container').on('click', '.post_reply', reply_fn); $('#post-container').on('click', '.post_reply', reply_fn);
$('#post_reply').on('click', reply_fn); $('#post_reply').on('click', reply_fn);
$('.post-container').on('click', '.quote', function() { $('#post-container').on('click', '.quote', function() {
if (thread_state.locked !== '1') { if (thread_state.locked !== '1') {
var pid = $(this).parents('li').attr('data-pid'); var pid = $(this).parents('li').attr('data-pid');
@@ -243,60 +267,72 @@
cmp.push(tid, null, null, quoted); cmp.push(tid, null, null, quoted);
}); });
}); });
socket.emit('api:posts.getRawPost', { pid: pid }); socket.emit('api:posts.getRawPost', {
pid: pid
});
} }
}); });
$('.post-container').on('click', '.favourite', function() { $('#post-container').on('click', '.favourite', function() {
var ids = this.id.replace('favs_', '').split('_'), var pid = $(this).parents('li').attr('data-pid');
pid = ids[0], var uid = $(this).parents('li').attr('data-uid');
uid = ids[1];
var element = $(this).find('i'); var element = $(this).find('i');
if(element.attr('class') == 'icon-star-empty') { if (element.attr('class') == 'icon-star-empty') {
element.attr('class', 'icon-star'); socket.emit('api:posts.favourite', {
socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room}); pid: pid,
} room_id: app.current_room
else { });
element.attr('class', 'icon-star-empty'); } else {
socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room}); socket.emit('api:posts.unfavourite', {
pid: pid,
room_id: app.current_room
});
} }
}); });
$('.post-container').delegate('.edit', 'click', function(e) { $('#post-container').on('click', '.link', function() {
var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1]; var pid = $(this).parents('li').attr('data-pid');
$('#post_' + pid + '_link').val(window.location.href + "#" + pid).stop(true, false).fadeIn().select();
$('#post_' + pid + '_link').off('blur').on('blur', function() {
$(this).fadeOut();
});
});
var main = $(this).parents('.main-post'); $('#post-container').delegate('.edit', 'click', function(e) {
var pid = $(this).parents('li').attr('data-pid'),
main = $(this).parents('.main-post');
require(['composer'], function(cmp) { require(['composer'], function(cmp) {
cmp.push(null, null, pid); cmp.push(null, null, pid);
}); });
}); });
$('.post-container').delegate('.delete', 'click', function(e) { $('#post-container').delegate('.delete', 'click', function(e) {
var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1], var pid = $(this).parents('li').attr('data-pid'),
postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')), postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
deleteAction = !postEl.hasClass('deleted') ? true : false, deleteAction = !postEl.hasClass('deleted') ? true : false,
confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?'); confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
if (confirmDel) { if (confirmDel) {
deleteAction ? deleteAction ?
socket.emit('api:posts.delete', { pid: pid }) : socket.emit('api:posts.delete', {
socket.emit('api:posts.restore', { pid: pid }); pid: pid
}) :
socket.emit('api:posts.restore', {
pid: pid
});
} }
}); });
$('.post-container').delegate('.chat', 'click', function(e) { $('#post-container').on('click', '.chat', function(e) {
var username = $(this).parents('li.row').attr('data-username');
var touid = $(this).parents('li.row').attr('data-uid');
var username = $(this).parents('li').attr('data-username'); if (username === app.username || !app.username)
var touid = $(this).parents('li').attr('data-uid'); return;
require(['chat'], function(chat){
var chatModal = chat.createModalIfDoesntExist(username, touid);
chatModal.show();
chat.bringModalToTop(chatModal);
});
app.openChat(username, touid);
}); });
ajaxify.register_events([ ajaxify.register_events([
@@ -310,7 +346,7 @@
socket.on('api:get_users_in_room', function(data) { socket.on('api:get_users_in_room', function(data) {
var activeEl = $('#thread_active_users'); var activeEl = $('#thread_active_users');
if(activeEl.length) if (activeEl.length)
activeEl.html(data); activeEl.html(data);
app.populate_online_users(); app.populate_online_users();
@@ -371,9 +407,9 @@
socket.on('event:post_edited', function(data) { socket.on('event:post_edited', function(data) {
var editedPostEl = document.getElementById('content_' + data.pid); var editedPostEl = document.getElementById('content_' + data.pid);
var editedPostTitle = $('#topic_title_'+data.pid); var editedPostTitle = $('#topic_title_' + data.pid);
if(editedPostTitle.length > 0) { if (editedPostTitle.length > 0) {
editedPostTitle.fadeOut(250, function() { editedPostTitle.fadeOut(250, function() {
editedPostTitle.html(data.title); editedPostTitle.html(data.title);
editedPostTitle.fadeIn(250); editedPostTitle.fadeIn(250);
@@ -384,12 +420,26 @@
this.innerHTML = data.content; this.innerHTML = data.content;
$(this).fadeIn(250); $(this).fadeIn(250);
}); });
}); });
socket.on('api:posts.favourite', function(data) { socket.on('api:posts.favourite', function(data) {
if (data.status !== 'ok' && data.pid) { if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling; var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
if (favEl) favEl.className = 'icon-star-empty'; if (favEl) {
favEl.className = 'icon-star';
$(favEl).parent().addClass('btn-warning');
}
}
});
socket.on('api:posts.unfavourite', function(data) {
if (data.status === 'ok' && data.pid) {
var favEl = document.querySelector('.post_rep_' + data.pid).nextSibling;
if (favEl) {
favEl.className = 'icon-star-empty';
$(favEl).parent().removeClass('btn-warning');
}
} }
}); });
@@ -415,8 +465,8 @@
ptotal += value; ptotal += value;
utotal += value; utotal += value;
post_rep.html(ptotal+ ' '); post_rep.html(ptotal + ' ');
user_rep.html(utotal+ ' '); user_rep.html(utotal + ' ');
} }
function set_locked_state(locked, alert) { function set_locked_state(locked, alert) {
@@ -433,7 +483,7 @@
lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread'; lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread';
threadReplyBtn.disabled = true; threadReplyBtn.disabled = true;
threadReplyBtn.innerHTML = 'Locked <i class="icon-lock"></i>'; threadReplyBtn.innerHTML = 'Locked <i class="icon-lock"></i>';
for(x=0;x<numPosts;x++) { for (x = 0; x < numPosts; x++) {
postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>'; postReplyBtns[x].innerHTML = 'Locked <i class="icon-lock"></i>';
quoteBtns[x].style.display = 'none'; quoteBtns[x].style.display = 'none';
editBtns[x].style.display = 'none'; editBtns[x].style.display = 'none';
@@ -455,7 +505,7 @@
lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread'; lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread';
threadReplyBtn.disabled = false; threadReplyBtn.disabled = false;
threadReplyBtn.innerHTML = 'Reply'; threadReplyBtn.innerHTML = 'Reply';
for(x=0;x<numPosts;x++) { for (x = 0; x < numPosts; x++) {
postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>'; postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
quoteBtns[x].style.display = 'inline-block'; quoteBtns[x].style.display = 'inline-block';
editBtns[x].style.display = 'inline-block'; editBtns[x].style.display = 'inline-block';
@@ -479,23 +529,23 @@
function set_delete_state(deleted) { function set_delete_state(deleted) {
var deleteThreadEl = document.getElementById('delete_thread'), var deleteThreadEl = document.getElementById('delete_thread'),
deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0], deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0],
threadEl = document.querySelector('.post-container'), threadEl = $('#post-container'),
deleteNotice = document.getElementById('thread-deleted') || document.createElement('div'); deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
if (deleted) { if (deleted) {
deleteTextEl.innerHTML = '<i class="icon-comment"></i> Restore Thread'; deleteTextEl.innerHTML = '<i class="icon-comment"></i> Restore Thread';
$(threadEl).addClass('deleted'); threadEl.addClass('deleted');
// Spawn a 'deleted' notice at the top of the page // Spawn a 'deleted' notice at the top of the page
deleteNotice.setAttribute('id', 'thread-deleted'); deleteNotice.setAttribute('id', 'thread-deleted');
deleteNotice.className = 'alert'; deleteNotice.className = 'alert alert-warning';
deleteNotice.innerHTML = 'This thread has been deleted. Only users with thread management privileges can see it.'; deleteNotice.innerHTML = 'This thread has been deleted. Only users with thread management privileges can see it.';
document.getElementById('content').insertBefore(deleteNotice, threadEl); threadEl.before(deleteNotice);
thread_state.deleted = '1'; thread_state.deleted = '1';
} else { } else {
deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread'; deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread';
$(threadEl).removeClass('deleted'); threadEl.removeClass('deleted');
deleteNotice.parentNode.removeChild(deleteNotice); deleteNotice.parentNode.removeChild(deleteNotice);
thread_state.deleted = '0'; thread_state.deleted = '0';
@@ -591,4 +641,76 @@
deleteEl.addClass('none'); deleteEl.addClass('none');
} }
} }
var postAuthorImage, mobileAuthorOverlay, pagination;
var postcount = templates.get('postcount');
function updateHeader() {
if (pagination == null) {
jQuery('.pagination-block i:first').on('click', function() {
app.scrollToTop();
});
jQuery('.pagination-block i:last').on('click', function() {
app.scrollToBottom();
});
}
jQuery('.mobile-author-overlay').css('bottom', '0px');
postAuthorImage = postAuthorImage || document.getElementById('mobile-author-image');
mobileAuthorOverlay = mobileAuthorOverlay || document.getElementById('mobile-author-overlay');
pagination = pagination || document.getElementById('pagination');
pagination.parentNode.style.display = 'block';
var windowHeight = jQuery(window).height();
var scrollTop = jQuery(window).scrollTop();
var scrollBottom = scrollTop + windowHeight;
if (scrollTop < 50 && postcount > 1) {
localStorage.removeItem("topic:" + tid + ":bookmark");
postAuthorImage.src = (jQuery('.main-post .avatar img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery('.main-post').attr('data-username') + ', ' + jQuery('.main-post').find('.relativeTimeAgo').html();
pagination.innerHTML = '0 out of ' + postcount;
return;
}
var count = 0, smallestNonNegative = 0;
jQuery('.sub-posts').each(function() {
count++;
this.postnumber = count;
var el = jQuery(this);
var elTop = el.offset().top;
var height = Math.floor(el.height());
var elBottom = elTop + (height < 300 ? height : 300);
var inView = ((elBottom >= scrollTop) && (elTop <= scrollBottom) && (elBottom <= scrollBottom) && (elTop >= scrollTop));
if (inView) {
if(elTop - scrollTop > smallestNonNegative) {
localStorage.setItem("topic:" + tid + ":bookmark", el.attr('data-pid'));
smallestNonNegative = Number.MAX_VALUE;
}
pagination.innerHTML = this.postnumber + ' out of ' + postcount;
postAuthorImage.src = (jQuery(this).find('.profile-image-block img').attr('src'));
mobileAuthorOverlay.innerHTML = 'Posted by ' + jQuery(this).attr('data-username') + ', ' + jQuery(this).find('.relativeTimeAgo').html();
}
});
setTimeout(function() {
if (scrollTop + windowHeight == jQuery(document).height()) {
pagination.innerHTML = postcount + ' out of ' + postcount;
}
}, 100);
}
window.onscroll = updateHeader;
window.onload = updateHeader;
})(); })();

View File

@@ -8,7 +8,8 @@
'event:new_post' 'event:new_post'
]); ]);
var newTopicCount = 0, newPostCount = 0; var newTopicCount = 0,
newPostCount = 0;
$('#new-topics-alert').on('click', function() { $('#new-topics-alert').on('click', function() {
$(this).hide(); $(this).hide();
@@ -24,16 +25,16 @@
function updateAlertText() { function updateAlertText() {
var text = ''; var text = '';
if(newTopicCount > 1) if (newTopicCount > 1)
text = 'There are ' + newTopicCount + ' new topics'; text = 'There are ' + newTopicCount + ' new topics';
else if(newTopicCount === 1) else if (newTopicCount === 1)
text = 'There is 1 new topic'; text = 'There is 1 new topic';
else else
text = 'There are no new topics'; text = 'There are no new topics';
if(newPostCount > 1) if (newPostCount > 1)
text += ' and ' + newPostCount + ' new posts.'; text += ' and ' + newPostCount + ' new posts.';
else if(newPostCount === 1) else if (newPostCount === 1)
text += ' and 1 new post.'; text += ' and 1 new post.';
else else
text += ' and no new posts.'; text += ' and no new posts.';
@@ -50,12 +51,16 @@
$('#mark-allread-btn').on('click', function() { $('#mark-allread-btn').on('click', function() {
var btn = $(this); var btn = $(this);
socket.emit('api:topics.markAllRead', {} , function(success) { socket.emit('api:topics.markAllRead', {}, function(success) {
if(success) { if (success) {
btn.remove(); btn.remove();
$('#topics-container').empty(); $('#topics-container').empty();
$('#category-no-topics').removeClass('hidden'); $('#category-no-topics').removeClass('hidden');
app.alertSuccess('All topics marked as read!'); app.alertSuccess('All topics marked as read!');
$('#numUnreadBadge')
.removeClass('badge-important')
.addClass('badge-inverse')
.html('0');
} else { } else {
app.alertError('There was an error marking topics read!'); app.alertError('There was an error marking topics read!');
} }
@@ -64,7 +69,9 @@
function onTopicsLoaded(topics) { function onTopicsLoaded(topics) {
var html = templates.prepare(templates['unread'].blocks['topics']).parse({ topics: topics }), var html = templates.prepare(templates['unread'].blocks['topics']).parse({
topics: topics
}),
container = $('#topics-container'); container = $('#topics-container');
$('#category-no-topics').remove(); $('#category-no-topics').remove();
@@ -74,25 +81,30 @@
function loadMoreTopics() { function loadMoreTopics() {
loadingMoreTopics = true; loadingMoreTopics = true;
socket.emit('api:topics.loadMoreUnreadTopics', {after:parseInt($('#topics-container').attr('data-next-start'), 10)}, function(data) { socket.emit('api:topics.loadMoreUnreadTopics', {
if(data.topics && data.topics.length) { after: parseInt($('#topics-container').attr('data-next-start'), 10)
}, function(data) {
if (data.topics && data.topics.length) {
onTopicsLoaded(data.topics); onTopicsLoaded(data.topics);
$('#topics-container').attr('data-next-start', data.nextStart); $('#topics-container').attr('data-next-start', data.nextStart);
} else {
$('#load-more-btn').hide();
} }
loadingMoreTopics = false; loadingMoreTopics = false;
}); });
} }
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9; var bottom = ($(document).height() - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreTopics) { if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
loadMoreTopics(); loadMoreTopics();
} }
}); });
if($("body").height() <= $(window).height() && $('#topics-container').children().length) if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
$('#load-more-btn').show(); $('#load-more-btn').show();
$('#load-more-btn').on('click', function() { $('#load-more-btn').on('click', function() {

View File

@@ -6,7 +6,11 @@
var url = window.location.href, var url = window.location.href,
parts = url.split('/'), parts = url.split('/'),
active = parts[parts.length-1]; active = parts[parts.length - 1];
var lastSearch = null;
app.addCommasToNumbers();
jQuery('.nav-pills li').removeClass('active'); jQuery('.nav-pills li').removeClass('active');
jQuery('.nav-pills li a').each(function() { jQuery('.nav-pills li a').each(function() {
@@ -16,8 +20,8 @@
} }
}); });
jQuery('#search-user').on('keyup', function () { jQuery('#search-user').on('keyup', function() {
if(timeoutId !== 0) { if (timeoutId !== 0) {
clearTimeout(timeoutId); clearTimeout(timeoutId);
timeoutId = 0; timeoutId = 0;
} }
@@ -25,93 +29,105 @@
timeoutId = setTimeout(function() { timeoutId = setTimeout(function() {
var username = $('#search-user').val(); var username = $('#search-user').val();
jQuery('.icon-spinner').removeClass('none'); if (username == '') {
jQuery('#user-notfound-notify').html('<i class="icon icon-circle-blank"></i>');
jQuery('#user-notfound-notify').parent().removeClass('btn-warning label-warning btn-success label-success');
return;
}
if (lastSearch === username) return;
lastSearch = username;
jQuery('#user-notfound-notify').html('<i class="icon-spinner icon-spin"></i>');
setTimeout(function() {
socket.emit('api:admin.user.search', username); socket.emit('api:admin.user.search', username);
}, 500); //replace this with global throttling function/constant
}, 250); }, 250);
}); });
socket.removeAllListeners('api:admin.user.search'); socket.removeAllListeners('api:admin.user.search');
socket.on('api:admin.user.search', function(data) { socket.on('api:admin.user.search', function(data) {
if (data === null) {
jQuery('.icon-spinner').addClass('none'); $('#user-notfound-notify').html('You need to be logged in to search!');
$('#user-notfound-notify').parent().addClass('btn-warning label-warning');
if(data === null) {
$('#user-notfound-notify').html('You need to be logged in to search!')
.show()
.addClass('label-important')
.removeClass('label-success');
return; return;
} }
var html = templates.prepare(templates['users'].blocks['users']).parse({ var html = templates.prepare(templates['users'].blocks['users']).parse({
users: data users: data
}), }),
userListEl = document.querySelector('.users'); userListEl = document.querySelector('#users-container');
userListEl.innerHTML = html; userListEl.innerHTML = html;
if(data && data.length === 0) { if (data && data.length === 0) {
$('#user-notfound-notify').html('User not found!') $('#user-notfound-notify').html('User not found!');
.show() $('#user-notfound-notify').parent().addClass('btn-warning label-warning');
.addClass('label-important') } else {
.removeClass('label-success'); $('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!');
} $('#user-notfound-notify').parent().addClass('btn-success label-success');
else {
$('#user-notfound-notify').html(data.length + ' user'+(data.length>1?'s':'') + ' found!')
.show()
.addClass('label-success')
.removeClass('label-important');
} }
}); });
$('.reputation').each(function(index, element) { socket.on('api:user.isOnline', function(data) {
$(element).html(app.addCommas($(element).html())); if(active == 'online' && !loadingMoreUsers) {
}); $('#users-container').empty();
startLoading('users:online', 0);
$('.postcount').each(function(index, element) { }
$(element).html(app.addCommas($(element).html()));
}); });
function onUsersLoaded(users) { function onUsersLoaded(users) {
var html = templates.prepare(templates['users'].blocks['users']).parse({ users: users }); var html = templates.prepare(templates['users'].blocks['users']).parse({
users: users
});
$('#users-container').append(html); $('#users-container').append(html);
} }
function loadMoreUsers() { function loadMoreUsers() {
var set = ''; var set = '';
if(active === 'users-latest' || active === 'users') { if (active === 'latest' || active === 'users') {
set = 'users:joindate'; set = 'users:joindate';
} else if(active === 'users-sort-posts') { } else if (active === 'sort-posts') {
set = 'users:postcount'; set = 'users:postcount';
} else if(active === 'users-sort-reputation') { } else if (active === 'sort-reputation') {
set = 'users:reputation'; set = 'users:reputation';
} else if (active === 'online') {
set = 'users:online';
} }
if(set) { if (set) {
startLoading(set, $('#users-container').children().length);
}
}
function startLoading(set, after) {
loadingMoreUsers = true; loadingMoreUsers = true;
socket.emit('api:users.loadMore', { socket.emit('api:users.loadMore', {
set: set, set: set,
after: $('#users-container').children().length after: after
}, function(data) { }, function(data) {
if(data.users.length) { if (data.users.length) {
onUsersLoaded(data.users); onUsersLoaded(data.users);
$('#load-more-users-btn').removeClass('disabled');
} else { } else {
$('#load-more-users-btn').addClass('disabled'); $('#load-more-users-btn').addClass('disabled');
} }
loadingMoreUsers = false; loadingMoreUsers = false;
}); });
} }
}
$('#load-more-users-btn').on('click', loadMoreUsers); $('#load-more-users-btn').on('click', loadMoreUsers);
$(window).off('scroll').on('scroll', function() { $(window).off('scroll').on('scroll', function() {
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9; var bottom = ($(document).height() - $(window).height()) * 0.9;
if (document.body.scrollTop > bottom && !loadingMoreUsers) { if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
loadMoreUsers(); loadMoreUsers();
} }
}); });

View File

@@ -2,7 +2,6 @@ define(['taskbar'], function(taskbar) {
var module = {}; var module = {};
module.bringModalToTop = function(chatModal) { module.bringModalToTop = function(chatModal) {
var topZ = 0; var topZ = 0;
$('.modal').each(function() { $('.modal').each(function() {
@@ -11,26 +10,63 @@ define(['taskbar'], function(taskbar) {
topZ = thisZ; topZ = thisZ;
} }
}); });
chatModal.css('zIndex', topZ+1); chatModal.css('zIndex', topZ + 1);
} }
module.createModalIfDoesntExist = function(username, touid) { module.getModal = function(touid) {
var chatModal = $('#chat-modal-'+touid); return $('#chat-modal-' + touid);
}
if(!chatModal.length) { module.modalExists = function(touid) {
var chatModal = $('#chat-modal').clone(); return $('#chat-modal-' + touid).length !== 0;
chatModal.attr('id','chat-modal-'+touid); }
var uuid = utils.generateUUID();
function checkStatus(chatModal, callback) {
socket.emit('api:user.isOnline', chatModal.touid, function(data) {
if(data.online !== chatModal.online) {
if(data.online) {
module.appendChatMessage(chatModal, chatModal.username + ' is currently online.\n', data.timestamp);
} else {
module.appendChatMessage(chatModal, chatModal.username + ' is currently offline.\n', data.timestamp);
}
chatModal.online = data.online;
}
if(callback)
callback(data.online);
});
}
function checkOnlineStatus(chatModal) {
if(chatModal.intervalId === 0) {
chatModal.intervalId = setInterval(function() {
checkStatus(chatModal);
}, 1000);
}
}
module.createModal = function(username, touid, callback) {
var chatModal = $('#chat-modal').clone(),
uuid = utils.generateUUID();
chatModal.intervalId = 0;
chatModal.touid = touid;
chatModal.username = username;
chatModal.attr('id', 'chat-modal-' + touid);
chatModal.attr('UUID', uuid); chatModal.attr('UUID', uuid);
chatModal.appendTo($('body')); chatModal.appendTo($('body'));
chatModal.draggable({ chatModal.draggable({
start:function(){ start:function() {
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
} }
}); });
chatModal.find('#chat-with-name').html(username); chatModal.find('#chat-with-name').html(username);
chatModal.find('.close').on('click',function(e){ chatModal.find('.close').on('click', function(e) {
clearInterval(chatModal.intervalId);
chatModal.intervalId = 0;
chatModal.hide(); chatModal.hide();
taskbar.discard('chat', uuid); taskbar.discard('chat', uuid);
}); });
@@ -39,10 +75,13 @@ define(['taskbar'], function(taskbar) {
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
}); });
addSendHandler(chatModal, touid); addSendHandler(chatModal);
}
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username}); getChatMessages(chatModal, function() {
checkOnlineStatus(chatModal);
});
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with ' + username});
return chatModal; return chatModal;
} }
@@ -50,43 +89,56 @@ define(['taskbar'], function(taskbar) {
var chatModal = $('div[UUID="'+uuid+'"]'); var chatModal = $('div[UUID="'+uuid+'"]');
chatModal.show(); chatModal.show();
module.bringModalToTop(chatModal); module.bringModalToTop(chatModal);
checkOnlineStatus(chatModal);
} }
module.minimize = function(uuid) { module.minimize = function(uuid) {
var chatModal = $('div[UUID="'+uuid+'"]'); var chatModal = $('div[UUID="'+uuid+'"]');
chatModal.hide(); chatModal.hide();
taskbar.minimize('chat', uuid); taskbar.minimize('chat', uuid);
clearInterval(chatModal.intervalId);
chatModal.intervalId = 0;
} }
function addSendHandler(chatModal, touid) { function getChatMessages(chatModal, callback) {
socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) {
for(var i = 0; i<messages.length; ++i) {
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
}
callback();
});
}
function addSendHandler(chatModal) {
chatModal.find('#chat-message-input').off('keypress'); chatModal.find('#chat-message-input').off('keypress');
chatModal.find('#chat-message-input').on('keypress', function(e) { chatModal.find('#chat-message-input').on('keypress', function(e) {
if(e.which === 13) { if(e.which === 13) {
sendMessage(chatModal, touid); sendMessage(chatModal);
} }
}); });
chatModal.find('#chat-message-send-btn').off('click'); chatModal.find('#chat-message-send-btn').off('click');
chatModal.find('#chat-message-send-btn').on('click', function(e){ chatModal.find('#chat-message-send-btn').on('click', function(e){
sendMessage(chatModal, touid); sendMessage(chatModal);
return false; return false;
}); });
} }
function sendMessage(chatModal, touid) { function sendMessage(chatModal) {
var msg = app.strip_tags(chatModal.find('#chat-message-input').val()); var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
if(msg.length) { if(msg.length) {
msg = msg +'\n'; msg = msg +'\n';
socket.emit('sendChatMessage', { touid:touid, message:msg}); socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg});
chatModal.find('#chat-message-input').val(''); chatModal.find('#chat-message-input').val('');
module.appendChatMessage(chatModal, 'You : ' + msg);
} }
} }
module.appendChatMessage = function(chatModal, message, timestamp) {
module.appendChatMessage = function(chatModal, message){
var chatContent = chatModal.find('#chat-content'); var chatContent = chatModal.find('#chat-content');
chatContent.append(message);
var date = new Date(parseInt(timestamp, 10));
chatContent.append('[' + date.toLocaleTimeString() + '] ' + message);
chatContent.scrollTop( chatContent.scrollTop(
chatContent[0].scrollHeight - chatContent.height() chatContent[0].scrollHeight - chatContent.height()
); );

View File

@@ -7,12 +7,33 @@ define(['taskbar'], function(taskbar) {
postContainer: undefined, postContainer: undefined,
}; };
var uploadsInProgress = [];
function createImagePlaceholder(img) {
var text = $('.post-window textarea').val(),
textarea = $('.post-window textarea'),
imgText = "!["+img.name+"](uploading...)";
text += imgText;
textarea.val(text + " ");
uploadsInProgress.push(1);
socket.emit("api:posts.uploadImage", img, function(err, data) {
var currentText = textarea.val();
imgText = "!["+data.name+"](uploading...)";
if(!err)
textarea.val(currentText.replace(imgText, "!["+data.name+"]("+data.url+")"));
else
textarea.val(currentText.replace(imgText, "!["+data.name+"](upload error)"));
uploadsInProgress.pop();
});
}
function loadFile(file) { function loadFile(file) {
var reader = new FileReader(); var reader = new FileReader(),
var dropDiv = $('#imagedrop'); dropDiv = $('.post-window .imagedrop'),
var imagelist = $('#imagelist'); uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
var uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
var posts = composer.posts[uuid];
$(reader).on('loadend', function(e) { $(reader).on('loadend', function(e) {
var bin = this.result; var bin = this.result;
@@ -23,21 +44,8 @@ define(['taskbar'], function(taskbar) {
data: bin data: bin
}; };
posts.images.push(img); createImagePlaceholder(img);
var imageLabel = $('<div class="label"><span>'+ file.name +'</span></div>');
var closeButton = $('<button class="close">&times;</button>');
closeButton.on('click', function(e) {
imageLabel.remove();
var index = posts.images.indexOf(img);
if(index !== -1) {
posts.images.splice(index, 1);
}
});
imageLabel.append(closeButton);
imagelist.append(imageLabel);
dropDiv.hide(); dropDiv.hide();
}); });
@@ -47,11 +55,28 @@ define(['taskbar'], function(taskbar) {
function initializeFileReader() { function initializeFileReader() {
jQuery.event.props.push( "dataTransfer" ); jQuery.event.props.push( "dataTransfer" );
if(window.FileReader) { var draggingDocument = false;
var drop = $('#imagedrop');
$(composer.postContainer).on('dragenter dragover', function() { if(window.FileReader) {
var drop = $('.post-window .imagedrop'),
textarea = $('.post-window textarea');
$(document).on('dragstart', function(e) {
draggingDocument = true;
}).on('dragend', function(e) {
draggingDocument = false;
});
textarea.on('dragenter', function(e) {
if(draggingDocument)
return;
drop.css('top', textarea.position().top + 'px');
drop.show(); drop.show();
drop.on('dragleave', function(ev) {
drop.hide();
drop.off('dragleave');
});
}); });
function cancel(e) { function cancel(e) {
@@ -64,19 +89,18 @@ define(['taskbar'], function(taskbar) {
drop.on('drop', function(e) { drop.on('drop', function(e) {
e.preventDefault(); e.preventDefault();
var uuid = drop.parents('[data-uuid]').attr('data-uuid'); var uuid = drop.parents('[data-uuid]').attr('data-uuid'),
var posts = composer.posts[uuid]; dt = e.dataTransfer,
files = dt.files;
var dt = e.dataTransfer;
var files = dt.files;
for (var i=0; i<files.length; i++) { for (var i=0; i<files.length; i++) {
loadFile(files[i]); loadFile(files[i]);
} }
if(!files.length)
drop.hide();
return false; return false;
}); });
} }
} }
@@ -85,25 +109,22 @@ define(['taskbar'], function(taskbar) {
var taskbar = document.getElementById('taskbar'); var taskbar = document.getElementById('taskbar');
composer.postContainer = document.createElement('div'); composer.postContainer = document.createElement('div');
composer.postContainer.className = 'post-window row-fluid'; composer.postContainer.className = 'post-window row';
composer.postContainer.innerHTML = '<div class="span5">' + composer.postContainer.innerHTML = '<div class="col-md-5">' +
'<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' + '<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' +
'<div class="btn-toolbar">' + '<div class="btn-toolbar formatting-bar">' +
'<div class="btn-group formatting-bar">' + '<div class="btn-group">' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-bold"></i></span>' + '<span class="btn btn-link" tabindex="-1"><i class="icon-bold"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-italic"></i></span>' + '<span class="btn btn-link" tabindex="-1"><i class="icon-italic"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-list"></i></span>' + '<span class="btn btn-link" tabindex="-1"><i class="icon-list"></i></span>' +
'<span class="btn btn-link" tabindex="-1"><i class="icon-link"></i></span>' + '<span class="btn btn-link" tabindex="-1"><i class="icon-link"></i></span>' +
'</div>' + '</div>' +
'</div>' + '</div>' +
'<div style="position:relative;">'+
'<div id="imagedrop" class=""><div>Drag and Drop Images Here</div></div>'+
'<textarea tabIndex="2"></textarea>' + '<textarea tabIndex="2"></textarea>' +
'<div id="imagelist"></div>'+ '<div class="imagedrop"><div>Drag and Drop Images Here</div></div>'+
'</div>'+ '<div class="btn-toolbar action-bar">' +
'<div class="btn-toolbar">' + '<div class="btn-group" style="float: right; margin-right: -8px">' +
'<div class="btn-group action-bar" style="float: right; margin-right: -8px">' + '<button data-action="minimize" class="btn hidden-xs" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
'<button data-action="minimize" class="btn hidden-phone" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
'<button class="btn" data-action="discard" tabIndex="5"><i class="icon-remove"></i> Discard</button>' + '<button class="btn" data-action="discard" tabIndex="5"><i class="icon-remove"></i> Discard</button>' +
'<button data-action="post" class="btn" tabIndex="3"><i class="icon-ok"></i> Submit</button>' + '<button data-action="post" class="btn" tabIndex="3"><i class="icon-ok"></i> Submit</button>' +
'</div>' + '</div>' +
@@ -112,6 +133,7 @@ define(['taskbar'], function(taskbar) {
document.body.insertBefore(composer.postContainer, taskbar); document.body.insertBefore(composer.postContainer, taskbar);
if(config.imgurClientIDSet)
initializeFileReader(); initializeFileReader();
socket.on('api:composer.push', function(threadData) { socket.on('api:composer.push', function(threadData) {
@@ -128,13 +150,12 @@ define(['taskbar'], function(taskbar) {
cid: threadData.cid, cid: threadData.cid,
pid: threadData.pid, pid: threadData.pid,
title: threadData.title || '', title: threadData.title || '',
body: threadData.body || '', body: threadData.body || ''
images: []
}; };
composer.load(uuid); composer.load(uuid);
} else { } else {
app.alert({ app.alert({
type: 'error', type: 'danger',
timeout: 5000, timeout: 5000,
alert_id: 'post_error', alert_id: 'post_error',
title: 'Please Log In to Post', title: 'Please Log In to Post',
@@ -152,21 +173,32 @@ define(['taskbar'], function(taskbar) {
// Post Window events // Post Window events
var jPostContainer = $(composer.postContainer), var jPostContainer = $(composer.postContainer),
postContentEl = composer.postContainer.querySelector('textarea') postContentEl = composer.postContainer.querySelector('textarea');
jPostContainer.on('change', 'input, textarea', function() { jPostContainer.on('change', 'input, textarea', function() {
var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid'); var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value; if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value; else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
}); });
jPostContainer.on('click', '.action-bar button', function() { jPostContainer.on('click', '.action-bar button', function() {
var action = this.getAttribute('data-action'), var action = this.getAttribute('data-action'),
uuid = $(this).parents('.post-window').attr('data-uuid'); uuid = $(this).parents('.post-window').attr('data-uuid');
switch(action) { switch(action) {
case 'post': composer.post(uuid); break; case 'post': composer.post(uuid); break;
case 'minimize': composer.minimize(uuid); break; case 'minimize': composer.minimize(uuid); break;
case 'discard': composer.discard(uuid); break; case 'discard':
if (postContentEl.value.length > 0) {
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
if (discard) composer.discard(uuid);
});
} else {
composer.discard(uuid);
}
break;
} }
}); });
jPostContainer.on('click', '.formatting-bar span', function() { jPostContainer.on('click', '.formatting-bar span', function() {
var iconClass = this.querySelector('i').className, var iconClass = this.querySelector('i').className,
cursorEnd = postContentEl.value.length, cursorEnd = postContentEl.value.length,
@@ -242,12 +274,7 @@ define(['taskbar'], function(taskbar) {
composer.load = function(post_uuid) { composer.load = function(post_uuid) {
var post_data = composer.posts[post_uuid], var post_data = composer.posts[post_uuid],
titleEl = composer.postContainer.querySelector('input'), titleEl = composer.postContainer.querySelector('input'),
bodyEl = composer.postContainer.querySelector('textarea'), bodyEl = composer.postContainer.querySelector('textarea');
dropDiv = $(composer.postContainer).find('#imagedrop'),
imagelist = $(composer.postContainer).find('#imagelist');
dropDiv.hide();
imagelist.empty();
composer.reposition(post_uuid); composer.reposition(post_uuid);
composer.active = post_uuid; composer.active = post_uuid;
@@ -264,7 +291,8 @@ define(['taskbar'], function(taskbar) {
titleEl.value = post_data.title; titleEl.value = post_data.title;
titleEl.readOnly = false; titleEl.readOnly = false;
} }
bodyEl.value = post_data.body bodyEl.value = post_data.body;
// Direct user focus to the correct element // Direct user focus to the correct element
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) { if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
@@ -277,7 +305,7 @@ define(['taskbar'], function(taskbar) {
} }
composer.reposition = function(post_uuid) { composer.reposition = function(post_uuid) {
var postWindowEl = composer.postContainer.querySelector('.span5'), var postWindowEl = composer.postContainer.querySelector('.col-md-5'),
taskbarBtn = document.querySelector('#taskbar [data-uuid="' + post_uuid + '"]'), taskbarBtn = document.querySelector('#taskbar [data-uuid="' + post_uuid + '"]'),
btnRect = taskbarBtn.getBoundingClientRect(), btnRect = taskbarBtn.getBoundingClientRect(),
taskbarRect = document.getElementById('taskbar').getBoundingClientRect(), taskbarRect = document.getElementById('taskbar').getBoundingClientRect(),
@@ -299,9 +327,19 @@ define(['taskbar'], function(taskbar) {
titleEl.value = titleEl.value.trim(); titleEl.value = titleEl.value.trim();
bodyEl.value = bodyEl.value.trim(); bodyEl.value = bodyEl.value.trim();
if(uploadsInProgress.length) {
return app.alert({
type: 'warning',
timeout: 2000,
title: 'Still uploading',
message: "Please wait for uploads to complete.",
alert_id: 'post_error'
});
}
if (titleEl.value.length < config.minimumTitleLength) { if (titleEl.value.length < config.minimumTitleLength) {
return app.alert({ return app.alert({
type: 'error', type: 'danger',
timeout: 2000, timeout: 2000,
title: 'Title too short', title: 'Title too short',
message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.", message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.",
@@ -311,7 +349,7 @@ define(['taskbar'], function(taskbar) {
if (bodyEl.value.length < config.minimumPostLength) { if (bodyEl.value.length < config.minimumPostLength) {
return app.alert({ return app.alert({
type: 'error', type: 'danger',
timeout: 2000, timeout: 2000,
title: 'Content too short', title: 'Content too short',
message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.", message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
@@ -324,21 +362,18 @@ define(['taskbar'], function(taskbar) {
socket.emit('api:topics.post', { socket.emit('api:topics.post', {
'title' : titleEl.value, 'title' : titleEl.value,
'content' : bodyEl.value, 'content' : bodyEl.value,
'category_id' : postData.cid, 'category_id' : postData.cid
images: composer.posts[post_uuid].images
}); });
} else if (parseInt(postData.tid) > 0) { } else if (parseInt(postData.tid) > 0) {
socket.emit('api:posts.reply', { socket.emit('api:posts.reply', {
'topic_id' : postData.tid, 'topic_id' : postData.tid,
'content' : bodyEl.value, 'content' : bodyEl.value
images: composer.posts[post_uuid].images
}); });
} else if (parseInt(postData.pid) > 0) { } else if (parseInt(postData.pid) > 0) {
socket.emit('api:posts.edit', { socket.emit('api:posts.edit', {
pid: postData.pid, pid: postData.pid,
content: bodyEl.value, content: bodyEl.value,
title: titleEl.value, title: titleEl.value
images: composer.posts[post_uuid].images
}); });
} }
@@ -347,7 +382,7 @@ define(['taskbar'], function(taskbar) {
composer.discard = function(post_uuid) { composer.discard = function(post_uuid) {
if (composer.posts[post_uuid]) { if (composer.posts[post_uuid]) {
$(composer.postContainer).find('#imagedrop').html(''); $(composer.postContainer).find('.imagedrop').hide();
delete composer.posts[post_uuid]; delete composer.posts[post_uuid];
composer.minimize(); composer.minimize();
taskbar.discard('composer', post_uuid); taskbar.discard('composer', post_uuid);

View File

@@ -99,6 +99,7 @@ define(function() {
mobileMenu.init = function() { mobileMenu.init = function() {
return; // disabling until this can be pluginified.
overlay = overlay || document.getElementById('mobile-menu-overlay'); overlay = overlay || document.getElementById('mobile-menu-overlay');
menuBtn = menuBtn || document.getElementById('mobile-menu-btn'); menuBtn = menuBtn || document.getElementById('mobile-menu-btn');
postBtn = postBtn || document.getElementById('mobile-post-btn'); postBtn = postBtn || document.getElementById('mobile-post-btn');

View File

@@ -8,12 +8,12 @@ define(function() {
taskbar.taskbar = document.createElement('div'); taskbar.taskbar = document.createElement('div');
var jTaskbar = $(taskbar.taskbar); var jTaskbar = $(taskbar.taskbar);
taskbar.taskbar.innerHTML = '<div class="navbar-inner"><ul class="nav pull-right"></ul></div>'; taskbar.taskbar.innerHTML = '<div class="navbar-inner"><ul class="nav navbar-nav pull-right"></ul></div>';
taskbar.taskbar.className = 'taskbar navbar navbar-fixed-bottom'; taskbar.taskbar.className = 'taskbar navbar navbar-default navbar-fixed-bottom';
taskbar.taskbar.id = 'taskbar'; taskbar.taskbar.id = 'taskbar';
taskbar.tasklist = taskbar.taskbar.querySelector('ul'); taskbar.tasklist = taskbar.taskbar.querySelector('ul');
document.body.insertBefore(taskbar.taskbar, footerEl); document.body.insertBefore(taskbar.taskbar, footerEl.nextSibling);
// Posts bar events // Posts bar events
jTaskbar.on('click', 'li', function() { jTaskbar.on('click', 'li', function() {

View File

@@ -1,5 +1,3 @@
(function (module) { (function (module) {
var config = {}, var config = {},
@@ -14,11 +12,11 @@
fs = require('fs'); fs = require('fs');
} catch (e) {} } catch (e) {}
templates.force_refresh = function(tpl) { templates.force_refresh = function (tpl) {
return !!config.force_refresh[tpl]; return !!config.force_refresh[tpl];
} }
templates.get_custom_map = function(tpl) { templates.get_custom_map = function (tpl) {
if (config['custom_mapping'] && tpl) { if (config['custom_mapping'] && tpl) {
for (var pattern in config['custom_mapping']) { for (var pattern in config['custom_mapping']) {
if (tpl.match(pattern)) { if (tpl.match(pattern)) {
@@ -30,11 +28,11 @@
return false; return false;
} }
templates.is_available = function(tpl) { templates.is_available = function (tpl) {
return jQuery.inArray(tpl, available_templates) !== -1; return jQuery.inArray(tpl, available_templates) !== -1;
}; };
templates.ready = function(callback) { templates.ready = function (callback) {
if (callback == null) { if (callback == null) {
if (this.ready_callback) { if (this.ready_callback) {
this.ready_callback(); this.ready_callback();
@@ -50,7 +48,7 @@
} }
}; };
templates.prepare = function(raw_tpl, data) { templates.prepare = function (raw_tpl, data) {
var template = {}; var template = {};
template.html = raw_tpl; template.html = raw_tpl;
template.parse = parse; template.parse = parse;
@@ -64,10 +62,10 @@
var loaded = templatesToLoad.length; var loaded = templatesToLoad.length;
for (var t in templatesToLoad) { for (var t in templatesToLoad) {
(function(file) { (function (file) {
fs.readFile(global.configuration.ROOT_DIRECTORY + '/public/templates/' + file + '.tpl', function(err, html) { fs.readFile(__dirname + '/../templates/' + file + '.tpl', function (err, html) {
var template = function() { var template = function () {
this.toString = function() { this.toString = function () {
return this.html; return this.html;
}; };
} }
@@ -86,12 +84,9 @@
} }
function loadClient() { function loadClient() {
jQuery.when(jQuery.getJSON(RELATIVE_PATH + '/templates/config.json'), jQuery.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function(config_data, templates_data) { jQuery.when(jQuery.getJSON(RELATIVE_PATH + '/templates/config.json'), jQuery.getJSON(RELATIVE_PATH + '/api/get_templates_listing')).done(function (config_data, templates_data) {
config = config_data[0]; config = config_data[0];
available_templates = templates_data[0]; available_templates = templates_data[0];
templates.ready(); templates.ready();
}); });
} }
@@ -101,14 +96,14 @@
} }
templates.init = function(templates_to_load) { templates.init = function (templates_to_load) {
loadTemplates(templates_to_load || []); loadTemplates(templates_to_load || []);
} }
templates.getTemplateNameFromUrl = function(url) { templates.getTemplateNameFromUrl = function (url) {
var parts = url.split('?')[0].split('/'); var parts = url.split('?')[0].split('/');
for(var i=0; i<parts.length; ++i) { for (var i = 0; i < parts.length; ++i) {
if (templates.is_available(parts[i])) { if (templates.is_available(parts[i])) {
return parts[i]; return parts[i];
} }
@@ -117,30 +112,24 @@
} }
templates.load_template = function(callback, url, template) { templates.load_template = function (callback, url, template) {
var location = document.location || window.location, var location = document.location || window.location,
rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''); api_url = (url === '' || url === '/') ? 'home' : url,
tpl_url = templates.get_custom_map(api_url.split('?')[0]),
trimmed = api_url;
var api_url = (url === '' || url === '/') ? 'home' : url; if (!tpl_url) {
var tpl_url = templates.get_custom_map(api_url.split('?')[0]);
var trimmed = api_url;
if(!tpl_url) {
tpl_url = templates.getTemplateNameFromUrl(api_url); tpl_url = templates.getTemplateNameFromUrl(api_url);
} }
var template_data = null; var template_data = null;
(function() {
var timestamp = new Date().getTime(); //debug var timestamp = new Date().getTime(); //debug
if (!templates[tpl_url]) { if (!templates[tpl_url]) {
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function(html) { jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function (html) {
var template = function() { var template = function () {
this.toString = function() { this.toString = function () {
return this.html; return this.html;
}; };
} }
@@ -157,40 +146,34 @@
parse_template(); parse_template();
} }
}()); jQuery.get(RELATIVE_PATH + '/api/' + api_url, function (data) {
(function() { if (!data) {
jQuery.get(API_URL + api_url, function(data) {
if(!data) {
ajaxify.go('404'); ajaxify.go('404');
return; return;
} }
template_data = data; template_data = data;
parse_template(); parse_template();
}).fail(function(data) { }).fail(function (data) {
template_data = {}; app.alertError("Can't load template data!");
parse_template();
}); });
}());
function parse_template() { function parse_template() {
if (!templates[tpl_url] || !template_data) return; if (!templates[tpl_url] || !template_data) return;
if(typeof global !== "undefined") if (typeof global !== "undefined")
template_data['relative_path'] = global.nconf.get('relative_path'); template_data['relative_path'] = nconf.get('relative_path');
else else
template_data['relative_path'] = RELATIVE_PATH; template_data['relative_path'] = RELATIVE_PATH;
document.getElementById('content').innerHTML = templates[tpl_url].parse(template_data); translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
document.getElementById('content').innerHTML = translatedTemplate;
jQuery('#content [template-variable]').each(function(index, element) { jQuery('#content [template-variable]').each(function (index, element) {
var value = null; var value = null;
switch(element.getAttribute('template-type')) { switch (element.getAttribute('template-type')) {
case 'boolean': case 'boolean':
value = (element.value === 'true' || element.value === '1') ? true : false; value = (element.value === 'true' || element.value === '1') ? true : false;
break; break;
@@ -209,24 +192,25 @@
if (callback) { if (callback) {
callback(true); callback(true);
} }
});
} }
} }
templates.flush = function() { templates.flush = function () {
parsed_variables = {}; parsed_variables = {};
} }
templates.get = function(key) { templates.get = function (key) {
return parsed_variables[key]; return parsed_variables[key];
} }
templates.set = function(key, value) { templates.set = function (key, value) {
parsed_variables[key] = value; parsed_variables[key] = value;
} }
//modified from https://github.com/psychobunny/dcp.templates //modified from https://github.com/psychobunny/dcp.templates
var parse = function(data) { var parse = function (data) {
var self = this; var self = this;
function replace(key, value, template) { function replace(key, value, template) {
@@ -235,7 +219,7 @@
} }
function makeRegex(block) { function makeRegex(block) {
return new RegExp("<!-- BEGIN " + block + " -->[^]*<!-- END " + block + " -->", 'g'); return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
} }
function getBlock(regex, block, template) { function getBlock(regex, block, template) {
@@ -255,7 +239,8 @@
return template.replace(regex, block); return template.replace(regex, block);
} }
var template = this.html, regex, block; var template = this.html,
regex, block;
return (function parse(data, namespace, template) { return (function parse(data, namespace, template) {
if (!data || data.length == 0) { if (!data || data.length == 0) {
@@ -270,14 +255,16 @@
namespace += d + '.'; namespace += d + '.';
var regex = makeRegex(d), var regex = makeRegex(d),
block = getBlock(regex, namespace.substring(0, namespace.length-1), template); block = getBlock(regex, namespace.substring(0, namespace.length - 1), template);
if (block == null) { if (block == null) {
namespace = namespace.replace(d + '.', ''); namespace = namespace.replace(d + '.', '');
continue; continue;
} }
var numblocks = data[d].length - 1, i = 0, result = ""; var numblocks = data[d].length - 1,
i = 0,
result = "";
do { do {
result += parse(data[d][i], namespace, block); result += parse(data[d][i], namespace, block);
@@ -301,7 +288,7 @@
} }
if (namespace) { if (namespace) {
var regex = new RegExp("{" + namespace + "[^]*?}", 'g'); var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
template = template.replace(regex, ''); template = template.replace(regex, '');
} }
@@ -315,7 +302,8 @@
templates.init(); templates.init();
} }
})('undefined' === typeof module ? {module:{exports:{}}} : module) })('undefined' === typeof module ? {
module: {
exports: {}
}
} : module)

165
public/src/translator.js Normal file
View File

@@ -0,0 +1,165 @@
(function (module) {
"use strict";
/*global RELATIVE_PATH*/
/*
* TODO:
*
* 1. language en is hardcoded while system is developed. to switch language packs for now please edit DEFAULT_LANGUAGE
* b. need to write fallback system to default language if keys are missing (is this even necessary?)
* 2. recursion needed when parsing language keys (ex. topics:modal.delete.title), right now json is all one level deep
* 3. server side settings for default language
* 4. user side settings for preferred language
*
*/
var DEFAULT_LANGUAGE = 'en';
var translator = {},
files = {
loaded: {},
loading: {},
callbacks: {} // could be combined with "loading" in future.
},
isServer = false;
module.exports = translator;
translator.get = function (key, callback) {
var parsedKey = key.split(':'),
languageFile = parsedKey[0];
parsedKey = parsedKey[1];
translator.load(languageFile, function (languageData) {
if (callback) {
callback(languageData[parsedKey]);
}
return languageData[parsedKey];
});
};
/*
* TODO: Not fully converted to server side yet, ideally server should be able to parse whole templates on demand if necessary
* fix: translator.load should determine if server side and immediately return appropriate language file.
*/
translator.translate = function (data, callback) {
var keys = data.match(/\[\[.*?\]\]/g),
loading = 0;
function insertLanguage(text, key, value, variables) {
if (value) {
for (var i = 1, ii = variables.length; i < ii; i++) {
var variable = variables[i].replace(']]', '');
value = value.replace('%' + i, variable);
}
text = text.replace(key, value);
}
return text;
}
for (var key in keys) {
if (keys.hasOwnProperty(key)) {
var variables = keys[key].split(/[,][?\s+]/);
var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':'),
languageFile = parsedKey[0];
parsedKey = parsedKey[1].split(',')[0];
if (files.loaded[languageFile]) {
data = insertLanguage(data, keys[key], files.loaded[languageFile][parsedKey], variables);
} else {
loading++;
(function (languageKey, parsedKey) {
translator.load(languageFile, function (languageData) {
data = insertLanguage(data, languageKey, languageData[parsedKey], variables);
loading--;
checkComplete();
});
}(keys[key], parsedKey));
}
}
}
checkComplete();
function checkComplete() {
if (loading === 0) {
callback(data);
}
}
};
translator.load = function (filename, callback) {
if (isServer === true) {
if (callback) {
callback(files.loaded[filename]);
}
return files.loaded[filename];
}
if (files.loaded[filename] && !files.loading[filename]) {
if (callback) {
callback(files.loaded[filename]);
}
} else if (files.loading[filename]) {
if (callback) {
files.callbacks[filename] = files.callbacks[filename] || [];
files.callbacks[filename].push(callback);
}
} else {
var timestamp = new Date().getTime(); //debug
files.loading[filename] = true;
jQuery.getJSON(RELATIVE_PATH + '/language/' + DEFAULT_LANGUAGE + '/' + filename + '.json?v=' + timestamp, function (language) {
files.loaded[filename] = language;
if (callback) {
callback(language);
}
while (files.callbacks[filename] && files.callbacks[filename].length) {
files.callbacks[filename].pop()(language);
}
files.loading[filename] = false;
});
}
};
translator.loadServer = function () {
isServer = true;
var utils = require('./utils.js'),
path = require('path'),
fs = require('fs');
utils.walk(path.join(__dirname, '../../', 'public/language/' + DEFAULT_LANGUAGE), function (err, data) {
var loaded = data.length;
for (var d in data) {
if (data.hasOwnProperty(d)) {
files.loaded[path.basename(data[d]).replace('.json', '')] = require(data[d]);
}
}
});
};
if ('undefined' !== typeof window) {
window.translator = module.exports;
}
})('undefined' === typeof module ? {
module: {
exports: {}
}
} : module);

View File

@@ -1,4 +1,4 @@
(function (module) { (function(module) {
var utils, fs; var utils, fs;
@@ -10,15 +10,18 @@
module.exports = utils = { module.exports = utils = {
generateUUID: function() { generateUUID: function() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) { return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8); var r = Math.random() * 16 | 0,
v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16); return v.toString(16);
}); });
}, },
//Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search //Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
walk: function(dir, done) { walk: function(dir, done) {
var main_dir = global.configuration.ROOT_DIRECTORY + '/public/templates/'; var results = [],
var results = []; path = require('path'),
main_dir = path.join(__dirname, '..', 'templates');
fs.readdir(dir, function(err, list) { fs.readdir(dir, function(err, list) {
if (err) return done(err); if (err) return done(err);
var pending = list.length; var pending = list.length;
@@ -32,7 +35,7 @@
if (!--pending) done(null, results); if (!--pending) done(null, results);
}); });
} else { } else {
results.push(file.replace(main_dir, '').replace('.tpl', '')); results.push(file.replace(main_dir + '/', '').replace('.tpl', ''));
if (!--pending) done(null, results); if (!--pending) done(null, results);
} }
}); });
@@ -72,7 +75,7 @@
// remove accents, swap ñ for n, etc // remove accents, swap ñ for n, etc
var from = "àáäâèéëêìíïîıòóöôùúüûñçşğ·/_,:;"; var from = "àáäâèéëêìíïîıòóöôùúüûñçşğ·/_,:;";
var to = "aaaaeeeeiiiiioooouuuuncsg------"; var to = "aaaaeeeeiiiiioooouuuuncsg------";
for (var i=0, l=from.length ; i<l ; i++) { for (var i = 0, l = from.length; i < l; i++) {
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i)); str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
} }
@@ -91,11 +94,11 @@
}, },
isUserNameValid: function(name) { isUserNameValid: function(name) {
return (name && name !== "" && (/^[a-zA-Z0-9 _-]{3,14}$/.test(name))); return (name && name !== "" && (/^[a-zA-Z0-9 _-]+$/.test(name)));
}, },
isPasswordValid: function(password) { isPasswordValid: function(password) {
return password && password.indexOf(' ') === -1 && password.length > 5; return password && password.indexOf(' ') === -1;
}, },
// Blatently stolen from: http://phpjs.org/functions/strip_tags/ // Blatently stolen from: http://phpjs.org/functions/strip_tags/
@@ -104,7 +107,7 @@
var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi, var tags = /<\/?([a-z][a-z0-9]*)\b[^>]*>/gi,
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi; commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) { return input.replace(commentsAndPhpTags, '').replace(tags, function($0, $1) {
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : ''; return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
}); });
}, },
@@ -112,10 +115,10 @@
buildMetaTags: function(tagsArr) { buildMetaTags: function(tagsArr) {
var tags = '', var tags = '',
tag; tag;
for(var x=0,numTags=tagsArr.length;x<numTags;x++) { for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
if (tags.length > 0) tags += "\n\t"; if (tags.length > 0) tags += "\n\t";
tag = '<meta'; tag = '<meta';
for(y in tagsArr[x]) { for (y in tagsArr[x]) {
tag += ' ' + y + '="' + tagsArr[x][y] + '"'; tag += ' ' + y + '="' + tagsArr[x][y] + '"';
} }
tag += ' />'; tag += ' />';
@@ -126,31 +129,65 @@
return tags; return tags;
}, },
refreshTitle: function() { refreshTitle: function(url) {
if (!url) {
var a = document.createElement('a'); var a = document.createElement('a');
a.href = document.location; a.href = document.location;
socket.emit('api:meta.buildTitle', a.pathname.slice(1), function(title) { url = a.pathname.slice(1);
document.title = title; }
var notificationIcon;
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
document.title = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
notificationIcon = notificationIcon || document.querySelector('.notifications a i');
if (numNotifications > 0 && notificationIcon) notificationIcon.className = 'icon-circle active';
}); });
jQuery.getJSON(RELATIVE_PATH + '/api/unread/total', function(data) {
var badge = jQuery('#numUnreadBadge');
badge.html(data.count > 20 ? '20+' : data.count);
if (data.count > 0) {
badge
.removeClass('badge-inverse')
.addClass('badge-important')
} else {
badge
.removeClass('badge-important')
.addClass('badge-inverse')
}
});
},
isRelativeUrl: function(url) {
var firstChar = url.slice(0, 1);
return (firstChar === '.' || firstChar === '/');
} }
} }
if (!String.prototype.trim) { if (!String.prototype.trim) {
String.prototype.trim=function(){return this.replace(/^\s+|\s+$/g, '');}; String.prototype.trim = function() {
return this.replace(/^\s+|\s+$/g, '');
};
} }
if (!String.prototype.ltrim) { if (!String.prototype.ltrim) {
String.prototype.ltrim=function(){return this.replace(/^\s+/,'');}; String.prototype.ltrim = function() {
return this.replace(/^\s+/, '');
};
} }
if (!String.prototype.rtrim) { if (!String.prototype.rtrim) {
String.prototype.rtrim=function(){return this.replace(/\s+$/,'');}; String.prototype.rtrim = function() {
return this.replace(/\s+$/, '');
};
} }
if (!String.prototype.fulltrim) { if (!String.prototype.fulltrim) {
String.prototype.fulltrim=function(){return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');}; String.prototype.fulltrim = function() {
return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g, '').replace(/\s+/g, ' ');
};
} }
@@ -158,4 +195,8 @@
window.utils = module.exports; window.utils = module.exports;
} }
})('undefined' === typeof module ? {module:{exports:{}}} : module) })('undefined' === typeof module ? {
module: {
exports: {}
}
} : module)

View File

@@ -1,4 +1,4 @@
<div class="alert alert-error"> <div class="alert alert-danger">
<strong>Access Denied</strong> <strong>[[global:403.title]]</strong>
<p>You seem to have stumbled upon a page that you do not have access to. Perhaps you should <a href="/login">try logging in?</a></p> <p>[[global:403.message]]</p>
</div> </div>

View File

@@ -1,4 +1,4 @@
<div class="alert alert-error"> <div class="alert alert-danger">
<strong>Not found</strong> <strong>[[global:404.title]]</strong>
<p>You seem to have stumbled upon a page that does not exist. Return to the <a href="/">home page</a></p> <p>[[global:404.message]]</p>
</div> </div>

View File

@@ -1,37 +1,30 @@
<div class="well"> <div class="well account">
<div class="account-username-box" data-userslug="{userslug}">
<div class="account-username-box">
<span class="account-username"> <span class="account-username">
<a href="/users/{userslug}">{username}</a> <a href="/user/{userslug}">{username}</a>
</span> </span>
<div class="account-sub-links inline-block pull-right">
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
</div>
</div> </div>
<div class="row">
<div class="row-fluid"> <div class="col-md-2 account-block" style="text-align: center; margin-bottom:20px;">
<div class="span2" style="text-align: center; margin-bottom:20px;">
<div class="account-picture-block"> <div class="account-picture-block">
<img src="{picture}" class="user-profile-picture img-polaroid"/> <img src="{picture}" class="user-profile-picture img-thumbnail"/>
</div> </div>
<div class="account-online-status"> <div class="account-online-status">
<span><i class="icon-circle-blank"></i> <span>offline</span></span> <span><i class="icon-circle-blank"></i> <span>offline</span></span>
</div> </div>
<div class="{show_banned}">
<span class="label label-danger">banned</span>
</div>
<div id="user-actions"> <div id="user-actions">
<a id="follow-btn" href="#" class="btn hide">Follow</a> <a id="follow-btn" href="#" class="btn btn-default">Follow</a>
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a>
</div> </div>
</div> </div>
<div class="span4"> <div class="col-md-4">
<h4>profile</h4>
<div class="inline-block"> <div class="inline-block">
<div class="account-bio-block"> <div class="account-bio-block">
<span class="account-bio-label">email</span><i class="icon-eye-close {emailClass}" title="Email hidden"></i> <span class="account-bio-label">email</span><i class="icon-eye-close {emailClass}" title="Email hidden"></i>
@@ -54,24 +47,28 @@
<span>{age}</span> <span>{age}</span>
<br/> <br/>
<hr/> <hr/>
<span class="account-bio-label">member for</span> <span class="account-bio-label">joined</span>
<span>{joindate}</span> <span class="timeago" title="{joindate}"></span>
<br/>
<span class="account-bio-label">profile views</span>
<span class="formatted-number">{profileviews}</span>
<br/> <br/>
<span class="account-bio-label">reputation</span> <span class="account-bio-label">reputation</span>
<span id='reputation'>{reputation}</span> <span class="formatted-number">{reputation}</span>
<br/> <br/>
<span class="account-bio-label">posts</span> <span class="account-bio-label">posts</span>
<span id='postcount'>{postcount}</span> <span class="formatted-number">{postcount}</span>
<br/> <br/>
<span class="account-bio-label">followers</span> <span class="account-bio-label">followers</span>
<span>{followerCount}</span> <span class="formatted-number">{followerCount}</span>
<br/> <br/>
<span class="account-bio-label">following</span> <span class="account-bio-label">following</span>
<span>{followingCount}</span> <span class="formatted-number">{followingCount}</span>
<br/> <br/>
<hr/> <hr/>
@@ -83,12 +80,11 @@
</div> </div>
</div> </div>
<div class="span6 user-recent-posts"> <div class="col-md-6 user-recent-posts">
<h4>recent posts </h4>
<!-- BEGIN posts --> <!-- BEGIN posts -->
<div class="topic-row img-polaroid clearfix" topic-url="topic/{posts.tid}/#{posts.pid}"> <div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
<span>{posts.content}</span> <span>{posts.content}</span>
<span class="pull-right">{posts.relativeTime} ago</span> <span class="pull-right timeago" title="{posts.relativeTime}"></span>
</div> </div>
<!-- END posts --> <!-- END posts -->
</div> </div>

View File

@@ -1,132 +1,132 @@
<div class="well"> <div class="well">
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Change Picture" aria-hidden="true">
<!-- Change Picture Modal --> <div class="modal-dialog">
<div id="change-picture-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Change Picture</h3> <h3 id="myModalLabel">Change Picture</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<div id="gravatar-box"> <div id="gravatar-box">
<img id="user-gravatar-picture" src="" class="img-polaroid user-profile-picture"> <img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">Gravatar</span> <span class="user-picture-label">Gravatar</span>
<i class='icon-ok icon-2x'></i> <i class='icon-ok icon-2x'></i>
</div> </div>
<br/> <br/>
<div id="uploaded-box"> <div id="uploaded-box">
<img id="user-uploaded-picture" src="" class="img-polaroid user-profile-picture"> <img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture">
<span class="user-picture-label">Uploaded picture</span> <span class="user-picture-label">Uploaded picture</span>
<i class='icon-ok icon-2x'></i> <i class='icon-ok icon-2x'></i>
</div> </div>
<a id="uploadPictureBtn" href="#">Upload new picture</a> <a id="uploadPictureBtn" href="#">Upload new picture</a>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
<button id="savePictureChangesBtn" class="btn btn-primary">Save changes</button> <button id="savePictureChangesBtn" class="btn btn-primary">Save changes</button>
</div> </div>
</div> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<!-- Upload picture modal--> <div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
<div id="upload-picture-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div class="modal-dialog">
<div class="modal-content">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
<h3 id="myModalLabel">Upload Picture</h3> <h3 id="myModalLabel">Upload Picture</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<form id="uploadForm" action="{relative_path}/user/uploadpicture" method="post" enctype="multipart/form-data">
<form id="uploadForm" action="{relative_path}/users/uploadpicture" method="post" enctype="multipart/form-data"> <div class="form-group">
<input id="userPhotoInput" type="file" name="userPhoto" /> <label for="userPhoto">Upload a picture</label>
<input type="file" id="userPhotoInput" name="userPhoto">
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
</div>
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" /> <input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
</form> </form>
<div id="upload-progress-box" class="progress progress-striped active hide"> <div id="upload-progress-box" class="progress progress-striped">
<div id="upload-progress-bar" class="bar" style="width: 0%;"></div> <div id="upload-progress-bar" class="progress-bar progress-bar-success" role="progressbar" aria-valuenow="0" aria-valuemin="0">
<span class="sr-only"> success</span>
</div>
</div> </div>
<div id="alert-status" class="alert hide"></div> <div id="alert-status" class="alert alert-info hide"></div>
<div id="alert-success" class="alert alert-success hide"></div> <div id="alert-success" class="alert alert-success hide"></div>
<div id="alert-error" class="alert alert-error hide"></div> <div id="alert-error" class="alert alert-danger hide"></div>
</div> </div>
<div class="modal-footer"> <div class="modal-footer">
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button> <button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button> <button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
</div> </div>
</div> </div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div class="account-username-box"> <div class="account-username-box" data-userslug="{userslug}">
<span class="account-username"> <span class="account-username">
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
<a href="/users/{userslug}/edit">edit</a> <a href="/user/{userslug}/edit">edit</a>
</span> </span>
<div class="account-sub-links inline-block pull-right">
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
</div>
</div> </div>
<div class="row-fluid"> <div class="row">
<div class="span2" style="text-align: center; margin-bottom:20px;"> <div class="col-md-2" style="text-align: center; margin-bottom:20px;">
<div class="account-picture-block text-center"> <div class="account-picture-block text-center">
<img id="user-current-picture" class="user-profile-picture img-polaroid" src="{picture}" /><br/> <img id="user-current-picture" class="user-profile-picture img-thumbnail" src="{picture}" /><br /><br />
<a id="changePictureBtn" href="#" class="btn btn-primary">change picture</a> <a id="changePictureBtn" href="#" class="btn btn-primary">change picture</a>
</div> </div>
</div> </div>
<div class="span10"> <div class="col-md-5">
<div class="inline-block"> <div>
<form class='form-horizontal'> <form class='form-horizontal'>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputEmail">Email</label> <label class="control-label" for="inputEmail">Email</label>
<div class="controls"> <div class="controls">
<input type="text" id="inputEmail" placeholder="Email" value="{email}"> <input class="form-control" type="text" id="inputEmail" placeholder="Email" value="{email}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputFullname">Full Name</label> <label class="control-label" for="inputFullname">Full Name</label>
<div class="controls"> <div class="controls">
<input type="text" id="inputFullname" placeholder="Full Name" value="{fullname}"> <input class="form-control" type="text" id="inputFullname" placeholder="Full Name" value="{fullname}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputWebsite">Website</label> <label class="control-label" for="inputWebsite">Website</label>
<div class="controls"> <div class="controls">
<input type="text" id="inputWebsite" placeholder="http://website.com" value="{website}"> <input class="form-control" type="text" id="inputWebsite" placeholder="http://website.com" value="{website}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputLocation">Location</label> <label class="control-label" for="inputLocation">Location</label>
<div class="controls"> <div class="controls">
<input type="text" id="inputLocation" placeholder="Location" value="{location}"> <input class="form-control" type="text" id="inputLocation" placeholder="Location" value="{location}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputBirthday">Birthday</label> <label class="control-label" for="inputBirthday">Birthday</label>
<div class="controls"> <div class="controls">
<input type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}"> <input class="form-control" type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputSignature">Signature</label> <label class="control-label" for="inputSignature">Signature</label>
<div class="controls"> <div class="controls">
<textarea id="inputSignature" placeholder="max 150 chars" rows="5">{signature}</textarea> <textarea class="form-control" id="inputSignature" placeholder="max 150 chars" rows="5">{signature}</textarea>
</div> </div>
</div> </div>
<input type="hidden" id="inputUID" value="{uid}"> <input type="hidden" id="inputUID" value="{uid}"><br />
<div class="form-actions"> <div class="form-actions">
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a> <a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
@@ -134,29 +134,36 @@
</form> </form>
</div> </div>
<div class="inline-block" style="vertical-align:top;">
<hr/>
</div>
<div class="col-md-5">
<div style="vertical-align:top;">
<form class='form-horizontal'> <form class='form-horizontal'>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputCurrentPassword">Current Password</label> <label class="control-label" for="inputCurrentPassword">Current Password</label>
<div class="controls"> <div class="controls">
<input type="password" id="inputCurrentPassword" placeholder="Current Password" value=""> <input class="form-control" type="password" id="inputCurrentPassword" placeholder="Current Password" value="">
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputNewPassword">Password</label> <label class="control-label" for="inputNewPassword">Password</label>
<div class="controls"> <div class="controls">
<input type="password" id="inputNewPassword" placeholder="New Password" value=""><br/><span id="password-notify" class="label label-important"></span> <input class="form-control" type="password" id="inputNewPassword" placeholder="New Password" value="">
<div id="password-notify" class="alert alert-danger hide"></div>
</div> </div>
</div> </div>
<div class="control-group"> <div class="control-group">
<label class="control-label" for="inputNewPasswordAgain">Confirm Password</label> <label class="control-label" for="inputNewPasswordAgain">Confirm Password</label>
<div class="controls"> <div class="controls">
<input type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value=""><br/><span id="password-confirm-notify" class="label label-important"></span> <input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value="">
<div id="password-confirm-notify" class="alert alert-danger hide"></div>
</div> </div>
</div> </div>
<br/>
<div class="form-actions"> <div class="form-actions">
<a id="changePasswordBtn" href="#" class="btn btn-primary">Change Password</a> <a id="changePasswordBtn" href="#" class="btn btn-primary">Change Password</a>
</div> </div>
@@ -171,4 +178,5 @@
<input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" /> <input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
<input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" /> <input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script>

View File

@@ -1,28 +1,24 @@
<div class="well"> <div class="well">
<div class="account-username-box"> <div class="account-username-box" data-userslug="{userslug}">
<span class="account-username"> <span class="account-username">
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
<a href="/users/{userslug}/settings">settings</a> <a href="/user/{userslug}/settings">settings</a>
</span> </span>
<div class="account-sub-links inline-block pull-right">
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
</div>
</div> </div>
<div class="row-fluid"> <div class="row">
<div class="span6"> <div class="col-md-6">
<h4>privacy</h4> <h4>privacy</h4>
<label class="checkbox"> <div class="checkbox">
<label>
<input id="showemailCheckBox" type="checkbox" {showemail}> Show my email <input id="showemailCheckBox" type="checkbox" {showemail}> Show my email
</label> </label>
</div> </div>
</div>
<div class="span6"> <div class="col-md-6">
</div> </div>
</div> </div>
@@ -31,4 +27,5 @@
</div> </div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/accountsettings.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/accountsettings.js"></script>

File diff suppressed because one or more lines are too long

View File

@@ -2,20 +2,25 @@
<hr /> <hr />
<form> <form>
<div class="alert alert-notify"> <div class="alert alert-warning">
<p> <p>
Create a <strong>Facebook Application</strong> via the Create a <strong>Facebook Application</strong> via the
<a href="https://developers.facebook.com/">Facebook Developers Page</a> and <a href="https://developers.facebook.com/">Facebook Developers Page</a> and
then paste your application details here. then paste your application details here.
</p> </p>
<br /> <br />
<input type="text" data-field="social:facebook:app_id" title="Application ID" class="input-medium" placeholder="App ID"><br /> <input type="text" data-field="social:facebook:app_id" title="Application ID" class="form-control input-lg" placeholder="App ID"><br />
<input type="text" data-field="social:facebook:secret" title="Application Secret" class="input-large" placeholder="App Secret"><br /> <input type="text" data-field="social:facebook:secret" title="Application Secret" class="form-control input-md" placeholder="App Secret"><br />
</div> </div>
</form> </form>
<button class="btn btn-large btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare(); nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
</script> </script>

View File

@@ -1,6 +1,9 @@
</div> </div>
</div> </div>
<div id="alert_window"></div>
<div id="footer" class="container" style="padding-top: 50px; display:none;"> <div id="footer" class="container" style="padding-top: 50px; display:none;">
<footer class="footer">Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.com">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></footer> <footer class="footer">Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.com">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></footer>
</div> </div>

View File

@@ -2,20 +2,25 @@
<hr /> <hr />
<form> <form>
<div class="alert alert-notify"> <div class="alert alert-warning">
<p> <p>
Create a <strong>Google Application</strong> via the Create a <strong>Google Application</strong> via the
<a href="https://code.google.com/apis/console/">API Console</a> and then paste <a href="https://code.google.com/apis/console/">API Console</a> and then paste
your application details here. your application details here.
</p> </p>
<br /> <br />
<input type="text" data-field="social:google:id" title="Client ID" class="input-xxlarge" placeholder="Client ID"><br /> <input type="text" data-field="social:google:id" title="Client ID" class="form-control input-lg" placeholder="Client ID"><br />
<input type="text" data-field="social:google:secret" title="Client Secret" class="input-large" placeholder="Client Secret"><br /> <input type="text" data-field="social:google:secret" title="Client Secret" class="form-control" placeholder="Client Secret"><br />
</div> </div>
</form> </form>
<button class="btn btn-large btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare(); nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
</script> </script>

View File

@@ -0,0 +1,100 @@
<h1>Groups</h1>
<hr />
<div class="groups">
<ul id="groups-list">
<!-- BEGIN groups -->
<li data-gid="{groups.gid}">
<div class="row">
<div class="col-lg-8">
<h2>{groups.name}</h2>
<p>{groups.description}</p>
<div class="btn-group">
<button class="btn btn-default" data-action="members">Members</button>
<button class="btn btn-danger" data-action="delete">Delete Group</button>
</div>
</div>
<div class="col-lg-4">
<ul class="pull-right members">
<!-- BEGIN members -->
<li data-uid="{groups.members.uid}" title="{groups.members.username}"><img src="{groups.members.picture}" /></li>
<!-- END members -->
</ul>
</div>
</div>
</li>
<!-- END groups -->
</ul>
<div class="text-center">
<button class="btn btn-primary" id="create">New Group</button>
</div>
<div class="modal fade" id="create-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Create Group</h4>
</div>
<div class="modal-body">
<div class="alert alert-danger hide" id="create-modal-error"></div>
<form>
<div class="form-group">
<label for="group-name">Group Name</label>
<input type="text" class="form-control" id="create-group-name" placeholder="Group Name" />
</div>
<div class="form-group">
<label for="group-name">Description</label>
<input type="text" class="form-control" id="create-group-desc" placeholder="A short description about your group" />
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
<button type="button" class="btn btn-primary" id="create-modal-go">Create</button>
</div>
</div>
</div>
</div>
<div class="modal fade" id="group-details-modal">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h4 class="modal-title">Manage Group</h4>
</div>
<div class="modal-body">
<div class="alert alert-danger hide" id="create-modal-error"></div>
<form>
<div class="form-group">
<label for="group-name">Group Name</label>
<input type="text" class="form-control" id="change-group-name" placeholder="Group Name" />
</div>
<div class="form-group">
<label for="group-name">Description</label>
<input type="text" class="form-control" id="change-group-desc" placeholder="A short description about your group" />
</div>
<div class="form-group">
<label>Members</label>
<p>Click on a user to remove them from the group</p>
<ul class="members current_members" id="group-details-members"></ul>
</div>
<div class="form-group">
<label for="add-member">Add User to Group</label>
<input type="text" class="form-control" id="group-details-search" placeholder="Search Users" />
<ul class="members" id="group-details-search-results"></ul>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">Close</button>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/groups.js"></script>

View File

@@ -16,9 +16,12 @@
<script type="text/javascript" src="{relative_path}/socket.io/socket.io.js"></script> <script type="text/javascript" src="{relative_path}/socket.io/socket.io.js"></script>
<script type="text/javascript" src="{relative_path}/src/app.js"></script> <script type="text/javascript" src="{relative_path}/src/app.js"></script>
<script type="text/javascript" src="{relative_path}/src/templates.js"></script> <script type="text/javascript" src="{relative_path}/src/templates.js"></script>
<script type="text/javascript" src="{relative_path}/src/translator.js"></script>
<script type="text/javascript" src="{relative_path}/src/ajaxify.js"></script> <script type="text/javascript" src="{relative_path}/src/ajaxify.js"></script>
<script src="{relative_path}/vendor/jquery/js/jquery.timeago.js"></script>
<script src="{relative_path}/vendor/requirejs/require.js"></script> <script src="{relative_path}/vendor/requirejs/require.js"></script>
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script> <script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
<script> <script>
require.config({ require.config({
baseUrl: "{relative_path}/src/modules", baseUrl: "{relative_path}/src/modules",
@@ -27,22 +30,25 @@
</script> </script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css"> <link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.10.3/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script> <script type="text/javascript" src="http://code.jquery.com/ui/1.10.3/jquery-ui.js"></script>
<link rel="stylesheet" type="text/css" href="{relative_path}/css/style.css" /> <script src="{relative_path}/src/utils.js"></script>
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
<link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" /> <link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" />
</head> </head>
<body class="admin"> <body class="admin">
<div class="navbar navbar-inverse navbar-fixed-top"> <div class="navbar navbar-inverse navbar-fixed-top">
<div class="navbar-inner"> <div class="container">
<div class="container-fluid"> <div class="navbar-header">
<a class="brand" href="/admin/index">NodeBB ACP</a> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<div class="nav-collapse collapse"> <a class="navbar-brand" href="/admin/index">NodeBB ACP</a>
<ul class="nav"> </div>
<div class="collapse navbar-collapse">
<ul class="nav navbar-nav">
<li> <li>
<a href="/" target="_blank"><i class="icon-book"></i> Forum</a> <a href="/" target="_blank"><i class="icon-book"></i> Forum</a>
</li> </li>
@@ -52,6 +58,9 @@
<li> <li>
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a> <a href="/admin/settings"><i class="icon-cogs"></i> Settings</a>
</li> </li>
<li>
<a href="#" id="reconnect"></a>
</li>
</ul> </ul>
<ul class="nav pull-right" id="right-menu"> <ul class="nav pull-right" id="right-menu">
<li><a href="/users" id="user_label"></a></li> <li><a href="/users" id="user_label"></a></li>
@@ -59,19 +68,17 @@
</div> </div>
</div> </div>
</div> </div>
</div>
<div id="alert_window"></div> <div class="container">
<div class="row">
<div class="container-fluid"> <div class="col-md-3">
<div class="row-fluid">
<div class="span3">
<div class="well sidebar-nav"> <div class="well sidebar-nav">
<ul class="nav nav-list"> <ul class="nav nav-list">
<li class="nav-header">NodeBB</li> <li class="nav-header">NodeBB</li>
<li class='active'><a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a></li> <li class='active'><a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a></li>
<li class=''><a href='{relative_path}/admin/categories/active'><i class='icon-folder-close-alt'></i> Categories</a></li> <li class=''><a href='{relative_path}/admin/categories/active'><i class='icon-folder-close-alt'></i> Categories</a></li>
<li class=''><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li> <li class=''><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li>
<li class=""><a href="{relative_path}/admin/groups"><i class="icon-group"></i> Groups</a></li>
<li class=''><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li> <li class=''><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li>
<li class=''><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li> <li class=''><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li>
<li class=''><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li> <li class=''><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li>
@@ -100,4 +107,4 @@
</ul> </ul>
</div><!--/.well --> </div><!--/.well -->
</div><!--/span--> </div><!--/span-->
<div class="span9" id="content"> <div class="col-md-9" id="content">

View File

@@ -1,14 +1,14 @@
<div class="hero-unit"> <div class="jumbotron">
<h1>Welcome to NodeBB</h1> <h1>Welcome to NodeBB</h1>
<br /> <br />
<p> <p>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-large"><i class="icon-comment"></i> NodeBB Forum</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-comment"></i> NodeBB Forum</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-large"><i class="icon-github-alt"></i> Get Plugins</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-github-alt"></i> Get Plugins</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-large"><i class="icon-github-alt"></i> Get Themes</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-github-alt"></i> Get Themes</a>
<a target="_blank" href="http://www.nodebb.org" class="btn btn-large"><i class="icon-twitter"></i> dcplabs</a> <a target="_blank" href="http://www.nodebb.org" class="btn btn-default btn-lg"><i class="icon-twitter"></i> dcplabs</a>
</p> </p>
<p><small>You are running <strong>NodeBB v{version}</strong>. This is where we will check to make sure your <strong>NodeBB</strong> is latest, etc.</small></p> <p><small>You are running <strong>NodeBB v{version}</strong>. Always make sure that your <strong>NodeBB</strong> is up to date for the latest security patches and bug fixes.</small></p>
</div> </div>

View File

@@ -1,7 +1,7 @@
<h1>MOTD</h1> <h1>MOTD</h1>
<hr />
<div class="alert motd"> <div class="alert alert-warning motd">
<p> <p>
The <strong>Message of the Day</strong> (MOTD) is typically a message shown to users when they first log into a forum or chat room. The <strong>Message of the Day</strong> (MOTD) is typically a message shown to users when they first log into a forum or chat room.
In NodeBB, the MOTD is present at the top of the forum homepage, and can be customized much like a header. In NodeBB, the MOTD is present at the top of the forum homepage, and can be customized much like a header.
@@ -9,14 +9,25 @@
<p> <p>
You can enter either full HTML or Markdown text. You can enter either full HTML or Markdown text.
</p> </p>
<textarea placeholder="Welcome to NodeBB!" data-field="motd" rows="10"></textarea> <br />
<textarea class="form-control" placeholder="Welcome to NodeBB!" data-field="motd" rows="10"></textarea>
<br />
<form class="form-inline"> <form class="form-inline">
<label class="checkbox" for="show_motd"><input type="checkbox" id="show_motd" data-field="show_motd" /> Show the Message of the Day</label> <div class="checkbox">
<label for="show_motd">
<input type="checkbox" id="show_motd" data-field="show_motd" /> Show the Message of the Day
</label>
</div>
</form> </form>
</div> </div>
<button class="btn btn-large btn-primary" id="save" checked>Save</button> <button class="btn btn-lg btn-primary" id="save" checked>Save</button>
<script> <script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare(); nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
</script> </script>

View File

@@ -13,9 +13,9 @@
<!-- END plugins --> <!-- END plugins -->
</ul> </ul>
<div class="alert"> <div class="alert alert-warning">
<p> <p>
<strong>Interesed in writing plugins for NodeBB?</strong> <strong>Interested in writing plugins for NodeBB?</strong>
</p> </p>
<p> <p>
Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>. Full documentation regarding plugin authoring can be found in the <a target="_blank" href="https://github.com/designcreateplay/NodeBB/wiki/Writing-Plugins-for-NodeBB">NodeBB Wiki</a>.

View File

@@ -2,64 +2,85 @@
<hr /> <hr />
<h3>General Settings</h3> <h3>General Settings</h3>
<div class="alert"> <div class="alert alert-warning">
<form> <form>
<label>Site Title</label> <label>Site Title</label>
<input type="text" placeholder="Your Community Name" data-field="title" /> <input class="form-control" type="text" placeholder="Your Community Name" data-field="title" /><br />
<label>Site Description</label> <label>Site Description</label>
<input type="text" class="input-xxlarge" placeholder="A short description about your community" data-field="description" /> <input type="text" class="form-control" placeholder="A short description about your community" data-field="description" /><br />
<label>Imgur Client ID</label>
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
<label>Maximum User Image Size</label>
<input type="text" class="form-control" placeholder="Maximum size of uploaded user images in kilobytes" data-field="maximumProfileImageSize" />
</form> </form>
</div> </div>
<form> <form>
<h3>Privilege Thresholds</h3> <h3>Privilege Thresholds</h3>
<div class="alert alert-notify"> <div class="alert alert-warning">
<p>Use <strong>privilege thresholds</strong> to manage how much reputation a user must gain to receive moderator access.</p><br /> <p>Use <strong>privilege thresholds</strong> to manage how much reputation a user must gain to receive moderator access.</p><br />
<strong>Manage Thread</strong><br /> <input type="text" class="" value="1000"><br /> <strong>Manage Thread</strong><br /> <input type="text" class="form-control" value="1000"><br />
<strong>Moderate Users</strong><br /> <input type="text" class="" value="10000"><br /> <strong>Moderate Users</strong><br /> <input type="text" class="form-control" value="10000"><br />
<strong>Create Pinned Topics</strong><br /> <input type="text" class="" value="100000"><br /> <strong>Create Pinned Topics</strong><br /> <input type="text" class="form-control" value="100000"><br />
</div> </div>
</form> </form>
<form> <form>
<h3>Email Settings</h3> <h3>Email Settings</h3>
<div class="alert alert-notify"> <div class="alert alert-warning">
<div> <div>
<p> <p>
<strong>Email Address</strong><br /> <strong>Email Address</strong><br />
The following email address refers to the email that the recipient will see in the "From" and "Reply To" fields. The following email address refers to the email that the recipient will see in the "From" and "Reply To" fields.
</p> </p>
<input type="text" class="input-large" data-field="email:from" placeholder="info@example.org" /> <input type="text" class="form-control input-lg" data-field="email:from" placeholder="info@example.org" /><br />
</div> </div>
<div> <div>
<p> <p>
<strong>SMTP Server Host</strong><br /> <strong>SMTP Server Host</strong><br />
(Default: <em>127.0.0.1</em>) (Default: <em>127.0.0.1</em>)
</p> </p>
<input type="text" class="input-medium" data-field="email:smtp:host" placeholder="127.0.0.1" /> <input type="text" class="form-control input-md" data-field="email:smtp:host" placeholder="127.0.0.1" /><br />
</div> </div>
<div> <div>
<p> <p>
<strong>SMTP Server Port</strong> <strong>SMTP Server Port</strong>
</p> </p>
<input type="text" class="input-mini" data-field="email:smtp:port" placeholder="25" /> <input type="text" class="form-control input-md" data-field="email:smtp:port" placeholder="25" /><br />
</div> </div>
</div> </div>
</form> </form>
<form>
<h3>User Settings</h3>
<div class="alert alert-warning">
<strong>Minimum Username Length</strong><br />
<input type="text" class="form-control" value="2" data-field="minimumUsernameLength"><br />
<strong>Maximum Username Length</strong><br />
<input type="text" class="form-control" value="16" data-field="maximumUsernameLength"><br />
<strong>Minimum Password Length</strong><br />
<input type="text" class="form-control" value="6" data-field="minimumPasswordLength"><br />
</div>
</form>
<form> <form>
<h3>Post Settings</h3> <h3>Post Settings</h3>
<div class="alert alert-notify"> <div class="alert alert-warning">
<strong>Post Delay</strong><br /> <input type="text" class="" value="10000" data-field="postDelay"><br /> <strong>Post Delay</strong><br /> <input type="text" class="form-control" value="10000" data-field="postDelay"><br />
<strong>Minimum Title Length</strong><br /> <input type="text" class="" value="3" data-field="minimumTitleLength"><br /> <strong>Minimum Title Length</strong><br /> <input type="text" class="form-control" value="3" data-field="minimumTitleLength"><br />
<strong>Minimum Post Length</strong><br /> <input type="text" class="" value="8" data-field="minimumPostLength"><br /> <strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br />
</div> </div>
</form> </form>
<button class="btn btn-large btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare(); nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
</script> </script>

View File

@@ -10,7 +10,7 @@ jQuery(document).ready(function () {
QUnit.init(); QUnit.init();
asyncTest( "Loading Categories", function() { asyncTest( "Loading Categories", function() {
jQuery.get(config.api_url + 'home', function(data) { jQuery.get(RELATIVE_PATH + '/api/home', function(data) {
ok( data.categories.length > 0, JSON.stringify(data.categories) ); ok( data.categories.length > 0, JSON.stringify(data.categories) );
start(); start();

View File

@@ -20,7 +20,7 @@
</ul> </ul>
<div class="text-center"> <div class="text-center">
<button id="topics_loadmore" class="btn btn-large">Load More Topics</button> <button id="topics_loadmore" class="btn btn-primary btn-lg">Load More Topics</button>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/admin/topics.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/admin/topics.js"></script>

View File

@@ -2,20 +2,25 @@
<hr /> <hr />
<form> <form>
<div class="alert alert-notify"> <div class="alert alert-warning">
<p> <p>
Create a <strong>Twitter Application</strong> via the Create a <strong>Twitter Application</strong> via the
<a href="https://dev.twitter.com/">Twitter Developers Page</a> and then <a href="https://dev.twitter.com/">Twitter Developers Page</a> and then
paste your application details here. paste your application details here.
</p> </p>
<br /> <br />
<input type="text" data-field="social:twitter:key" title="Consumer Key" class="input-large" placeholder="Consumer Key"><br /> <input type="text" data-field="social:twitter:key" title="Consumer Key" class="form-control input-lg" placeholder="Consumer Key"><br />
<input type="text" data-field="social:twitter:secret" title="Consumer Secret" class="input-xlarge" placeholder="Consumer Secret"> <input type="text" data-field="social:twitter:secret" title="Consumer Secret" class="form-control input-md" placeholder="Consumer Secret">
</div> </div>
</form> </form>
<button class="btn btn-large btn-primary" id="save">Save</button> <button class="btn btn-lg btn-primary" id="save">Save</button>
<script> <script>
var loadDelay = setInterval(function() {
if (nodebb_admin) {
nodebb_admin.prepare(); nodebb_admin.prepare();
clearInterval(loadDelay);
}
}, 500);
</script> </script>

View File

@@ -7,21 +7,21 @@
<li class=''><a href='/admin/users/search'>Search</a></li> <li class=''><a href='/admin/users/search'>Search</a></li>
</ul> </ul>
<br />
<div class="search {search_display} well"> <div class="search {search_display} well">
<input id="search-user" type="text" placeholder="Enter a username to search"/><br /> <input class="form-control" id="search-user" type="text" placeholder="Enter a username to search"/><br />
<i class="icon-spinner icon-spin none"></i> <i class="icon-spinner icon-spin none"></i>
<span id="user-notfound-notify" class="label label-important hide">User not found!</span><br/> <span id="user-notfound-notify" class="label label-danger hide">User not found!</span><br/>
</div> </div>
<ul id="users-container" class="users"> <ul id="users-container" class="users admin">
<!-- BEGIN users --> <!-- BEGIN users -->
<div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}"> <div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}" data-banned="{users.banned}">
<a href="/users/{users.userslug}"> <a href="/user/{users.userslug}">
<img src="{users.picture}" class="img-polaroid"/> <img src="{users.picture}" class="img-thumbnail"/>
</a> </a>
<br/> <br/>
<a href="/users/{users.userslug}">{users.username}</a> <a href="/user/{users.userslug}">{users.username}</a>
<br/> <br/>
<div title="reputation"> <div title="reputation">
<span id='reputation'>{users.reputation}</span> <span id='reputation'>{users.reputation}</span>
@@ -32,17 +32,14 @@
<i class='icon-pencil'></i> <i class='icon-pencil'></i>
</div> </div>
<div> <div>
<a href="#" class="btn admin-btn">Admin</a> <a href="#" class="btn btn-default ban-btn">Ban</a>
</div>
<div>
<a href="#" class="btn delete-btn btn-danger">Delete</a>
</div> </div>
</div> </div>
<!-- END users --> <!-- END users -->
</ul> </ul>
<div class="text-center {loadmore_display}"> <div class="text-center {loadmore_display}">
<button id="load-more-users-btn" class="btn">Load More</button> <button id="load-more-users-btn" class="btn btn-primary">Load More</button>
</div> </div>
<input type="hidden" template-variable="yourid" value="{yourid}" /> <input type="hidden" template-variable="yourid" value="{yourid}" />

View File

@@ -1,95 +1,92 @@
<div class="container"> <ol class="breadcrumb">
<ul class="breadcrumb">
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="/" itemprop="url"><span itemprop="title">Home</span></a> <a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
<span class="divider">/</span>
</li> </li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">{category_name}</span> <span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="icon-rss-sign"></i></a></span>
</li> </li>
<div id="category_active_users"></div> <div id="category_active_users"></div>
</ul> </ol>
</div>
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
<strong>There are no topics in this category.</strong><br />
Why don't you try posting one?
</div>
<div> <div>
<button id="new_post" class="btn btn-primary btn-lg {show_topic_button}">[[category:new_topic_button]]</button>
<button id="new_post" class="btn btn-primary btn-large {show_topic_button}">New Topic</button>
<div class="inline-block pull-right"> <div class="inline-block pull-right">
<a target="_blank" href="../{category_id}.rss"><i class="icon-rss-sign icon-2x"></i></a>&nbsp;
<a href="#" id="facebook-share"><i class="icon-facebook-sign icon-2x"></i></a>&nbsp; <a href="#" id="facebook-share"><i class="icon-facebook-sign icon-2x"></i></a>&nbsp;
<a href="#" id="twitter-intent"><i class="icon-twitter-sign icon-2x"></i></a>&nbsp; <a href="#" id="twitter-intent"><i class="icon-twitter-sign icon-2x"></i></a>&nbsp;
<a href="#" id="google-share"><i class="icon-google-plus-sign icon-2x"></i></a>&nbsp; <a href="#" id="google-share"><i class="icon-google-plus-sign icon-2x"></i></a>&nbsp;
</div> </div>
</div> </div>
<hr class="{show_sidebar}" /> <hr/>
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
[[category:no_topics]]
</div>
<div class="category row"> <div class="category row">
<div class="{topic_row_size}"> <div class="{topic_row_size}">
<ul id="topics-container"> <ul id="topics-container">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<a href="../../topic/{topics.slug}"><li class="category-item {topics.deleted-class}">
<div class="row-fluid"> <li class="category-item {topics.deleted-class}">
<!-- <div class="span1 thread-rating hidden-phone hidden-tablet"> <div class="row">
<span> <div class="col-md-12 topic-row">
<i class="icon-star icon-3x"></i><br /> <div class="latest-post visible-lg visible-md">
38 <a href="../../topic/{topics.slug}#{topics.teaser_pid}">
</span>
</div> -->
<div class="span12 topic-row">
<div class="latest-post visible-desktop">
<div class="pull-right"> <div class="pull-right">
<img style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" /> <img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
<p><strong>{topics.teaser_username}</strong>: {topics.teaser_text}</p> <p>{topics.teaser_text}</p>
<span>posted {topics.teaser_timestamp} ago</span> <p class="meta">
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
</p>
</div> </div>
</a>
</div> </div>
<a href="../../topic/{topics.slug}">
<div> <div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3> <h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small> <small>
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong> <strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
Posted {topics.relativeTime} ago by Posted <span class="timeago" title="{topics.relativeTime}"></span> by
<strong>{topics.username}</strong>. <strong>{topics.username}</strong>.
</small> </small>
</div> </div>
</a>
</div> </div>
</div> </div>
</li></a> </li>
<!-- END topics --> <!-- END topics -->
</ul> </ul>
</div> </div>
<div class="span3 {show_sidebar} category-sidebar mobile-sidebar"> <div class="col-md-3 {show_sidebar} category-sidebar">
<div class="sidebar-block img-polaroid"> <div class="sidebar-block img-thumbnail">
<div class="block-header"> <div class="block-header">
Recent Replies [[category:sidebar.recent_replies]]
</div> </div>
<div class="block-content recent-replies"> <div class="block-content recent-replies">
<ul id="category_recent_replies"></ul> <ul id="category_recent_replies"></ul>
</div> </div>
</div> </div>
<div class="sidebar-block img-polaroid"> <div class="sidebar-block img-thumbnail">
<div class="block-header"> <div class="block-header">
Active Participants [[category:sidebar.active_participants]]
</div> </div>
<div class="block-content"> <div class="block-content">
<!-- BEGIN active_users --> <!-- BEGIN active_users -->
<a href="/users/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-polaroid" /></a> <a href="/user/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-rounded" /></a>
<!-- END active_users --> <!-- END active_users -->
</div> </div>
</div> </div>
<div class="sidebar-block img-polaroid {moderator_block_class}"> <div class="sidebar-block img-thumbnail {moderator_block_class}">
<div class="block-header"> <div class="block-header">
Moderators [[category:sidebar.moderators]]
</div> </div>
<div class="block-content"> <div class="block-content">
<!-- BEGIN moderators --> <!-- BEGIN moderators -->
<a href="/users/{moderators.userslug}"><img title="{moderators.username}" src="{moderators.picture}" class="img-polaroid" /></a> <a href="/user/{moderators.userslug}"><img title="{moderators.username}" src="{moderators.picture}" class="img-rounded" /></a>
<!-- END moderators --> <!-- END moderators -->
</div> </div>
</div> </div>

View File

@@ -1,37 +1,42 @@
{ {
"custom_mapping": { "custom_mapping": {
"admin/testing/categories[^]*": "admin/testing/categories", "admin/testing/categories.*": "admin/testing/categories",
"admin/topics[^]*": "admin/topics", "admin/topics.*": "admin/topics",
"admin/categories[^]*": "admin/categories", "admin/categories.*": "admin/categories",
"admin/users[^]*": "admin/users", "admin/users.*": "admin/users",
"admin/redis[^]*": "admin/redis", "admin/redis.*": "admin/redis",
"admin/index[^]*": "admin/index", "admin/index.*": "admin/index",
"admin/themes[^]*": "admin/themes", "admin/themes.*": "admin/themes",
"admin/plugins[^]*": "admin/plugins", "admin/plugins.*": "admin/plugins",
"admin/settings[^]*": "admin/settings", "^admin/settings.*": "admin/settings",
"admin/twitter[^]*": "admin/twitter", "admin/twitter.*": "admin/twitter",
"admin/facebook[^]*": "admin/facebook", "admin/facebook.*": "admin/facebook",
"admin/gplus[^]*": "admin/gplus", "admin/gplus.*": "admin/gplus",
"admin/motd/?$": "admin/motd", "admin/motd/?$": "admin/motd",
"admin/groups/?$": "admin/groups",
"install/?$": "install/mail", "install/?$": "install/mail",
"install/mail/?": "install/mail", "install/mail/?": "install/mail",
"install/social/?": "install/social", "install/social/?": "install/social",
"install/privileges/?": "install/privileges", "install/privileges/?": "install/privileges",
"users-sort-posts": "users", "users/sort-posts": "users",
"users-latest": "users", "users/latest": "users",
"users-sort-reputation": "users", "users/sort-reputation": "users",
"users-search": "users", "users/search": "users",
"users[^]*edit": "accountedit", "user.*edit": "accountedit",
"users[^]*following": "following", "user.*following": "following",
"users[^]*followers": "followers", "user.*followers": "followers",
"users[^]*settings": "accountsettings", "user.*settings": "accountsettings",
"users/[^]*": "account", "user.*favourites": "favourites",
"user/.*": "account",
"recent": "recent", "recent": "recent",
"unread": "unread", "unread": "unread",
"popular": "category", "popular": "category",
"active": "category", "active": "category",
"search": "search" "search": "search",
"reset/.*": "reset_code",
"reset": "reset"
}, },
"force_refresh": { "force_refresh": {
"logout": true "logout": true

View File

@@ -0,0 +1,27 @@
<div class="well favourites">
<div class="account-username-box" data-userslug="{userslug}">
<span class="account-username">
<a href="/user/{userslug}">{username}</a>
</span>
</div>
<div id="no-favourites-notice" class="alert alert-warning {show_nofavourites}">You don't have any favourites, favourite some posts to see them here!</div>
<div class="row">
<div class="col-md-12 user-favourite-posts">
<!-- BEGIN posts -->
<div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
<span><strong>{posts.username}</strong> : </span>
<span>{posts.content}</span>
<div>
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
</div>
</div>
<br/>
<!-- END posts -->
</div>
</div>
</div>
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
<script type="text/javascript" src="{relative_path}/src/forum/favourites.js"></script>

View File

@@ -1,38 +1,31 @@
<div class="well"> <div class="well users">
<div class="account-username-box"> <div class="account-username-box" data-userslug="{userslug}">
<span class="account-username"> <span class="account-username">
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
<a href="/users/{userslug}/followers">followers</a> <a href="/user/{userslug}/followers">followers</a>
</span> </span>
<div class="account-sub-links inline-block pull-right">
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
</div>
</div> </div>
<div> <div>
<!-- BEGIN followers --> <!-- BEGIN followers -->
<div class="users-box"> <div class="users-box">
<a href="/users/{followers.userslug}"> <a href="/user/{followers.userslug}">
<img src="{followers.picture}" class="img-polaroid"/> <img src="{followers.picture}" class="img-thumbnail"/>
</a> </a>
<br/> <br/>
<a href="/users/{followers.userslug}">{followers.username}</a> <a href="/user/{followers.userslug}">{followers.username}</a>
<br/> <br/>
<div title="reputation"> <div title="reputation">
<span class='reputation'>{followers.reputation}</span> <span class='formatted-number'>{followers.reputation}</span>
<i class='icon-star'></i> <i class='icon-star'></i>
</div> </div>
<div title="post count"> <div title="post count">
<span class='postcount'>{followers.postcount}</span> <span class='formatted-number'>{followers.postcount}</span>
<i class='icon-pencil'></i> <i class='icon-pencil'></i>
</div> </div>
</div> </div>
<!-- END followers --> <!-- END followers -->
</div> </div>
<div id="no-followers-notice" class="alert alert-warning hide">This user doesn't have any followers :(</div> <div id="no-followers-notice" class="alert alert-warning hide">This user doesn't have any followers :(</div>

View File

@@ -1,39 +1,31 @@
<div class="well"> <div class="well users">
<div class="account-username-box" data-userslug="{userslug}">
<div class="account-username-box">
<span class="account-username"> <span class="account-username">
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i> <a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
<a href="/users/{userslug}/following">following</a> <a href="/user/{userslug}/following">following</a>
</span> </span>
<div class="account-sub-links inline-block pull-right">
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
</div>
</div> </div>
<div> <div>
<!-- BEGIN following --> <!-- BEGIN following -->
<div class="users-box"> <div class="users-box">
<a href="/users/{following.userslug}"> <a href="/user/{following.userslug}">
<img src="{following.picture}" class="img-polaroid"/> <img src="{following.picture}" class="img-thumbnail"/>
</a> </a>
<br/> <br/>
<a href="/users/{following.userslug}">{following.username}</a> <a href="/user/{following.userslug}">{following.username}</a>
<br/> <br/>
<div title="reputation"> <div title="reputation">
<span class='reputation'>{following.reputation}</span> <span class='formatted-number'>{following.reputation}</span>
<i class='icon-star'></i> <i class='icon-star'></i>
</div> </div>
<div title="post count"> <div title="post count">
<span class='postcount'>{following.postcount}</span> <span class='formatted-number'>{following.postcount}</span>
<i class='icon-pencil'></i> <i class='icon-pencil'></i>
</div> </div>
<a id="unfollow-btn" href="#" class="btn unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a> <a id="unfollow-btn" href="#" class="btn btn-default unfollow-btn" followingUid="{following.uid}" data-username="{following.username}">Unfollow</a>
</div> </div>
<!-- END following --> <!-- END following -->
</div> </div>

View File

@@ -2,51 +2,56 @@
</div><!--END container --> </div><!--END container -->
<div id="disconnect-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true"> <div id="chat-modal" class="modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
<div class="modal-header"> <div class="modal-dialog">
<h3 id="myModalLabel">Socket Disconnect</h3> <div class="modal-content">
</div>
<div class="modal-body">
<span id="disconnect-text">Looks like you disconnected, try reloading the page.</span>
</div>
<div class="modal-footer">
<a id="reload-button" href="/" class="btn btn-primary">Reload</a>
</div>
</div>
<div id="chat-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-header"> <div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button> <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3 id="myModalLabel">Chat with <span id="chat-with-name"></span></h3> <h3 id="myModalLabel">[[footer:chat.chatting_with]]</h3>
</div> </div>
<div class="modal-body"> <div class="modal-body">
<textarea id="chat-content" cols="40" rows="10" readonly></textarea><br/> <textarea class="form-control" id="chat-content" cols="40" rows="10" readonly></textarea><br/>
<input id="chat-message-input" type="text" name="chat-message" placeholder="type chat message here press enter to send"/><br/> <input id="chat-message-input" type="text" class="form-control" name="chat-message" placeholder="[[footer:chat.placeholder]]"/>
<button type="button" id="chat-message-send-btn" href="#" class="btn btn-primary">Send</button>
</div> </div>
<div class="modal-footer">
<button type="button" id="chat-message-send-btn" href="#" class="btn btn-primary btn-lg btn-block
">[[footer:chat.send]]</button>
</div> </div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div><!-- /.modal -->
<div id="alert_window"></div> <div id="alert_window"></div>
<div id="mobile-sidebar">
<footer id="footer" class="container footer">
<div class="row footer-stats">
<div class="col-md-3 col-xs-6">
<div class="stats-card well">
<h2><span id="stats_online"></span><br /><small>[[footer:stats.online]]</small></h2>
</div>
</div>
<div class="col-md-3 col-xs-6">
<div class="stats-card well">
<h2><span id="stats_users"></span><br /><small>[[footer:stats.users]]</small></h2>
</div>
</div>
<div class="col-md-3 col-xs-6">
<div class="stats-card well">
<h2><span id="stats_topics"></span><br /><small>[[footer:stats.topics]]</small></h2>
</div>
</div>
<div class="col-md-3 col-xs-6">
<div class="stats-card well">
<h2><span id="stats_posts"></span><br /><small>[[footer:stats.posts]]</small></h2>
</div>
</div>
</div> </div>
<!-- START Forum Info --> <div class="copyright">Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.org">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></div>
<div id="footer" class="container hidden-phone" style="padding-top: 50px; display: none"> </footer>
<div class="alert alert-info">
<span id="active_users"></span>; <span id="active_record"></span><br />
<span id="number_of_users"></span> <span id="post_stats"></span><br />
<span id="latest_user"></span>
</div>
<footer class="footer">Copyright &copy; 2013 <a target="_blank" href="http://www.nodebb.org">NodeBB</a> by <a target="_blank" href="https://github.com/psychobunny">psychobunny</a>, <a href="https://github.com/julianlam" target="_blank">julianlam</a>, <a href="https://github.com/barisusakli" target="_blank">barisusakli</a> from <a target="_blank" href="http://www.designcreateplay.com">designcreateplay</a></footer>
</div>
<div id="mobile-menu">
<button id="mobile-menu-btn" type="button" class="btn btn-none"><i class="icon-th icon-2x"></i></button>
<button id="mobile-post-btn" type="button" class="btn btn-none"><i class="icon-plus icon-2x"></i></button>
</div>
<!-- END Forum Info -->
<script> <script>
$.getScript(RELATIVE_PATH + '/src/forum/footer.js'); $.getScript(RELATIVE_PATH + '/src/forum/footer.js');
</script> </script>

View File

@@ -4,71 +4,109 @@
<title>{browserTitle}</title> <title>{browserTitle}</title>
{meta_tags} {meta_tags}
<link href="{cssSrc}" rel="stylesheet" media="screen"> <link href="{cssSrc}" rel="stylesheet" media="screen">
<link href="{relative_path}/vendor/bootstrap/css/bootstrap-responsive.min.css" rel="stylesheet" media="screen">
<link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css"> <link rel="stylesheet" href="{relative_path}/vendor/fontawesome/css/font-awesome.min.css">
<script> <script>
var RELATIVE_PATH = "{relative_path}"; var RELATIVE_PATH = "{relative_path}";
</script> </script>
<script src="http://code.jquery.com/jquery.js"></script>
<script src="{relative_path}/vendor/jquery/js/jquery-ui-1.10.3.custom.min.js"></script>
<script src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
<script src="{relative_path}/socket.io/socket.io.js"></script> <script src="{relative_path}/socket.io/socket.io.js"></script>
<script src="{relative_path}/src/app.js"></script> <!-- BEGIN clientScripts -->
<script src="{relative_path}/vendor/requirejs/require.js"></script> <script src="{relative_path}/{clientScripts.script}"></script>
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script> <!-- END clientScripts -->
<script> <script>
require.config({ require.config({
baseUrl: "{relative_path}/src/modules", baseUrl: "{relative_path}/src/modules",
waitSeconds: 3 waitSeconds: 3
}); });
</script> </script>
<script src="{relative_path}/src/templates.js"></script>
<script src="{relative_path}/src/ajaxify.js"></script>
<script src="{relative_path}/src/jquery.form.js"></script>
<script src="{relative_path}/src/utils.js"></script>
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" /> <link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
</head> </head>
<body> <body>
<div id="mobile-menu-overlay"> <div class="navbar navbar-inverse navbar-fixed-top header" role="navigation" id="header-menu">
</div>
<div class="navbar navbar-inverse navbar-fixed-top" id="header-menu">
<div class="navbar-inner">
<div class="container"> <div class="container">
<a class="brand" href="/">{title}</a> <div class="navbar-header">
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
</button> </button>
<div class="nav-collapse collapse"> <a href="/">
<ul id="main-nav" class="nav nodebb-inline-block"> <h1 class="navbar-brand forum-title">{title}</h1>
</a>
</div>
<div class="navbar-collapse collapse navbar-ex1-collapse">
<ul id="main-nav" class="nav navbar-nav">
<li> <li>
<a href="/recent">Recent</a> <a href="/recent">[[global:header.recent]]</a>
</li>
<li class="nodebb-loggedin">
<a href="/unread"><span id="numUnreadBadge" class="badge badge-inverse">0</span> [[global:header.unread]]</a>
</li> </li>
<li> <li>
<a href="/unread">Unread</a> <a href="/users">[[global:header.users]]</a>
</li>
<li class="visible-xs">
<a href="/search">[[global:header.search]]</a>
</li>
<li class="visible-xs">
<a href="/search">Search</a>
</li> </li>
<li> <li>
<a href="/users">Users</a> <a href="/"></a>
</li> </li>
</ul> </ul>
<ul id="right-menu" class="nav pull-right nodebb-inline-block">
<li class="notifications dropdown text-center"> <form id="search-form" class="navbar-form navbar-right hidden-xs" role="search" method="GET" action="">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="icon-circle-blank"></i></a> <div class="hide" id="search-fields">
<ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown"> <div class="form-group">
<li><a href="#"><i class="icon-refresh icon-spin"></i> Loading Notifications</a></li> <input type="text" class="form-control" placeholder="Search" name="query" value="">
</ul> </div>
</li> <button type="submit" class="btn btn-default hide">[[global:search]]</button>
<form id="search-form" </div>
class="form-search form-inline" action="" method="GET"> <button id="search-button" type="button" class="btn btn-link"><i class="icon-search"></i></button>
<input type="text" name="query" class="input-medium search-query">
<button type="submit" class="btn hide">Search</button>
</form> </form>
<ul id="logged-in-menu" class="nav navbar-nav navbar-right hide">
<li>
<a href="#" id="reconnect"></a>
</li>
<li id="notifications-list" class="notifications dropdown text-center hidden-xs">
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="notif_dropdown"><i class="icon-circle-blank"></i></a>
<ul id="notif-list" class="dropdown-menu" aria-labelledby="notif_dropdown">
<li>
<a href="#"><i class="icon-refresh icon-spin"></i> [[global:notifications.loading]]</a>
</li>
</ul> </ul>
</li>
<li>
<a id="user_label" href="">
<img src=""/>
<span></span>
</a>
</li>
<li id="logout-link">
<a href="#">Log out</a>
</li>
</ul>
<ul id="logged-out-menu" class="nav navbar-nav navbar-right">
<li id="register-link">
<a href="/register">Register</a>
</li>
<li id="login-link">
<a href="/login">Login</a>
</li>
</ul>
<div class="pagination-block">
<i class="icon-upload pointer"></i>
<span id="pagination"></span>
<i class="icon-upload pointer icon-rotate-180"></i>
</div> </div>
</div> </div>
</div> </div>
@@ -76,6 +114,4 @@
<input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" /> <input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" />
<div class="container" id="content"> <div class="container" id="content">

View File

@@ -1,13 +1,13 @@
<div class="hero-unit {motd_class}"> <div class="jumbotron {motd_class}">
{motd} {motd}
</div> </div>
<div class="row category-row"> <div class="row home">
<!-- BEGIN categories --> <!-- BEGIN categories -->
<div class="span3"> <div class="col-md-3 col-xs-6">
<a href="category/{categories.slug}"> <a href="category/{categories.slug}">
<h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4> <h4><span class="badge {categories.badgeclass}">{categories.topic_count} </span> {categories.name}</h4>
<div class="category-icon {categories.blockclass}"> <div class="icon {categories.blockclass}">
<div id="category-{categories.cid}" class="category-slider-{categories.post_count}"> <div id="category-{categories.cid}" class="category-slider-{categories.post_count}">
<div class="category-box"><i class="{categories.icon} icon-4x"></i></div> <div class="category-box"><i class="{categories.icon} icon-4x"></i></div>
<div class="category-box">{categories.description}</div> <div class="category-box">{categories.description}</div>

View File

@@ -30,10 +30,10 @@
<hr /> <hr />
<div class="pull-right"> <div class="pull-right">
<button data-path="mail" class="btn btn-primary btn-large">Next &ndash; <i class="icon-envelope"></i> Mail</button> <button data-path="mail" class="btn btn-primary btn-lg">Next &ndash; <i class="icon-envelope"></i> Mail</button>
</div> </div>
<div> <div>
<button data-path="redis" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-hdd"></i> Redis</button> <button data-path="redis" class="btn btn-primary btn-lg">Previous &ndash; <i class="icon-hdd"></i> Redis</button>
</div> </div>
<script> <script>

View File

@@ -9,7 +9,7 @@
// ajaxify.go('install/redis'); // ajaxify.go('install/redis');
// app.alert({ // app.alert({
// alert_id: 'config-ready', // alert_id: 'config-ready',
// type: 'error', // type: 'danger',
// timeout: 10000, // timeout: 10000,
// title: 'NodeBB Configuration Not Ready!', // title: 'NodeBB Configuration Not Ready!',
// message: 'NodeBB cannot proceed with setup at this time as Redis database information ' + // message: 'NodeBB cannot proceed with setup at this time as Redis database information ' +
@@ -110,7 +110,6 @@
contentEl.addEventListener('click', function(e) { contentEl.addEventListener('click', function(e) {
if (e.target.hasAttribute('data-path')) { if (e.target.hasAttribute('data-path')) {
var href = 'install/' + e.target.getAttribute('data-path'); var href = 'install/' + e.target.getAttribute('data-path');
console.log(href);
if (!e.target.disabled) ajaxify.go(href); if (!e.target.disabled) ajaxify.go(href);
} }
}, false); }, false);
@@ -135,7 +134,7 @@
timeout: 2500, timeout: 2500,
title: 'Configuration Not Saved', title: 'Configuration Not Saved',
message: 'NodeBB encountered a problem saving your changes', message: 'NodeBB encountered a problem saving your changes',
type: 'error' type: 'danger'
}); });
} }
}); });

View File

@@ -26,7 +26,7 @@
<div class="navbar-inner"> <div class="navbar-inner">
<div class="container"> <div class="container">
<a class="brand" href="/install">NodeBB Installation</a> <a class="brand" href="/install">NodeBB Installation</a>
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse"> <button type="button" class="btn navbar-btn" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>
<span class="icon-bar"></span> <span class="icon-bar"></span>

View File

@@ -23,7 +23,7 @@
<hr /> <hr />
<div class="pull-right"> <div class="pull-right">
<button data-path="social" class="btn btn-primary btn-large">Next &ndash; <i class="icon-facebook"></i> Social</button> <button data-path="social" class="btn btn-primary btn-lg">Next &ndash; <i class="icon-facebook"></i> Social</button>
</div> </div>
<script> <script>

View File

@@ -27,10 +27,10 @@
<hr /> <hr />
<div class="pull-right"> <div class="pull-right">
<button id="start-nodebb" class="btn btn-success btn-large"><i class="icon-thumbs-up"></i> Start using NodeBB!</button> <button id="start-nodebb" class="btn btn-success btn-lg"><i class="icon-thumbs-up"></i> Start using NodeBB!</button>
</div> </div>
<div> <div>
<button data-path="social" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-facebook"></i> Social</button> <button data-path="social" class="btn btn-primary btn-lg">Previous &ndash; <i class="icon-facebook"></i> Social</button>
</div> </div>
<script> <script>

View File

@@ -32,7 +32,7 @@
<hr /> <hr />
<div class="pull-right"> <div class="pull-right">
<button data-path="basic" class="btn btn-primary btn-large" disabled>Next &ndash; <i class="icon-cog"></i> Basic</button> <button data-path="basic" class="btn btn-primary btn-lg" disabled>Next &ndash; <i class="icon-cog"></i> Basic</button>
</div> </div>
<script> <script>

View File

@@ -34,10 +34,10 @@
<hr /> <hr />
<div class="pull-right"> <div class="pull-right">
<button data-path="privileges" class="btn btn-primary btn-large">Next &ndash; <i class="icon-legal"></i> Privileges</button> <button data-path="privileges" class="btn btn-primary btn-lg">Next &ndash; <i class="icon-legal"></i> Privileges</button>
</div> </div>
<div> <div>
<button data-path="mail" class="btn btn-primary btn-large">Previous &ndash; <i class="icon-envelope"></i> Mail</button> <button data-path="mail" class="btn btn-primary btn-lg">Previous &ndash; <i class="icon-envelope"></i> Mail</button>
</div> </div>
<script> <script>

View File

@@ -1,30 +1,64 @@
<h1>Login</h1> <ol class="breadcrumb">
<div class="row-fluid"> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<div class="well {login_window:spansize}"> <a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
<h4>Login via Username &amp; Password</h4> </li>
<div class="alert alert-error" id="error" style="display:none"> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">[[login:login]]</span>
</li>
</ol>
<div class="row">
<div class="{login_window:spansize}">
<div class="well well-lg">
<div class="alert alert-danger" id="login-error-notify" style="display:none">
<button type="button" class="close" data-dismiss="alert">&times;</button> <button type="button" class="close" data-dismiss="alert">&times;</button>
<strong>Failed Login Attempt</strong> <p></p> <strong>[[login:failed_login_attempt]]</strong> <p></p>
</div> </div>
<form> <form class="form-horizontal" role="form">
<label>Username</label><input type="text" placeholder="Enter Username" name="username" id="username" /><br /> <div class="form-group">
<label>Password</label><input type="password" placeholder="Enter Password" name="password" id="password" /><br /> <label for="username" class="col-lg-2 control-label">[[login:username]]</label>
<div class="col-lg-10">
<input class="form-control" type="text" placeholder="Enter Username" name="username" id="username" />
</div>
</div>
<div class="form-group">
<label for="password" class="col-lg-2 control-label">[[login:password]]</label>
<div class="col-lg-10">
<input class="form-control" type="password" placeholder="Enter Password" name="password" id="password" />
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<div class="checkbox">
<label>
<input type="checkbox"> [[login:remember_me]]
</label>
</div>
</div>
</div>
<div class="form-group">
<div class="col-lg-offset-2 col-lg-10">
<button class="btn btn-primary" id="login" type="submit">[[login:login]]</button> &nbsp; <a href="/reset">[[login:forgot_password]]</a>
</div>
</div>
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" /> <input type="hidden" name="_csrf" value="{token}" id="csrf-token" />
<button class="btn btn-primary" id="login" type="submit">Login</button> &nbsp; <a href="/reset">Forgot Password?</a>
</form> </form>
<span id="login-error-notify" class="label label-important hide">Invalid username/password</span><br/> </div>
</div> </div>
<div class="well span6 {alternate_logins:display}"> <div class="col-md-6 {alternate_logins:display}">
<h4>Alternative Logins</h4> <div class="well well-lg">
<h4>[[login:alternative_logins]]</h4>
<ul class="alt-logins"> <ul class="alt-logins">
<li data-url="/auth/twitter" class="twitter {twitter:display}"><i class="icon-twitter-sign icon-3x"></i></li> <li data-url="/auth/twitter" class="twitter {twitter:display}"><i class="icon-twitter-sign icon-3x"></i></li>
<li data-url="/auth/google" class="google {google:display}"><i class="icon-google-plus-sign icon-3x"></i></li> <li data-url="/auth/google" class="google {google:display}"><i class="icon-google-plus-sign icon-3x"></i></li>
<li data-url="/auth/facebook" class="facebook {facebook:display}"><i class="icon-facebook-sign icon-3x"></i></li> <li data-url="/auth/facebook" class="facebook {facebook:display}"><i class="icon-facebook-sign icon-3x"></i></li>
</ul> </ul>
</div> </div>
</div>
</div> </div>
<script type="text/javascript" src="{relative_path}/src/forum/login.js"></script> <script type="text/javascript" src="{relative_path}/src/forum/login.js"></script>

View File

@@ -1,7 +1,13 @@
<div class="alert" id="message"> <ol class="breadcrumb">
<strong>Logout</strong> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<p>You have successfully logged out of NodeBB</p> <a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
<p> </li>
<a href="/">NodeBB Home</a> <li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
</p> <span itemprop="title">[[global:logout]]</span>
</li>
</ol>
<div class="alert alert-success" id="message">
<h4>[[global:logout.title]]</h4>
<p>[[global:logout.message]]</p>
</div> </div>

View File

@@ -1,17 +1,19 @@
<ul class="breadcrumb"> <ol class="breadcrumb">
<li><a href="/">Home</a><span class="divider">/</span></li> <li><a href="/">Home</a></li>
<li class="active">{category_name}</li> <li class="active">{category_name}</li>
<div id="category_active_users"></div> <div id="category_active_users"></div>
</ul> </ol>
<ul class="topics"> <ul class="topics">
<!-- BEGIN topics --> <!-- BEGIN topics -->
<li> <li>
<span class="timestamp">{topics.teaser_timestamp}</span>
<a href="../../topic/{topics.slug}">{topics.title} ({topics.postcount})</a> <a href="../../topic/{topics.slug}">{topics.title} ({topics.postcount})</a>
<div class="teaser"> <div class="teaser">
<img class="img-polaroid" src="../../graph/users/{topics.teaser_username}/picture" /> <img class="img-thumbnail" src="{topics.teaser_userpicture}" />
<p> <p>
{topics.teaser_text} &mdash; {topics.teaser_timestamp} ago {topics.teaser_text}
</p> </p>
<div class="clear"></div>
</div> </div>
</li> </li>
<!-- END topics --> <!-- END topics -->

View File

@@ -1,6 +1,6 @@
<div class="alert alert-error"> <div class="alert alert-danger">
<p> <p>
Your browser does not seem to support javascript. As a result, your viewing experience will be diminished. Your browser does not seem to support javascript. As a result, your viewing experience will be diminished, and you have been placed in <strong>read-only mode</strong>.
</p> </p>
<p> <p>
Please download a browser that supports javascript, or enable it, if it disabled (i.e. NoScript). Please download a browser that supports javascript, or enable it, if it disabled (i.e. NoScript).

View File

@@ -1,12 +1,13 @@
<ul class="posts"> <ul class="posts">
<!-- BEGIN main_posts --> <!-- BEGIN main_posts -->
<li> <li>
<div class="row-fluid"> <a name="{main_posts.pid}"></a>
<div class="span2"> <div class="row">
<img src="{main_posts.picture}" /><br /> <div class="col-lg-2 profile">
{main_posts.username} <img class="img-thumbnail" src="{main_posts.picture}" /><br />
<span class="username">{main_posts.username}</span>
</div> </div>
<div class="span10"> <div class="col-lg-10">
{main_posts.content} {main_posts.content}
</div> </div>
</div> </div>
@@ -14,12 +15,13 @@
<!-- END main_posts --> <!-- END main_posts -->
<!-- BEGIN posts --> <!-- BEGIN posts -->
<li> <li>
<div class="row-fluid"> <a name="{posts.pid}"></a>
<div class="span2"> <div class="row">
<img src="{posts.picture}" /><br /> <div class="col-lg-2 profile">
{posts.username} <img class="img-thumbnail" src="{posts.picture}" /><br />
<span class="username">{posts.username}</span>
</div> </div>
<div class="span10"> <div class="col-lg-10">
{posts.content} {posts.content}
</div> </div>
<div class="clear"></div> <div class="clear"></div>

View File

@@ -1,11 +1,27 @@
<div class="hero-unit"> <div class="outgoing">
<h2 class="form-signin-heading">Now Leaving NodeBB</h2> <ol class="breadcrumb">
<p> <li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<a href="/" itemprop="url"><span itemprop="title">Home</span></a>
</li>
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
<span itemprop="title">Outgoing Link</span>
</li>
</ol>
<div class="well">
<h3>
You are now leaving NodeBB. You are now leaving NodeBB.
</p> </h3>
<br />
<p> <p>
<a href="{url}" rel="nofollow" class="btn btn-large">Continue to {url}</a> <a href="{url}" rel="nofollow" class="btn btn-primary btn-lg">Continue to {url}</a>
<a href="{home}" class="btn btn-large btn-inverse">Return to NodeBB</a> <a id="return-btn" href="#" class="btn btn-lg btn-warning">Return to NodeBB</a>
</p> </p>
</div>
</div> </div>
<script>
$('#return-btn').on('click', function() {
history.back();
return false;
});
</script>

View File

@@ -1,11 +1,8 @@
<div class="container"> <ol class="breadcrumb">
<ul class="breadcrumb"> <li><a href="/">Home</a></li>
<li><a href="/">Home</a><span class="divider">/</span></li>
<li class="active">{category_name}</li> <li class="active">{category_name}</li>
<div id="category_active_users"></div> <div id="category_active_users"></div>
</ul> </ol>
</div>
<a href="/recent"> <a href="/recent">
<div class="alert hide" id="new-topics-alert"></div> <div class="alert hide" id="new-topics-alert"></div>
@@ -21,23 +18,29 @@
<!-- BEGIN topics --> <!-- BEGIN topics -->
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}"> <a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
<li class="category-item {topics.deleted-class}"> <li class="category-item {topics.deleted-class}">
<div class="row-fluid"> <div class="row">
<div class="span12 topic-row img-polaroid"> <div class="col-md-12 col-xs-12 topic-row img-thumbnail">
<div class="latest-post visible-desktop"> <div class="latest-post visible-lg visible-md">
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
<div class="pull-right"> <div class="pull-right">
<img style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" /> <img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
<p><strong>{topics.teaser_username}</strong>: {topics.teaser_text}</p> <p>{topics.teaser_text}</p>
<span>posted {topics.teaser_timestamp} ago</span> <p class="meta">
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
</p>
</div> </div>
</a>
</div> </div>
<a href="../../topic/{topics.slug}">
<div> <div>
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3> <h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
<small> <small>
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong> <strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
Posted {topics.relativeTime} ago by Posted <span class="timeago" title="{topics.relativeTime}"></span> by
<strong>{topics.username}</strong>. <strong>{topics.username}</strong>.
</small> </small>
</div> </div>
</a>
</div> </div>
</div> </div>
</li> </li>

Some files were not shown because too many files have changed in this diff Show More