mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-17 14:00:29 +01:00
Compare commits
460 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
48a7c48f7b | ||
|
|
7a919fbac4 | ||
|
|
181220621e | ||
|
|
249c45dfe2 | ||
|
|
b19d84f1a7 | ||
|
|
865edb70c2 | ||
|
|
e78369f0fa | ||
|
|
d40a6a5c3f | ||
|
|
cb7768d095 | ||
|
|
4290516d29 | ||
|
|
6a7a2301ee | ||
|
|
9a79c58e33 | ||
|
|
4c39c1ec30 | ||
|
|
571cb8b44f | ||
|
|
4207792ffd | ||
|
|
9f67282a79 | ||
|
|
ee71c1cf0d | ||
|
|
06f2284bcd | ||
|
|
8cbe79655a | ||
|
|
a4c1d733b7 | ||
|
|
839649d42f | ||
|
|
504e2aac4a | ||
|
|
eafb41602c | ||
|
|
2dcc4172c4 | ||
|
|
f96a711298 | ||
|
|
ad28b9b339 | ||
|
|
8dd8536f6b | ||
|
|
7bf5b2ec57 | ||
|
|
aa731aa894 | ||
|
|
c58cb257dc | ||
|
|
06f59cf853 | ||
|
|
83b4b5434f | ||
|
|
9de5214a2f | ||
|
|
f08067bab2 | ||
|
|
1160f39cb0 | ||
|
|
d633ecd160 | ||
|
|
4cea313060 | ||
|
|
9b7f8076eb | ||
|
|
571259f241 | ||
|
|
607ee8bbc1 | ||
|
|
36d2f74887 | ||
|
|
cdaf409a99 | ||
|
|
fba487d8a2 | ||
|
|
3ab7306199 | ||
|
|
2e4e94d5f8 | ||
|
|
3348ed3524 | ||
|
|
942a21b4be | ||
|
|
9c14618d55 | ||
|
|
7d50551392 | ||
|
|
f2c1a92513 | ||
|
|
fe6595cd2a | ||
|
|
c2aac916aa | ||
|
|
e161b5387b | ||
|
|
585e5cd88f | ||
|
|
1637ffc5dc | ||
|
|
a28797ee03 | ||
|
|
49e28f9d1e | ||
|
|
4a7cd664fd | ||
|
|
253e11d55b | ||
|
|
eb1c1c78d4 | ||
|
|
0b0d64b52e | ||
|
|
1ae51ef5ea | ||
|
|
de1e3230f5 | ||
|
|
1fbc038e64 | ||
|
|
cd63dd429b | ||
|
|
fbfdf561fc | ||
|
|
83d5a84edd | ||
|
|
5abf02c6d1 | ||
|
|
b8037845d6 | ||
|
|
c80e2552b2 | ||
|
|
1a1fea535b | ||
|
|
5c7da4b686 | ||
|
|
b6ee89a6d8 | ||
|
|
2d8e6bd980 | ||
|
|
a173d61464 | ||
|
|
8f04a136c8 | ||
|
|
b7675e1ec7 | ||
|
|
964fbfe2bb | ||
|
|
8fb8956c0d | ||
|
|
c11920ac08 | ||
|
|
9a4eb26246 | ||
|
|
0d9958afe7 | ||
|
|
d6dd74b50a | ||
|
|
d5437ca8fa | ||
|
|
14720057c2 | ||
|
|
170ed8cc01 | ||
|
|
25576eb35a | ||
|
|
66cb1fb6ad | ||
|
|
bcc65fd879 | ||
|
|
99440585e6 | ||
|
|
290d69d14a | ||
|
|
4fce06677f | ||
|
|
f53cd8b5b6 | ||
|
|
da59f47624 | ||
|
|
521586f08f | ||
|
|
f39a7ada7c | ||
|
|
bae9f46d8b | ||
|
|
d7ea24e218 | ||
|
|
e621d7e601 | ||
|
|
aa6eff4c54 | ||
|
|
f36583e676 | ||
|
|
0a0a91d4b2 | ||
|
|
6c70f9e308 | ||
|
|
b25c3d8b67 | ||
|
|
bade99d069 | ||
|
|
776b51fef7 | ||
|
|
da2d014f00 | ||
|
|
7fdf83089d | ||
|
|
043aafd7b7 | ||
|
|
3254979568 | ||
|
|
6fd202fe36 | ||
|
|
5f00f1e18e | ||
|
|
c940ce3329 | ||
|
|
2366e2b209 | ||
|
|
2b7fd3c9d1 | ||
|
|
d3e37d1716 | ||
|
|
d2b858c997 | ||
|
|
de34c7580f | ||
|
|
bcb492751c | ||
|
|
e26cc79819 | ||
|
|
98cb2d4c17 | ||
|
|
2fc569715c | ||
|
|
3eb594df43 | ||
|
|
8243019a60 | ||
|
|
5a10f7fcfe | ||
|
|
5e89caf358 | ||
|
|
35b40ef650 | ||
|
|
56b7d6cb7c | ||
|
|
8b8a890ac9 | ||
|
|
5645bcee2d | ||
|
|
a631707db4 | ||
|
|
451ffafb9e | ||
|
|
b5274a0d91 | ||
|
|
db4dc03abc | ||
|
|
6be5bcc4c8 | ||
|
|
21efda4a84 | ||
|
|
082375f129 | ||
|
|
1204859263 | ||
|
|
c8b5d1561e | ||
|
|
6912e77d25 | ||
|
|
d84d096e7e | ||
|
|
29c17fd32e | ||
|
|
d721320af5 | ||
|
|
efef9c864c | ||
|
|
27cb837b08 | ||
|
|
047fcbe63f | ||
|
|
dbe0b1551b | ||
|
|
8f7b047b7a | ||
|
|
a38ace0df9 | ||
|
|
0b0b06e8b9 | ||
|
|
f83b61903a | ||
|
|
881fa41bb7 | ||
|
|
f9442db96f | ||
|
|
64521494ce | ||
|
|
e75e3399ab | ||
|
|
e5ef498164 | ||
|
|
24e4be77d8 | ||
|
|
0d55f2ef32 | ||
|
|
8ec3371139 | ||
|
|
ccca4d2914 | ||
|
|
28704a6164 | ||
|
|
cf4ba9d1d3 | ||
|
|
c69e30c146 | ||
|
|
fd32d75d3b | ||
|
|
afa228fd91 | ||
|
|
a01a7ae5fc | ||
|
|
64b071f277 | ||
|
|
af246ad0d7 | ||
|
|
56a87329ec | ||
|
|
eddddc694d | ||
|
|
ce61138351 | ||
|
|
564662ee00 | ||
|
|
678db837fd | ||
|
|
c789f4230d | ||
|
|
1b9e451a6f | ||
|
|
1780b343b4 | ||
|
|
aea3181d27 | ||
|
|
c44461e33f | ||
|
|
221b9bc149 | ||
|
|
dc41c6bc0d | ||
|
|
828d937dec | ||
|
|
cf889cfe25 | ||
|
|
6bab2523d5 | ||
|
|
d714b5c592 | ||
|
|
08316496cd | ||
|
|
d5fd985f0d | ||
|
|
eab7839195 | ||
|
|
18717dd6f5 | ||
|
|
d249f411f8 | ||
|
|
b85e2545d6 | ||
|
|
79280b195e | ||
|
|
4a18728e19 | ||
|
|
b7498416fa | ||
|
|
49b201db0f | ||
|
|
be4d6761b1 | ||
|
|
c01ba1a3cb | ||
|
|
af59ff3209 | ||
|
|
b0a37c6ac5 | ||
|
|
8b3fd9d1e9 | ||
|
|
95b16690e0 | ||
|
|
f6b865a052 | ||
|
|
d1c756306a | ||
|
|
ad5af8e123 | ||
|
|
ba89285c74 | ||
|
|
43c0c2ec2a | ||
|
|
a277104ad5 | ||
|
|
84afffc761 | ||
|
|
5e5680fd13 | ||
|
|
727da38fb6 | ||
|
|
79096cfdce | ||
|
|
a848f82b8f | ||
|
|
71bdd4608b | ||
|
|
f04a948152 | ||
|
|
15be3e52ae | ||
|
|
b1003e954e | ||
|
|
f2b9a7ff0c | ||
|
|
19ad9ab224 | ||
|
|
8eae8a4fb5 | ||
|
|
85fbe38e71 | ||
|
|
e896fd1fd6 | ||
|
|
916150de01 | ||
|
|
732204f11b | ||
|
|
1df50ff855 | ||
|
|
11042858b8 | ||
|
|
89ca2319f5 | ||
|
|
2f4fd310bd | ||
|
|
6d7919ad85 | ||
|
|
59467c906d | ||
|
|
def871ac89 | ||
|
|
6fe5a93a73 | ||
|
|
7796c9269c | ||
|
|
ec150d4a4c | ||
|
|
49abff1609 | ||
|
|
be59c16aad | ||
|
|
f1144f3a7e | ||
|
|
9ebff816dc | ||
|
|
4c3eacd745 | ||
|
|
325e402d0f | ||
|
|
251587cb86 | ||
|
|
9fb1f8acf8 | ||
|
|
cffbc76da1 | ||
|
|
be51025048 | ||
|
|
ae6f9fc87c | ||
|
|
e61a7bff65 | ||
|
|
bb14881b63 | ||
|
|
a7f1bfa7c9 | ||
|
|
7b3384a6be | ||
|
|
efa8717fc5 | ||
|
|
9e42cee87c | ||
|
|
9fe30b905b | ||
|
|
2fc9afa4f4 | ||
|
|
732c8b1f7f | ||
|
|
19be3bc55c | ||
|
|
8a7e2577aa | ||
|
|
e32494879c | ||
|
|
ac4803961a | ||
|
|
de8d6d3c8c | ||
|
|
392b80fe14 | ||
|
|
9fa75a8b45 | ||
|
|
b19097ab8f | ||
|
|
31624b32e9 | ||
|
|
b8b6558f53 | ||
|
|
a53e75aeaa | ||
|
|
7798004568 | ||
|
|
63873575a5 | ||
|
|
9845df4d1b | ||
|
|
462d5f5d05 | ||
|
|
1d860923d0 | ||
|
|
c9a3fac654 | ||
|
|
a1c9685b49 | ||
|
|
c16c4aac44 | ||
|
|
e80ff9c551 | ||
|
|
3176ccc6e3 | ||
|
|
b24196be36 | ||
|
|
52365a9755 | ||
|
|
41bea9f50c | ||
|
|
3416c7bb3c | ||
|
|
ce126b11fb | ||
|
|
99db8fc3fe | ||
|
|
780cec2596 | ||
|
|
d3b4cb71c0 | ||
|
|
1758c3e3f2 | ||
|
|
995fa7d6fd | ||
|
|
87abe426d8 | ||
|
|
bec0b46a2c | ||
|
|
a88ddc2a4d | ||
|
|
1f60578a63 | ||
|
|
2f70489e22 | ||
|
|
3b75734672 | ||
|
|
ff4b35d6f1 | ||
|
|
e5b26fdad0 | ||
|
|
bcbcf40eae | ||
|
|
661fdfb43e | ||
|
|
31f08c49e7 | ||
|
|
2999d61ac9 | ||
|
|
ca72a4b0fc | ||
|
|
164977972e | ||
|
|
b6fbfcc814 | ||
|
|
418700ce3f | ||
|
|
1d52557562 | ||
|
|
45e24c54ce | ||
|
|
abd909d23b | ||
|
|
f39932ece7 | ||
|
|
03fb649274 | ||
|
|
54e9e95076 | ||
|
|
201fb4d73e | ||
|
|
e0cc35ba66 | ||
|
|
d3818e888e | ||
|
|
a2af4a3e91 | ||
|
|
02a02fa64c | ||
|
|
9fae0d2505 | ||
|
|
e23198bbcc | ||
|
|
0a8c43901d | ||
|
|
e9054301d1 | ||
|
|
caaede7e84 | ||
|
|
3ed9e1dd51 | ||
|
|
207eccc505 | ||
|
|
45d5524df7 | ||
|
|
1f08e407d1 | ||
|
|
2c215afe33 | ||
|
|
d1d2b03dfd | ||
|
|
c1a41c6605 | ||
|
|
d1a17b39ea | ||
|
|
bf365bedfd | ||
|
|
6a1ab47a08 | ||
|
|
5fdaa6b0ee | ||
|
|
a6b0c2638a | ||
|
|
a671d08f0b | ||
|
|
5e869a5e5c | ||
|
|
0e6109ff2b | ||
|
|
0bff6ee504 | ||
|
|
7cbe429be9 | ||
|
|
09b578522f | ||
|
|
40897d0be6 | ||
|
|
f49bc088fc | ||
|
|
7795a9ead2 | ||
|
|
2c238f49e0 | ||
|
|
599789634a | ||
|
|
77359f7b83 | ||
|
|
7cff55a160 | ||
|
|
8d26eb4e07 | ||
|
|
ad5e3ebce3 | ||
|
|
2deb0e1708 | ||
|
|
b2f383f095 | ||
|
|
22ababd87a | ||
|
|
167c1fa348 | ||
|
|
3b1bf67436 | ||
|
|
d2eb73df96 | ||
|
|
4d075efcd3 | ||
|
|
8a53c56a46 | ||
|
|
73d4152d1d | ||
|
|
c0a90bd677 | ||
|
|
106157a951 | ||
|
|
ab9cf6d036 | ||
|
|
54ba6efc93 | ||
|
|
85ac4aac94 | ||
|
|
691b6611d0 | ||
|
|
1b33b4425b | ||
|
|
41defbcf9c | ||
|
|
928594fc7c | ||
|
|
cc1f668308 | ||
|
|
f2b8813fb9 | ||
|
|
5ed7c31278 | ||
|
|
13b456bffd | ||
|
|
938dee481b | ||
|
|
a69140faa9 | ||
|
|
6a5ebdb1ef | ||
|
|
ece4d083a5 | ||
|
|
96688a8ffe | ||
|
|
e9b0aa537f | ||
|
|
4052c3e817 | ||
|
|
4a6e838b18 | ||
|
|
3773f6b44f | ||
|
|
a19e836d63 | ||
|
|
aa5e5f9cc0 | ||
|
|
6647e75c77 | ||
|
|
f85a514ee3 | ||
|
|
619dd84d81 | ||
|
|
f9fdfab19e | ||
|
|
ca46d0f8e1 | ||
|
|
bb8f75b4be | ||
|
|
0db599a478 | ||
|
|
902e60fab2 | ||
|
|
87f48e2cc9 | ||
|
|
cc55073107 | ||
|
|
96c43b4607 | ||
|
|
f1b4367168 | ||
|
|
4416f8530d | ||
|
|
4e48ab2363 | ||
|
|
b5a26696f8 | ||
|
|
4307229ae0 | ||
|
|
b681f01be9 | ||
|
|
c262027728 | ||
|
|
6a08fedf18 | ||
|
|
e471b4edf5 | ||
|
|
dd86812822 | ||
|
|
ac311ab75d | ||
|
|
c9387313e2 | ||
|
|
97641dbcda | ||
|
|
54fdbcd947 | ||
|
|
7636e09284 | ||
|
|
7666ed3010 | ||
|
|
e82a9ec37a | ||
|
|
770d9e5b7c | ||
|
|
5f0e0c993e | ||
|
|
7be69b0b54 | ||
|
|
251beb7f9b | ||
|
|
1c27cbd90a | ||
|
|
4b4b26651a | ||
|
|
1a6ba8c230 | ||
|
|
06904506d4 | ||
|
|
62a28825f8 | ||
|
|
c6c693acd2 | ||
|
|
8d04454457 | ||
|
|
aeb831eeff | ||
|
|
67db78ad66 | ||
|
|
06809ab5fc | ||
|
|
08ba911738 | ||
|
|
59f4b6788a | ||
|
|
a317a4d689 | ||
|
|
6e164e61aa | ||
|
|
2098bf84a1 | ||
|
|
860a83ba90 | ||
|
|
abce5fd120 | ||
|
|
0243e9c5be | ||
|
|
8a4a0154f6 | ||
|
|
469a5221ed | ||
|
|
f251b9c6c5 | ||
|
|
c4228be86c | ||
|
|
94e565aa00 | ||
|
|
cc6e028b1d | ||
|
|
33a69abece | ||
|
|
6a4947533f | ||
|
|
879a31f51e | ||
|
|
49fc87e295 | ||
|
|
fe89f1f096 | ||
|
|
346681ba27 | ||
|
|
13e13cd5a8 | ||
|
|
0f8aad52b2 | ||
|
|
b6a5cbf956 | ||
|
|
a482d4db5f | ||
|
|
df15dceaef | ||
|
|
9f2e192993 | ||
|
|
d62f36c6a0 | ||
|
|
2427724868 | ||
|
|
9b35d8f8e1 | ||
|
|
a859f4524c | ||
|
|
8be896aebb | ||
|
|
a008cf971d | ||
|
|
316077fffb | ||
|
|
4373037071 | ||
|
|
24e81c873c | ||
|
|
9e4a3905a0 | ||
|
|
b54b4a6580 | ||
|
|
f33a9c3941 | ||
|
|
d792798963 | ||
|
|
e5af4f6299 | ||
|
|
ef5bd9dc61 | ||
|
|
8834feac65 | ||
|
|
2f70dd732d |
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*.js, *.css, *.tpl]
|
||||
indent_style = tab
|
||||
8
.gitignore
vendored
8
.gitignore
vendored
@@ -1,4 +1,4 @@
|
||||
#################
|
||||
#################
|
||||
## npm
|
||||
#################
|
||||
|
||||
@@ -6,10 +6,14 @@ npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
config.json
|
||||
public/src/nodebb.min.js
|
||||
public/config.json
|
||||
public/css/*.css
|
||||
public/themes/*
|
||||
!/public/themes/vanilla
|
||||
!/public/themes/cerulean
|
||||
!/public/themes/modern
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
plugins/*
|
||||
.project
|
||||
.project
|
||||
|
||||
17
.jsbeautifyrc
Normal file
17
.jsbeautifyrc
Normal 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
86
.jshintrc
Normal 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
|
||||
}
|
||||
21
README.md
21
README.md
@@ -1,12 +1,15 @@
|
||||
Please support NodeBB development! Check out our IndieGoGo campaign and like, share, and follow us :)
|
||||
[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")
|
||||
|
||||
# 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 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
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
@@ -16,7 +19,11 @@ Please support NodeBB development! Check out our IndieGoGo campaign and like, sh
|
||||
|
||||
## 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
|
||||
|
||||
@@ -54,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:
|
||||
|
||||
# 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)
|
||||
208
app.js
208
app.js
@@ -16,80 +16,86 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
// Configuration setup
|
||||
nconf = require('nconf');
|
||||
nconf.argv().file({ file: __dirname + '/config.json'});
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
var fs = require('fs'),
|
||||
winston = require('winston'),
|
||||
pkg = require('./package.json'),
|
||||
url = require('url'),
|
||||
meta = require('./src/meta.js');
|
||||
// Configuration setup
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env();
|
||||
|
||||
// Runtime environment
|
||||
global.env = process.env.NODE_ENV || 'production',
|
||||
var fs = require('fs'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
pkg = require('./package.json'),
|
||||
path = require('path'),
|
||||
meta;
|
||||
|
||||
// Runtime environment
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize:true
|
||||
});
|
||||
|
||||
winston.add(winston.transports.File, {
|
||||
filename:'error.log',
|
||||
level:'error'
|
||||
})
|
||||
|
||||
// TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
|
||||
winston.err = function(err) {
|
||||
winston.error(err.stack);
|
||||
};
|
||||
|
||||
// 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(nconf.get('upgrade')) {
|
||||
meta.configs.init(function() {
|
||||
require('./src/upgrade').upgrade();
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize: true
|
||||
});
|
||||
} else if (!nconf.get('setup') && nconf.get('base_url')) {
|
||||
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('Base Configuration OK.');
|
||||
winston.add(winston.transports.File, {
|
||||
filename: 'error.log',
|
||||
level: 'error'
|
||||
});
|
||||
|
||||
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);
|
||||
// TODO: remove once https://github.com/flatiron/winston/issues/280 is fixed
|
||||
winston.err = function (err) {
|
||||
winston.error(err.stack);
|
||||
};
|
||||
|
||||
// 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.');
|
||||
}
|
||||
|
||||
var categories = require('./src/categories.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')
|
||||
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);
|
||||
};
|
||||
|
||||
DEVELOPMENT = true;
|
||||
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
|
||||
|
||||
global.configuration = {};
|
||||
global.templates = {};
|
||||
websockets.init(SocketIO);
|
||||
|
||||
(function(config) {
|
||||
config['ROOT_DIRECTORY'] = __dirname;
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
|
||||
translator.loadServer();
|
||||
|
||||
// todo: replace below with read directory code, derp.
|
||||
templates.init([
|
||||
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
|
||||
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
|
||||
@@ -99,62 +105,36 @@ if(nconf.get('upgrade')) {
|
||||
]);
|
||||
|
||||
templates.ready(webserver.init);
|
||||
});
|
||||
|
||||
//setup scripts to be moved outside of the app in future.
|
||||
function setup_categories() {
|
||||
winston.info('Checking categories...');
|
||||
categories.getAllCategories(function(data) {
|
||||
if (data.categories.length === 0) {
|
||||
winston.info('Setting up default categories...');
|
||||
} else if (nconf.get('upgrade')) {
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
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]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
winston.info('Hardcoding uid 1 as an admin');
|
||||
var user = require('./src/user.js');
|
||||
user.makeAdministrator(1);
|
||||
|
||||
|
||||
} else {
|
||||
winston.info('Categories OK. Found ' + data.categories.length + ' categories.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
setup_categories();
|
||||
}(global.configuration));
|
||||
});
|
||||
|
||||
} else {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) winston.info('NodeBB Setup Triggered via Command Line');
|
||||
else winston.info('Configuration not found, starting NodeBB setup');
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
process.stdout.write(
|
||||
"\nWelcome to NodeBB!\nThis looks like a new installation, so you'll have to answer a " +
|
||||
"few questions about your environment before we can proceed.\n\n" +
|
||||
"Press enter to accept the default setting (shown in brackets).\n\n\n"
|
||||
);
|
||||
|
||||
install.setup(function(err) {
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) {
|
||||
winston.info('NodeBB Setup Triggered via Command Line');
|
||||
} else {
|
||||
if (!nconf.get('setup')) {
|
||||
process.stdout.write(
|
||||
"Please start NodeBB again and register a new user. This user will automatically become an administrator.\n\n"
|
||||
);
|
||||
}
|
||||
winston.warn('Configuration not found, starting NodeBB setup');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
var install = require('./src/install');
|
||||
|
||||
winston.info('Welcome to NodeBB!');
|
||||
winston.info('This looks like a new installation, so you\'ll have to answer a few questions about your environment before we can proceed.');
|
||||
winston.info('Press enter to accept the default setting (shown in brackets).');
|
||||
|
||||
install.setup(function (err) {
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
winston.info('NodeBB Setup Completed.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
}
|
||||
}());
|
||||
@@ -1,7 +1,7 @@
|
||||
[
|
||||
{
|
||||
"name": "Announcements",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"description": "Announcements regarding our community",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-bullhorn"
|
||||
},
|
||||
@@ -13,61 +13,61 @@
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"icon" : "icon-github"
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "Blog posts from individual members",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-pencil"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"icon" : "icon-lightbulb"
|
||||
},
|
||||
{
|
||||
"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",
|
||||
"icon" : "icon-cogs"
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Addons",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"name": "NodeBB Plugins",
|
||||
"description": "Enhance your NodeBB with plugins!",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-plus-sign"
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Link Exchange",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "Link exchange",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-exchange"
|
||||
},
|
||||
{
|
||||
"name": "News",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "News from around the world",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-globe"
|
||||
},
|
||||
{
|
||||
"name": "Movies",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "Discuss the latest movies here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-film"
|
||||
},
|
||||
{
|
||||
"name": "Games",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "Discuss the latest games here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-screenshot"
|
||||
},
|
||||
{
|
||||
"name": "Random",
|
||||
"description": "In future an example of how a hidden category should look like.",
|
||||
"description": "Anything and (almost) everything welcome!",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-beer"
|
||||
}
|
||||
|
||||
17
package.json
17
package.json
@@ -2,7 +2,7 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.0.6",
|
||||
"version": "0.0.7",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@@ -10,10 +10,10 @@
|
||||
},
|
||||
"main": "app.js",
|
||||
"dependencies": {
|
||||
"socket.io": "0.9.14",
|
||||
"socket.io": "~0.9.16",
|
||||
"redis": "0.8.3",
|
||||
"express": "3.2.0",
|
||||
"express-namespace": "0.1.1",
|
||||
"express-namespace": "~0.1.1",
|
||||
"emailjs": "0.3.4",
|
||||
"cookie": "0.0.6",
|
||||
"connect-redis": "1.4.5",
|
||||
@@ -27,7 +27,6 @@
|
||||
"bcrypt": "0.7.5",
|
||||
"async": "0.2.8",
|
||||
"node-imagemagick": "0.1.8",
|
||||
"node-rss": "1.0.1",
|
||||
"gravatar": "1.0.6",
|
||||
"nconf": "~0.6.7",
|
||||
"sitemap": "~0.6.0",
|
||||
@@ -35,7 +34,15 @@
|
||||
"request": "~2.25.0",
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"nodebb-plugin-mentions": "~0.1.0"
|
||||
"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": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
|
||||
@@ -1,5 +1 @@
|
||||
@import "style";
|
||||
|
||||
@import "topic";
|
||||
@import "category";
|
||||
@import "noscript";
|
||||
@import "../themes/cerulean/cerulean.less";
|
||||
@@ -1,885 +0,0 @@
|
||||
@import "mixins";
|
||||
|
||||
html {
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
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: 2%;
|
||||
}
|
||||
|
||||
#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-block {
|
||||
div {
|
||||
padding-bottom:10px;
|
||||
}
|
||||
}
|
||||
|
||||
.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;
|
||||
}
|
||||
|
||||
.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;
|
||||
overflow:hidden;
|
||||
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;
|
||||
|
||||
@-webkit-keyframes glow
|
||||
{
|
||||
from {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
50% {text-shadow: 0 0 10px #aaf, 0 0 10px #aaf, 0 0 10px #aaf;}
|
||||
to {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
}
|
||||
@keyframes glow
|
||||
{
|
||||
from {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
50% {text-shadow: 0 0 10px #aaf, 0 0 10px #aaf, 0 0 10px #aaf;}
|
||||
to {text-shadow: 0 0 5px #aaf, 0 0 5px #aaf, 0 0 5px #aaf;}
|
||||
}
|
||||
|
||||
|
||||
&.active {
|
||||
color: #558;
|
||||
|
||||
text-shadow: 0 0 1em #aaf, 0 0 1em #aaf, 0 0 1em #aaf;
|
||||
-webkit-animation:glow 1.5s infinite linear;
|
||||
animation:glow 1.5s infinite linear;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#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 {
|
||||
&.formatting-bar {
|
||||
width: 90%;
|
||||
margin: 0 auto 8px auto;
|
||||
|
||||
span {
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.action-bar {
|
||||
width: 90%;
|
||||
margin: 8px auto 0 auto;
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 98%;
|
||||
text-align: center;
|
||||
border: none;
|
||||
padding: 0.5em 0;
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
margin: 5px auto 10px auto;
|
||||
display: block;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: rgba(64, 64, 64, 0.95);
|
||||
border: none;
|
||||
padding: 0.5em;
|
||||
display: block;
|
||||
width: 90%;
|
||||
margin: 0em auto;
|
||||
resize: none;
|
||||
color: white;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
.imagedrop {
|
||||
text-align: center;
|
||||
color: white;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height: 214px;
|
||||
line-height: 214px;
|
||||
font-size: 20px;
|
||||
vertical-align: middle;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.imagelist {
|
||||
position: absolute;
|
||||
bottom: 50px;
|
||||
left: 5%;
|
||||
|
||||
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: 5px;
|
||||
}
|
||||
|
||||
.search-result-post {
|
||||
width: 100%;
|
||||
line-height: 16px;
|
||||
padding: 5px;
|
||||
overflow:hidden;
|
||||
img {
|
||||
display: block;
|
||||
float: left;
|
||||
width:48px;
|
||||
height:48px;
|
||||
padding-right:10px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,226 +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 {
|
||||
position:relative;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 979px) {
|
||||
.speech-bubble:after
|
||||
{
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -7px;
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 0;
|
||||
border-color: transparent #FFFFFF;
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.speech-bubble:before
|
||||
{
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 9px;
|
||||
left: -8px;
|
||||
border-style: solid;
|
||||
border-width: 7px 7px 7px 0;
|
||||
border-color: transparent rgba(0, 0, 0, 0.125);
|
||||
display: block;
|
||||
width: 0;
|
||||
z-index: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.main-post {
|
||||
h3 {
|
||||
margin: 0;
|
||||
|
||||
.topic-title {
|
||||
width: auto;
|
||||
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
3
public/language/TODO
Normal 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.
|
||||
7
public/language/en/category.json
Normal file
7
public/language/en/category.json
Normal 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"
|
||||
}
|
||||
9
public/language/en/footer.json
Normal file
9
public/language/en/footer.json
Normal 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"
|
||||
}
|
||||
17
public/language/en/global.json
Normal file
17
public/language/en/global.json
Normal 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"
|
||||
}
|
||||
10
public/language/en/login.json
Normal file
10
public/language/en/login.json
Normal 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!"
|
||||
}
|
||||
16
public/language/en/register.json
Normal file
16
public/language/en/register.json
Normal 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"
|
||||
}
|
||||
22
public/language/en/topic.json
Normal file
22
public/language/en/topic.json
Normal 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"
|
||||
}
|
||||
5
public/language/en/unread.json
Normal file
5
public/language/en/unread.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"no_unread_topics": "There are no unread topics.",
|
||||
"mark_all_read": "Mark all as Read",
|
||||
"load_more": "Load More"
|
||||
}
|
||||
9
public/language/en/users.json
Normal file
9
public/language/en/users.json
Normal 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.
Binary file not shown.
File diff suppressed because one or more lines are too long
2
public/socket.io/socket.io.min.js
vendored
2
public/socket.io/socket.io.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -1,7 +1,8 @@
|
||||
var ajaxify = {};
|
||||
|
||||
|
||||
(function($) {
|
||||
(function ($) {
|
||||
/*global app, templates, utils*/
|
||||
|
||||
var location = document.location || window.location,
|
||||
rootUrl = location.protocol + '//' + (location.hostname || location.host) + (location.port ? ':' + location.port : ''),
|
||||
@@ -11,8 +12,8 @@ var ajaxify = {};
|
||||
var executed = {};
|
||||
|
||||
var events = [];
|
||||
ajaxify.register_events = function(new_page_events) {
|
||||
for (var i = 0, ii = events.length; i<ii; i++) {
|
||||
ajaxify.register_events = function (new_page_events) {
|
||||
for (var i = 0, ii = events.length; i < ii; i++) {
|
||||
socket.removeAllListeners(events[i]); // optimize this to user removeListener(event, listener) instead.
|
||||
}
|
||||
|
||||
@@ -20,28 +21,36 @@ var ajaxify = {};
|
||||
};
|
||||
|
||||
|
||||
window.onpopstate = function(event) {
|
||||
window.onpopstate = function (event) {
|
||||
// "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);
|
||||
};
|
||||
|
||||
ajaxify.go = function(url, callback, template, quiet) {
|
||||
var pagination;
|
||||
|
||||
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');
|
||||
// leave room and join 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;
|
||||
|
||||
if(url.indexOf(RELATIVE_PATH.slice(1)) !== -1) {
|
||||
if (url.indexOf(RELATIVE_PATH.slice(1)) !== -1) {
|
||||
url = url.slice(RELATIVE_PATH.length);
|
||||
}
|
||||
|
||||
var tpl_url = templates.get_custom_map(url.split('?')[0]);
|
||||
|
||||
if (tpl_url == false && !templates[url]) {
|
||||
if(url === '' || url === '/') {
|
||||
if (url === '' || url === '/') {
|
||||
tpl_url = 'home';
|
||||
} else {
|
||||
tpl_url = url.split('/')[0].split('?')[0];
|
||||
@@ -53,27 +62,31 @@ var ajaxify = {};
|
||||
|
||||
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
|
||||
if (quiet !== true) {
|
||||
window.history.pushState({
|
||||
"url": url
|
||||
}, url, RELATIVE_PATH + "/" + url);
|
||||
if (window.history && window.history.pushState) {
|
||||
window.history.pushState({
|
||||
"url": url
|
||||
}, url, RELATIVE_PATH + "/" + url);
|
||||
}
|
||||
}
|
||||
|
||||
translator.load(tpl_url);
|
||||
|
||||
jQuery('#footer, #content').fadeOut(100);
|
||||
|
||||
templates.flush();
|
||||
templates.load_template(function() {
|
||||
templates.load_template(function () {
|
||||
exec_body_scripts(content);
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
app.process_page();
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function() {
|
||||
if(window.location.hash)
|
||||
app.process_page();
|
||||
if (window.location.hash)
|
||||
hash = window.location.hash;
|
||||
if(hash)
|
||||
if (hash)
|
||||
app.scrollToPost(hash.substr(1));
|
||||
});
|
||||
|
||||
@@ -85,53 +98,60 @@ var ajaxify = {};
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
$('document').ready(function() {
|
||||
$('document').ready(function () {
|
||||
if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers
|
||||
|
||||
content = content || document.getElementById('content');
|
||||
|
||||
// Enhancing all anchors to ajaxify...
|
||||
$(document.body).on('click', 'a', function(e) {
|
||||
|
||||
$(document.body).on('click', 'a', function (e) {
|
||||
function hrefEmpty(href) {
|
||||
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
|
||||
}
|
||||
|
||||
if(hrefEmpty(this.href)) return;
|
||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
|
||||
return;
|
||||
|
||||
var url = this.href.replace(rootUrl +'/', '');
|
||||
|
||||
if (this.target !== '') return;
|
||||
app.previousUrl = window.location.href;
|
||||
|
||||
if (!e.ctrlKey && e.which === 1) {
|
||||
if (ajaxify.go(url)) {
|
||||
if (this.host === window.location.host) {
|
||||
// Internal link
|
||||
var url = this.href.replace(rootUrl + '/', '');
|
||||
|
||||
if (ajaxify.go(url)) {
|
||||
|
||||
e.preventDefault();
|
||||
}
|
||||
} else if (window.location.pathname !== '/outgoing') {
|
||||
// External Link
|
||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function exec_body_scripts(body_el) {
|
||||
// modified from http://stackoverflow.com/questions/2592092/executing-script-elements-inserted-with-innerhtml
|
||||
|
||||
function nodeName(elem, name) {
|
||||
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
|
||||
};
|
||||
}
|
||||
|
||||
function evalScript(elem) {
|
||||
var data = (elem.text || elem.textContent || elem.innerHTML || "" ),
|
||||
head = document.getElementsByTagName("head")[0] ||
|
||||
document.documentElement,
|
||||
var data = (elem.text || elem.textContent || elem.innerHTML || ""),
|
||||
head = document.getElementsByTagName("head")[0] ||
|
||||
document.documentElement,
|
||||
script = document.createElement("script");
|
||||
|
||||
script.type = "text/javascript";
|
||||
try {
|
||||
script.appendChild(document.createTextNode(data));
|
||||
} catch(e) {
|
||||
script.text = data;
|
||||
script.appendChild(document.createTextNode(data));
|
||||
} catch (e) {
|
||||
script.text = data;
|
||||
}
|
||||
|
||||
if (elem.src) {
|
||||
@@ -141,18 +161,18 @@ var ajaxify = {};
|
||||
head.insertBefore(script, head.firstChild);
|
||||
//TODO: remove from head before inserting?, doing this breaks scripts in safari so commented out for now
|
||||
//head.removeChild(script);
|
||||
};
|
||||
}
|
||||
|
||||
var scripts = [],
|
||||
script,
|
||||
children_nodes = body_el.childNodes,
|
||||
children_nodes = $(body_el).children(),
|
||||
child,
|
||||
i;
|
||||
|
||||
for (i = 0; children_nodes[i]; i++) {
|
||||
child = children_nodes[i];
|
||||
if (nodeName(child, "script" ) &&
|
||||
(!child.type || child.type.toLowerCase() === "text/javascript")) {
|
||||
if (nodeName(child, "script") &&
|
||||
(!child.type || child.type.toLowerCase() === "text/javascript")) {
|
||||
scripts.push(child);
|
||||
}
|
||||
}
|
||||
@@ -164,6 +184,6 @@ var ajaxify = {};
|
||||
}
|
||||
evalScript(scripts[i]);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
@@ -1,119 +1,118 @@
|
||||
var socket,
|
||||
config,
|
||||
app = {},
|
||||
API_URL = null;
|
||||
app = {};
|
||||
|
||||
|
||||
(function() {
|
||||
(function () {
|
||||
var showWelcomeMessage = false;
|
||||
|
||||
function loadConfig() {
|
||||
app.loadConfig = function() {
|
||||
|
||||
$.ajax({
|
||||
url: RELATIVE_PATH + '/api/config',
|
||||
success: function(data) {
|
||||
API_URL = data.api_url;
|
||||
|
||||
success: function (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 reconnectTries = 0;
|
||||
var reconnecting = false,
|
||||
reconnectEl, reconnectTimer;
|
||||
|
||||
socket.on('event:connect', function(data) {
|
||||
console.log('connected to nodebb socket: ', data);
|
||||
app.username = data.username;
|
||||
app.showLoginMessage();
|
||||
});
|
||||
|
||||
socket.on('event:alert', function(data) {
|
||||
app.alert(data);
|
||||
});
|
||||
|
||||
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;
|
||||
reconnectTries = 0;
|
||||
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function(data) {
|
||||
function showDisconnectModal() {
|
||||
$('#disconnect-modal').modal({
|
||||
backdrop:'static',
|
||||
show:true
|
||||
socket.on('event:connect', function (data) {
|
||||
app.username = data.username;
|
||||
app.showLoginMessage();
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
|
||||
$('#reload-button').on('click',function(){
|
||||
$('#disconnect-modal').modal('hide');
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
reconnecting = true;
|
||||
reconnectTries++;
|
||||
|
||||
if(reconnectTries > 4) {
|
||||
showDisconnectModal();
|
||||
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) {
|
||||
jQuery('.username-field').each(function() {
|
||||
if (this.processed === true)
|
||||
return;
|
||||
socket.on('event:alert', function (data) {
|
||||
app.alert(data);
|
||||
});
|
||||
|
||||
var el = jQuery(this),
|
||||
uid = el.parents('li').attr('data-uid');
|
||||
socket.on('connect', function (data) {
|
||||
if (reconnecting) {
|
||||
reconnectEl.html('<i class="icon-ok"></i> Connected!');
|
||||
reconnecting = false;
|
||||
|
||||
if (uid && jQuery.inArray(uid, users) !== -1) {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle"></i>');
|
||||
} else {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle-blank"></i>');
|
||||
setTimeout(function() {
|
||||
reconnectEl.removeClass('active');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
el.processed = true;
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
socket.on('event:disconnect', function() {
|
||||
socket.socket.connect();
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function (data) {
|
||||
if (!reconnectEl) reconnectEl = $('#reconnect');
|
||||
reconnecting = true;
|
||||
|
||||
reconnectEl.addClass('active');
|
||||
reconnectEl.html('<i class="icon-spinner icon-spin"></i> Reconnecting...');
|
||||
});
|
||||
|
||||
socket.on('api:user.get_online_users', function (users) {
|
||||
jQuery('a.username-field').each(function () {
|
||||
if (this.processed === true)
|
||||
return;
|
||||
|
||||
var el = jQuery(this),
|
||||
uid = el.parents('li').attr('data-uid');
|
||||
|
||||
if (uid && jQuery.inArray(uid, users) !== -1) {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle"></i>');
|
||||
} else {
|
||||
el.find('i').remove();
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
}
|
||||
},
|
||||
async: false
|
||||
});
|
||||
}
|
||||
|
||||
// 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,");
|
||||
}
|
||||
|
||||
// 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>)
|
||||
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;
|
||||
|
||||
return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
|
||||
@@ -127,14 +126,14 @@ var socket,
|
||||
// message = alert message content
|
||||
// timeout default = permanent
|
||||
// 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 = $('#'+alert_id);
|
||||
var alert = $('#' + alert_id);
|
||||
|
||||
function startTimeout(div, timeout) {
|
||||
var timeoutId = setTimeout(function() {
|
||||
$(div).fadeOut(1000, function() {
|
||||
var timeoutId = setTimeout(function () {
|
||||
$(div).fadeOut(1000, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, timeout);
|
||||
@@ -142,15 +141,14 @@ var socket,
|
||||
$(div).attr('timeoutId', timeoutId);
|
||||
}
|
||||
|
||||
if(alert.length > 0) {
|
||||
if (alert.length > 0) {
|
||||
alert.find('strong').html(params.title);
|
||||
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'));
|
||||
startTimeout(alert, params.timeout);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
var div = document.createElement('div'),
|
||||
button = document.createElement('button'),
|
||||
strong = document.createElement('strong'),
|
||||
@@ -159,7 +157,7 @@ var socket,
|
||||
p.innerHTML = params.message;
|
||||
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.appendChild(button);
|
||||
@@ -168,23 +166,23 @@ var socket,
|
||||
|
||||
button.className = 'close';
|
||||
button.innerHTML = '×';
|
||||
button.onclick = function(ev) {
|
||||
button.onclick = function (ev) {
|
||||
div.parentNode.removeChild(div);
|
||||
}
|
||||
|
||||
if (params.location == null)
|
||||
params.location = 'alert_window';
|
||||
|
||||
jQuery('#'+params.location).prepend(jQuery(div).fadeIn('100'));
|
||||
jQuery('#' + params.location).prepend(jQuery(div).fadeIn('100'));
|
||||
|
||||
if (params.timeout) {
|
||||
startTimeout(div, params.timeout);
|
||||
}
|
||||
|
||||
if (params.clickfn) {
|
||||
div.onclick = function() {
|
||||
div.onclick = function () {
|
||||
params.clickfn();
|
||||
jQuery(div).fadeOut(500, function() {
|
||||
jQuery(div).fadeOut(500, function () {
|
||||
this.remove();
|
||||
});
|
||||
}
|
||||
@@ -192,8 +190,8 @@ var socket,
|
||||
}
|
||||
}
|
||||
|
||||
app.alertSuccess = function(message, timeout) {
|
||||
if(!timeout)
|
||||
app.alertSuccess = function (message, timeout) {
|
||||
if (!timeout)
|
||||
timeout = 2000;
|
||||
|
||||
app.alert({
|
||||
@@ -204,21 +202,21 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
app.alertError = function(message, timeout) {
|
||||
if(!timeout)
|
||||
app.alertError = function (message, timeout) {
|
||||
if (!timeout)
|
||||
timeout = 2000;
|
||||
|
||||
app.alert({
|
||||
title: 'Error',
|
||||
message: message,
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
timeout: timeout
|
||||
});
|
||||
}
|
||||
|
||||
app.current_room = null;
|
||||
app.enter_room = function(room) {
|
||||
if(socket) {
|
||||
app.enter_room = function (room) {
|
||||
if (socket) {
|
||||
if (app.current_room === room)
|
||||
return;
|
||||
|
||||
@@ -231,20 +229,20 @@ var socket,
|
||||
}
|
||||
};
|
||||
|
||||
app.populate_online_users = function() {
|
||||
app.populate_online_users = function () {
|
||||
var uids = [];
|
||||
|
||||
jQuery('.post-row').each(function() {
|
||||
jQuery('.post-row').each(function () {
|
||||
uids.push(this.getAttribute('data-uid'));
|
||||
});
|
||||
|
||||
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.
|
||||
require(['mobileMenu'], function(mobileMenu) {
|
||||
require(['mobileMenu'], function (mobileMenu) {
|
||||
mobileMenu.onNavigate();
|
||||
});
|
||||
|
||||
@@ -252,13 +250,13 @@ var socket,
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('#main-nav li').removeClass('active');
|
||||
if(active) {
|
||||
jQuery('#main-nav li a').each(function() {
|
||||
if (active) {
|
||||
jQuery('#main-nav li a').each(function () {
|
||||
var href = this.getAttribute('href');
|
||||
if(active.match(/^users/))
|
||||
if (active == "sort-posts" || active == "sort-reputation" || active == "search" || active == "latest" || active == "online")
|
||||
active = 'users';
|
||||
if (href && href.match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
@@ -267,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.
|
||||
}, 100);
|
||||
}
|
||||
|
||||
app.showLoginMessage = function() {
|
||||
app.showLoginMessage = function () {
|
||||
function showAlert() {
|
||||
app.alert({
|
||||
type: 'success',
|
||||
@@ -282,9 +283,9 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
if(showWelcomeMessage) {
|
||||
if (showWelcomeMessage) {
|
||||
showWelcomeMessage = false;
|
||||
if(document.readyState !== 'complete') {
|
||||
if (document.readyState !== 'complete') {
|
||||
$(document).ready(showAlert);
|
||||
} else {
|
||||
showAlert();
|
||||
@@ -292,56 +293,90 @@ var socket,
|
||||
}
|
||||
}
|
||||
|
||||
app.addCommasToNumbers = function() {
|
||||
$('.formatted-number').each(function(index, element) {
|
||||
app.addCommasToNumbers = function () {
|
||||
$('.formatted-number').each(function (index, element) {
|
||||
$(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';
|
||||
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data),
|
||||
uniqueid = new Date().getTime(),
|
||||
tempContainer = jQuery('<div id="' + uniqueid + '"></div>')
|
||||
.appendTo("#post-container")
|
||||
.hide()
|
||||
.append(html)
|
||||
.fadeIn('slow');
|
||||
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var uniqueid = new Date().getTime(),
|
||||
tempContainer = jQuery('<div id="' + uniqueid + '"></div>')
|
||||
.appendTo("#post-container")
|
||||
.hide()
|
||||
.append(translatedHTML)
|
||||
.fadeIn('slow');
|
||||
|
||||
for(var x=0,numPosts=data.posts.length;x<numPosts;x++) {
|
||||
socket.emit('api:post.privileges', data.posts[x].pid);
|
||||
}
|
||||
for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) {
|
||||
socket.emit('api:post.privileges', data.posts[x].pid);
|
||||
}
|
||||
|
||||
tempContainer.replaceWith(tempContainer.contents());
|
||||
infiniteLoaderActive = false;
|
||||
tempContainer.replaceWith(tempContainer.contents());
|
||||
infiniteLoaderActive = false;
|
||||
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
$('span.timeago').timeago();
|
||||
});
|
||||
}
|
||||
|
||||
app.infiniteLoaderActive = false;
|
||||
|
||||
app.loadMorePosts = function(tid, callback) {
|
||||
if(app.infiniteLoaderActive)
|
||||
app.loadMorePosts = function (tid, callback) {
|
||||
if (app.infiniteLoaderActive)
|
||||
return;
|
||||
app.infiniteLoaderActive = true;
|
||||
|
||||
if ($('#loading-indicator').attr('done') === '0')
|
||||
$('#loading-indicator').removeClass('hide');
|
||||
|
||||
socket.emit('api:topic.loadMore', {
|
||||
tid: tid,
|
||||
after: document.querySelectorAll('#post-container li[data-pid]').length
|
||||
}, function(data) {
|
||||
}, function (data) {
|
||||
app.infiniteLoaderActive = false;
|
||||
if(data.posts.length) {
|
||||
if (data.posts.length) {
|
||||
$('#loading-indicator').attr('done', '0');
|
||||
app.createNewPosts(data);
|
||||
} else {
|
||||
$('#loading-indicator').attr('done', '1');
|
||||
}
|
||||
|
||||
if(callback)
|
||||
$('#loading-indicator').addClass('hide');
|
||||
if (callback)
|
||||
callback(data.posts);
|
||||
});
|
||||
}
|
||||
|
||||
app.scrollToPost = function(pid) {
|
||||
app.scrollToTop = function () {
|
||||
$('body,html').animate({
|
||||
scrollTop: 0
|
||||
});
|
||||
};
|
||||
|
||||
if(!pid)
|
||||
app.scrollToBottom = function () {
|
||||
$('body,html').animate({
|
||||
scrollTop: $('html').height() - 100
|
||||
});
|
||||
}
|
||||
|
||||
app.scrollToPost = function (pid) {
|
||||
|
||||
if (!pid)
|
||||
return;
|
||||
|
||||
var container = $(document.body),
|
||||
@@ -351,96 +386,42 @@ var socket,
|
||||
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) {
|
||||
if (!scrollTo.length && tid) {
|
||||
|
||||
var intervalID = setInterval(function() {
|
||||
app.loadMorePosts(tid, function(posts) {
|
||||
var intervalID = setInterval(function () {
|
||||
app.loadMorePosts(tid, function (posts) {
|
||||
scrollTo = $('#post_anchor_' + pid);
|
||||
|
||||
if(tid && scrollTo.length) {
|
||||
if (tid && scrollTo.length) {
|
||||
animateScroll();
|
||||
}
|
||||
|
||||
if(!posts.length || scrollTo.length)
|
||||
if (!posts.length || scrollTo.length)
|
||||
clearInterval(intervalID);
|
||||
});
|
||||
}, 100);
|
||||
|
||||
} else if(tid) {
|
||||
} 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');
|
||||
ajaxify.go("search/"+input.val(), null, "search");
|
||||
ajaxify.go("search/" + input.val(), null, "search");
|
||||
input.val('');
|
||||
return false;
|
||||
})
|
||||
});
|
||||
});
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
}
|
||||
}());
|
||||
}());
|
||||
@@ -4,26 +4,32 @@
|
||||
isFollowing = templates.get('isFollowing');
|
||||
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enter_room('user/' + theirid);
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
|
||||
if(yourid !== theirid) {
|
||||
if(isFollowing) {
|
||||
if (yourid !== theirid) {
|
||||
if (isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
} else {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
} else {
|
||||
followBtn.hide();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {uid: theirid}, function(success) {
|
||||
var username = $('.account-username a').html();
|
||||
if(success) {
|
||||
socket.emit('api:user.follow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
@@ -35,9 +41,10 @@
|
||||
});
|
||||
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {uid: theirid}, function(success) {
|
||||
var username = $('.account-username a').html();
|
||||
if(success) {
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
@@ -51,20 +58,27 @@
|
||||
$('.user-recent-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
|
||||
|
||||
var onlineStatus = $('.account-online-status');
|
||||
|
||||
socket.on('api:user.isOnline', function(online) {
|
||||
if(online) {
|
||||
|
||||
function handleUserOnline(data) {
|
||||
if (data.online) {
|
||||
onlineStatus.find('span span').text('online');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle');
|
||||
} else {
|
||||
onlineStatus.find('span span').text('offline');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle-blank');
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
});
|
||||
|
||||
socket.emit('api:user.isOnline', theirid);
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -1,212 +1,214 @@
|
||||
|
||||
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
|
||||
|
||||
|
||||
$('#uploadForm').submit(function() {
|
||||
status('uploading the file ...');
|
||||
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show();
|
||||
|
||||
if(!$('#userPhotoInput').val()) {
|
||||
$('#upload-progress-box').removeClass('hide');
|
||||
|
||||
if (!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
$(this).find('#imageUploadCsrf').val($('#csrf_token').val());
|
||||
|
||||
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
|
||||
|
||||
error: function(xhr) {
|
||||
error('Error: ' + xhr.status);
|
||||
},
|
||||
|
||||
uploadProgress : function(event, position, total, percent) {
|
||||
$('#upload-progress-bar').css('width', percent+'%');
|
||||
|
||||
uploadProgress: function(event, position, total, percent) {
|
||||
console.log(percent);
|
||||
$('#upload-progress-bar').css('width', percent + '%');
|
||||
},
|
||||
|
||||
|
||||
|
||||
|
||||
success: function(response) {
|
||||
if(response.error) {
|
||||
if (response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var imageUrlOnServer = response.path;
|
||||
|
||||
|
||||
$('#user-current-picture').attr('src', imageUrlOnServer);
|
||||
$('#user-uploaded-picture').attr('src', imageUrlOnServer);
|
||||
|
||||
uploadedPicture = imageUrlOnServer;
|
||||
|
||||
|
||||
uploadedPicture = imageUrlOnServer;
|
||||
|
||||
setTimeout(function() {
|
||||
hideAlerts();
|
||||
$('#upload-picture-modal').modal('hide');
|
||||
}, 750);
|
||||
|
||||
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
success('File uploaded successfully!');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
function hideAlerts() {
|
||||
$('#alert-status').hide();
|
||||
$('#alert-success').hide();
|
||||
$('#alert-error').hide();
|
||||
$('#upload-progress-box').hide();
|
||||
$('#alert-status').addClass('hide');
|
||||
$('#alert-success').addClass('hide');
|
||||
$('#alert-error').addClass('hide');
|
||||
$('#upload-progress-box').addClass('hide');
|
||||
}
|
||||
|
||||
|
||||
function status(message) {
|
||||
hideAlerts();
|
||||
$('#alert-status').text(message).show();
|
||||
$('#alert-status').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
function success(message) {
|
||||
hideAlerts();
|
||||
$('#alert-success').text(message).show();
|
||||
$('#alert-success').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
function error(message) {
|
||||
hideAlerts();
|
||||
$('#alert-error').text(message).show();
|
||||
$('#alert-error').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function changeUserPicture(type) {
|
||||
|
||||
function changeUserPicture(type) {
|
||||
var userData = {
|
||||
type: type
|
||||
};
|
||||
|
||||
|
||||
socket.emit('api:user.changePicture', userData, function(success) {
|
||||
if(!success) {
|
||||
if (!success) {
|
||||
app.alertError('There was an error changing picture!');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
var selectedImageType = '';
|
||||
|
||||
$('#submitBtn').on('click',function(){
|
||||
|
||||
var userData = {
|
||||
uid:$('#inputUID').val(),
|
||||
email:$('#inputEmail').val(),
|
||||
fullname:$('#inputFullname').val(),
|
||||
website:$('#inputWebsite').val(),
|
||||
birthday:$('#inputBirthday').val(),
|
||||
location:$('#inputLocation').val(),
|
||||
signature:$('#inputSignature').val()
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
location: $('#inputLocation').val(),
|
||||
signature: $('#inputSignature').val()
|
||||
};
|
||||
|
||||
socket.emit('api:user.updateProfile', userData, function(data) {
|
||||
if(data.success) {
|
||||
socket.emit('api:user.updateProfile', userData, function(err, data) {
|
||||
if (data.success) {
|
||||
app.alertSuccess('Your profile has been updated successfully!');
|
||||
if(data.picture) {
|
||||
if (data.picture) {
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
$('#user_label img').attr('src', data.picture);
|
||||
}
|
||||
if(data.gravatarpicture) {
|
||||
if (data.gravatarpicture) {
|
||||
$('#user-gravatar-picture').attr('src', data.gravatarpicture);
|
||||
gravatarPicture = data.gravatarpicture;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
app.alertError('There was an error updating your profile!');
|
||||
app.alertError('There was an error updating your profile! ' + err.error);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
function updateImages() {
|
||||
var currentPicture = $('#user-current-picture').attr('src');
|
||||
|
||||
if(gravatarPicture) {
|
||||
if (gravatarPicture) {
|
||||
$('#user-gravatar-picture').attr('src', gravatarPicture);
|
||||
$('#gravatar-box').show();
|
||||
}
|
||||
else
|
||||
} else
|
||||
$('#gravatar-box').hide();
|
||||
|
||||
if(uploadedPicture) {
|
||||
if (uploadedPicture) {
|
||||
$('#user-uploaded-picture').attr('src', uploadedPicture);
|
||||
$('#uploaded-box').show();
|
||||
}
|
||||
else
|
||||
} else
|
||||
$('#uploaded-box').hide();
|
||||
|
||||
|
||||
if(currentPicture == gravatarPicture)
|
||||
|
||||
|
||||
if (currentPicture == gravatarPicture)
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
else
|
||||
$('#gravatar-box .icon-ok').hide();
|
||||
|
||||
if(currentPicture == uploadedPicture)
|
||||
|
||||
if (currentPicture == uploadedPicture)
|
||||
$('#uploaded-box .icon-ok').show();
|
||||
else
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
updateImages();
|
||||
|
||||
|
||||
$('#change-picture-modal').modal('show');
|
||||
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function(){
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
$('#uploaded-box').on('click', function(){
|
||||
|
||||
$('#uploaded-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').hide();
|
||||
$('#uploaded-box .icon-ok').show();
|
||||
selectedImageType = 'uploaded';
|
||||
});
|
||||
|
||||
|
||||
$('#savePictureChangesBtn').on('click', function() {
|
||||
$('#change-picture-modal').modal('hide');
|
||||
|
||||
if(selectedImageType) {
|
||||
if (selectedImageType) {
|
||||
changeUserPicture(selectedImageType);
|
||||
|
||||
if(selectedImageType == 'gravatar')
|
||||
$('#user-current-picture').attr('src', gravatarPicture);
|
||||
else if(selectedImageType == 'uploaded')
|
||||
$('#user-current-picture').attr('src', uploadedPicture);
|
||||
|
||||
if (selectedImageType == 'gravatar')
|
||||
$('#user-current-picture').attr('src', gravatarPicture);
|
||||
else if (selectedImageType == 'uploaded')
|
||||
$('#user-current-picture').attr('src', uploadedPicture);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
$('#upload-picture-modal').on('hide', function() {
|
||||
$('#userPhotoInput').val('');
|
||||
});
|
||||
|
||||
$('#uploadPictureBtn').on('click', function(){
|
||||
|
||||
|
||||
$('#uploadPictureBtn').on('click', function() {
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
$('#upload-picture-modal').modal('show');
|
||||
$('#upload-picture-modal').removeClass('hide');
|
||||
|
||||
hideAlerts();
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
$('#pictureUploadSubmitBtn').on('click', function() {
|
||||
$('#uploadForm').submit();
|
||||
});
|
||||
|
||||
|
||||
(function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
@@ -219,59 +221,71 @@ $(document).ready(function() {
|
||||
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < 6) {
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
password_notify.html('Password too short');
|
||||
password_notify.attr('class', 'label label-important');
|
||||
} else if(!passwordvalid) {
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else if (!passwordvalid) {
|
||||
password_notify.html('Invalid password');
|
||||
password_notify.attr('class', 'label label-important');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else {
|
||||
password_notify.html('OK!');
|
||||
password_notify.attr('class', 'label label-success');
|
||||
password_notify.attr('class', 'alert alert-success');
|
||||
password_notify.removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
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.attr('class', 'label label-important');
|
||||
password_confirm_notify.attr('class', 'alert alert-danger');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('keyup', onPasswordChanged);
|
||||
password_confirm.on('keyup', onPasswordConfirmChanged);
|
||||
password.on('blur', onPasswordChanged);
|
||||
password_confirm.on('blur', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
|
||||
if(passwordvalid && passwordsmatch && currentPassword.val()) {
|
||||
socket.emit('api:user.changePassword', {'currentPassword': currentPassword.val(),'newPassword': password.val() }, function(data) {
|
||||
|
||||
|
||||
if (passwordvalid && passwordsmatch && currentPassword.val()) {
|
||||
socket.emit('api:user.changePassword', {
|
||||
'currentPassword': currentPassword.val(),
|
||||
'newPassword': password.val()
|
||||
}, function(err) {
|
||||
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
password_notify.html('');
|
||||
password_confirm_notify.html('');
|
||||
password_notify.addClass('hide');
|
||||
password_confirm_notify.addClass('hide');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if(data.err) {
|
||||
app.alertError(data.err);
|
||||
|
||||
if (err) {
|
||||
app.alertError(err.error);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
app.alertSuccess('Your password is updated!');
|
||||
|
||||
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
}());
|
||||
});
|
||||
@@ -6,31 +6,37 @@
|
||||
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="/users/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/users/' + userslug + '/favourites">favourites</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>\
|
||||
<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() {
|
||||
|
||||
|
||||
createMenu();
|
||||
|
||||
var editLink = $('#editLink');
|
||||
var settingsLink = $('#settingsLink');
|
||||
var favouritesLink = $('#favouritesLink');
|
||||
|
||||
if(yourid === "0" || yourid !== theirid) {
|
||||
|
||||
if (yourid === "0" || yourid !== theirid) {
|
||||
editLink.hide();
|
||||
settingsLink.hide();
|
||||
favouritesLink.hide();
|
||||
}
|
||||
|
||||
|
||||
jQuery('.account-sub-links span a').removeClass('bold').each(function() {
|
||||
var href = this.getAttribute('href');
|
||||
if (window.location.href.indexOf(href) !== -1) {
|
||||
jQuery(this).addClass('bold');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -1,16 +1,13 @@
|
||||
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked')?1:0
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if(success) {
|
||||
if (success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
|
||||
var modified_categories = {};
|
||||
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('li').attr('data-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() {
|
||||
@@ -14,25 +13,28 @@ function save() {
|
||||
}
|
||||
|
||||
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).parent().addClass('selected');
|
||||
if (selected)
|
||||
jQuery('#icons .' + selected).parent().addClass('selected');
|
||||
|
||||
|
||||
bootbox.confirm('<h2>Select an icon.</h2>' + document.getElementById('icons').innerHTML, function(confirm) {
|
||||
if (confirm) {
|
||||
var iconClass = jQuery('.bootbox .selected').children(':first').attr('class');
|
||||
el.className = iconClass + ' icon icon-2x';
|
||||
el.value = iconClass;
|
||||
el.attr('class', iconClass + ' icon-2x');
|
||||
el.val(iconClass);
|
||||
|
||||
modified(el);
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('.bootbox .span3').on('click', function() {
|
||||
jQuery('.bootbox .selected').removeClass('selected');
|
||||
jQuery(this).addClass('selected');
|
||||
});
|
||||
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(this).addClass('selected');
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
|
||||
@@ -46,12 +48,44 @@ 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 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() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
@@ -62,9 +96,11 @@ jQuery('.blockclass').each(function() {
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
jQuery('#addNew').on('click', showCreateCategoryModal);
|
||||
jQuery('#create-category-btn').on('click', createNewCategory);
|
||||
|
||||
jQuery('.icon').on('click', function(ev) {
|
||||
select_icon(ev.target);
|
||||
jQuery('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('.blockclass').on('change', function(ev) {
|
||||
@@ -77,7 +113,7 @@ jQuery('.blockclass').each(function() {
|
||||
|
||||
jQuery('.entry-row button').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if(disabled == "0" || disabled == "")
|
||||
if (disabled == "0" || disabled == "")
|
||||
$(element).html('Disable');
|
||||
else
|
||||
$(element).html('Enable');
|
||||
@@ -89,7 +125,7 @@ jQuery('.blockclass').each(function() {
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = btn.html() == "Disable" ? "1":"0";
|
||||
var disabled = btn.html() == "Disable" ? "1" : "0";
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
@@ -99,5 +135,5 @@ jQuery('.blockclass').each(function() {
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}());
|
||||
@@ -1,10 +1,7 @@
|
||||
|
||||
|
||||
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
|
||||
|
||||
nodebb_admin.config = undefined;
|
||||
|
||||
|
||||
nodebb_admin.prepare = function() {
|
||||
// Come back in 500ms if the config isn't ready yet
|
||||
if (nodebb_admin.config === undefined) {
|
||||
@@ -19,21 +16,21 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
numFields = fields.length,
|
||||
saveBtn = document.getElementById('save'),
|
||||
x, key, inputType;
|
||||
for(x=0;x<numFields;x++) {
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
inputType = fields[x].getAttribute('type');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
if (nodebb_admin.config[key]) {
|
||||
switch(inputType) {
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
fields[x].value = nodebb_admin.config[key];
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
fields[x].checked = nodebb_admin.config[key] === '1' ? true : false;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
@@ -45,32 +42,35 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
var key, value;
|
||||
e.preventDefault();
|
||||
|
||||
for(x=0;x<numFields;x++) {
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
inputType = fields[x].getAttribute('type');
|
||||
switch(inputType) {
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
value = fields[x].value;
|
||||
break;
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
value = fields[x].checked ? '1' : '0';
|
||||
break;
|
||||
break;
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', { key: key, value: value });
|
||||
socket.emit('api:config.set', {
|
||||
key: key,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
nodebb_admin.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
}
|
||||
socket.emit('api:config.remove', key);
|
||||
}
|
||||
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
@@ -82,7 +82,7 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
menuEl.addEventListener('click', function(e) {
|
||||
parentEl = e.target.parentNode;
|
||||
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');
|
||||
else jQuery(parentEl).addClass('active');
|
||||
}
|
||||
@@ -93,7 +93,7 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
socket.once('api:config.get', function(config) {
|
||||
nodebb_admin.config = config;
|
||||
});
|
||||
|
||||
|
||||
socket.emit('api:config.get');
|
||||
|
||||
socket.on('api:config.set', function(data) {
|
||||
@@ -102,7 +102,7 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
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'
|
||||
});
|
||||
} else {
|
||||
@@ -111,12 +111,11 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
timeout: 2500,
|
||||
title: 'Changes Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'error'
|
||||
type: 'danger'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
194
public/src/forum/admin/groups.js
Normal file
194
public/src/forum/admin/groups.js
Normal 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');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1,18 +1,18 @@
|
||||
|
||||
(function() {
|
||||
|
||||
|
||||
ajaxify.register_events(['api:get_all_rooms']);
|
||||
socket.on('api:get_all_rooms', function(data) {
|
||||
|
||||
var active_users = document.getElementById('active_users'),
|
||||
total = 0;
|
||||
active_users.innerHTML = '';
|
||||
|
||||
for(var room in data) {
|
||||
|
||||
for (var room in data) {
|
||||
if (room !== '') {
|
||||
var count = data[room].length;
|
||||
total += count;
|
||||
active_users.innerHTML = active_users.innerHTML + "<div class='alert alert-success'><strong>" + room + "</strong> " + count + " active user" + (count > 1 ? "s" : "") + "</div>";
|
||||
active_users.innerHTML = active_users.innerHTML + "<div class='alert alert-success'><strong>" + room + "</strong> " + count + " active user" + (count > 1 ? "s" : "") + "</div>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
var nodebb_admin = nodebb_admin || {};
|
||||
var nodebb_admin = nodebb_admin || {};
|
||||
|
||||
(function() {
|
||||
var plugins = {
|
||||
init: function() {
|
||||
var pluginsList = $('.plugins'),
|
||||
numPlugins = pluginsList[0].querySelectorAll('li').length,
|
||||
pluginID, pluginTgl;
|
||||
var plugins = {
|
||||
init: function() {
|
||||
var pluginsList = $('.plugins'),
|
||||
numPlugins = pluginsList[0].querySelectorAll('li').length,
|
||||
pluginID, pluginTgl;
|
||||
|
||||
if (numPlugins > 0) {
|
||||
pluginsList.on('click', 'button[data-action="toggleActive"]', function() {
|
||||
pluginID = $(this).parents('li').attr('data-plugin-id');
|
||||
socket.emit('api:admin.plugins.toggle', pluginID);
|
||||
});
|
||||
if (numPlugins > 0) {
|
||||
pluginsList.on('click', 'button[data-action="toggleActive"]', function() {
|
||||
pluginID = $(this).parents('li').attr('data-plugin-id');
|
||||
socket.emit('api:admin.plugins.toggle', pluginID);
|
||||
});
|
||||
|
||||
socket.on('api:admin.plugins.toggle', function(status) {
|
||||
pluginTgl = document.querySelector('.plugins li[data-plugin-id="' + status.id + '"] button');
|
||||
pluginTgl.innerHTML = '<i class="icon-off"></i> ' + (status.active ? 'Dea' : 'A') + 'ctivate';
|
||||
socket.on('api:admin.plugins.toggle', function(status) {
|
||||
pluginTgl = document.querySelector('.plugins li[data-plugin-id="' + status.id + '"] button');
|
||||
pluginTgl.innerHTML = '<i class="icon-off"></i> ' + (status.active ? 'Dea' : 'A') + 'ctivate';
|
||||
|
||||
app.alert({
|
||||
alert_id: 'plugin_toggled_' + status.id,
|
||||
title: 'Plugin Enabled',
|
||||
message: 'You may need to restart NodeBB in order for these changes to be reflected.',
|
||||
type: 'notify',
|
||||
timeout: 5000
|
||||
})
|
||||
});
|
||||
} else {
|
||||
pluginsList.append('<li><p><i>No plugins found.</i></p></li>');
|
||||
}
|
||||
app.alert({
|
||||
alert_id: 'plugin_toggled_' + status.id,
|
||||
title: 'Plugin Enabled',
|
||||
message: 'You may need to restart NodeBB in order for these changes to be reflected.',
|
||||
type: 'warning',
|
||||
timeout: 5000
|
||||
})
|
||||
});
|
||||
} else {
|
||||
pluginsList.append('<li><p><i>No plugins found.</i></p></li>');
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
nodebb_admin.plugins = plugins;
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
|
||||
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
|
||||
var themes = {};
|
||||
@@ -10,60 +8,62 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
themeContainer = document.querySelector('#bootstrap_themes'),
|
||||
numThemes = bootswatch.themes.length;
|
||||
|
||||
for(var x=0;x<numThemes;x++) {
|
||||
for (var x = 0; x < numThemes; x++) {
|
||||
var theme = bootswatch.themes[x];
|
||||
themeEl.setAttribute('data-css', theme.cssMin);
|
||||
themeEl.setAttribute('data-theme', theme.name);
|
||||
themeEl.innerHTML = '<img src="' + theme.thumbnail + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + theme.name + '</h4>' +
|
||||
'<p>' + theme.description + '</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
themeEl.innerHTML = '<img src="' + theme.thumbnail + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn btn-default" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + theme.name + '</h4>' +
|
||||
'<p>' + theme.description + '</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
themeFrag.appendChild(themeEl.cloneNode(true));
|
||||
}
|
||||
themeContainer.innerHTML = '';
|
||||
themeContainer.appendChild(themeFrag);
|
||||
}
|
||||
|
||||
|
||||
nodebb_admin.themes = themes;
|
||||
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
|
||||
|
||||
(function() {
|
||||
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);
|
||||
|
||||
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
|
||||
var bootstrapThemeContainer = document.querySelector('#bootstrap_themes'),
|
||||
installedThemeContainer = document.querySelector('#installed_themes'),
|
||||
themeEvent = function(e) {
|
||||
if (e.target.hasAttribute('data-action')) {
|
||||
switch(e.target.getAttribute('data-action')) {
|
||||
switch (e.target.getAttribute('data-action')) {
|
||||
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.href = cssSrc;
|
||||
break;
|
||||
break;
|
||||
case 'use':
|
||||
var parentEl = $(e.target).parents('li'),
|
||||
var parentEl = $(e.target).parents('li'),
|
||||
cssSrc = parentEl.attr('data-css'),
|
||||
cssName = parentEl.attr('data-theme');
|
||||
socket.emit('api:config.set', {
|
||||
key: 'theme:id', value: 'bootswatch:' + cssName
|
||||
key: 'theme:id',
|
||||
value: 'bootswatch:' + cssName
|
||||
});
|
||||
socket.emit('api:config.set', {
|
||||
key: 'theme:src', value: cssSrc
|
||||
key: 'theme:src',
|
||||
value: cssSrc
|
||||
});
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -82,27 +82,27 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
|
||||
// Installed Themes
|
||||
socket.emit('api:admin.themes.getInstalled', function(themes) {
|
||||
var instListEl = document.getElementById('installed_themes'),
|
||||
var instListEl = document.getElementById('installed_themes'),
|
||||
themeFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
|
||||
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-css', themes[x].src);
|
||||
liEl.innerHTML = '<img src="' + themes[x].screenshot + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + themes[x].name + '</h4>' +
|
||||
'<p>' +
|
||||
themes[x].description +
|
||||
(themes[x].url ? ' (<a href="' + themes[x].url + '">Homepage</a>)' : '') +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
liEl.innerHTML = '<img src="' + themes[x].screenshot + '" />' +
|
||||
'<div>' +
|
||||
'<div class="pull-right">' +
|
||||
'<button class="btn btn-primary" data-action="use">Use</button> ' +
|
||||
'<button class="btn btn-default" data-action="preview">Preview</button>' +
|
||||
'</div>' +
|
||||
'<h4>' + themes[x].name + '</h4>' +
|
||||
'<p>' +
|
||||
themes[x].description +
|
||||
(themes[x].url ? ' (<a href="' + themes[x].url + '">Homepage</a>)' : '') +
|
||||
'</p>' +
|
||||
'</div>' +
|
||||
'<div class="clear">';
|
||||
themeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -7,25 +7,37 @@ $(document).ready(function() {
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('data-tid');
|
||||
|
||||
switch(action) {
|
||||
switch (action) {
|
||||
case 'pin':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.pin', { tid: tid });
|
||||
else socket.emit('api:topic.unpin', { tid: tid });
|
||||
break;
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.pin', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.unpin', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'lock':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.lock', { tid: tid });
|
||||
else socket.emit('api:topic.unlock', { tid: tid });
|
||||
break;
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.lock', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.unlock', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
case 'delete':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.delete', { tid: tid });
|
||||
else socket.emit('api:topic.restore', { tid: tid });
|
||||
break;
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.delete', {
|
||||
tid: tid
|
||||
});
|
||||
else socket.emit('api:topic.restore', {
|
||||
tid: tid
|
||||
});
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
loadMoreEl.addEventListener('click', function() {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
var topics = document.querySelectorAll('.topics li[data-tid]'),
|
||||
var topics = document.querySelectorAll('.topics li[data-tid]'),
|
||||
lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||
|
||||
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
||||
@@ -37,9 +49,9 @@ $(document).ready(function() {
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
@@ -54,9 +66,9 @@ $(document).ready(function() {
|
||||
}, false);
|
||||
|
||||
// Resolve proper button state for all topics
|
||||
var topicEls = topicsListEl.querySelectorAll('li'),
|
||||
var topicEls = topicsListEl.querySelectorAll('li'),
|
||||
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-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';
|
||||
|
||||
@@ -1,120 +1,63 @@
|
||||
|
||||
(function() {
|
||||
|
||||
function initUsers() {
|
||||
var yourid = templates.get('yourid');
|
||||
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-admin') !== "0");
|
||||
}
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
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 isUserBanned(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
|
||||
}
|
||||
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
}
|
||||
|
||||
jQuery('.admin-btn').each(function(index, element) {
|
||||
var adminBtn = $(element);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
|
||||
if(isAdmin)
|
||||
adminBtn.addClass('btn-success');
|
||||
else
|
||||
adminBtn.removeClass('btn-success');
|
||||
|
||||
});
|
||||
|
||||
jQuery('.delete-btn').each(function(index, element) {
|
||||
var deleteBtn = $(element);
|
||||
var isAdmin = isUserAdmin(deleteBtn);
|
||||
|
||||
if(isAdmin)
|
||||
deleteBtn.addClass('disabled');
|
||||
else
|
||||
deleteBtn.show();
|
||||
});
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
}
|
||||
|
||||
function updateUserButtons() {
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var isAdmin = isUserAdmin(banBtn);
|
||||
var isBanned = isUserBanned(banBtn);
|
||||
|
||||
if(isAdmin)
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
banBtn.addClass('disabled');
|
||||
else if(isBanned)
|
||||
else if (isUserBanned(banBtn))
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
jQuery('.admin-btn').on('click', function() {
|
||||
var adminBtn = $(this);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
var parent = adminBtn.parents('.users-box');
|
||||
var uid = getUID(adminBtn);
|
||||
function initUsers() {
|
||||
|
||||
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;
|
||||
});
|
||||
updateUserButtons();
|
||||
|
||||
jQuery('.delete-btn').on('click', function() {
|
||||
var deleteBtn = $(this);
|
||||
var isAdmin = isUserAdmin(deleteBtn);
|
||||
var parent = deleteBtn.parents('.users-box');
|
||||
var uid = getUID(deleteBtn);
|
||||
|
||||
if(!isAdmin) {
|
||||
bootbox.confirm('Do you really want to delete "' + parent.attr('data-username') +'"?', function(confirm) {
|
||||
socket.emit('api:admin.user.deleteUser', uid);
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
jQuery('.ban-btn').on('click', function() {
|
||||
$('#users-container').on('click', '.ban-btn', function() {
|
||||
var banBtn = $(this);
|
||||
var isAdmin = isUserAdmin(banBtn);
|
||||
var isBanned = isUserBanned(banBtn);
|
||||
var parent = banBtn.parents('.users-box');
|
||||
var uid = getUID(banBtn);
|
||||
|
||||
if(!isAdmin) {
|
||||
if(isBanned) {
|
||||
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) {
|
||||
socket.emit('api:admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
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;
|
||||
});
|
||||
}
|
||||
@@ -122,13 +65,12 @@
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
timeoutId = 0,
|
||||
var timeoutId = 0,
|
||||
loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
@@ -138,72 +80,74 @@
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#search-user').on('keyup', function () {
|
||||
if(timeoutId !== 0) {
|
||||
jQuery('#search-user').on('keyup', function() {
|
||||
if (timeoutId !== 0) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
var username = $('#search-user').val();
|
||||
|
||||
|
||||
jQuery('.icon-spinner').removeClass('none');
|
||||
socket.emit('api:admin.user.search', username);
|
||||
|
||||
|
||||
}, 250);
|
||||
});
|
||||
|
||||
|
||||
initUsers();
|
||||
|
||||
|
||||
socket.removeAllListeners('api:admin.user.search');
|
||||
|
||||
|
||||
socket.on('api:admin.user.search', function(data) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('.users');
|
||||
|
||||
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!')
|
||||
.show()
|
||||
.addClass('label-important')
|
||||
.addClass('label-danger')
|
||||
.removeClass('label-success');
|
||||
}
|
||||
else {
|
||||
$('#user-notfound-notify').html(data.length + ' user'+(data.length>1?'s':'') + ' found!')
|
||||
} else {
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-important');
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
|
||||
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);
|
||||
updateUserButtons();
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if(active === 'latest') {
|
||||
if (active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if(active === 'sort-posts') {
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if(active === 'sort-reputation') {
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
|
||||
if(set) {
|
||||
if (set) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: $('#users-container').children().length
|
||||
set: set,
|
||||
after: $('#users-container').children().length
|
||||
}, function(data) {
|
||||
if(data.users.length) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
(function() {
|
||||
(function () {
|
||||
var cid = templates.get('category_id'),
|
||||
room = 'category_' + cid,
|
||||
twitterEl = document.getElementById('twitter-intent'),
|
||||
@@ -11,22 +11,22 @@
|
||||
|
||||
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');
|
||||
return false;
|
||||
}, false);
|
||||
facebookEl.addEventListener('click', function() {
|
||||
facebookEl.addEventListener('click', function () {
|
||||
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
googleEl.addEventListener('click', function() {
|
||||
googleEl.addEventListener('click', function () {
|
||||
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
|
||||
var new_post = document.getElementById('new_post');
|
||||
new_post.onclick = function() {
|
||||
require(['composer'], function(cmp) {
|
||||
new_post.onclick = function () {
|
||||
require(['composer'], function (cmp) {
|
||||
cmp.push(0, cid);
|
||||
});
|
||||
}
|
||||
@@ -36,38 +36,38 @@
|
||||
]);
|
||||
|
||||
function onNewTopic(data) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({ topics: [data] }),
|
||||
topic = document.createElement('div'),
|
||||
container = document.getElementById('topics-container'),
|
||||
topics = document.querySelectorAll('#topics-container a'),
|
||||
numTopics = topics.length,
|
||||
x;
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: [data]
|
||||
}),
|
||||
topic = $(html),
|
||||
container = $('#topics-container'),
|
||||
topics = $('#topics-container').children(),
|
||||
numTopics = topics.length;
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
jQuery('#category-no-topics').remove();
|
||||
|
||||
topic.innerHTML = html;
|
||||
topic = topic.querySelector('a');
|
||||
|
||||
if (numTopics > 0) {
|
||||
for(x=0;x<numTopics;x++) {
|
||||
if (topics[x].querySelector('.icon-pushpin')) continue;
|
||||
container.insertBefore(topic, topics[x]);
|
||||
$(topic).hide().fadeIn('slow');
|
||||
for (var x = 0; x < numTopics; x++) {
|
||||
if ($(topics[x]).find('.icon-pushpin').length)
|
||||
continue;
|
||||
topic.insertBefore(topics[x]);
|
||||
topic.hide().fadeIn('slow');
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
container.insertBefore(topic, null);
|
||||
$(topic).hide().fadeIn('slow');
|
||||
container.append(topic);
|
||||
topic.hide().fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
$('#topics-container span.timeago').timeago();
|
||||
}
|
||||
|
||||
socket.on('event:new_topic', onNewTopic);
|
||||
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -76,37 +76,41 @@
|
||||
|
||||
recent_replies.innerHTML = '';
|
||||
|
||||
var frag = document.createDocumentFragment(),
|
||||
var frag = document.createDocumentFragment(),
|
||||
li = document.createElement('li');
|
||||
for (var i=0,numPosts=posts.length; i<numPosts; i++) {
|
||||
var dateString = utils.relativeTime(posts[i].timestamp);
|
||||
for (var i = 0, numPosts = posts.length; i < numPosts; i++) {
|
||||
|
||||
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>' +
|
||||
'<a href="/topic/'+ posts[i].topicSlug + '#' + posts[i].pid + '">' +
|
||||
'<p>' +
|
||||
'<strong>' + posts[i].username + '</strong>: ' + posts[i].content +
|
||||
'</p>' +
|
||||
'<span>posted ' + utils.relativeTime(posts[i].timestamp) + ' ago</span>' +
|
||||
'</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 + '">' +
|
||||
'<p>' +
|
||||
posts[i].content +
|
||||
'</p>' +
|
||||
'<p class="meta"><strong>' + posts[i].username + '</strong></span> -<span class="timeago" title="' + posts[i].relativeTime + '"></span></p>' +
|
||||
'</a>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
});
|
||||
|
||||
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');
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
jQuery('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
}
|
||||
|
||||
$('#topics-container span.timeago').timeago();
|
||||
}
|
||||
|
||||
|
||||
function loadMoreTopics(cid) {
|
||||
@@ -114,15 +118,15 @@
|
||||
socket.emit('api:category.loadMore', {
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
}, function(data) {
|
||||
if(data.topics.length) {
|
||||
}, function (data) {
|
||||
if (data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function(ev) {
|
||||
$(window).off('scroll').on('scroll', function (ev) {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
|
||||
@@ -1,11 +1,7 @@
|
||||
(function() {
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
$('.user-favourite-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -5,14 +5,14 @@
|
||||
followersCount = templates.get('followersCount');
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if(parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').show();
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
}());
|
||||
@@ -5,23 +5,24 @@
|
||||
followingCount = templates.get('followingCount');
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if(parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').show();
|
||||
|
||||
if (parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
if(yourid !== theirid) {
|
||||
if (yourid !== theirid) {
|
||||
$('.unfollow-btn').hide();
|
||||
}
|
||||
else {
|
||||
$('.unfollow-btn').on('click',function() {
|
||||
} else {
|
||||
$('.unfollow-btn').on('click', function() {
|
||||
var unfollowBtn = $(this);
|
||||
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');
|
||||
if(success) {
|
||||
if (success) {
|
||||
unfollowBtn.parent().remove();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
@@ -34,6 +35,6 @@
|
||||
|
||||
app.addCommasToNumbers();
|
||||
});
|
||||
|
||||
|
||||
|
||||
}());
|
||||
@@ -1,91 +1,93 @@
|
||||
(function() {
|
||||
var num_users = document.getElementById('number_of_users'),
|
||||
post_stats = document.getElementById('post_stats'),
|
||||
latest_user = document.getElementById('latest_user'),
|
||||
active_users = document.getElementById('active_users'),
|
||||
user_label = document.getElementById('user_label'),
|
||||
active_record = document.getElementById('active_record'),
|
||||
right_menu = document.getElementById('right-menu');
|
||||
var stats_users = document.getElementById('stats_users'),
|
||||
stats_topics = document.getElementById('stats_topics'),
|
||||
stats_posts = document.getElementById('stats_posts'),
|
||||
stats_online = document.getElementById('stats_online'),
|
||||
user_label = document.getElementById('user_label');
|
||||
|
||||
socket.emit('user.count', {});
|
||||
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.on('post.stats', function(data) {
|
||||
post_stats.innerHTML = "Our users have created <b>" + data.topics + "</b> topics and made <b>" + data.posts + "</b> 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>.";
|
||||
}
|
||||
stats_topics.innerHTML = data.topics;
|
||||
stats_posts.innerHTML = data.posts;
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
|
||||
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';
|
||||
stats_online.innerHTML = data.users;
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get_record');
|
||||
socket.on('api:user.active.get_record', function(data) {
|
||||
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.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
|
||||
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() {
|
||||
jQuery('#search-fields').hide();
|
||||
jQuery('#search-button').show();
|
||||
});
|
||||
|
||||
$('#search-fields input').on('blur', function() {
|
||||
$('#search-fields').hide();
|
||||
$('#search-button').show();
|
||||
});
|
||||
});
|
||||
|
||||
var loggedInMenu = $('#logged-in-menu'),
|
||||
isLoggedIn = data.uid > 0;
|
||||
|
||||
if (isLoggedIn) {
|
||||
jQuery('.nodebb-loggedin').show();
|
||||
jQuery('.nodebb-loggedout').hide();
|
||||
|
||||
var userLabel = rightMenu.find('#user_label');
|
||||
if(userLabel.length) {
|
||||
if(data['userslug'])
|
||||
userLabel.attr('href','/users/' + data['userslug']);
|
||||
if(data['picture'])
|
||||
userLabel.find('img').attr('src',data['picture']);
|
||||
if(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);
|
||||
$('#logged-out-menu').addClass('hide');
|
||||
$('#logged-in-menu').removeClass('hide');
|
||||
|
||||
var logoutli = $('<li><a href="' + RELATIVE_PATH + '/logout">Log out</a></li>');
|
||||
rightMenu.append(logoutli);
|
||||
$('#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']);
|
||||
|
||||
$('#logout-link').on('click', function() {
|
||||
var csrf_token = $('#csrf_token').val();
|
||||
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: csrf_token
|
||||
}, function() {
|
||||
window.location = RELATIVE_PATH + '/';
|
||||
});
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$('#search-button').hide();
|
||||
|
||||
jQuery('.nodebb-loggedin').hide();
|
||||
jQuery('.nodebb-loggedout').show();
|
||||
|
||||
rightMenu.html('');
|
||||
$('#logged-out-menu').removeClass('hide');
|
||||
$('#logged-in-menu').addClass('hide');
|
||||
|
||||
var registerEl = document.createElement('li'),
|
||||
loginEl = document.createElement('li');
|
||||
|
||||
registerEl.innerHTML = '<a href="/register">Register</a>';
|
||||
loginEl.innerHTML = '<a href="/login">Login</a>';
|
||||
|
||||
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
|
||||
@@ -97,20 +99,20 @@
|
||||
e.preventDefault();
|
||||
if (notifContainer.className.indexOf('open') === -1) {
|
||||
socket.emit('api:notifications.get', null, function(data) {
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
notifEl = document.createElement('li'),
|
||||
numRead = data.read.length,
|
||||
numUnread = data.unread.length,
|
||||
x;
|
||||
notifList.innerHTML = '';
|
||||
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.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>';
|
||||
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.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>';
|
||||
@@ -132,25 +134,33 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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;
|
||||
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() {
|
||||
document.querySelector('.notifications a i').className = 'icon-circle active';
|
||||
app.alert({
|
||||
alert_id: 'new_notif',
|
||||
title: 'New notification',
|
||||
message: 'You have unread notifications.',
|
||||
type: 'notify',
|
||||
type: 'warning',
|
||||
timeout: 2000
|
||||
});
|
||||
utils.refreshTitle();
|
||||
@@ -158,20 +168,20 @@
|
||||
|
||||
|
||||
socket.on('chatMessage', function(data) {
|
||||
var username = data.username;
|
||||
var fromuid = data.fromuid;
|
||||
var message = data.message;
|
||||
|
||||
require(['chat'], function(chat) {
|
||||
var chatModal = chat.createModalIfDoesntExist(username, fromuid);
|
||||
chatModal.show();
|
||||
chat.bringModalToTop(chatModal);
|
||||
var modal = null;
|
||||
if (chat.modalExists(data.fromuid)) {
|
||||
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) {
|
||||
mobileMenu.init();
|
||||
});
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
|
||||
|
||||
(function() {
|
||||
// Alternate Logins
|
||||
var altLoginEl = document.querySelector('.alt-logins');
|
||||
altLoginEl.addEventListener('click', function(e) {
|
||||
var target;
|
||||
switch(e.target.nodeName) {
|
||||
case 'LI': target = e.target; break;
|
||||
case 'I': target = e.target.parentNode; break;
|
||||
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');
|
||||
@@ -26,14 +28,19 @@
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
if(!data.success) {
|
||||
$('#login-error-notify').html(data.message).show();
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||
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();
|
||||
},
|
||||
dataType: 'json',
|
||||
@@ -44,5 +51,10 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
}());
|
||||
}());
|
||||
@@ -8,32 +8,33 @@
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
var newTopicCount = 0, newPostCount = 0;
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if(newTopicCount > 1)
|
||||
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if(newTopicCount === 1)
|
||||
else if (newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if(newPostCount > 1)
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if(newPostCount === 1)
|
||||
else if (newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
@@ -42,7 +43,7 @@
|
||||
|
||||
$('#new-topics-alert').html(text).fadeIn('slow');
|
||||
}
|
||||
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
@@ -50,7 +51,9 @@
|
||||
|
||||
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');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
@@ -60,8 +63,10 @@
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreRecentTopics', {after:$('#topics-container').children().length}, function(data) {
|
||||
if(data.topics && data.topics.length) {
|
||||
socket.emit('api:topics.loadMoreRecentTopics', {
|
||||
after: $('#topics-container').children().length
|
||||
}, function(data) {
|
||||
if (data.topics && data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
|
||||
@@ -1,157 +1,154 @@
|
||||
(function() {
|
||||
var username = document.getElementById('username'),
|
||||
password = document.getElementById('password'),
|
||||
password_confirm = document.getElementById('password-confirm'),
|
||||
register = document.getElementById('register'),
|
||||
emailEl = document.getElementById('email'),
|
||||
username_notify = document.getElementById('username-notify'),
|
||||
email_notify = document.getElementById('email-notify'),
|
||||
password_notify = document.getElementById('password-notify'),
|
||||
password_confirm_notify = document.getElementById('password-confirm-notify'),
|
||||
usernamevalid = false;
|
||||
emailexists = false,
|
||||
emailvalid = false,
|
||||
userexists = false,
|
||||
passwordsmatch = false,
|
||||
passwordvalid = false;
|
||||
var username = $('#username'),
|
||||
password = $('#password'),
|
||||
password_confirm = $('#password-confirm'),
|
||||
register = $('#register'),
|
||||
emailEl = $('#email'),
|
||||
username_notify = $('#username-notify'),
|
||||
email_notify = $('#email-notify'),
|
||||
password_notify = $('#password-notify'),
|
||||
password_confirm_notify = $('#password-confirm-notify'),
|
||||
validationError = false,
|
||||
successIcon = '<i class="icon icon-ok"></i>';
|
||||
|
||||
$(username).on('keyup change', function() {
|
||||
usernamevalid = utils.isUserNameValid(username.value);
|
||||
$('#referrer').val(app.previousUrl);
|
||||
|
||||
|
||||
if(username.value.length < 3) {
|
||||
username_notify.innerHTML = 'Username too short';
|
||||
username_notify.className = 'label label-important';
|
||||
} else if(username.value.length > 13) {
|
||||
username_notify.innerHTML = 'Username too long';
|
||||
username_notify.className = 'label label-important';
|
||||
} else if(!usernamevalid) {
|
||||
username_notify.innerHTML = 'Invalid username';
|
||||
username_notify.className = 'label label-important';
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
}
|
||||
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
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 {
|
||||
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() {
|
||||
emailvalid = utils.isEmailValid(email.value);
|
||||
function validatePassword() {
|
||||
if (!password.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if(!emailvalid) {
|
||||
email_notify.innerHTML = 'Invalid email address';
|
||||
email_notify.className = 'label label-important';
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, 'Password too short!');
|
||||
} else if (!utils.isPasswordValid(password.val())) {
|
||||
showError(password_notify, 'Invalid password!');
|
||||
} else {
|
||||
showSuccess(password_notify, successIcon);
|
||||
}
|
||||
else
|
||||
socket.emit('user.email.exists', { email: emailEl.value });
|
||||
|
||||
if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
}
|
||||
}
|
||||
|
||||
$(password).on('blur', function() {
|
||||
validatePassword();
|
||||
});
|
||||
|
||||
$(password).on('keyup', function() {
|
||||
passwordvalid = utils.isPasswordValid(password.value);
|
||||
if (password.value.length < 6) {
|
||||
password_notify.innerHTML = 'Password too short';
|
||||
password_notify.className = 'label label-important';
|
||||
} else if(!passwordvalid) {
|
||||
password_notify.innerHTML = 'Invalid password';
|
||||
password_notify.className = 'label label-important';
|
||||
} else {
|
||||
password_notify.innerHTML = 'OK!';
|
||||
password_notify.className = 'label label-success';
|
||||
|
||||
function validatePasswordConfirm() {
|
||||
if (!password.val() || password_notify.hasClass('alert-error')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(password.value !== password_confirm.value) {
|
||||
password_confirm_notify.innerHTML = 'Passwords must match!';
|
||||
password_confirm_notify.className = 'label label-important';
|
||||
passwordsmatch = false;
|
||||
}
|
||||
});
|
||||
|
||||
$(password_confirm).on('keyup', function() {
|
||||
if(password.value !== password_confirm.value) {
|
||||
password_confirm_notify.innerHTML = 'Passwords must match!';
|
||||
password_confirm_notify.className = 'label label-important';
|
||||
passwordsmatch = false;
|
||||
}
|
||||
else {
|
||||
password_confirm_notify.innerHTML = 'OK!';
|
||||
password_confirm_notify.className = 'label label-success';
|
||||
passwordsmatch = true;
|
||||
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
} else {
|
||||
showSuccess(password_confirm_notify, successIcon);
|
||||
}
|
||||
}
|
||||
|
||||
$(password_confirm).on('blur', function() {
|
||||
validatePasswordConfirm();
|
||||
});
|
||||
|
||||
ajaxify.register_events(['user.exists', 'user.email.exists']);
|
||||
|
||||
socket.on('user.exists', function(data) {
|
||||
userexists = data.exists;
|
||||
if (data.exists === true) {
|
||||
username_notify.innerHTML = 'Username exists';
|
||||
username_notify.className = 'label label-important';
|
||||
showError(username_notify, 'Username already taken!');
|
||||
} else {
|
||||
username_notify.innerHTML = 'OK!';
|
||||
username_notify.className = 'label label-success';
|
||||
showSuccess(username_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
emailexists = data.exists;
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
email_notify.innerHTML = 'Email Address exists';
|
||||
email_notify.className = 'label label-important';
|
||||
}
|
||||
else {
|
||||
email_notify.innerHTML = 'OK!';
|
||||
email_notify.className = 'label label-success';
|
||||
showError(email_notify, 'Email address already taken!');
|
||||
} else {
|
||||
showSuccess(email_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
// Alternate Logins
|
||||
var altLoginEl = document.querySelector('.alt-logins');
|
||||
altLoginEl.addEventListener('click', function(e) {
|
||||
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');
|
||||
}
|
||||
$('.alt-logins li').on('click', function(e) {
|
||||
document.location.href = $(this).attr('data-url');
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
var validated = true;
|
||||
|
||||
if (username.value.length < 2 || !usernamevalid) {
|
||||
username_notify.innerHTML = 'Invalid username';
|
||||
username_notify.className = 'label label-important';
|
||||
validated = false;
|
||||
}
|
||||
validationError = false;
|
||||
|
||||
if (password.value.length < 5) {
|
||||
password_notify.innerHTML = 'Password too short';
|
||||
validated = false;
|
||||
}
|
||||
|
||||
if(password.value !== password_confirm.value) {
|
||||
password_confirm_notify.innerHTML = 'Passwords must match!';
|
||||
}
|
||||
validateEmail();
|
||||
validateUsername();
|
||||
validatePassword();
|
||||
validatePasswordConfirm();
|
||||
|
||||
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;
|
||||
return validationError;
|
||||
}
|
||||
|
||||
register.addEventListener('click', function(e) {
|
||||
if (!validateForm()) e.preventDefault();
|
||||
}, false);
|
||||
|
||||
}());
|
||||
|
||||
register.on('click', function(e) {
|
||||
if (validateForm()) e.preventDefault();
|
||||
});
|
||||
|
||||
}());
|
||||
@@ -1,11 +1,13 @@
|
||||
(function() {
|
||||
var inputEl = document.getElementById('email'),
|
||||
var inputEl = document.getElementById('email'),
|
||||
errorEl = document.getElementById('error'),
|
||||
errorTextEl = errorEl.querySelector('p');
|
||||
|
||||
document.getElementById('reset').onclick = function() {
|
||||
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 {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
@@ -14,9 +16,9 @@
|
||||
};
|
||||
|
||||
ajaxify.register_events(['user.send_reset']);
|
||||
|
||||
|
||||
socket.on('user.send_reset', function(data) {
|
||||
var submitEl = document.getElementById('reset');
|
||||
var submitEl = document.getElementById('reset');
|
||||
|
||||
if (data.status === 'ok') {
|
||||
jQuery('#error').hide();
|
||||
@@ -26,13 +28,13 @@
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
switch(data.message) {
|
||||
switch (data.message) {
|
||||
case 'invalid-email':
|
||||
errorTextEl.innerHTML = 'The email you put in (<span>' + data.email + '</span>) is not registered with us. Please try again.';
|
||||
break;
|
||||
break;
|
||||
case 'send-failed':
|
||||
errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
|
||||
break;
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
var reset_code = templates.get('reset_code');
|
||||
|
||||
var resetEl = document.getElementById('reset'),
|
||||
var resetEl = document.getElementById('reset'),
|
||||
password = document.getElementById('password'),
|
||||
repeat = document.getElementById('repeat'),
|
||||
noticeEl = document.getElementById('notice');
|
||||
@@ -10,20 +10,30 @@
|
||||
if (password.value.length < 6) {
|
||||
$('#error').hide();
|
||||
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';
|
||||
} else if (password.value === repeat.value) {
|
||||
socket.emit('user:reset.commit', { code: reset_code, password: password.value });
|
||||
} else if (password.value !== repeat.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);
|
||||
|
||||
// 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']);
|
||||
socket.on('user:reset.valid', function(data) {
|
||||
if (!!data.valid) resetEl.disabled = false;
|
||||
if ( !! data.valid) resetEl.disabled = false;
|
||||
else {
|
||||
var formEl = document.getElementById('reset-form');
|
||||
// Show error message
|
||||
|
||||
@@ -2,13 +2,23 @@
|
||||
|
||||
$(document).ready(function() {
|
||||
var searchQuery = $('#topics-container').attr('data-search-query');
|
||||
|
||||
|
||||
$('.search-result-text').each(function() {
|
||||
var text = $(this).html();
|
||||
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);
|
||||
});
|
||||
|
||||
|
||||
$('#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;
|
||||
});
|
||||
});
|
||||
|
||||
})();
|
||||
@@ -1,5 +1,5 @@
|
||||
(function() {
|
||||
var expose_tools = templates.get('expose_tools'),
|
||||
var expose_tools = templates.get('expose_tools'),
|
||||
tid = templates.get('topic_id'),
|
||||
postListEl = document.getElementById('post-container'),
|
||||
editBtns = document.querySelectorAll('#post-container .post-buttons .edit, #post-container .post-buttons .edit i'),
|
||||
@@ -15,7 +15,7 @@
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
var room = 'topic_' + tid,
|
||||
var room = 'topic_' + tid,
|
||||
adminTools = document.getElementById('thread-tools');
|
||||
|
||||
app.enter_room(room);
|
||||
@@ -34,11 +34,15 @@
|
||||
$('#delete_thread').on('click', function(e) {
|
||||
if (thread_state.deleted !== '1') {
|
||||
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 {
|
||||
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
|
||||
});
|
||||
});
|
||||
}
|
||||
return false;
|
||||
@@ -46,18 +50,26 @@
|
||||
|
||||
$('#lock_thread').on('click', function(e) {
|
||||
if (thread_state.locked !== '1') {
|
||||
socket.emit('api:topic.lock', { tid: tid });
|
||||
socket.emit('api:topic.lock', {
|
||||
tid: tid
|
||||
});
|
||||
} else {
|
||||
socket.emit('api:topic.unlock', { tid: tid });
|
||||
socket.emit('api:topic.unlock', {
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#pin_thread').on('click', function(e) {
|
||||
if (thread_state.pinned !== '1') {
|
||||
socket.emit('api:topic.pin', { tid: tid });
|
||||
socket.emit('api:topic.pin', {
|
||||
tid: tid
|
||||
});
|
||||
} else {
|
||||
socket.emit('api:topic.unpin', { tid: tid });
|
||||
socket.emit('api:topic.unpin', {
|
||||
tid: tid
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
@@ -67,12 +79,13 @@
|
||||
return false;
|
||||
});
|
||||
|
||||
moveThreadModal.on('shown', function() {
|
||||
moveThreadModal.on('shown.bs.modal', function() {
|
||||
|
||||
var loadingEl = document.getElementById('categories-loading');
|
||||
if (loadingEl) {
|
||||
socket.once('api:categories.get', function(data) {
|
||||
// Render categories
|
||||
var categoriesFrag = document.createDocumentFragment(),
|
||||
var categoriesFrag = document.createDocumentFragment(),
|
||||
categoryEl = document.createElement('li'),
|
||||
numCategories = data.categories.length,
|
||||
modalBody = moveThreadModal.find('.modal-body'),
|
||||
@@ -84,7 +97,7 @@
|
||||
x, info, targetCid, targetCatLabel;
|
||||
|
||||
categoriesEl.className = 'category-list';
|
||||
for(x=0;x<numCategories;x++) {
|
||||
for (x = 0; x < numCategories; x++) {
|
||||
info = data.categories[x];
|
||||
categoryEl.className = info.blockclass;
|
||||
categoryEl.innerHTML = '<i class="' + info.icon + '"></i> ' + info.name;
|
||||
@@ -124,14 +137,17 @@
|
||||
} else {
|
||||
app.alert({
|
||||
'alert_id': 'thread_move',
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
title: 'Unable to Move Topic',
|
||||
message: 'This topic could not be moved to ' + targetCatLabel + '.<br />Please try again later',
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
});
|
||||
socket.emit('api:topic.move', { tid: tid, cid: targetCid });
|
||||
socket.emit('api:topic.move', {
|
||||
tid: tid,
|
||||
cid: targetCid
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -141,8 +157,8 @@
|
||||
}
|
||||
|
||||
// Fix delete state for this thread's posts
|
||||
var postEls = document.querySelectorAll('#post-container li[data-deleted]');
|
||||
for(var x=0,numPosts=postEls.length;x<numPosts;x++) {
|
||||
var postEls = document.querySelectorAll('#post-container li[data-deleted]');
|
||||
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'));
|
||||
postEls[x].removeAttribute('data-deleted');
|
||||
}
|
||||
@@ -183,7 +199,7 @@
|
||||
if (data.status && data.status === 'ok') set_follow_state(data.follow);
|
||||
else {
|
||||
app.alert({
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
alert_id: 'topic_follow',
|
||||
title: 'Please Log In',
|
||||
message: 'Please register or log in in order to subscribe to this topic',
|
||||
@@ -193,12 +209,26 @@
|
||||
});
|
||||
|
||||
socket.emit('api:topic.followCheck', tid);
|
||||
if(followEl[0]) {
|
||||
if (followEl[0]) {
|
||||
followEl[0].addEventListener('click', function() {
|
||||
socket.emit('api:topic.follow', tid);
|
||||
}, false);
|
||||
}
|
||||
|
||||
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');
|
||||
});
|
||||
});
|
||||
|
||||
function enableInfiniteLoading() {
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
@@ -206,31 +236,27 @@
|
||||
app.loadMorePosts(tid);
|
||||
}
|
||||
});
|
||||
|
||||
$('.post-container').on('click', '.deleted', function(ev) {
|
||||
$(this).toggleClass('deleted-expanded');
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var reply_fn = function() {
|
||||
var selectionText = '',
|
||||
selection = window.getSelection() || document.getSelection();
|
||||
var selectionText = '',
|
||||
selection = window.getSelection() || document.getSelection();
|
||||
|
||||
if ($(selection.baseNode).parents('.post-content').length > 0) {
|
||||
var snippet = selection.toString();
|
||||
if (snippet.length > 0) selectionText = '> ' + snippet.replace(/\n/g, '\n> ');
|
||||
}
|
||||
if ($(selection.baseNode).parents('.post-content').length > 0) {
|
||||
var snippet = selection.toString();
|
||||
if (snippet.length > 0) selectionText = '> ' + snippet.replace(/\n/g, '\n> ');
|
||||
}
|
||||
|
||||
if (thread_state.locked !== '1') {
|
||||
require(['composer'], function(cmp) {
|
||||
cmp.push(tid, null, null, selectionText.length > 0 ? selectionText + '\n\n' : '');
|
||||
});
|
||||
}
|
||||
};
|
||||
$('.post-container').on('click', '.post_reply', reply_fn);
|
||||
if (thread_state.locked !== '1') {
|
||||
require(['composer'], function(cmp) {
|
||||
cmp.push(tid, null, null, selectionText.length > 0 ? selectionText + '\n\n' : '');
|
||||
});
|
||||
}
|
||||
};
|
||||
$('#post-container').on('click', '.post_reply', 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') {
|
||||
var pid = $(this).parents('li').attr('data-pid');
|
||||
|
||||
@@ -241,60 +267,72 @@
|
||||
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() {
|
||||
var ids = this.id.replace('favs_', '').split('_'),
|
||||
pid = ids[0],
|
||||
uid = ids[1];
|
||||
$('#post-container').on('click', '.favourite', function() {
|
||||
var pid = $(this).parents('li').attr('data-pid');
|
||||
var uid = $(this).parents('li').attr('data-uid');
|
||||
|
||||
var element = $(this).find('i');
|
||||
if(element.attr('class') == 'icon-star-empty') {
|
||||
socket.emit('api:posts.favourite', {pid: pid, room_id: app.current_room});
|
||||
}
|
||||
else {
|
||||
socket.emit('api:posts.unfavourite', {pid: pid, room_id: app.current_room});
|
||||
if (element.attr('class') == 'icon-star-empty') {
|
||||
socket.emit('api:posts.favourite', {
|
||||
pid: pid,
|
||||
room_id: app.current_room
|
||||
});
|
||||
} else {
|
||||
socket.emit('api:posts.unfavourite', {
|
||||
pid: pid,
|
||||
room_id: app.current_room
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('.post-container').delegate('.edit', 'click', function(e) {
|
||||
var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1];
|
||||
$('#post-container').on('click', '.link', function() {
|
||||
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) {
|
||||
cmp.push(null, null, pid);
|
||||
});
|
||||
});
|
||||
|
||||
$('.post-container').delegate('.delete', 'click', function(e) {
|
||||
var pid = ($(this).attr('id') || $(this.parentNode).attr('id')).split('_')[1],
|
||||
postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
deleteAction = !postEl.hasClass('deleted') ? true : false,
|
||||
confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
|
||||
$('#post-container').delegate('.delete', 'click', function(e) {
|
||||
var pid = $(this).parents('li').attr('data-pid'),
|
||||
postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
deleteAction = !postEl.hasClass('deleted') ? true : false,
|
||||
confirmDel = confirm((deleteAction ? 'Delete' : 'Restore') + ' this post?');
|
||||
|
||||
if (confirmDel) {
|
||||
deleteAction ?
|
||||
socket.emit('api:posts.delete', { pid: pid }) :
|
||||
socket.emit('api:posts.restore', { pid: pid });
|
||||
socket.emit('api:posts.delete', {
|
||||
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');
|
||||
var touid = $(this).parents('li').attr('data-uid');
|
||||
|
||||
if(username === app.username || !app.username)
|
||||
if (username === app.username || !app.username)
|
||||
return;
|
||||
|
||||
require(['chat'], function(chat) {
|
||||
var chatModal = chat.createModalIfDoesntExist(username, touid);
|
||||
chatModal.show();
|
||||
chat.bringModalToTop(chatModal);
|
||||
});
|
||||
app.openChat(username, touid);
|
||||
});
|
||||
|
||||
ajaxify.register_events([
|
||||
@@ -308,7 +346,7 @@
|
||||
|
||||
socket.on('api:get_users_in_room', function(data) {
|
||||
var activeEl = $('#thread_active_users');
|
||||
if(activeEl.length)
|
||||
if (activeEl.length)
|
||||
activeEl.html(data);
|
||||
|
||||
app.populate_online_users();
|
||||
@@ -369,9 +407,9 @@
|
||||
socket.on('event:post_edited', function(data) {
|
||||
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.html(data.title);
|
||||
editedPostTitle.fadeIn(250);
|
||||
@@ -382,6 +420,7 @@
|
||||
this.innerHTML = data.content;
|
||||
$(this).fadeIn(250);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
socket.on('api:posts.favourite', function(data) {
|
||||
@@ -426,12 +465,12 @@
|
||||
ptotal += value;
|
||||
utotal += value;
|
||||
|
||||
post_rep.html(ptotal+ ' ');
|
||||
user_rep.html(utotal+ ' ');
|
||||
post_rep.html(ptotal + ' ');
|
||||
user_rep.html(utotal + ' ');
|
||||
}
|
||||
|
||||
function set_locked_state(locked, alert) {
|
||||
var threadReplyBtn = document.getElementById('post_reply'),
|
||||
var threadReplyBtn = document.getElementById('post_reply'),
|
||||
postReplyBtns = document.querySelectorAll('#post-container .post_reply'),
|
||||
quoteBtns = document.querySelectorAll('#post-container .quote'),
|
||||
editBtns = document.querySelectorAll('#post-container .edit'),
|
||||
@@ -444,7 +483,7 @@
|
||||
lockThreadEl.innerHTML = '<i class="icon-unlock"></i> Unlock Thread';
|
||||
threadReplyBtn.disabled = true;
|
||||
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>';
|
||||
quoteBtns[x].style.display = 'none';
|
||||
editBtns[x].style.display = 'none';
|
||||
@@ -466,7 +505,7 @@
|
||||
lockThreadEl.innerHTML = '<i class="icon-lock"></i> Lock Thread';
|
||||
threadReplyBtn.disabled = false;
|
||||
threadReplyBtn.innerHTML = 'Reply';
|
||||
for(x=0;x<numPosts;x++) {
|
||||
for (x = 0; x < numPosts; x++) {
|
||||
postReplyBtns[x].innerHTML = 'Reply <i class="icon-reply"></i>';
|
||||
quoteBtns[x].style.display = 'inline-block';
|
||||
editBtns[x].style.display = 'inline-block';
|
||||
@@ -488,25 +527,25 @@
|
||||
}
|
||||
|
||||
function set_delete_state(deleted) {
|
||||
var deleteThreadEl = document.getElementById('delete_thread'),
|
||||
var deleteThreadEl = document.getElementById('delete_thread'),
|
||||
deleteTextEl = deleteThreadEl.getElementsByTagName('span')[0],
|
||||
threadEl = document.querySelector('.post-container'),
|
||||
threadEl = $('#post-container'),
|
||||
deleteNotice = document.getElementById('thread-deleted') || document.createElement('div');
|
||||
|
||||
if (deleted) {
|
||||
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
|
||||
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.';
|
||||
document.getElementById('content').insertBefore(deleteNotice, threadEl);
|
||||
threadEl.before(deleteNotice);
|
||||
|
||||
thread_state.deleted = '1';
|
||||
} else {
|
||||
deleteTextEl.innerHTML = '<i class="icon-trash"></i> Delete Thread';
|
||||
$(threadEl).removeClass('deleted');
|
||||
threadEl.removeClass('deleted');
|
||||
deleteNotice.parentNode.removeChild(deleteNotice);
|
||||
|
||||
thread_state.deleted = '0';
|
||||
@@ -546,7 +585,7 @@
|
||||
}
|
||||
|
||||
function toggle_post_delete_state(pid) {
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]'));
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]'));
|
||||
|
||||
if (postEl[0]) {
|
||||
quoteEl = $(postEl[0].querySelector('.quote')),
|
||||
@@ -573,7 +612,7 @@
|
||||
}
|
||||
|
||||
function toggle_post_tools(pid, state) {
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
quoteEl = $(postEl[0].querySelector('.quote')),
|
||||
favEl = $(postEl[0].querySelector('.favourite')),
|
||||
replyEl = $(postEl[0].querySelector('.post_reply'));
|
||||
@@ -590,7 +629,7 @@
|
||||
}
|
||||
|
||||
function toggle_mod_tools(pid, state) {
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
var postEl = $(document.querySelector('#post-container li[data-pid="' + pid + '"]')),
|
||||
editEl = postEl.find('.edit'),
|
||||
deleteEl = postEl.find('.delete');
|
||||
|
||||
@@ -602,4 +641,76 @@
|
||||
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;
|
||||
})();
|
||||
|
||||
@@ -8,32 +8,33 @@
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
var newTopicCount = 0, newPostCount = 0;
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
|
||||
});
|
||||
|
||||
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if(newTopicCount > 1)
|
||||
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if(newTopicCount === 1)
|
||||
else if (newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if(newPostCount > 1)
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if(newPostCount === 1)
|
||||
else if (newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
@@ -42,19 +43,19 @@
|
||||
|
||||
$('#new-topics-alert').html(text).fadeIn('slow');
|
||||
}
|
||||
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
});
|
||||
|
||||
|
||||
$('#mark-allread-btn').on('click', function() {
|
||||
var btn = $(this);
|
||||
socket.emit('api:topics.markAllRead', {} , function(success) {
|
||||
if(success) {
|
||||
socket.emit('api:topics.markAllRead', {}, function(success) {
|
||||
if (success) {
|
||||
btn.remove();
|
||||
$('#topics-container').empty();
|
||||
$('#category-no-topics').removeClass('hidden');
|
||||
$('#category-no-topics').removeClass('hidden');
|
||||
app.alertSuccess('All topics marked as read!');
|
||||
$('#numUnreadBadge')
|
||||
.removeClass('badge-important')
|
||||
@@ -68,7 +69,9 @@
|
||||
|
||||
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');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
@@ -78,12 +81,14 @@
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreUnreadTopics', {after:parseInt($('#topics-container').attr('data-next-start'), 10)}, function(data) {
|
||||
if(data.topics && data.topics.length) {
|
||||
socket.emit('api:topics.loadMoreUnreadTopics', {
|
||||
after: parseInt($('#topics-container').attr('data-next-start'), 10)
|
||||
}, function(data) {
|
||||
if (data.topics && data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
$('#topics-container').attr('data-next-start', data.nextStart);
|
||||
} else {
|
||||
$('#load-more-btn').hide();
|
||||
$('#load-more-btn').hide();
|
||||
}
|
||||
|
||||
loadingMoreTopics = false;
|
||||
@@ -92,14 +97,14 @@
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
if($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
$('#load-more-btn').show();
|
||||
|
||||
$('#load-more-btn').on('click', function() {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
(function() {
|
||||
|
||||
|
||||
$(document).ready(function() {
|
||||
var timeoutId = 0;
|
||||
var loadingMoreUsers = false;
|
||||
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
var lastSearch = null;
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
@@ -17,93 +19,111 @@
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#search-user').on('keyup', function () {
|
||||
if(timeoutId !== 0) {
|
||||
|
||||
jQuery('#search-user').on('keyup', function() {
|
||||
if (timeoutId !== 0) {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = 0;
|
||||
}
|
||||
|
||||
timeoutId = setTimeout(function() {
|
||||
var username = $('#search-user').val();
|
||||
|
||||
jQuery('.icon-spinner').removeClass('none');
|
||||
socket.emit('api:admin.user.search', username);
|
||||
|
||||
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);
|
||||
}, 500); //replace this with global throttling function/constant
|
||||
|
||||
}, 250);
|
||||
});
|
||||
|
||||
|
||||
socket.removeAllListeners('api:admin.user.search');
|
||||
|
||||
|
||||
socket.on('api:admin.user.search', function(data) {
|
||||
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
|
||||
if(data === null) {
|
||||
$('#user-notfound-notify').html('You need to be logged in to search!')
|
||||
.show()
|
||||
.addClass('label-important')
|
||||
.removeClass('label-success');
|
||||
if (data === null) {
|
||||
$('#user-notfound-notify').html('You need to be logged in to search!');
|
||||
$('#user-notfound-notify').parent().addClass('btn-warning label-warning');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('.users');
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('#users-container');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
|
||||
|
||||
if(data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!')
|
||||
.show()
|
||||
.addClass('label-important')
|
||||
.removeClass('label-success');
|
||||
}
|
||||
else {
|
||||
$('#user-notfound-notify').html(data.length + ' user'+(data.length>1?'s':'') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-important');
|
||||
if (data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!');
|
||||
$('#user-notfound-notify').parent().addClass('btn-warning label-warning');
|
||||
} else {
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!');
|
||||
$('#user-notfound-notify').parent().addClass('btn-success label-success');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
|
||||
|
||||
|
||||
socket.on('api:user.isOnline', function(data) {
|
||||
if(active == 'online' && !loadingMoreUsers) {
|
||||
$('#users-container').empty();
|
||||
startLoading('users:online', 0);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if(active === 'users-latest' || active === 'users') {
|
||||
if (active === 'latest' || active === 'users') {
|
||||
set = 'users:joindate';
|
||||
} else if(active === 'users-sort-posts') {
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if(active === 'users-sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
} else if (active === 'online') {
|
||||
set = 'users:online';
|
||||
}
|
||||
|
||||
if(set) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: $('#users-container').children().length
|
||||
}, function(data) {
|
||||
if(data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
} else {
|
||||
$('#load-more-users-btn').addClass('disabled');
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
if (set) {
|
||||
startLoading(set, $('#users-container').children().length);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function startLoading(set, after) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: after
|
||||
}, function(data) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users);
|
||||
$('#load-more-users-btn').removeClass('disabled');
|
||||
} else {
|
||||
$('#load-more-users-btn').addClass('disabled');
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
|
||||
@@ -13,37 +13,75 @@ define(['taskbar'], function(taskbar) {
|
||||
chatModal.css('zIndex', topZ + 1);
|
||||
}
|
||||
|
||||
module.createModalIfDoesntExist = function(username, touid) {
|
||||
var chatModal = $('#chat-modal-'+touid);
|
||||
module.getModal = function(touid) {
|
||||
return $('#chat-modal-' + touid);
|
||||
}
|
||||
|
||||
if(!chatModal.length) {
|
||||
var chatModal = $('#chat-modal').clone();
|
||||
chatModal.attr('id','chat-modal-'+touid);
|
||||
var uuid = utils.generateUUID();
|
||||
chatModal.attr('UUID', uuid);
|
||||
chatModal.appendTo($('body'));
|
||||
chatModal.draggable({
|
||||
start:function(){
|
||||
module.bringModalToTop(chatModal);
|
||||
module.modalExists = function(touid) {
|
||||
return $('#chat-modal-' + touid).length !== 0;
|
||||
}
|
||||
|
||||
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.find('#chat-with-name').html(username);
|
||||
|
||||
chatModal.find('.close').on('click',function(e){
|
||||
chatModal.hide();
|
||||
taskbar.discard('chat', uuid);
|
||||
});
|
||||
|
||||
chatModal.on('click', function(e) {
|
||||
module.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
addSendHandler(chatModal, touid);
|
||||
|
||||
getChatMessages(chatModal, touid);
|
||||
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;
|
||||
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username});
|
||||
chatModal.attr('id', 'chat-modal-' + touid);
|
||||
chatModal.attr('UUID', uuid);
|
||||
chatModal.appendTo($('body'));
|
||||
chatModal.draggable({
|
||||
start:function() {
|
||||
module.bringModalToTop(chatModal);
|
||||
}
|
||||
});
|
||||
|
||||
chatModal.find('#chat-with-name').html(username);
|
||||
|
||||
chatModal.find('.close').on('click', function(e) {
|
||||
clearInterval(chatModal.intervalId);
|
||||
chatModal.intervalId = 0;
|
||||
chatModal.hide();
|
||||
taskbar.discard('chat', uuid);
|
||||
});
|
||||
|
||||
chatModal.on('click', function(e) {
|
||||
module.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
addSendHandler(chatModal);
|
||||
|
||||
getChatMessages(chatModal, function() {
|
||||
checkOnlineStatus(chatModal);
|
||||
});
|
||||
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with ' + username});
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
@@ -51,51 +89,56 @@ define(['taskbar'], function(taskbar) {
|
||||
var chatModal = $('div[UUID="'+uuid+'"]');
|
||||
chatModal.show();
|
||||
module.bringModalToTop(chatModal);
|
||||
checkOnlineStatus(chatModal);
|
||||
}
|
||||
|
||||
module.minimize = function(uuid) {
|
||||
var chatModal = $('div[UUID="'+uuid+'"]');
|
||||
chatModal.hide();
|
||||
taskbar.minimize('chat', uuid);
|
||||
clearInterval(chatModal.intervalId);
|
||||
chatModal.intervalId = 0;
|
||||
}
|
||||
|
||||
function getChatMessages(chatModal, touid) {
|
||||
socket.emit('getChatMessages', {touid:touid}, function(messages) {
|
||||
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);
|
||||
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function addSendHandler(chatModal, touid) {
|
||||
function addSendHandler(chatModal) {
|
||||
chatModal.find('#chat-message-input').off('keypress');
|
||||
chatModal.find('#chat-message-input').on('keypress', function(e) {
|
||||
if(e.which === 13) {
|
||||
sendMessage(chatModal, touid);
|
||||
sendMessage(chatModal);
|
||||
}
|
||||
});
|
||||
|
||||
chatModal.find('#chat-message-send-btn').off('click');
|
||||
chatModal.find('#chat-message-send-btn').on('click', function(e){
|
||||
sendMessage(chatModal, touid);
|
||||
sendMessage(chatModal);
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function sendMessage(chatModal, touid) {
|
||||
function sendMessage(chatModal) {
|
||||
var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
|
||||
if(msg.length) {
|
||||
msg = msg +'\n';
|
||||
socket.emit('sendChatMessage', { touid:touid, message:msg});
|
||||
socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg});
|
||||
chatModal.find('#chat-message-input').val('');
|
||||
module.appendChatMessage(chatModal, 'You : ' + msg);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.appendChatMessage = function(chatModal, message){
|
||||
module.appendChatMessage = function(chatModal, message, timestamp) {
|
||||
var chatContent = chatModal.find('#chat-content');
|
||||
chatContent.append(message);
|
||||
|
||||
var date = new Date(parseInt(timestamp, 10));
|
||||
|
||||
chatContent.append('[' + date.toLocaleTimeString() + '] ' + message);
|
||||
chatContent.scrollTop(
|
||||
chatContent[0].scrollHeight - chatContent.height()
|
||||
);
|
||||
|
||||
@@ -1,38 +1,42 @@
|
||||
define(['taskbar'], function(taskbar) {
|
||||
var composer = {
|
||||
initialized: false,
|
||||
active: undefined,
|
||||
taskbar: taskbar,
|
||||
posts: {},
|
||||
postContainer: undefined,
|
||||
};
|
||||
initialized: false,
|
||||
active: undefined,
|
||||
taskbar: taskbar,
|
||||
posts: {},
|
||||
postContainer: undefined,
|
||||
};
|
||||
|
||||
function createImageLabel(img, postImages) {
|
||||
var imageLabel = $('<div class="label"><span>'+ img.name +'</span></div>');
|
||||
var closeButton = $('<button class="close">×</button>');
|
||||
var uploadsInProgress = [];
|
||||
|
||||
closeButton.on('click', function(e) {
|
||||
function createImagePlaceholder(img) {
|
||||
var text = $('.post-window textarea').val(),
|
||||
textarea = $('.post-window textarea'),
|
||||
imgText = "";
|
||||
|
||||
imageLabel.remove();
|
||||
var index = postImages.indexOf(img);
|
||||
if(index !== -1) {
|
||||
postImages.splice(index, 1);
|
||||
}
|
||||
text += imgText;
|
||||
textarea.val(text + " ");
|
||||
uploadsInProgress.push(1);
|
||||
socket.emit("api:posts.uploadImage", img, function(err, data) {
|
||||
|
||||
var currentText = textarea.val();
|
||||
imgText = "";
|
||||
|
||||
if(!err)
|
||||
textarea.val(currentText.replace(imgText, ""));
|
||||
else
|
||||
textarea.val(currentText.replace(imgText, ""));
|
||||
uploadsInProgress.pop();
|
||||
});
|
||||
|
||||
imageLabel.append(closeButton);
|
||||
return imageLabel;
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
var reader = new FileReader(),
|
||||
dropDiv = $('.post-window .imagedrop'),
|
||||
imagelist = $('.post-window .imagelist'),
|
||||
uuid = dropDiv.parents('[data-uuid]').attr('data-uuid'),
|
||||
posts = composer.posts[uuid];
|
||||
|
||||
uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
|
||||
|
||||
$(reader).on('loadend', function(e) {
|
||||
var bin = this.result;
|
||||
var bin = this.result;
|
||||
bin = bin.split(',')[1];
|
||||
|
||||
var img = {
|
||||
@@ -40,20 +44,17 @@ define(['taskbar'], function(taskbar) {
|
||||
data: bin
|
||||
};
|
||||
|
||||
posts.images.push(img);
|
||||
createImagePlaceholder(img);
|
||||
|
||||
var imageLabel = createImageLabel(img, posts.images);
|
||||
|
||||
imagelist.append(imageLabel);
|
||||
dropDiv.hide();
|
||||
});
|
||||
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function initializeFileReader() {
|
||||
jQuery.event.props.push( "dataTransfer" );
|
||||
|
||||
|
||||
var draggingDocument = false;
|
||||
|
||||
if(window.FileReader) {
|
||||
@@ -77,26 +78,25 @@ define(['taskbar'], function(taskbar) {
|
||||
drop.off('dragleave');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
function cancel(e) {
|
||||
e.preventDefault();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
drop.on('dragover', cancel);
|
||||
drop.on('dragenter', cancel);
|
||||
|
||||
drop.on('drop', function(e) {
|
||||
e.preventDefault();
|
||||
var uuid = drop.parents('[data-uuid]').attr('data-uuid'),
|
||||
posts = composer.posts[uuid],
|
||||
dt = e.dataTransfer,
|
||||
files = dt.files;
|
||||
|
||||
for (var i=0; i<files.length; i++) {
|
||||
loadFile(files[i]);
|
||||
}
|
||||
|
||||
|
||||
if(!files.length)
|
||||
drop.hide();
|
||||
return false;
|
||||
@@ -109,8 +109,8 @@ define(['taskbar'], function(taskbar) {
|
||||
var taskbar = document.getElementById('taskbar');
|
||||
|
||||
composer.postContainer = document.createElement('div');
|
||||
composer.postContainer.className = 'post-window row-fluid';
|
||||
composer.postContainer.innerHTML = '<div class="span5">' +
|
||||
composer.postContainer.className = 'post-window row';
|
||||
composer.postContainer.innerHTML = '<div class="col-md-5">' +
|
||||
'<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' +
|
||||
'<div class="btn-toolbar formatting-bar">' +
|
||||
'<div class="btn-group">' +
|
||||
@@ -121,11 +121,10 @@ define(['taskbar'], function(taskbar) {
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<textarea tabIndex="2"></textarea>' +
|
||||
'<div class="imagelist"></div>'+
|
||||
'<div class="imagedrop"><div>Drag and Drop Images Here</div></div>'+
|
||||
'<div class="btn-toolbar action-bar">' +
|
||||
'<div class="btn-group" style="float: right; margin-right: -8px">' +
|
||||
'<button data-action="minimize" class="btn hidden-phone" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
|
||||
'<button data-action="minimize" class="btn hidden-xs" 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 data-action="post" class="btn" tabIndex="3"><i class="icon-ok"></i> Submit</button>' +
|
||||
'</div>' +
|
||||
@@ -151,13 +150,12 @@ define(['taskbar'], function(taskbar) {
|
||||
cid: threadData.cid,
|
||||
pid: threadData.pid,
|
||||
title: threadData.title || '',
|
||||
body: threadData.body || '',
|
||||
images: []
|
||||
body: threadData.body || ''
|
||||
};
|
||||
composer.load(uuid);
|
||||
} else {
|
||||
app.alert({
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
timeout: 5000,
|
||||
alert_id: 'post_error',
|
||||
title: 'Please Log In to Post',
|
||||
@@ -189,7 +187,15 @@ define(['taskbar'], function(taskbar) {
|
||||
switch(action) {
|
||||
case 'post': composer.post(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;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -265,18 +271,6 @@ define(['taskbar'], function(taskbar) {
|
||||
});
|
||||
}
|
||||
|
||||
function createPostImages(images) {
|
||||
var imagelist = $(composer.postContainer).find('.imagelist');
|
||||
imagelist.empty();
|
||||
|
||||
if(images && images.length) {
|
||||
for(var i=0; i<images.length; ++i) {
|
||||
var imageLabel = createImageLabel(images[i], images);
|
||||
imagelist.append(imageLabel);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composer.load = function(post_uuid) {
|
||||
var post_data = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
@@ -284,7 +278,7 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
composer.reposition(post_uuid);
|
||||
composer.active = post_uuid;
|
||||
|
||||
|
||||
composer.postContainer.setAttribute('data-uuid', post_uuid);
|
||||
if (parseInt(post_data.tid) > 0) {
|
||||
titleEl.value = 'Replying to: ' + post_data.title;
|
||||
@@ -299,7 +293,6 @@ define(['taskbar'], function(taskbar) {
|
||||
}
|
||||
bodyEl.value = post_data.body;
|
||||
|
||||
createPostImages(post_data.images);
|
||||
|
||||
// Direct user focus to the correct element
|
||||
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
|
||||
@@ -312,7 +305,7 @@ define(['taskbar'], function(taskbar) {
|
||||
}
|
||||
|
||||
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 + '"]'),
|
||||
btnRect = taskbarBtn.getBoundingClientRect(),
|
||||
taskbarRect = document.getElementById('taskbar').getBoundingClientRect(),
|
||||
@@ -330,13 +323,23 @@ define(['taskbar'], function(taskbar) {
|
||||
var postData = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
bodyEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
|
||||
titleEl.value = titleEl.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) {
|
||||
return app.alert({
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
timeout: 2000,
|
||||
title: 'Title too short',
|
||||
message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.",
|
||||
@@ -346,7 +349,7 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
if (bodyEl.value.length < config.minimumPostLength) {
|
||||
return app.alert({
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
timeout: 2000,
|
||||
title: 'Content too short',
|
||||
message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
|
||||
@@ -359,21 +362,18 @@ define(['taskbar'], function(taskbar) {
|
||||
socket.emit('api:topics.post', {
|
||||
'title' : titleEl.value,
|
||||
'content' : bodyEl.value,
|
||||
'category_id' : postData.cid,
|
||||
images: composer.posts[post_uuid].images
|
||||
'category_id' : postData.cid
|
||||
});
|
||||
} else if (parseInt(postData.tid) > 0) {
|
||||
socket.emit('api:posts.reply', {
|
||||
'topic_id' : postData.tid,
|
||||
'content' : bodyEl.value,
|
||||
images: composer.posts[post_uuid].images
|
||||
'content' : bodyEl.value
|
||||
});
|
||||
} else if (parseInt(postData.pid) > 0) {
|
||||
socket.emit('api:posts.edit', {
|
||||
pid: postData.pid,
|
||||
content: bodyEl.value,
|
||||
title: titleEl.value,
|
||||
images: composer.posts[post_uuid].images
|
||||
title: titleEl.value
|
||||
});
|
||||
}
|
||||
|
||||
@@ -383,7 +383,6 @@ define(['taskbar'], function(taskbar) {
|
||||
composer.discard = function(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$(composer.postContainer).find('.imagedrop').hide();
|
||||
$(composer.postContainer).find('.imagelist').empty();
|
||||
delete composer.posts[post_uuid];
|
||||
composer.minimize();
|
||||
taskbar.discard('composer', post_uuid);
|
||||
|
||||
@@ -99,10 +99,11 @@ define(function() {
|
||||
|
||||
|
||||
mobileMenu.init = function() {
|
||||
return; // disabling until this can be pluginified.
|
||||
overlay = overlay || document.getElementById('mobile-menu-overlay');
|
||||
menuBtn = menuBtn || document.getElementById('mobile-menu-btn');
|
||||
postBtn = postBtn || document.getElementById('mobile-post-btn');
|
||||
|
||||
|
||||
menuBtn.onclick = function() {
|
||||
animateIcons();
|
||||
}
|
||||
@@ -111,7 +112,7 @@ define(function() {
|
||||
displayCategories();
|
||||
mobileMenu.onNavigate();
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
|
||||
@@ -5,15 +5,15 @@ define(function() {
|
||||
tasklist: undefined,
|
||||
init: function() {
|
||||
var footerEl = document.getElementById('footer');
|
||||
|
||||
|
||||
taskbar.taskbar = document.createElement('div');
|
||||
var jTaskbar = $(taskbar.taskbar);
|
||||
taskbar.taskbar.innerHTML = '<div class="navbar-inner"><ul class="nav pull-right"></ul></div>';
|
||||
taskbar.taskbar.className = 'taskbar navbar navbar-fixed-bottom';
|
||||
taskbar.taskbar.innerHTML = '<div class="navbar-inner"><ul class="nav navbar-nav pull-right"></ul></div>';
|
||||
taskbar.taskbar.className = 'taskbar navbar navbar-default navbar-fixed-bottom';
|
||||
taskbar.taskbar.id = 'taskbar';
|
||||
|
||||
taskbar.tasklist = taskbar.taskbar.querySelector('ul');
|
||||
document.body.insertBefore(taskbar.taskbar, footerEl);
|
||||
document.body.insertBefore(taskbar.taskbar, footerEl.nextSibling);
|
||||
|
||||
// Posts bar events
|
||||
jTaskbar.on('click', 'li', function() {
|
||||
@@ -60,7 +60,7 @@ define(function() {
|
||||
var element = $(taskbar.tasklist).find('li[data-uuid="'+uuid+'"]');
|
||||
if(element.length)
|
||||
return;
|
||||
|
||||
|
||||
var btnEl = document.createElement('li');
|
||||
|
||||
btnEl.innerHTML = '<a href="#">' +
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
|
||||
|
||||
(function (module) {
|
||||
|
||||
|
||||
var config = {},
|
||||
templates,
|
||||
fs = null,
|
||||
@@ -14,11 +12,11 @@
|
||||
fs = require('fs');
|
||||
} catch (e) {}
|
||||
|
||||
templates.force_refresh = function(tpl) {
|
||||
templates.force_refresh = function (tpl) {
|
||||
return !!config.force_refresh[tpl];
|
||||
}
|
||||
|
||||
templates.get_custom_map = function(tpl) {
|
||||
templates.get_custom_map = function (tpl) {
|
||||
if (config['custom_mapping'] && tpl) {
|
||||
for (var pattern in config['custom_mapping']) {
|
||||
if (tpl.match(pattern)) {
|
||||
@@ -30,11 +28,11 @@
|
||||
return false;
|
||||
}
|
||||
|
||||
templates.is_available = function(tpl) {
|
||||
templates.is_available = function (tpl) {
|
||||
return jQuery.inArray(tpl, available_templates) !== -1;
|
||||
};
|
||||
|
||||
templates.ready = function(callback) {
|
||||
templates.ready = function (callback) {
|
||||
if (callback == null) {
|
||||
if (this.ready_callback) {
|
||||
this.ready_callback();
|
||||
@@ -50,13 +48,13 @@
|
||||
}
|
||||
};
|
||||
|
||||
templates.prepare = function(raw_tpl, data) {
|
||||
templates.prepare = function (raw_tpl, data) {
|
||||
var template = {};
|
||||
template.html = raw_tpl;
|
||||
template.parse = parse;
|
||||
template.blocks = {};
|
||||
|
||||
return template;
|
||||
return template;
|
||||
};
|
||||
|
||||
function loadTemplates(templatesToLoad) {
|
||||
@@ -64,10 +62,10 @@
|
||||
var loaded = templatesToLoad.length;
|
||||
|
||||
for (var t in templatesToLoad) {
|
||||
(function(file) {
|
||||
fs.readFile(global.configuration.ROOT_DIRECTORY + '/public/templates/' + file + '.tpl', function(err, html) {
|
||||
var template = function() {
|
||||
this.toString = function() {
|
||||
(function (file) {
|
||||
fs.readFile(__dirname + '/../templates/' + file + '.tpl', function (err, html) {
|
||||
var template = function () {
|
||||
this.toString = function () {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
@@ -75,7 +73,7 @@
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
|
||||
|
||||
global.templates[file] = new template;
|
||||
|
||||
loaded--;
|
||||
@@ -86,147 +84,133 @@
|
||||
}
|
||||
|
||||
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];
|
||||
available_templates = templates_data[0];
|
||||
|
||||
|
||||
|
||||
templates.ready();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
if (fs === null) loadClient();
|
||||
else loadServer();
|
||||
}
|
||||
|
||||
|
||||
templates.init = function(templates_to_load) {
|
||||
templates.init = function (templates_to_load) {
|
||||
loadTemplates(templates_to_load || []);
|
||||
}
|
||||
|
||||
templates.getTemplateNameFromUrl = function(url) {
|
||||
templates.getTemplateNameFromUrl = function (url) {
|
||||
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])) {
|
||||
return parts[i];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
|
||||
templates.load_template = function(callback, url, template) {
|
||||
|
||||
templates.load_template = function (callback, url, template) {
|
||||
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;
|
||||
|
||||
var tpl_url = templates.get_custom_map(api_url.split('?')[0]);
|
||||
|
||||
var trimmed = api_url;
|
||||
|
||||
if(!tpl_url) {
|
||||
if (!tpl_url) {
|
||||
tpl_url = templates.getTemplateNameFromUrl(api_url);
|
||||
}
|
||||
|
||||
|
||||
var template_data = null;
|
||||
|
||||
var timestamp = new Date().getTime(); //debug
|
||||
|
||||
(function() {
|
||||
var timestamp = new Date().getTime(); //debug
|
||||
|
||||
if (!templates[tpl_url]) {
|
||||
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function(html) {
|
||||
var template = function() {
|
||||
this.toString = function() {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
template.prototype.blocks = {};
|
||||
|
||||
templates[tpl_url] = new template;
|
||||
|
||||
parse_template();
|
||||
});
|
||||
} else {
|
||||
parse_template();
|
||||
}
|
||||
|
||||
}());
|
||||
|
||||
(function() {
|
||||
|
||||
jQuery.get(API_URL + api_url, function(data) {
|
||||
|
||||
if(!data) {
|
||||
ajaxify.go('404');
|
||||
return;
|
||||
if (!templates[tpl_url]) {
|
||||
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_url + '.tpl?v=' + timestamp, function (html) {
|
||||
var template = function () {
|
||||
this.toString = function () {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
|
||||
template_data = data;
|
||||
parse_template();
|
||||
}).fail(function(data) {
|
||||
template_data = {};
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
template.prototype.blocks = {};
|
||||
|
||||
templates[tpl_url] = new template;
|
||||
|
||||
parse_template();
|
||||
});
|
||||
}());
|
||||
|
||||
} else {
|
||||
parse_template();
|
||||
}
|
||||
|
||||
jQuery.get(RELATIVE_PATH + '/api/' + api_url, function (data) {
|
||||
|
||||
if (!data) {
|
||||
ajaxify.go('404');
|
||||
return;
|
||||
}
|
||||
|
||||
template_data = data;
|
||||
parse_template();
|
||||
}).fail(function (data) {
|
||||
app.alertError("Can't load template data!");
|
||||
});
|
||||
|
||||
function parse_template() {
|
||||
if (!templates[tpl_url] || !template_data) return;
|
||||
|
||||
if(typeof global !== "undefined")
|
||||
template_data['relative_path'] = nconf.get('relative_path');
|
||||
else
|
||||
template_data['relative_path'] = RELATIVE_PATH;
|
||||
|
||||
document.getElementById('content').innerHTML = templates[tpl_url].parse(template_data);
|
||||
|
||||
jQuery('#content [template-variable]').each(function(index, element) {
|
||||
var value = null;
|
||||
|
||||
switch(element.getAttribute('template-type')) {
|
||||
if (typeof global !== "undefined")
|
||||
template_data['relative_path'] = nconf.get('relative_path');
|
||||
else
|
||||
template_data['relative_path'] = RELATIVE_PATH;
|
||||
|
||||
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
|
||||
document.getElementById('content').innerHTML = translatedTemplate;
|
||||
|
||||
jQuery('#content [template-variable]').each(function (index, element) {
|
||||
var value = null;
|
||||
|
||||
switch (element.getAttribute('template-type')) {
|
||||
case 'boolean':
|
||||
value = (element.value === 'true' || element.value === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt(element.value);
|
||||
break;
|
||||
break;
|
||||
default:
|
||||
value = element.value;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
|
||||
templates.set(element.getAttribute('template-variable'), value);
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
|
||||
templates.set(element.getAttribute('template-variable'), value);
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
templates.flush = function() {
|
||||
templates.flush = function () {
|
||||
parsed_variables = {};
|
||||
}
|
||||
|
||||
templates.get = function(key) {
|
||||
templates.get = function (key) {
|
||||
return parsed_variables[key];
|
||||
}
|
||||
|
||||
templates.set = function(key, value) {
|
||||
templates.set = function (key, value) {
|
||||
parsed_variables[key] = value;
|
||||
}
|
||||
|
||||
//modified from https://github.com/psychobunny/dcp.templates
|
||||
var parse = function(data) {
|
||||
var parse = function (data) {
|
||||
var self = this;
|
||||
|
||||
function replace(key, value, template) {
|
||||
@@ -235,11 +219,11 @@
|
||||
}
|
||||
|
||||
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) {
|
||||
data = template.match(regex);
|
||||
data = template.match(regex);
|
||||
if (data == null) return;
|
||||
|
||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
||||
@@ -255,7 +239,8 @@
|
||||
return template.replace(regex, block);
|
||||
}
|
||||
|
||||
var template = this.html, regex, block;
|
||||
var template = this.html,
|
||||
regex, block;
|
||||
|
||||
return (function parse(data, namespace, template) {
|
||||
if (!data || data.length == 0) {
|
||||
@@ -268,16 +253,18 @@
|
||||
template = replace(namespace + d, '', template);
|
||||
} else if (data[d].constructor == Array) {
|
||||
namespace += 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) {
|
||||
namespace = namespace.replace(d + '.', '');
|
||||
continue;
|
||||
}
|
||||
|
||||
var numblocks = data[d].length - 1, i = 0, result = "";
|
||||
var numblocks = data[d].length - 1,
|
||||
i = 0,
|
||||
result = "";
|
||||
|
||||
do {
|
||||
result += parse(data[d][i], namespace, block);
|
||||
@@ -287,7 +274,7 @@
|
||||
template = setBlock(regex, result, template);
|
||||
} else if (data[d] instanceof Object) {
|
||||
namespace += d + '.';
|
||||
|
||||
|
||||
regex = makeRegex(d),
|
||||
block = getBlock(regex, namespace, template)
|
||||
if (block == null) continue;
|
||||
@@ -301,12 +288,12 @@
|
||||
}
|
||||
|
||||
if (namespace) {
|
||||
var regex = new RegExp("{" + namespace + "[^]*?}", 'g');
|
||||
var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
|
||||
template = template.replace(regex, '');
|
||||
}
|
||||
|
||||
return template;
|
||||
|
||||
|
||||
})(data, "", template);
|
||||
}
|
||||
|
||||
@@ -315,7 +302,8 @@
|
||||
templates.init();
|
||||
}
|
||||
|
||||
})('undefined' === typeof module ? {module:{exports:{}}} : module)
|
||||
|
||||
|
||||
|
||||
})('undefined' === typeof module ? {
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
165
public/src/translator.js
Normal file
165
public/src/translator.js
Normal 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);
|
||||
@@ -1,5 +1,5 @@
|
||||
(function (module) {
|
||||
|
||||
(function(module) {
|
||||
|
||||
var utils, fs;
|
||||
|
||||
try {
|
||||
@@ -10,15 +10,18 @@
|
||||
module.exports = utils = {
|
||||
generateUUID: function() {
|
||||
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);
|
||||
});
|
||||
},
|
||||
|
||||
//Adapted from http://stackoverflow.com/questions/5827612/node-js-fs-readdir-recursive-directory-search
|
||||
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) {
|
||||
if (err) return done(err);
|
||||
var pending = list.length;
|
||||
@@ -32,22 +35,22 @@
|
||||
if (!--pending) done(null, results);
|
||||
});
|
||||
} else {
|
||||
results.push(file.replace(main_dir, '').replace('.tpl', ''));
|
||||
results.push(file.replace(main_dir + '/', '').replace('.tpl', ''));
|
||||
if (!--pending) done(null, results);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
|
||||
relativeTime: function(timestamp, min) {
|
||||
var now = +new Date(),
|
||||
var now = +new Date(),
|
||||
difference = now - Math.floor(parseFloat(timestamp));
|
||||
|
||||
difference = Math.floor(difference / 1000);
|
||||
|
||||
|
||||
if (difference < 60) return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 60) return difference + (min ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
|
||||
@@ -63,7 +66,7 @@
|
||||
difference = Math.floor(difference / 12);
|
||||
return difference + (min ? 'y' : ' year') + (difference !== 1 && !min ? 's' : '');
|
||||
},
|
||||
|
||||
|
||||
//http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
|
||||
slugify: function(str) {
|
||||
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
||||
@@ -71,14 +74,14 @@
|
||||
|
||||
// remove accents, swap ñ for n, etc
|
||||
var from = "àáäâèéëêìíïîıòóöôùúüûñçşğ·/_,:;";
|
||||
var to = "aaaaeeeeiiiiioooouuuuncsg------";
|
||||
for (var i=0, l=from.length ; i<l ; i++) {
|
||||
var to = "aaaaeeeeiiiiioooouuuuncsg------";
|
||||
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(/[^a-z0-9 -]/g, '') // remove invalid chars
|
||||
.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||||
.replace(/-+/g, '-'); // collapse dashes
|
||||
.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||||
.replace(/-+/g, '-'); // collapse dashes
|
||||
|
||||
return str;
|
||||
},
|
||||
@@ -91,31 +94,31 @@
|
||||
},
|
||||
|
||||
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) {
|
||||
return password && password.indexOf(' ') === -1 && password.length > 5;
|
||||
return password && password.indexOf(' ') === -1;
|
||||
},
|
||||
|
||||
// Blatently stolen from: http://phpjs.org/functions/strip_tags/
|
||||
'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>)
|
||||
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;
|
||||
|
||||
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 : '';
|
||||
});
|
||||
},
|
||||
|
||||
buildMetaTags: function(tagsArr) {
|
||||
var tags = '',
|
||||
var tags = '',
|
||||
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";
|
||||
tag = '<meta';
|
||||
for(y in tagsArr[x]) {
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
tag += ' />';
|
||||
@@ -128,14 +131,16 @@
|
||||
|
||||
refreshTitle: function(url) {
|
||||
if (!url) {
|
||||
var a = document.createElement('a');
|
||||
var a = document.createElement('a');
|
||||
a.href = document.location;
|
||||
url = a.pathname.slice(1);
|
||||
}
|
||||
var notificationIcon;
|
||||
|
||||
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
|
||||
document.title = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
|
||||
if (numNotifications > 0) document.querySelector('.notifications a i').className = 'icon-circle active';
|
||||
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) {
|
||||
@@ -146,8 +151,7 @@
|
||||
badge
|
||||
.removeClass('badge-inverse')
|
||||
.addClass('badge-important')
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
badge
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
@@ -156,31 +160,43 @@
|
||||
},
|
||||
|
||||
isRelativeUrl: function(url) {
|
||||
var firstChar = url.slice(0, 1);
|
||||
var firstChar = url.slice(0, 1);
|
||||
return (firstChar === '.' || firstChar === '/');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
String.prototype.ltrim=function(){return this.replace(/^\s+/,'');};
|
||||
}
|
||||
|
||||
if (!String.prototype.rtrim) {
|
||||
String.prototype.rtrim=function(){return this.replace(/\s+$/,'');};
|
||||
}
|
||||
|
||||
if (!String.prototype.fulltrim) {
|
||||
String.prototype.fulltrim=function(){return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g,'').replace(/\s+/g,' ');};
|
||||
String.prototype.ltrim = function() {
|
||||
return this.replace(/^\s+/, '');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if (!String.prototype.rtrim) {
|
||||
String.prototype.rtrim = function() {
|
||||
return this.replace(/\s+$/, '');
|
||||
};
|
||||
}
|
||||
|
||||
if (!String.prototype.fulltrim) {
|
||||
String.prototype.fulltrim = function() {
|
||||
return this.replace(/(?:(?:^|\n)\s+|\s+(?:$|\n))/g, '').replace(/\s+/g, ' ');
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
if ('undefined' !== typeof window) {
|
||||
window.utils = module.exports;
|
||||
}
|
||||
|
||||
})('undefined' === typeof module ? {module:{exports:{}}} : module)
|
||||
})('undefined' === typeof module ? {
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="alert alert-error">
|
||||
<strong>Access Denied</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>
|
||||
<div class="alert alert-danger">
|
||||
<strong>[[global:403.title]]</strong>
|
||||
<p>[[global:403.message]]</p>
|
||||
</div>
|
||||
@@ -1,4 +1,4 @@
|
||||
<div class="alert alert-error">
|
||||
<strong>Not found</strong>
|
||||
<p>You seem to have stumbled upon a page that does not exist. Return to the <a href="/">home page</a></p>
|
||||
<div class="alert alert-danger">
|
||||
<strong>[[global:404.title]]</strong>
|
||||
<p>[[global:404.message]]</p>
|
||||
</div>
|
||||
@@ -1,30 +1,30 @@
|
||||
|
||||
<div class="well">
|
||||
<div class="well account">
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a>
|
||||
<a href="/user/{userslug}">{username}</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span2 account-block" style="text-align: center; margin-bottom:20px;">
|
||||
<div class="row">
|
||||
<div class="col-md-2 account-block" style="text-align: center; margin-bottom:20px;">
|
||||
<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 class="account-online-status">
|
||||
<span><i class="icon-circle-blank"></i> <span>offline</span></span>
|
||||
</div>
|
||||
<div class="{show_banned}">
|
||||
<span class="label label-important">banned</span>
|
||||
<span class="label label-danger">banned</span>
|
||||
</div>
|
||||
<div id="user-actions">
|
||||
<a id="follow-btn" href="#" class="btn hide">Follow</a>
|
||||
<a id="unfollow-btn" href="#" class="btn hide">Unfollow</a>
|
||||
<a id="follow-btn" href="#" class="btn btn-default">Follow</a>
|
||||
<a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span4">
|
||||
<div class="col-md-4">
|
||||
<div class="inline-block">
|
||||
<div class="account-bio-block">
|
||||
<span class="account-bio-label">email</span><i class="icon-eye-close {emailClass}" title="Email hidden"></i>
|
||||
@@ -47,8 +47,8 @@
|
||||
<span>{age}</span>
|
||||
<br/>
|
||||
<hr/>
|
||||
<span class="account-bio-label">member for</span>
|
||||
<span>{joindate}</span>
|
||||
<span class="account-bio-label">joined</span>
|
||||
<span class="timeago" title="{joindate}"></span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">profile views</span>
|
||||
@@ -80,11 +80,11 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span6 user-recent-posts">
|
||||
<div class="col-md-6 user-recent-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 class="pull-right">{posts.relativeTime} ago</span>
|
||||
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
|
||||
</div>
|
||||
<!-- END posts -->
|
||||
</div>
|
||||
|
||||
@@ -1,165 +1,178 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Change Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Change Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div id="gravatar-box">
|
||||
<img id="user-gravatar-picture" src="" class="img-thumbnail user-profile-picture">
|
||||
<span class="user-picture-label">Gravatar</span>
|
||||
<i class='icon-ok icon-2x'></i>
|
||||
</div>
|
||||
<br/>
|
||||
<div id="uploaded-box">
|
||||
<img id="user-uploaded-picture" src="" class="img-thumbnail user-profile-picture">
|
||||
<span class="user-picture-label">Uploaded picture</span>
|
||||
<i class='icon-ok icon-2x'></i>
|
||||
</div>
|
||||
|
||||
<!-- Change Picture Modal -->
|
||||
<div id="change-picture-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Change Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<a id="uploadPictureBtn" href="#">Upload new picture</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="savePictureChangesBtn" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="gravatar-box">
|
||||
<img id="user-gravatar-picture" src="" class="img-polaroid user-profile-picture">
|
||||
<span class="user-picture-label">Gravatar</span>
|
||||
<i class='icon-ok icon-2x'></i>
|
||||
</div>
|
||||
<br/>
|
||||
<div id="uploaded-box">
|
||||
<img id="user-uploaded-picture" src="" class="img-polaroid user-profile-picture">
|
||||
<span class="user-picture-label">Uploaded picture</span>
|
||||
<i class='icon-ok icon-2x'></i>
|
||||
</div>
|
||||
|
||||
<a id="uploadPictureBtn" href="#">Upload new picture</a>
|
||||
|
||||
</div>
|
||||
<div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="uploadForm" action="{relative_path}/user/uploadpicture" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<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="" />
|
||||
</form>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="savePictureChangesBtn" class="btn btn-primary">Save changes</button>
|
||||
</div>
|
||||
</div>
|
||||
<div id="upload-progress-box" class="progress progress-striped">
|
||||
<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 id="alert-status" class="alert alert-info hide"></div>
|
||||
<div id="alert-success" class="alert alert-success hide"></div>
|
||||
<div id="alert-error" class="alert alert-danger hide"></div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
|
||||
</div>
|
||||
</div><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<!-- Upload picture modal-->
|
||||
<div id="upload-picture-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<form id="uploadForm" action="{relative_path}/users/uploadpicture" method="post" enctype="multipart/form-data">
|
||||
<input id="userPhotoInput" type="file" name="userPhoto" />
|
||||
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
|
||||
</form>
|
||||
|
||||
<div id="upload-progress-box" class="progress progress-striped active hide">
|
||||
<div id="upload-progress-bar" class="bar" style="width: 0%;"></div>
|
||||
</div>
|
||||
|
||||
<div id="alert-status" class="alert hide"></div>
|
||||
<div id="alert-success" class="alert alert-success hide"></div>
|
||||
<div id="alert-error" class="alert alert-error hide"></div>
|
||||
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
<button id="pictureUploadSubmitBtn" class="btn btn-primary">Upload Picture</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/edit">edit</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/edit">edit</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span2" style="text-align: center; margin-bottom:20px;">
|
||||
<div class="row">
|
||||
<div class="col-md-2" style="text-align: center; margin-bottom:20px;">
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span10">
|
||||
<div class="inline-block">
|
||||
|
||||
<div class="col-md-5">
|
||||
<div>
|
||||
<form class='form-horizontal'>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputEmail">Email</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputFullname">Full Name</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputWebsite">Website</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputLocation">Location</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputBirthday">Birthday</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputSignature">Signature</label>
|
||||
<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>
|
||||
|
||||
<input type="hidden" id="inputUID" value="{uid}">
|
||||
|
||||
|
||||
<input type="hidden" id="inputUID" value="{uid}"><br />
|
||||
|
||||
<div class="form-actions">
|
||||
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</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'>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputCurrentPassword">Current Password</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputNewPassword">Password</label>
|
||||
<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 class="control-group">
|
||||
<label class="control-label" for="inputNewPasswordAgain">Confirm Password</label>
|
||||
<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>
|
||||
|
||||
<br/>
|
||||
<div class="form-actions">
|
||||
<a id="changePasswordBtn" href="#" class="btn btn-primary">Change Password</a>
|
||||
</div>
|
||||
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" template-variable="gravatarpicture" value="{gravatarpicture}" />
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/settings">settings</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/settings">settings</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span6">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h4>privacy</h4>
|
||||
<label class="checkbox">
|
||||
<input id="showemailCheckBox" type="checkbox" {showemail}> Show my email
|
||||
</label>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input id="showemailCheckBox" type="checkbox" {showemail}> Show my email
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span6">
|
||||
|
||||
|
||||
<div class="col-md-6">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-actions">
|
||||
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
|
||||
</div>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -2,20 +2,25 @@
|
||||
<hr />
|
||||
|
||||
<form>
|
||||
<div class="alert alert-notify">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
Create a <strong>Facebook Application</strong> via the
|
||||
<a href="https://developers.facebook.com/">Facebook Developers Page</a> and
|
||||
then paste your application details here.
|
||||
</p>
|
||||
<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:secret" title="Application Secret" class="input-large" placeholder="App Secret"><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="form-control input-md" placeholder="App Secret"><br />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-large btn-primary" id="save">Save</button>
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
nodebb_admin.prepare();
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
@@ -1,6 +1,9 @@
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
<div id="footer" class="container" style="padding-top: 50px; display:none;">
|
||||
<footer class="footer">Copyright © 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>
|
||||
|
||||
@@ -2,20 +2,25 @@
|
||||
<hr />
|
||||
|
||||
<form>
|
||||
<div class="alert alert-notify">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
Create a <strong>Google Application</strong> via the
|
||||
<a href="https://code.google.com/apis/console/">API Console</a> and then paste
|
||||
your application details here.
|
||||
</p>
|
||||
<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:secret" title="Client Secret" class="input-large" placeholder="Client Secret"><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="form-control" placeholder="Client Secret"><br />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-large btn-primary" id="save">Save</button>
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
nodebb_admin.prepare();
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
100
public/templates/admin/groups.tpl
Normal file
100
public/templates/admin/groups.tpl
Normal 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">×</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">×</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>
|
||||
@@ -16,9 +16,12 @@
|
||||
<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/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 src="{relative_path}/vendor/jquery/js/jquery.timeago.js"></script>
|
||||
<script src="{relative_path}/vendor/requirejs/require.js"></script>
|
||||
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
|
||||
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: "{relative_path}/src/modules",
|
||||
@@ -28,52 +31,54 @@
|
||||
<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 src="{relative_path}/src/utils.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/style.css" />
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/nodebb.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" />
|
||||
</head>
|
||||
|
||||
<body class="admin">
|
||||
<div class="navbar navbar-inverse navbar-fixed-top">
|
||||
<div class="navbar-inner">
|
||||
<div class="container-fluid">
|
||||
<a class="brand" href="/admin/index">NodeBB ACP</a>
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<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>
|
||||
</button>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul class="nav">
|
||||
<li>
|
||||
<a href="/" target="_blank"><i class="icon-book"></i> Forum</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/index"><i class="icon-home"></i> Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav pull-right" id="right-menu">
|
||||
<li><a href="/users" id="user_label"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<a class="navbar-brand" href="/admin/index">NodeBB ACP</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse">
|
||||
<ul class="nav navbar-nav">
|
||||
<li>
|
||||
<a href="/" target="_blank"><i class="icon-book"></i> Forum</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/index"><i class="icon-home"></i> Home</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/admin/settings"><i class="icon-cogs"></i> Settings</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="reconnect"></a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul class="nav pull-right" id="right-menu">
|
||||
<li><a href="/users" id="user_label"></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-3">
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<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=''><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/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/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>
|
||||
@@ -102,4 +107,4 @@
|
||||
</ul>
|
||||
</div><!--/.well -->
|
||||
</div><!--/span-->
|
||||
<div class="span9" id="content">
|
||||
<div class="col-md-9" id="content">
|
||||
@@ -1,14 +1,14 @@
|
||||
|
||||
<div class="hero-unit">
|
||||
<div class="jumbotron">
|
||||
<h1>Welcome to NodeBB</h1>
|
||||
<br />
|
||||
<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-large"><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-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-comment"></i> NodeBB Forum</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-default btn-lg"><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-twitter"></i> dcplabs</a>
|
||||
</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>
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
|
||||
<h1>MOTD</h1>
|
||||
|
||||
<div class="alert motd">
|
||||
<hr />
|
||||
<div class="alert alert-warning motd">
|
||||
<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.
|
||||
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>
|
||||
You can enter either full HTML or Markdown text.
|
||||
</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">
|
||||
<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>
|
||||
</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>
|
||||
nodebb_admin.prepare();
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
@@ -13,9 +13,9 @@
|
||||
<!-- END plugins -->
|
||||
</ul>
|
||||
|
||||
<div class="alert">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
<strong>Interesed in writing plugins for NodeBB?</strong>
|
||||
<strong>Interested in writing plugins for NodeBB?</strong>
|
||||
</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>.
|
||||
|
||||
@@ -2,66 +2,85 @@
|
||||
<hr />
|
||||
|
||||
<h3>General Settings</h3>
|
||||
<div class="alert">
|
||||
<div class="alert alert-warning">
|
||||
<form>
|
||||
<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>
|
||||
<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="input-xxlarge" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" />
|
||||
<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>
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<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 />
|
||||
<strong>Manage Thread</strong><br /> <input type="text" class="" value="1000"><br />
|
||||
<strong>Moderate Users</strong><br /> <input type="text" class="" value="10000"><br />
|
||||
<strong>Create Pinned Topics</strong><br /> <input type="text" class="" value="100000"><br />
|
||||
|
||||
<strong>Manage Thread</strong><br /> <input type="text" class="form-control" value="1000"><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="form-control" value="100000"><br />
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<h3>Email Settings</h3>
|
||||
<div class="alert alert-notify">
|
||||
<div class="alert alert-warning">
|
||||
<div>
|
||||
<p>
|
||||
<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.
|
||||
</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>
|
||||
<p>
|
||||
<strong>SMTP Server Host</strong><br />
|
||||
(Default: <em>127.0.0.1</em>)
|
||||
</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>
|
||||
<p>
|
||||
<strong>SMTP Server Port</strong>
|
||||
</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>
|
||||
</form>
|
||||
|
||||
<form>
|
||||
<h3>Post Settings</h3>
|
||||
<div class="alert alert-notify">
|
||||
<strong>Post Delay</strong><br /> <input type="text" class="" value="10000" data-field="postDelay"><br />
|
||||
<strong>Minimum Title Length</strong><br /> <input type="text" class="" value="3" data-field="minimumTitleLength"><br />
|
||||
<strong>Minimum Post Length</strong><br /> <input type="text" class="" value="8" data-field="minimumPostLength"><br />
|
||||
|
||||
<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>
|
||||
|
||||
<button class="btn btn-large btn-primary" id="save">Save</button>
|
||||
<form>
|
||||
<h3>Post Settings</h3>
|
||||
<div class="alert alert-warning">
|
||||
<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="form-control" value="3" data-field="minimumTitleLength"><br />
|
||||
<strong>Minimum Post Length</strong><br /> <input type="text" class="form-control" value="8" data-field="minimumPostLength"><br />
|
||||
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
nodebb_admin.prepare();
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
@@ -10,7 +10,7 @@ jQuery(document).ready(function () {
|
||||
QUnit.init();
|
||||
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) );
|
||||
|
||||
start();
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
</ul>
|
||||
|
||||
<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>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/topics.js"></script>
|
||||
@@ -2,20 +2,25 @@
|
||||
<hr />
|
||||
|
||||
<form>
|
||||
<div class="alert alert-notify">
|
||||
<div class="alert alert-warning">
|
||||
<p>
|
||||
Create a <strong>Twitter Application</strong> via the
|
||||
<a href="https://dev.twitter.com/">Twitter Developers Page</a> and then
|
||||
paste your application details here.
|
||||
</p>
|
||||
<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:secret" title="Consumer Secret" class="input-xlarge" placeholder="Consumer Secret">
|
||||
<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="form-control input-md" placeholder="Consumer Secret">
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-large btn-primary" id="save">Save</button>
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
nodebb_admin.prepare();
|
||||
var loadDelay = setInterval(function() {
|
||||
if (nodebb_admin) {
|
||||
nodebb_admin.prepare();
|
||||
clearInterval(loadDelay);
|
||||
}
|
||||
}, 500);
|
||||
</script>
|
||||
|
||||
@@ -7,21 +7,21 @@
|
||||
<li class=''><a href='/admin/users/search'>Search</a></li>
|
||||
</ul>
|
||||
|
||||
|
||||
<br />
|
||||
<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>
|
||||
<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>
|
||||
|
||||
<ul id="users-container" class="users">
|
||||
<ul id="users-container" class="users admin">
|
||||
<!-- BEGIN users -->
|
||||
<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}">
|
||||
<img src="{users.picture}" class="img-polaroid"/>
|
||||
<a href="/user/{users.userslug}">
|
||||
<img src="{users.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="/users/{users.userslug}">{users.username}</a>
|
||||
<a href="/user/{users.userslug}">{users.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span id='reputation'>{users.reputation}</span>
|
||||
@@ -32,20 +32,14 @@
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn admin-btn">Admin</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn delete-btn btn-danger">Delete</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn ban-btn">Ban</a>
|
||||
<a href="#" class="btn btn-default ban-btn">Ban</a>
|
||||
</div>
|
||||
</div>
|
||||
<!-- END users -->
|
||||
</ul>
|
||||
|
||||
<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>
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
|
||||
|
||||
@@ -1,21 +1,17 @@
|
||||
<div class="container">
|
||||
<ul class="breadcrumb">
|
||||
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<a href="/" itemprop="url"><span itemprop="title">Home</span></a>
|
||||
<span class="divider">/</span>
|
||||
</li>
|
||||
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<span itemprop="title">{category_name}</span>
|
||||
</li>
|
||||
<div id="category_active_users"></div>
|
||||
</ul>
|
||||
</div>
|
||||
<ol class="breadcrumb">
|
||||
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
|
||||
</li>
|
||||
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<span itemprop="title">{category_name} <a target="_blank" href="../{category_id}.rss"><i class="icon-rss-sign"></i></a></span>
|
||||
</li>
|
||||
<div id="category_active_users"></div>
|
||||
</ol>
|
||||
|
||||
<div>
|
||||
<button id="new_post" class="btn btn-primary btn-large {show_topic_button}">New Topic</button>
|
||||
<button id="new_post" class="btn btn-primary btn-lg {show_topic_button}">[[category:new_topic_button]]</button>
|
||||
|
||||
<div class="inline-block pull-right">
|
||||
<a target="_blank" href="../{category_id}.rss"><i class="icon-rss-sign icon-2x"></i></a>
|
||||
<a href="#" id="facebook-share"><i class="icon-facebook-sign icon-2x"></i></a>
|
||||
<a href="#" id="twitter-intent"><i class="icon-twitter-sign icon-2x"></i></a>
|
||||
<a href="#" id="google-share"><i class="icon-google-plus-sign icon-2x"></i></a>
|
||||
@@ -25,71 +21,72 @@
|
||||
<hr/>
|
||||
|
||||
<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?
|
||||
[[category:no_topics]]
|
||||
</div>
|
||||
|
||||
<div class="category row">
|
||||
<div class="{topic_row_size}">
|
||||
<ul id="topics-container">
|
||||
<!-- BEGIN topics -->
|
||||
<a href="../../topic/{topics.slug}"><li class="category-item {topics.deleted-class}">
|
||||
<div class="row-fluid">
|
||||
<!-- <div class="span1 thread-rating hidden-phone hidden-tablet">
|
||||
<span>
|
||||
<i class="icon-star icon-3x"></i><br />
|
||||
38
|
||||
</span>
|
||||
</div> -->
|
||||
<div class="span12 topic-row">
|
||||
<div class="latest-post visible-desktop">
|
||||
<div class="pull-right">
|
||||
<img style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p><strong>{topics.teaser_username}</strong>: {topics.teaser_text}</p>
|
||||
<span>posted {topics.teaser_timestamp} ago</span>
|
||||
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row">
|
||||
<div class="col-md-12 topic-row">
|
||||
<div class="latest-post visible-lg visible-md">
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
<div class="pull-right">
|
||||
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p>{topics.teaser_text}</p>
|
||||
<p class="meta">
|
||||
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong>
|
||||
Posted {topics.relativeTime} ago by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li></a>
|
||||
</li>
|
||||
|
||||
<!-- END topics -->
|
||||
</ul>
|
||||
</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">
|
||||
Recent Replies
|
||||
[[category:sidebar.recent_replies]]
|
||||
</div>
|
||||
<div class="block-content recent-replies">
|
||||
<ul id="category_recent_replies"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-block img-polaroid">
|
||||
<div class="sidebar-block img-thumbnail">
|
||||
<div class="block-header">
|
||||
Active Participants
|
||||
[[category:sidebar.active_participants]]
|
||||
</div>
|
||||
<div class="block-content">
|
||||
<!-- 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 -->
|
||||
</div>
|
||||
</div>
|
||||
<div class="sidebar-block img-polaroid {moderator_block_class}">
|
||||
<div class="sidebar-block img-thumbnail {moderator_block_class}">
|
||||
<div class="block-header">
|
||||
Moderators
|
||||
[[category:sidebar.moderators]]
|
||||
</div>
|
||||
<div class="block-content">
|
||||
<!-- 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 -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,41 +1,42 @@
|
||||
{
|
||||
"custom_mapping": {
|
||||
"admin/testing/categories[^]*": "admin/testing/categories",
|
||||
"admin/topics[^]*": "admin/topics",
|
||||
"admin/categories[^]*": "admin/categories",
|
||||
"admin/users[^]*": "admin/users",
|
||||
"admin/redis[^]*": "admin/redis",
|
||||
"admin/index[^]*": "admin/index",
|
||||
"admin/themes[^]*": "admin/themes",
|
||||
"admin/plugins[^]*": "admin/plugins",
|
||||
"admin/settings[^]*": "admin/settings",
|
||||
"admin/twitter[^]*": "admin/twitter",
|
||||
"admin/facebook[^]*": "admin/facebook",
|
||||
"admin/gplus[^]*": "admin/gplus",
|
||||
"admin/testing/categories.*": "admin/testing/categories",
|
||||
"admin/topics.*": "admin/topics",
|
||||
"admin/categories.*": "admin/categories",
|
||||
"admin/users.*": "admin/users",
|
||||
"admin/redis.*": "admin/redis",
|
||||
"admin/index.*": "admin/index",
|
||||
"admin/themes.*": "admin/themes",
|
||||
"admin/plugins.*": "admin/plugins",
|
||||
"^admin/settings.*": "admin/settings",
|
||||
"admin/twitter.*": "admin/twitter",
|
||||
"admin/facebook.*": "admin/facebook",
|
||||
"admin/gplus.*": "admin/gplus",
|
||||
"admin/motd/?$": "admin/motd",
|
||||
"admin/groups/?$": "admin/groups",
|
||||
"install/?$": "install/mail",
|
||||
"install/mail/?": "install/mail",
|
||||
"install/social/?": "install/social",
|
||||
"install/privileges/?": "install/privileges",
|
||||
"users-sort-posts": "users",
|
||||
"users-latest": "users",
|
||||
"users-sort-reputation": "users",
|
||||
"users-search": "users",
|
||||
"users[^]*edit": "accountedit",
|
||||
"users[^]*following": "following",
|
||||
"users[^]*followers": "followers",
|
||||
"users[^]*settings": "accountsettings",
|
||||
"users[^]*favourites": "favourites",
|
||||
"users/[^]*": "account",
|
||||
"users/sort-posts": "users",
|
||||
"users/latest": "users",
|
||||
"users/sort-reputation": "users",
|
||||
"users/search": "users",
|
||||
"user.*edit": "accountedit",
|
||||
"user.*following": "following",
|
||||
"user.*followers": "followers",
|
||||
"user.*settings": "accountsettings",
|
||||
"user.*favourites": "favourites",
|
||||
"user/.*": "account",
|
||||
|
||||
"recent": "recent",
|
||||
"unread": "unread",
|
||||
"popular": "category",
|
||||
"active": "category",
|
||||
"search": "search",
|
||||
"reset/[^]*": "reset_code",
|
||||
"reset/.*": "reset_code",
|
||||
"reset": "reset"
|
||||
|
||||
|
||||
},
|
||||
"force_refresh": {
|
||||
"logout": true
|
||||
|
||||
@@ -1,34 +1,27 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
|
||||
|
||||
<div class="well favourites">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a>
|
||||
<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-fluid">
|
||||
<div class="span12 user-favourite-posts">
|
||||
<div class="row">
|
||||
<div class="col-md-12 user-favourite-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><strong>{posts.username}</strong> : </span>
|
||||
<span>{posts.content}</span>
|
||||
<div>
|
||||
<span class="pull-right">{posts.relativeTime} ago</span>
|
||||
<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>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/favourites.js"></script>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
|
||||
<div class="well">
|
||||
<div class="well users">
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/followers">followers</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/followers">followers</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- BEGIN followers -->
|
||||
<div class="users-box">
|
||||
<a href="/users/{followers.userslug}">
|
||||
<img src="{followers.picture}" class="img-polaroid"/>
|
||||
<a href="/user/{followers.userslug}">
|
||||
<img src="{followers.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="/users/{followers.userslug}">{followers.username}</a>
|
||||
<a href="/user/{followers.userslug}">{followers.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='formatted-number'>{followers.reputation}</span>
|
||||
|
||||
@@ -1,21 +1,21 @@
|
||||
|
||||
<div class="well">
|
||||
<div class="well users">
|
||||
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/users/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/users/{userslug}/following">following</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/following">following</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- BEGIN following -->
|
||||
<div class="users-box">
|
||||
<a href="/users/{following.userslug}">
|
||||
<img src="{following.picture}" class="img-polaroid"/>
|
||||
<a href="/user/{following.userslug}">
|
||||
<img src="{following.picture}" class="img-thumbnail"/>
|
||||
</a>
|
||||
<br/>
|
||||
<a href="/users/{following.userslug}">{following.username}</a>
|
||||
<a href="/user/{following.userslug}">{following.username}</a>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='formatted-number'>{following.reputation}</span>
|
||||
@@ -25,7 +25,7 @@
|
||||
<span class='formatted-number'>{following.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</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>
|
||||
<!-- END following -->
|
||||
</div>
|
||||
|
||||
@@ -2,54 +2,59 @@
|
||||
|
||||
</div><!--END container -->
|
||||
|
||||
<div id="disconnect-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<h3 id="myModalLabel">Socket Disconnect</h3>
|
||||
</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" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">[[footer:chat.chatting_with]]</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<textarea class="form-control" id="chat-content" cols="40" rows="10" readonly></textarea><br/>
|
||||
<input id="chat-message-input" type="text" class="form-control" name="chat-message" placeholder="[[footer:chat.placeholder]]"/>
|
||||
</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><!-- /.modal-content -->
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="chat-modal" class="modal hide" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Chat with <span id="chat-with-name"></span></h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<textarea 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/>
|
||||
<button type="button" id="chat-message-send-btn" href="#" class="btn btn-primary">Send</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
<div id="mobile-sidebar">
|
||||
</div>
|
||||
|
||||
<!-- START Forum Info -->
|
||||
<div id="footer" class="container hidden-phone" style="padding-top: 50px; display: none">
|
||||
<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>
|
||||
<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>
|
||||
<footer class="footer">Copyright © 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 -->
|
||||
<div class="copyright">Copyright © 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>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
$.getScript(RELATIVE_PATH + '/src/forum/footer.js');
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,79 +4,114 @@
|
||||
<title>{browserTitle}</title>
|
||||
{meta_tags}
|
||||
<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">
|
||||
<script>
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</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}/src/app.js"></script>
|
||||
<script src="{relative_path}/vendor/requirejs/require.js"></script>
|
||||
<script src="{relative_path}/vendor/bootbox/bootbox.min.js"></script>
|
||||
<!-- BEGIN clientScripts -->
|
||||
<script src="{relative_path}/{clientScripts.script}"></script>
|
||||
<!-- END clientScripts -->
|
||||
<script>
|
||||
require.config({
|
||||
baseUrl: "{relative_path}/src/modules",
|
||||
waitSeconds: 3
|
||||
});
|
||||
</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" />
|
||||
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="mobile-menu-overlay">
|
||||
</div>
|
||||
<div class="navbar navbar-inverse navbar-fixed-top" id="header-menu">
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<a class="brand" href="/">{title}</a>
|
||||
<button type="button" class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
|
||||
<div class="navbar navbar-inverse navbar-fixed-top header" role="navigation" id="header-menu">
|
||||
<div class="container">
|
||||
<div class="navbar-header">
|
||||
<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>
|
||||
</button>
|
||||
<div class="nav-collapse collapse">
|
||||
<ul id="main-nav" class="nav nodebb-inline-block">
|
||||
<li>
|
||||
<a href="/recent">Recent</a>
|
||||
</li>
|
||||
<li class="nodebb-loggedin">
|
||||
<a href="/unread"><span id="numUnreadBadge" class="badge badge-inverse">0</span> Unread</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="/users">Users</a>
|
||||
</li>
|
||||
</ul>
|
||||
<ul id="right-menu" class="nav pull-right nodebb-inline-block">
|
||||
<li class="notifications dropdown text-center">
|
||||
<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> Loading Notifications</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>
|
||||
<form id="search-form" class="form-search form-inline" action="" method="GET">
|
||||
<input type="text" name="query" class="input-medium search-query">
|
||||
<button type="submit" class="btn hide">Search</button>
|
||||
</form>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<a href="/">
|
||||
<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>
|
||||
<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>
|
||||
<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>
|
||||
<a href="/"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<form id="search-form" class="navbar-form navbar-right hidden-xs" role="search" method="GET" action="">
|
||||
<div class="hide" id="search-fields">
|
||||
<div class="form-group">
|
||||
<input type="text" class="form-control" placeholder="Search" name="query" value="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-default hide">[[global:search]]</button>
|
||||
</div>
|
||||
<button id="search-button" type="button" class="btn btn-link"><i class="icon-search"></i></button>
|
||||
</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>
|
||||
</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>
|
||||
|
||||
|
||||
<input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" />
|
||||
|
||||
|
||||
<div class="container" id="content">
|
||||
|
||||
<div class="container" id="content">
|
||||
@@ -1,13 +1,13 @@
|
||||
<div class="hero-unit {motd_class}">
|
||||
<div class="jumbotron {motd_class}">
|
||||
{motd}
|
||||
</div>
|
||||
|
||||
<div class="row category-row">
|
||||
<div class="row home">
|
||||
<!-- BEGIN categories -->
|
||||
<div class="span3">
|
||||
<div class="col-md-3 col-xs-6">
|
||||
<a href="category/{categories.slug}">
|
||||
<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 class="category-box"><i class="{categories.icon} icon-4x"></i></div>
|
||||
<div class="category-box">{categories.description}</div>
|
||||
|
||||
@@ -30,10 +30,10 @@
|
||||
|
||||
<hr />
|
||||
<div class="pull-right">
|
||||
<button data-path="mail" class="btn btn-primary btn-large">Next – <i class="icon-envelope"></i> Mail</button>
|
||||
<button data-path="mail" class="btn btn-primary btn-lg">Next – <i class="icon-envelope"></i> Mail</button>
|
||||
</div>
|
||||
<div>
|
||||
<button data-path="redis" class="btn btn-primary btn-large">Previous – <i class="icon-hdd"></i> Redis</button>
|
||||
<button data-path="redis" class="btn btn-primary btn-lg">Previous – <i class="icon-hdd"></i> Redis</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
// ajaxify.go('install/redis');
|
||||
// app.alert({
|
||||
// alert_id: 'config-ready',
|
||||
// type: 'error',
|
||||
// type: 'danger',
|
||||
// timeout: 10000,
|
||||
// title: 'NodeBB Configuration Not Ready!',
|
||||
// message: 'NodeBB cannot proceed with setup at this time as Redis database information ' +
|
||||
@@ -134,7 +134,7 @@
|
||||
timeout: 2500,
|
||||
title: 'Configuration Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'error'
|
||||
type: 'danger'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
<div class="navbar-inner">
|
||||
<div class="container">
|
||||
<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>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
<hr />
|
||||
<div class="pull-right">
|
||||
<button data-path="social" class="btn btn-primary btn-large">Next – <i class="icon-facebook"></i> Social</button>
|
||||
<button data-path="social" class="btn btn-primary btn-lg">Next – <i class="icon-facebook"></i> Social</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -27,10 +27,10 @@
|
||||
|
||||
<hr />
|
||||
<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>
|
||||
<button data-path="social" class="btn btn-primary btn-large">Previous – <i class="icon-facebook"></i> Social</button>
|
||||
<button data-path="social" class="btn btn-primary btn-lg">Previous – <i class="icon-facebook"></i> Social</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -32,7 +32,7 @@
|
||||
|
||||
<hr />
|
||||
<div class="pull-right">
|
||||
<button data-path="basic" class="btn btn-primary btn-large" disabled>Next – <i class="icon-cog"></i> Basic</button>
|
||||
<button data-path="basic" class="btn btn-primary btn-lg" disabled>Next – <i class="icon-cog"></i> Basic</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -34,10 +34,10 @@
|
||||
|
||||
<hr />
|
||||
<div class="pull-right">
|
||||
<button data-path="privileges" class="btn btn-primary btn-large">Next – <i class="icon-legal"></i> Privileges</button>
|
||||
<button data-path="privileges" class="btn btn-primary btn-lg">Next – <i class="icon-legal"></i> Privileges</button>
|
||||
</div>
|
||||
<div>
|
||||
<button data-path="mail" class="btn btn-primary btn-large">Previous – <i class="icon-envelope"></i> Mail</button>
|
||||
<button data-path="mail" class="btn btn-primary btn-lg">Previous – <i class="icon-envelope"></i> Mail</button>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
|
||||
@@ -1,29 +1,63 @@
|
||||
<h1>Login</h1>
|
||||
<div class="row-fluid">
|
||||
<div class="well {login_window:spansize}">
|
||||
<h4>Login via Username & Password</h4>
|
||||
<div class="alert alert-error" id="error" style="display:none">
|
||||
<button type="button" class="close" data-dismiss="alert">×</button>
|
||||
<strong>Failed Login Attempt</strong> <p></p>
|
||||
<ol class="breadcrumb">
|
||||
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
|
||||
</li>
|
||||
<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">×</button>
|
||||
<strong>[[login:failed_login_attempt]]</strong> <p></p>
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal" role="form">
|
||||
<div class="form-group">
|
||||
<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> <a href="/reset">[[login:forgot_password]]</a>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" />
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<label>Username</label><input type="text" placeholder="Enter Username" name="username" id="username" /><br />
|
||||
<label>Password</label><input type="password" placeholder="Enter Password" name="password" id="password" /><br />
|
||||
<input type="hidden" name="_csrf" value="{token}" id="csrf-token" />
|
||||
<button class="btn btn-primary" id="login" type="submit">Login</button> <a href="/reset">Forgot Password?</a>
|
||||
</form>
|
||||
|
||||
<div id="login-error-notify" class="alert alert-danger hide">Invalid username/password</div>
|
||||
</div>
|
||||
|
||||
<div class="well span6 {alternate_logins:display}">
|
||||
<h4>Alternative Logins</h4>
|
||||
<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/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>
|
||||
</ul>
|
||||
|
||||
<div class="col-md-6 {alternate_logins:display}">
|
||||
<div class="well well-lg">
|
||||
<h4>[[login:alternative_logins]]</h4>
|
||||
<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/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>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
<div class="alert" id="message">
|
||||
<strong>Logout</strong>
|
||||
<p>You have successfully logged out of NodeBB</p>
|
||||
<p>
|
||||
<a href="/">NodeBB Home</a>
|
||||
</p>
|
||||
<ol class="breadcrumb">
|
||||
<li itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<a href="/" itemprop="url"><span itemprop="title">[[global:home]]</span></a>
|
||||
</li>
|
||||
<li class="active" itemscope="itemscope" itemtype="http://data-vocabulary.org/Breadcrumb">
|
||||
<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>
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
<ul class="breadcrumb">
|
||||
<li><a href="/">Home</a><span class="divider">/</span></li>
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li class="active">{category_name}</li>
|
||||
<div id="category_active_users"></div>
|
||||
</ul>
|
||||
</ol>
|
||||
<ul class="topics">
|
||||
<!-- BEGIN topics -->
|
||||
<li>
|
||||
<span class="timestamp">{topics.teaser_timestamp}</span>
|
||||
<a href="../../topic/{topics.slug}">{topics.title} ({topics.postcount})</a>
|
||||
<div class="teaser">
|
||||
<img class="img-polaroid" src="../../graph/users/{topics.teaser_username}/picture" />
|
||||
<img class="img-thumbnail" src="{topics.teaser_userpicture}" />
|
||||
<p>
|
||||
{topics.teaser_text} — {topics.teaser_timestamp} ago
|
||||
{topics.teaser_text}
|
||||
</p>
|
||||
<div class="clear"></div>
|
||||
</div>
|
||||
</li>
|
||||
<!-- END topics -->
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<div class="alert alert-error">
|
||||
<div class="alert alert-danger">
|
||||
<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>
|
||||
Please download a browser that supports javascript, or enable it, if it disabled (i.e. NoScript).
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
<ul class="posts">
|
||||
<!-- BEGIN main_posts -->
|
||||
<li>
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<img src="{main_posts.picture}" /><br />
|
||||
{main_posts.username}
|
||||
<a name="{main_posts.pid}"></a>
|
||||
<div class="row">
|
||||
<div class="col-lg-2 profile">
|
||||
<img class="img-thumbnail" src="{main_posts.picture}" /><br />
|
||||
<span class="username">{main_posts.username}</span>
|
||||
</div>
|
||||
<div class="span10">
|
||||
<div class="col-lg-10">
|
||||
{main_posts.content}
|
||||
</div>
|
||||
</div>
|
||||
@@ -14,12 +15,13 @@
|
||||
<!-- END main_posts -->
|
||||
<!-- BEGIN posts -->
|
||||
<li>
|
||||
<div class="row-fluid">
|
||||
<div class="span2">
|
||||
<img src="{posts.picture}" /><br />
|
||||
{posts.username}
|
||||
<a name="{posts.pid}"></a>
|
||||
<div class="row">
|
||||
<div class="col-lg-2 profile">
|
||||
<img class="img-thumbnail" src="{posts.picture}" /><br />
|
||||
<span class="username">{posts.username}</span>
|
||||
</div>
|
||||
<div class="span10">
|
||||
<div class="col-lg-10">
|
||||
{posts.content}
|
||||
</div>
|
||||
<div class="clear"></div>
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
<div class="hero-unit">
|
||||
<h2 class="form-signin-heading">Now Leaving NodeBB</h2>
|
||||
<p>
|
||||
You are now leaving NodeBB.
|
||||
</p>
|
||||
<br />
|
||||
<p>
|
||||
<a href="{url}" rel="nofollow" class="btn btn-large">Continue to {url}</a>
|
||||
<a href="{home}" class="btn btn-large btn-inverse">Return to NodeBB</a>
|
||||
</p>
|
||||
</div>
|
||||
<div class="outgoing">
|
||||
<ol class="breadcrumb">
|
||||
<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.
|
||||
</h3>
|
||||
<p>
|
||||
<a href="{url}" rel="nofollow" class="btn btn-primary btn-lg">Continue to {url}</a>
|
||||
<a id="return-btn" href="#" class="btn btn-lg btn-warning">Return to NodeBB</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
$('#return-btn').on('click', function() {
|
||||
history.back();
|
||||
return false;
|
||||
});
|
||||
</script>
|
||||
@@ -1,11 +1,8 @@
|
||||
<div class="container">
|
||||
<ul class="breadcrumb">
|
||||
<li><a href="/">Home</a><span class="divider">/</span></li>
|
||||
<li class="active">{category_name}</li>
|
||||
<div id="category_active_users"></div>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<ol class="breadcrumb">
|
||||
<li><a href="/">Home</a></li>
|
||||
<li class="active">{category_name}</li>
|
||||
<div id="category_active_users"></div>
|
||||
</ol>
|
||||
|
||||
<a href="/recent">
|
||||
<div class="alert hide" id="new-topics-alert"></div>
|
||||
@@ -21,23 +18,29 @@
|
||||
<!-- BEGIN topics -->
|
||||
<a href="../../topic/{topics.slug}" id="tid-{topics.tid}">
|
||||
<li class="category-item {topics.deleted-class}">
|
||||
<div class="row-fluid">
|
||||
<div class="span12 topic-row img-polaroid">
|
||||
<div class="latest-post visible-desktop">
|
||||
<div class="pull-right">
|
||||
<img style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p><strong>{topics.teaser_username}</strong>: {topics.teaser_text}</p>
|
||||
<span>posted {topics.teaser_timestamp} ago</span>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xs-12 topic-row img-thumbnail">
|
||||
<div class="latest-post visible-lg visible-md">
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
<div class="pull-right">
|
||||
<img class="img-rounded" style="width: 48px; height: 48px; /*temporary*/" src="{topics.teaser_userpicture}" />
|
||||
<p>{topics.teaser_text}</p>
|
||||
<p class="meta">
|
||||
<strong>{topics.teaser_username}</strong> posted <span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
<a href="../../topic/{topics.slug}">
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
Posted <span class="timeago" title="{topics.relativeTime}"></span> by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h3><span class="topic-title"><span class="badge {topics.badgeclass}">{topics.postcount}</span>{topics.title}</span></h3>
|
||||
<small>
|
||||
<strong><i class="{topics.pin-icon}"></i><i class="{topics.lock-icon}"></i></strong>
|
||||
Posted {topics.relativeTime} ago by
|
||||
<strong>{topics.username}</strong>.
|
||||
</small>
|
||||
</div>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@@ -47,4 +50,4 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/recent.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/recent.js"></script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user