mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-16 21:40:23 +01:00
Compare commits
921 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
27fce2363d | ||
|
|
01340c87bd | ||
|
|
77c2f551d3 | ||
|
|
e6bb66705d | ||
|
|
88154c3ebf | ||
|
|
1c80a1bad5 | ||
|
|
79c52dfe84 | ||
|
|
411ba3542c | ||
|
|
fa9636a62a | ||
|
|
69fefc0625 | ||
|
|
e598ffa993 | ||
|
|
d99577ffb2 | ||
|
|
40108f92c9 | ||
|
|
a42b30fd40 | ||
|
|
37497fc5a0 | ||
|
|
9bea23bbfe | ||
|
|
4e39c50144 | ||
|
|
7e50bcba0c | ||
|
|
f81c583d86 | ||
|
|
5647d55147 | ||
|
|
4ce6ac5af9 | ||
|
|
d770963b69 | ||
|
|
625b96ba73 | ||
|
|
37877ed531 | ||
|
|
1f2ef2a7e4 | ||
|
|
885eec79c3 | ||
|
|
aa1994be67 | ||
|
|
4552e6286e | ||
|
|
d7856bcd4f | ||
|
|
7ea852fae3 | ||
|
|
c67c37bb20 | ||
|
|
7d1aa02fd1 | ||
|
|
2309ab2002 | ||
|
|
b87840d4c9 | ||
|
|
b5c22c7ff7 | ||
|
|
3b3e8348e4 | ||
|
|
259ad42b31 | ||
|
|
51cb33bccc | ||
|
|
2a8a62a253 | ||
|
|
82ae80090e | ||
|
|
1d6135150f | ||
|
|
41f98d29b7 | ||
|
|
7b5a6bd3c9 | ||
|
|
fe15366524 | ||
|
|
1cbbb3873f | ||
|
|
5e1ab7989a | ||
|
|
0e5724cd2c | ||
|
|
89e9d56dee | ||
|
|
58f9c2c18d | ||
|
|
1ec6726459 | ||
|
|
6b4520e526 | ||
|
|
98f20564de | ||
|
|
4da819b02b | ||
|
|
dc90db74c0 | ||
|
|
c5bc2dd64f | ||
|
|
bbb045698d | ||
|
|
d9e364cd86 | ||
|
|
b179991be4 | ||
|
|
05de4870b0 | ||
|
|
01f0131f5d | ||
|
|
d69847c54e | ||
|
|
cc78f6f155 | ||
|
|
1e2100902c | ||
|
|
4353a9da25 | ||
|
|
e480b1bace | ||
|
|
5a96f5f64b | ||
|
|
7296b701fa | ||
|
|
a21d91d870 | ||
|
|
6931695e64 | ||
|
|
e12d02f29c | ||
|
|
404865c32e | ||
|
|
75879c47c5 | ||
|
|
f946918176 | ||
|
|
6ca3df2431 | ||
|
|
c8ec095d99 | ||
|
|
0179a55ee4 | ||
|
|
04ed1df0ef | ||
|
|
de66ee1a89 | ||
|
|
e8c4bda984 | ||
|
|
7074b75b9d | ||
|
|
857756f636 | ||
|
|
e4c62200de | ||
|
|
89ec677d54 | ||
|
|
8ff656430d | ||
|
|
db22394976 | ||
|
|
ef5548a749 | ||
|
|
14c3bb7d63 | ||
|
|
1a415b60be | ||
|
|
85fa68bd92 | ||
|
|
e41ca491ff | ||
|
|
bb5962cda0 | ||
|
|
2de878821b | ||
|
|
5bea2999ef | ||
|
|
67e9bf74a7 | ||
|
|
ed42012058 | ||
|
|
9b7c9e4a81 | ||
|
|
85daacdf7a | ||
|
|
17ea41fdae | ||
|
|
ffd2a18837 | ||
|
|
5d7f38f99f | ||
|
|
e762267e03 | ||
|
|
a71870de28 | ||
|
|
7eba0b85f4 | ||
|
|
30a45ee78e | ||
|
|
ca9cd36067 | ||
|
|
1d5a208896 | ||
|
|
60e2938b58 | ||
|
|
89540172b2 | ||
|
|
bbb716723f | ||
|
|
842cd17979 | ||
|
|
e0e32efd26 | ||
|
|
a127fcd056 | ||
|
|
c614af2cd9 | ||
|
|
c2abff6e6d | ||
|
|
4444d2ee6a | ||
|
|
463bc1374c | ||
|
|
d9a60fc2ef | ||
|
|
22a3b227a3 | ||
|
|
12f3f1a45c | ||
|
|
84d4c2944c | ||
|
|
0a9b918c75 | ||
|
|
8ddf200ce7 | ||
|
|
207ff98211 | ||
|
|
1fb09a9c8c | ||
|
|
67f9b22c86 | ||
|
|
8b5cf0c696 | ||
|
|
26d9cc56d3 | ||
|
|
930a9c8bca | ||
|
|
51a9bd9e56 | ||
|
|
ac12bd0b8f | ||
|
|
d7651d1504 | ||
|
|
7e1f996079 | ||
|
|
1af4a9abb4 | ||
|
|
ed7c9348b7 | ||
|
|
ccf4ed1235 | ||
|
|
b22ee67612 | ||
|
|
b5a953b16c | ||
|
|
7613f02eff | ||
|
|
78a3dd68ea | ||
|
|
afc0e25b26 | ||
|
|
82e14eef35 | ||
|
|
81e9c9807f | ||
|
|
02e2b53a1d | ||
|
|
babe9b6f54 | ||
|
|
52f198481b | ||
|
|
252187f1fe | ||
|
|
99812c33e7 | ||
|
|
97c5f6009d | ||
|
|
5e15f8683e | ||
|
|
fba1f7ae05 | ||
|
|
865e5ae3a3 | ||
|
|
1af98835b1 | ||
|
|
6eadf67add | ||
|
|
f3f280d008 | ||
|
|
aecbe6d316 | ||
|
|
63419d7ca9 | ||
|
|
3480a1d60e | ||
|
|
190712e250 | ||
|
|
a662330b1b | ||
|
|
04ee1d137d | ||
|
|
27f421587e | ||
|
|
f9f0bd8685 | ||
|
|
53b12f50a7 | ||
|
|
63d49463da | ||
|
|
549017d035 | ||
|
|
45feef5884 | ||
|
|
24592cc696 | ||
|
|
c640c550fd | ||
|
|
0565b7b8c8 | ||
|
|
1691c74727 | ||
|
|
f6be3eacfc | ||
|
|
790df903ac | ||
|
|
25e6f72921 | ||
|
|
ab1015b11e | ||
|
|
55e990f71d | ||
|
|
40a8150519 | ||
|
|
87744302ba | ||
|
|
17083dc5e6 | ||
|
|
b29616fbd8 | ||
|
|
468688615f | ||
|
|
f4faee4283 | ||
|
|
62c85274a3 | ||
|
|
db2917193e | ||
|
|
705571de8c | ||
|
|
2ee29683a7 | ||
|
|
51395dda91 | ||
|
|
9babef0095 | ||
|
|
e9545c9a7f | ||
|
|
619214e462 | ||
|
|
31b600686a | ||
|
|
bccc4e8019 | ||
|
|
7039a4d762 | ||
|
|
7b7f0115e5 | ||
|
|
24e79b3f4e | ||
|
|
5c70b2b307 | ||
|
|
65a8de7845 | ||
|
|
44d2297546 | ||
|
|
a41280707e | ||
|
|
a8f2fd66ae | ||
|
|
7e8ddbadfb | ||
|
|
80aeb3677d | ||
|
|
f92bbdaefa | ||
|
|
fc73f16425 | ||
|
|
c1f47f536d | ||
|
|
9166c82dc2 | ||
|
|
3decc8b9b1 | ||
|
|
bac5da30e7 | ||
|
|
baf379c6d7 | ||
|
|
e9b6cdb37a | ||
|
|
9d36d2c749 | ||
|
|
5945ab1a0a | ||
|
|
40319a66ff | ||
|
|
754aef8a84 | ||
|
|
0613b530e8 | ||
|
|
5e9819b96f | ||
|
|
b46e334a40 | ||
|
|
af49845ae6 | ||
|
|
4cb8241334 | ||
|
|
530e6cb20e | ||
|
|
e0adc03588 | ||
|
|
e48f6e6d9b | ||
|
|
04ff4df5d7 | ||
|
|
d789e96d79 | ||
|
|
45761fd48b | ||
|
|
cb6a47a5d9 | ||
|
|
67dbdfd80d | ||
|
|
dee99c1752 | ||
|
|
a0c7e187f5 | ||
|
|
938503bd56 | ||
|
|
33bda6fd16 | ||
|
|
929336cb57 | ||
|
|
883aca038b | ||
|
|
77e03dc18d | ||
|
|
230ed1ab11 | ||
|
|
b31fa856d0 | ||
|
|
98b97b9898 | ||
|
|
41cf7c6814 | ||
|
|
da3a2f436c | ||
|
|
569a7178d7 | ||
|
|
7e7497c3bd | ||
|
|
905c78d5a3 | ||
|
|
37b1cb009a | ||
|
|
56586e1fda | ||
|
|
2040fcdba4 | ||
|
|
d24b57ae86 | ||
|
|
db72102de7 | ||
|
|
e8801a75f3 | ||
|
|
918826ff48 | ||
|
|
fa2fe5c941 | ||
|
|
8ef2761f53 | ||
|
|
9375369b88 | ||
|
|
ce77c82b0c | ||
|
|
1e39ae2f1e | ||
|
|
c143894547 | ||
|
|
37450ff00c | ||
|
|
ca9c468edd | ||
|
|
d6570d1496 | ||
|
|
bf677522a9 | ||
|
|
fd89f71fc0 | ||
|
|
83477ece18 | ||
|
|
32990794ce | ||
|
|
4b5bae4f9b | ||
|
|
2b07917020 | ||
|
|
338acb8fc2 | ||
|
|
2a4b228e19 | ||
|
|
c6c3ab94b1 | ||
|
|
8671516b95 | ||
|
|
5710ab47ae | ||
|
|
e3b0eb29f1 | ||
|
|
795594b6a6 | ||
|
|
e91da53d9e | ||
|
|
f807df84d8 | ||
|
|
7f32d5741d | ||
|
|
30c7113bd8 | ||
|
|
a63732027f | ||
|
|
13d8f51f6a | ||
|
|
5d48ed5fb4 | ||
|
|
4b89b3e2ed | ||
|
|
1ee24517e3 | ||
|
|
64e35c734e | ||
|
|
08130e8088 | ||
|
|
0c5937805b | ||
|
|
d315829eaf | ||
|
|
6dad1c3bbb | ||
|
|
3577c11c89 | ||
|
|
8326c223ab | ||
|
|
73de5f78fe | ||
|
|
4e59b85073 | ||
|
|
9af26db57a | ||
|
|
8e4ca8e474 | ||
|
|
79de3976bf | ||
|
|
4b80f13373 | ||
|
|
6210c6dbf4 | ||
|
|
97592eede6 | ||
|
|
73dafa6aff | ||
|
|
6c3e121b6c | ||
|
|
95ee7fb49f | ||
|
|
b3f73eace1 | ||
|
|
07a497362a | ||
|
|
afa078d00c | ||
|
|
3fd7d9a604 | ||
|
|
80a0d2d8d8 | ||
|
|
27f4fdd179 | ||
|
|
d1a94a91c2 | ||
|
|
69a31dcdd9 | ||
|
|
a2a6bf87f7 | ||
|
|
bd1e95b655 | ||
|
|
278f9bfc03 | ||
|
|
a3a8950afd | ||
|
|
f95913e623 | ||
|
|
8e4b51fba4 | ||
|
|
84915a1843 | ||
|
|
4b0e915698 | ||
|
|
b2e81b5d17 | ||
|
|
ef47f3fd15 | ||
|
|
f88f72abd2 | ||
|
|
715c14b78d | ||
|
|
4af7da3451 | ||
|
|
63ff572076 | ||
|
|
129af904f6 | ||
|
|
8cc71d2b47 | ||
|
|
0b299b2fe7 | ||
|
|
b1cef5f73d | ||
|
|
42067ce53c | ||
|
|
2bdf12fb67 | ||
|
|
c00b138bf2 | ||
|
|
1155eaf1f1 | ||
|
|
52f2e193d6 | ||
|
|
f5619a9b29 | ||
|
|
057608bac0 | ||
|
|
57465eb277 | ||
|
|
01e04d60a9 | ||
|
|
be8d9be832 | ||
|
|
07d07020f0 | ||
|
|
7c1f7e7a23 | ||
|
|
a3f6fee41f | ||
|
|
0414ec7f83 | ||
|
|
12af2a7ff6 | ||
|
|
4d6881fa65 | ||
|
|
019e8e0d14 | ||
|
|
763bd775c4 | ||
|
|
585e07bc79 | ||
|
|
b911d8d075 | ||
|
|
d583122c64 | ||
|
|
3968877b1e | ||
|
|
46f03de9f6 | ||
|
|
0e18ec022c | ||
|
|
64117ab613 | ||
|
|
038e04dee6 | ||
|
|
b49c7b8609 | ||
|
|
948949c571 | ||
|
|
f173a79a0d | ||
|
|
994791add6 | ||
|
|
d177e71b46 | ||
|
|
fcab1501f8 | ||
|
|
eb022220f4 | ||
|
|
f48687528e | ||
|
|
9007f9de9e | ||
|
|
55d84d0f9b | ||
|
|
90b4d688f8 | ||
|
|
aacd42f4bc | ||
|
|
51d7dda5a7 | ||
|
|
1c32acf7b6 | ||
|
|
22c73f3c12 | ||
|
|
9613ea9018 | ||
|
|
3d4802ac68 | ||
|
|
59c9bdb3a5 | ||
|
|
d7953eb779 | ||
|
|
48a7c48f7b | ||
|
|
7a919fbac4 | ||
|
|
181220621e | ||
|
|
249c45dfe2 | ||
|
|
b19d84f1a7 | ||
|
|
865edb70c2 | ||
|
|
e78369f0fa | ||
|
|
d40a6a5c3f | ||
|
|
cb7768d095 | ||
|
|
4290516d29 | ||
|
|
6a7a2301ee | ||
|
|
9a79c58e33 | ||
|
|
4c39c1ec30 | ||
|
|
571cb8b44f | ||
|
|
4207792ffd | ||
|
|
f5c4f98834 | ||
|
|
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 | ||
|
|
d4d4c3cc92 | ||
|
|
33a69abece | ||
|
|
3a12ba177a | ||
|
|
6a4947533f | ||
|
|
28e1538fdb | ||
|
|
879a31f51e | ||
|
|
eed66c099b | ||
|
|
49fc87e295 | ||
|
|
ece2edf579 | ||
|
|
1961f01cab | ||
|
|
fe89f1f096 | ||
|
|
346681ba27 | ||
|
|
4a214b6ef0 | ||
|
|
dda429ab5d | ||
|
|
15feaafd68 | ||
|
|
14e7907e06 | ||
|
|
b4b483b35a | ||
|
|
79c9bdba7f | ||
|
|
a6837a7869 | ||
|
|
0a485a7ff6 | ||
|
|
bca1602474 | ||
|
|
96ee0a2017 | ||
|
|
25550e18d0 | ||
|
|
154d0160bc | ||
|
|
aefa56221b | ||
|
|
dd40cbc139 | ||
|
|
23db2e5c9e | ||
|
|
eff1b174c0 | ||
|
|
545069b069 | ||
|
|
13e13cd5a8 | ||
|
|
f767535ce5 | ||
|
|
aeebd069e2 | ||
|
|
0f8aad52b2 | ||
|
|
b6a5cbf956 | ||
|
|
d2aa2a9a29 | ||
|
|
a482d4db5f | ||
|
|
d7eb30ccbd | ||
|
|
9bc12f28b4 | ||
|
|
4d11fba20a | ||
|
|
df15dceaef | ||
|
|
cb6f587f24 | ||
|
|
9f2e192993 | ||
|
|
c647793512 | ||
|
|
d62f36c6a0 | ||
|
|
2427724868 | ||
|
|
9b35d8f8e1 | ||
|
|
a859f4524c | ||
|
|
8be896aebb | ||
|
|
a008cf971d | ||
|
|
caa057ff4d | ||
|
|
43152bdaf9 | ||
|
|
4cdb7ff32b | ||
|
|
7cbb01a78a | ||
|
|
555eddff83 | ||
|
|
a3cab53b73 | ||
|
|
454d5827fd | ||
|
|
186c426691 | ||
|
|
5e1e1ecf6f | ||
|
|
2d3d0f688a | ||
|
|
7975844e4d | ||
|
|
f2b138510e | ||
|
|
1c95ef4060 | ||
|
|
b43e12a42a | ||
|
|
316077fffb | ||
|
|
4373037071 | ||
|
|
24e81c873c | ||
|
|
9e4a3905a0 | ||
|
|
b54b4a6580 | ||
|
|
f33a9c3941 | ||
|
|
d792798963 | ||
|
|
e5af4f6299 | ||
|
|
ef5bd9dc61 | ||
|
|
8834feac65 | ||
|
|
2f70dd732d | ||
|
|
3f793ae902 | ||
|
|
ac56f3a30a | ||
|
|
e2ffac74bc | ||
|
|
1c08ca54c5 | ||
|
|
f1547a7b1f | ||
|
|
746fa93c80 | ||
|
|
5ab1758d28 | ||
|
|
2e4e1df3d9 | ||
|
|
eded61d66e | ||
|
|
d9360da9a5 | ||
|
|
2b7a1b7515 | ||
|
|
1e66116e1d | ||
|
|
a95582b382 | ||
|
|
7860cfdec3 | ||
|
|
30bbea9c74 | ||
|
|
481105d6be | ||
|
|
f939a632a6 | ||
|
|
c05f56d28c | ||
|
|
b844251587 | ||
|
|
b9bd907a6b | ||
|
|
17d86a2a35 | ||
|
|
c70c67394a | ||
|
|
92d3559146 | ||
|
|
0e9a3c3a9f | ||
|
|
41263f0332 | ||
|
|
3747427538 | ||
|
|
b65554ca15 | ||
|
|
00cb15d3c8 | ||
|
|
6690f49c4e | ||
|
|
ff805a704d | ||
|
|
f83be710a0 | ||
|
|
3933549659 | ||
|
|
4993b74c23 | ||
|
|
76e7a98c88 | ||
|
|
999e98e43d | ||
|
|
74af205426 | ||
|
|
9ad82f4907 | ||
|
|
2e6b37e018 | ||
|
|
67070e335e | ||
|
|
22536e694c | ||
|
|
929282a2f7 | ||
|
|
b0092b68c6 | ||
|
|
91446378bd | ||
|
|
dceec0ce46 | ||
|
|
1856e394f3 | ||
|
|
8dc7a0dbbf | ||
|
|
6e17ff7981 |
4
.editorconfig
Normal file
4
.editorconfig
Normal file
@@ -0,0 +1,4 @@
|
||||
root = true
|
||||
|
||||
[*.js, *.css, *.tpl]
|
||||
indent_style = tab
|
||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -1,15 +1,14 @@
|
||||
#################
|
||||
## npm
|
||||
#################
|
||||
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
config.json
|
||||
public/src/nodebb.min.js
|
||||
public/config.json
|
||||
public/css/*.css
|
||||
public/themes/*
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
plugins/*
|
||||
.project
|
||||
.project
|
||||
*.swp
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
provision.sh
|
||||
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
|
||||
}
|
||||
25
README.md
25
README.md
@@ -1,23 +1,30 @@
|
||||
Please support NodeBB development! Check out our IndieGoGo campaign and like, share, and follow us :)
|
||||
[NodeBB Homepage](http://www.nodebb.org/ "NodeBB") # [IndieGoGo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/ "IndieGoGo") # [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter") # [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
|
||||
# NodeBB
|
||||
**NodeBB** 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")
|
||||
* [Demo & Meta Discussion](http://try.nodebb.org)
|
||||
* [Wiki Guides](https://github.com/designcreateplay/NodeBB/wiki) - includes setup for other platforms
|
||||
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
|
||||
* [Follow on Twitter](http://www.twitter.com/NodeBB/ "NodeBB Twitter")
|
||||
* [Like us on Facebook](http://www.facebook.com/NodeBB/ "NodeBB Facebook")
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
* Our [Indiegogo campaign](https://www.indiegogo.com/projects/nodebb-the-discussion-platform-of-the-future/) is accepting contributions until August 17th, 2013
|
||||
* Our feature roadmap is hosted on the project wiki's [Version History / Roadmap](https://github.com/designcreateplay/NodeBB/wiki/Version-History-%26-Roadmap)
|
||||
* If you are a developer, feel free to check out the source and submit pull requests.
|
||||
* If you are a designer, NodeBB needs themes! NodeBB will accept any LESS or CSS file and use it in place of the default Twitter Bootstrap theme. Consider extending Bootstrap themes by extending the base bootstrap LESS file.
|
||||
|
||||
## 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
|
||||
|
||||
@@ -55,3 +62,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)
|
||||
|
||||
238
app.js
238
app.js
@@ -16,127 +16,159 @@
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
var fs = require('fs'),
|
||||
nconf = require('nconf'),
|
||||
pkg = require('./package.json'),
|
||||
url = require('url');
|
||||
(function () {
|
||||
"use strict";
|
||||
|
||||
// Runtime environment
|
||||
global.env = process.env.NODE_ENV || 'production',
|
||||
// Configuration setup
|
||||
var nconf = require('nconf');
|
||||
nconf.argv().env();
|
||||
|
||||
// Configuration setup
|
||||
nconf.argv().file({ file: __dirname + '/config.json'});
|
||||
var fs = require('fs'),
|
||||
async = require('async'),
|
||||
winston = require('winston'),
|
||||
pkg = require('./package.json'),
|
||||
path = require('path'),
|
||||
meta;
|
||||
|
||||
// Log GNU copyright info along with server info
|
||||
console.log('Info: NodeBB v' + pkg.version + ' Copyright (C) 2013 DesignCreatePlay Inc.');
|
||||
console.log('Info: This program comes with ABSOLUTELY NO WARRANTY.');
|
||||
console.log('Info: This is free software, and you are welcome to redistribute it under certain conditions.');
|
||||
console.log('Info: ===');
|
||||
// Runtime environment
|
||||
global.env = process.env.NODE_ENV || 'production';
|
||||
|
||||
if(nconf.get('upgrade')) {
|
||||
require('./src/upgrade').upgrade();
|
||||
} 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/');
|
||||
global.nconf = nconf;
|
||||
winston.remove(winston.transports.Console);
|
||||
winston.add(winston.transports.Console, {
|
||||
colorize: true
|
||||
});
|
||||
|
||||
console.log('Info: Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
||||
console.log('Info: Base Configuration OK.');
|
||||
winston.add(winston.transports.File, {
|
||||
filename: 'error.log',
|
||||
level: 'error'
|
||||
});
|
||||
|
||||
// TODO: Replace this with nconf-redis
|
||||
var meta = require('./src/meta.js');
|
||||
global.config = {};
|
||||
meta.config.get(function(config) {
|
||||
for(c in config) {
|
||||
if (config.hasOwnProperty(c)) {
|
||||
global.config[c] = config[c];
|
||||
}
|
||||
// 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('help') && !nconf.get('setup') && !nconf.get('upgrade') && fs.existsSync(__dirname + '/config.json')) {
|
||||
// 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') === "0.0.0.0" || !nconf.get('bind_address')) ? 'Any address (0.0.0.0)' : nconf.get('bind_address')));
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
winston.info('Base Configuration OK.');
|
||||
}
|
||||
|
||||
var categories = require('./src/categories.js'),
|
||||
RDB = require('./src/redis.js'),
|
||||
templates = require('./public/src/templates.js'),
|
||||
webserver = require('./src/webserver.js'),
|
||||
websockets = require('./src/websockets.js'),
|
||||
plugins = require('./src/plugins'),
|
||||
admin = {
|
||||
'categories': require('./src/admin/categories.js')
|
||||
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'),
|
||||
posts = require('./src/posts.js'),
|
||||
plugins = require('./src/plugins'), // Don't remove this - plugins initializes itself
|
||||
Notifications = require('./src/notifications'),
|
||||
Upgrade = require('./src/upgrade');
|
||||
|
||||
global.configuration = {};
|
||||
global.templates = {};
|
||||
Upgrade.check(function(schema_ok) {
|
||||
if (schema_ok || nconf.get('check-schema') === false) {
|
||||
websockets.init(SocketIO);
|
||||
|
||||
(function(config) {
|
||||
config['ROOT_DIRECTORY'] = __dirname;
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
|
||||
templates.init([
|
||||
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
|
||||
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
|
||||
'emails/header', 'emails/footer', 'install/header', 'install/footer', 'install/redis',
|
||||
translator.loadServer();
|
||||
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
]);
|
||||
var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false;
|
||||
|
||||
templates.ready(webserver.init);
|
||||
// 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',
|
||||
'emails/header', 'emails/footer',
|
||||
|
||||
//setup scripts to be moved outside of the app in future.
|
||||
function setup_categories() {
|
||||
console.log('Info: Checking categories...');
|
||||
categories.getAllCategories(function(data) {
|
||||
if (data.categories.length === 0) {
|
||||
console.log('Info: Setting up default categories...');
|
||||
|
||||
fs.readFile(config.ROOT_DIRECTORY + '/install/data/categories.json', function(err, default_categories) {
|
||||
default_categories = JSON.parse(default_categories);
|
||||
|
||||
for (var category in default_categories) {
|
||||
admin.categories.create(default_categories[category]);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
console.log('Info: Hardcoding uid 1 as an admin');
|
||||
var user = require('./src/user.js');
|
||||
user.makeAdministrator(1);
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
], customTemplates);
|
||||
|
||||
|
||||
} else {
|
||||
console.log('Info: Categories OK. Found ' + data.categories.length + ' categories.');
|
||||
}
|
||||
});
|
||||
}
|
||||
templates.ready(webserver.init);
|
||||
|
||||
|
||||
|
||||
setup_categories();
|
||||
}(global.configuration));
|
||||
});
|
||||
} else {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) console.log('Info: NodeBB Setup Triggered via Command Line');
|
||||
else console.log('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) {
|
||||
console.log('Error: There was a problem completing NodeBB setup: ', err.message);
|
||||
Notifications.init();
|
||||
} else {
|
||||
winston.warn('Your NodeBB schema is out-of-date. Please run the following command to bring your dataset up to spec:');
|
||||
winston.warn(' node app --upgrade');
|
||||
winston.warn('To ignore this error (not recommended):');
|
||||
winston.warn(' node app --no-check-schema')
|
||||
process.exit();
|
||||
}
|
||||
});
|
||||
});
|
||||
} else if (nconf.get('setup') || !fs.existsSync(__dirname + '/config.json')) {
|
||||
// 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();
|
||||
});
|
||||
}
|
||||
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
|
||||
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. Run \'node app\' to manually start your NodeBB server.');
|
||||
}
|
||||
|
||||
process.exit();
|
||||
});
|
||||
|
||||
} else if (nconf.get('upgrade')) {
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else/* if (nconf.get('help') */{
|
||||
winston.info('Usage: node app [options] [arguments]');
|
||||
winston.info(' [NODE_ENV=development | NODE_ENV=production] node app [--start] [arguments]');
|
||||
winston.info('');
|
||||
winston.info('Options:');
|
||||
winston.info(' --help displays this usage information');
|
||||
winston.info(' --setup configure your environment and setup NodeBB');
|
||||
winston.info(' --upgrade upgrade NodeBB, first read: github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB');
|
||||
winston.info(' --start manually start NodeBB (default when no options are given)');
|
||||
};
|
||||
}());
|
||||
|
||||
2
feeds/categories/.gitignore
vendored
2
feeds/categories/.gitignore
vendored
@@ -1 +1 @@
|
||||
*.rss
|
||||
*.rss
|
||||
2
feeds/topics/.gitignore
vendored
2
feeds/topics/.gitignore
vendored
@@ -1 +1 @@
|
||||
*.rss
|
||||
*.rss
|
||||
@@ -1,74 +1,86 @@
|
||||
[
|
||||
{
|
||||
"name": "Announcements",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"description": "Announcements regarding our community",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-bullhorn"
|
||||
"icon" : "icon-bullhorn",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"name": "General Discussion",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-comment"
|
||||
"icon" : "icon-comment",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-github",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-pencil",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-lightbulb",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-cogs",
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-plus-sign",
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-exchange",
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-globe",
|
||||
"order": 9
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-film",
|
||||
"order": 10
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-screenshot",
|
||||
"order": 11
|
||||
},
|
||||
{
|
||||
"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"
|
||||
"icon" : "icon-beer",
|
||||
"order": 12
|
||||
}
|
||||
]
|
||||
69
mocks/redismock.js
Normal file
69
mocks/redismock.js
Normal file
@@ -0,0 +1,69 @@
|
||||
/**
|
||||
* Redis Mock - wrapper for redis.js, makes system use separate test db, instead of production
|
||||
* ATTENTION: testing db is flushed before every use!
|
||||
*/
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var RedisDB,
|
||||
redis = require('redis'),
|
||||
utils = require('./../public/src/utils.js'),
|
||||
path = require('path'),
|
||||
nconf = require('nconf'),
|
||||
winston = require('winston'),
|
||||
errorText;
|
||||
|
||||
|
||||
nconf.file({ file: path.join(__dirname, '../config.json') });
|
||||
|
||||
var testDbConfig = nconf.get('redis_test'),
|
||||
productionDbConfig = nconf.get('redis');
|
||||
if(!testDbConfig){
|
||||
errorText = 'redis_test database is not defined';
|
||||
winston.info(
|
||||
"\n===========================================================\n"+
|
||||
"Please, add parameters for test database in config.json\n"+
|
||||
"For example:\n"+
|
||||
'"redis_test": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "6379",' + '\n' +
|
||||
' "password": "",' + '\n' +
|
||||
' "database": "1"' + '\n' +
|
||||
'}\n'+
|
||||
"==========================================================="
|
||||
);
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
if( testDbConfig.database === productionDbConfig.database &&
|
||||
testDbConfig.host === productionDbConfig.host &&
|
||||
testDbConfig.port === productionDbConfig.port
|
||||
){
|
||||
errorText = 'redis_test database has the same config as production db';
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
nconf.set('redis',testDbConfig);
|
||||
|
||||
RedisDB = require('../src/redis.js');
|
||||
|
||||
|
||||
//Clean up
|
||||
RedisDB.send_command('flushdb', [], function(error){
|
||||
if(error){
|
||||
winston.error(error);
|
||||
throw new Error(error);
|
||||
} else {
|
||||
winston.info('redis_test db flushed');
|
||||
}
|
||||
});
|
||||
|
||||
//TODO: data seeding, if needed at all
|
||||
|
||||
|
||||
module.exports = RedisDB;
|
||||
|
||||
}(module));
|
||||
46
package.json
46
package.json
@@ -2,18 +2,21 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.0.5",
|
||||
"version": "0.1.0",
|
||||
"homepage": "http://www.nodebb.org",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/designcreateplay/NodeBB/"
|
||||
},
|
||||
"main": "app.js",
|
||||
"scripts": {
|
||||
"test": "mocha ./tests"
|
||||
},
|
||||
"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,13 +30,27 @@
|
||||
"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",
|
||||
"cheerio": "~0.12.0",
|
||||
"request": "~2.25.0",
|
||||
"reds": "~0.2.4"
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"rss": "~0.2.0",
|
||||
"prompt": "~0.2.11",
|
||||
"uglify-js": "~2.4.0",
|
||||
"validator": "~1.5.1",
|
||||
"nodebb-plugin-mentions": "~0.1.14",
|
||||
"nodebb-plugin-markdown": "~0.1.8",
|
||||
"nodebb-theme-vanilla": "designcreateplay/nodebb-theme-vanilla",
|
||||
"nodebb-theme-cerulean": "0.0.6",
|
||||
"cron": "~1.0.1"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"hiredis": "~0.1.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
@@ -44,15 +61,22 @@
|
||||
"contributors": [
|
||||
{
|
||||
"name": "Andrew Rodrigues",
|
||||
"email": "andrew@designcreateplay.com"
|
||||
"email": "andrew@designcreateplay.com",
|
||||
"url": "https://github.com/psychobunny"
|
||||
},
|
||||
{
|
||||
"name": "Julian Lam",
|
||||
"email": "julian@designcreateplay.com"
|
||||
"email": "julian@designcreateplay.com",
|
||||
"url": "https://github.com/julianlam"
|
||||
},
|
||||
{
|
||||
"name": "Barış Soner Uşaklı",
|
||||
"email": "baris@designcreateplay.com"
|
||||
"email": "baris@designcreateplay.com",
|
||||
"url": "https://github.com/barisusakli"
|
||||
},
|
||||
{
|
||||
"name": "Andrew Darqui",
|
||||
"url": "https://github.com/adarqui"
|
||||
},
|
||||
{
|
||||
"name": "Damian Bushong",
|
||||
@@ -61,6 +85,10 @@
|
||||
{
|
||||
"name": "Matt Smith",
|
||||
"url": "https://github.com/soimafreak"
|
||||
},
|
||||
{
|
||||
"name": "Quinton Marchi",
|
||||
"url": "https://github.com/iamcardinal"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
0
plugins/.gitignore
vendored
0
plugins/.gitignore
vendored
@@ -1,157 +0,0 @@
|
||||
@import "mixins";
|
||||
|
||||
.admin {
|
||||
.entry-row {
|
||||
border-radius: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 10px;
|
||||
cursor: move;
|
||||
}
|
||||
|
||||
input.description {
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.admin-categories {
|
||||
form {
|
||||
margin: 0 0 0px;
|
||||
}
|
||||
|
||||
input {
|
||||
height: 20px;
|
||||
padding: 5px;
|
||||
margin-left: 10px;
|
||||
width: 150px;
|
||||
border: 0;
|
||||
border-radius: 5px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
.category_description {
|
||||
width: 300px;
|
||||
}
|
||||
|
||||
select {
|
||||
border: 0;
|
||||
margin-left: 10px;
|
||||
padding: 5px;
|
||||
margin-top: -8px;
|
||||
}
|
||||
|
||||
button {
|
||||
margin-top: -7px;
|
||||
}
|
||||
|
||||
.icon{
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
text-align: center;
|
||||
line-height: 35px;
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
|
||||
.themes {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
padding: 10px 16px;
|
||||
margin: 0.25em 1em;
|
||||
list-style-type: none;
|
||||
.pointer;
|
||||
-webkit-border-radius: 10px;
|
||||
-moz-border-radius: 10px;
|
||||
border-radius: 10px;
|
||||
-webkit-transition: background-color 250ms linear;
|
||||
-moz-transition: background-color 250ms linear;
|
||||
-ms-transition: background-color 250ms linear;
|
||||
-o-transition: background-color 250ms linear;
|
||||
transition: background-color 250ms linear;
|
||||
|
||||
img {
|
||||
max-width: 150px;
|
||||
float: left;
|
||||
}
|
||||
|
||||
h4, p {
|
||||
margin-left: 170px;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background-color: rgba(128, 128, 128, 0.2);
|
||||
}
|
||||
|
||||
&.no-themes {
|
||||
font-style: italic;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.motd textarea {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.topics {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
|
||||
> li {
|
||||
.zebra;
|
||||
list-style-type: none;
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
|
||||
ul {
|
||||
margin: 0.5em 0;
|
||||
|
||||
li {
|
||||
list-style-type: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.icon-container {
|
||||
.row {
|
||||
margin: 0;
|
||||
.span3 {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
margin: 1px;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
text-align: center;
|
||||
|
||||
&:hover, &.selected {
|
||||
background: black;
|
||||
color: white;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.plugins {
|
||||
li {
|
||||
list-style-type: none;
|
||||
background: rgba(64, 64, 64, 0.05);
|
||||
padding: 1em;
|
||||
border-left: 5px solid #08c;
|
||||
|
||||
h2 {
|
||||
font-size: 16px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
p {
|
||||
font-size: 12px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,224 +0,0 @@
|
||||
|
||||
.category {
|
||||
.span9 {
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
|
||||
a {
|
||||
li {
|
||||
list-style: none;
|
||||
//border-bottom: 1px solid #eee;
|
||||
margin-bottom: 10px;
|
||||
padding-bottom: 10px;
|
||||
|
||||
&.deleted {
|
||||
-moz-opacity: 0.30;
|
||||
opacity: 0.30;
|
||||
}
|
||||
}
|
||||
|
||||
&:last-child li {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
ul {
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.thread-rating {
|
||||
color: #444;
|
||||
text-align: center;
|
||||
margin-top: 7px;
|
||||
|
||||
span {
|
||||
display: inline-block; margin-left: 5px;
|
||||
i {
|
||||
padding-left: 2.4px;
|
||||
}
|
||||
|
||||
|
||||
@media (max-width: 1200px)
|
||||
{
|
||||
margin-left: -1px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
.topic-row {
|
||||
border-radius: 5px;
|
||||
padding-left: 20px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.2);
|
||||
|
||||
small {
|
||||
vertical-align: 2px;
|
||||
strong {
|
||||
color: #666;
|
||||
}
|
||||
color: #999;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
h3 {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
margin-top: 4px;
|
||||
color: rgb(0, 136, 204);
|
||||
line-height: 25px;
|
||||
|
||||
.topic-title {
|
||||
width: 50%;
|
||||
display: inline-block;
|
||||
font-size: 20px;
|
||||
white-space: nowrap;
|
||||
text-overflow:ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
|
||||
.badge {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
@media (max-width: 979px)
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 979px)
|
||||
{
|
||||
width: 100%;
|
||||
margin-left: 0px;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.latest-post {
|
||||
float: right;
|
||||
padding-top: 2px;
|
||||
margin-right: 0px;
|
||||
width: 40%;
|
||||
|
||||
.pull-right {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 16px;
|
||||
margin-left: 1px;
|
||||
padding: 5px;
|
||||
border-left: 1px solid #bbb;
|
||||
padding-left: 20px;
|
||||
|
||||
img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
p {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 70%;
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
height: 32px;
|
||||
}
|
||||
span {
|
||||
display: block;
|
||||
float: left;
|
||||
width: 70%;
|
||||
margin-left: 10px;
|
||||
overflow: hidden;
|
||||
height: 16px;
|
||||
margin-top: -10px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.recent-replies {
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
ul {
|
||||
width: 100%;
|
||||
height: 50px;
|
||||
line-height: 16px;
|
||||
margin-left: 1px;
|
||||
padding: 5px 5px 5px 0px;
|
||||
list-style-type: none;
|
||||
|
||||
li {
|
||||
clear: both;
|
||||
line-height: 16px;
|
||||
margin-bottom: 1em;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
display: block;
|
||||
float: left;
|
||||
}
|
||||
p {
|
||||
display: block;
|
||||
padding-left:5px;
|
||||
padding-top: 2px;
|
||||
overflow: hidden;
|
||||
height: 32px;
|
||||
margin-bottom: 0.5em;
|
||||
}
|
||||
span {
|
||||
font-size: 12px;
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
height: 16px;
|
||||
padding-left:5px;
|
||||
color: #666;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.sidebar-block {
|
||||
.block-header {
|
||||
padding: 8px;
|
||||
padding-left: 13px;
|
||||
display: block;
|
||||
background: rgb(245,245,245);
|
||||
color: #676;
|
||||
font-weight: 400;
|
||||
font-size: 14px;
|
||||
border-radius: 5px 5px 0 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
|
||||
}
|
||||
.block-content {
|
||||
padding: 10px;
|
||||
.img-polaroid {
|
||||
width: 20%;
|
||||
height: auto;
|
||||
max-width: 48px;
|
||||
}
|
||||
}
|
||||
|
||||
border-radius: 5px;
|
||||
padding: 0;
|
||||
margin-bottom: 20px;
|
||||
|
||||
.img-polaroid {
|
||||
margin-top: 2px;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,33 +0,0 @@
|
||||
.no-select {
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.pointer {
|
||||
cursor: pointer;
|
||||
*cursor: hand;
|
||||
}
|
||||
|
||||
.inline-block {
|
||||
display: inline-block;
|
||||
*display: inline;
|
||||
zoom: 1;
|
||||
}
|
||||
|
||||
.clear {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.zebra {
|
||||
&:nth-child(even) {
|
||||
background: rgba(191,191,191,0.2);
|
||||
}
|
||||
|
||||
&:nth-child(odd) {
|
||||
background: rgba(223,223,223,0.2);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
@import "style";
|
||||
|
||||
@import "topic";
|
||||
@import "category";
|
||||
@import "noscript";
|
||||
@@ -1,57 +0,0 @@
|
||||
@import "mixins";
|
||||
|
||||
noscript {
|
||||
.default {
|
||||
list-style-type: none;
|
||||
padding: 1em;
|
||||
margin-bottom: 1em;
|
||||
.zebra;
|
||||
}
|
||||
|
||||
.categories {
|
||||
li {
|
||||
.default;
|
||||
|
||||
.icon {
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
a {
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.topics {
|
||||
li {
|
||||
.default;
|
||||
}
|
||||
|
||||
.teaser {
|
||||
margin-left: 16px;
|
||||
margin-top: 8px;
|
||||
|
||||
img {
|
||||
float: left;
|
||||
width: 32px;
|
||||
}
|
||||
|
||||
p {
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
margin-left: 64px;
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.posts {
|
||||
li {
|
||||
.default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,846 +0,0 @@
|
||||
@import "mixins";
|
||||
|
||||
body {
|
||||
/*background: #fdfdfd;*/ // port to default theme when it is implemented.
|
||||
-webkit-transition: margin-bottom 250ms ease;
|
||||
-moz-transition: margin-bottom 250ms ease;
|
||||
-ms-transition: margin-bottom 250ms ease;
|
||||
-o-transition: margin-bottom 250ms ease;
|
||||
transition: margin-bottom 250ms ease;
|
||||
|
||||
&.composing {
|
||||
margin-bottom: 350px;
|
||||
}
|
||||
|
||||
@media (min-width: 979px)
|
||||
{
|
||||
padding-top: 70px;
|
||||
}
|
||||
|
||||
@media (max-width: 979px)
|
||||
{
|
||||
padding-bottom: 50px;
|
||||
}
|
||||
}
|
||||
|
||||
button, a {
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
|
||||
.none {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.block, .show {
|
||||
display: block;
|
||||
}
|
||||
.badge {
|
||||
vertical-align: 17%;
|
||||
}
|
||||
.nav .badge {
|
||||
vertical-align: 10%;
|
||||
}
|
||||
|
||||
#alert_window {
|
||||
position: fixed;
|
||||
right: 20px;
|
||||
top: 60px;
|
||||
width: 300px;
|
||||
height: 0px;
|
||||
}
|
||||
|
||||
.toaster-alert {
|
||||
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
footer.footer {
|
||||
color: #555;
|
||||
text-align: center;
|
||||
|
||||
a {
|
||||
color: #222;
|
||||
}
|
||||
}
|
||||
|
||||
#post_window {
|
||||
width: 100%;
|
||||
position: fixed;
|
||||
height: 350px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
background: white;
|
||||
z-index: 1500;
|
||||
|
||||
input {
|
||||
width: 100%;
|
||||
height: 30px;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 100%;
|
||||
background: #222;
|
||||
height: 220px;
|
||||
resize: none;
|
||||
border-radius: 0;
|
||||
border: 1px solid #111;
|
||||
font-size: 16px;
|
||||
color: #bebebe;
|
||||
outline: 0;
|
||||
|
||||
&:focus {
|
||||
outline: 0;
|
||||
border:none !important;
|
||||
box-shadow:none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.post-title-container {
|
||||
opacity: 0.8;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.post-content-container {
|
||||
opacity: 0.8;
|
||||
background: #000;
|
||||
width: 100%;
|
||||
height: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#user_label { //belongs in header.less
|
||||
img {
|
||||
border: 1px solid #454;
|
||||
margin-right: 8px;
|
||||
margin-top: -2px;
|
||||
float: left;
|
||||
width:24px;
|
||||
height:24px;
|
||||
}
|
||||
|
||||
span {
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
color: #ded;
|
||||
}
|
||||
}
|
||||
|
||||
#reply_title {
|
||||
font-size: 17px;
|
||||
padding-top: 14px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.alt-logins {
|
||||
margin: 0 0 0 1em;
|
||||
padding: 0;
|
||||
|
||||
li {
|
||||
vertical-align: top;
|
||||
background: transparent;
|
||||
display: none;
|
||||
margin: 0.25em;
|
||||
.pointer;
|
||||
|
||||
&.active {
|
||||
.inline-block;
|
||||
}
|
||||
|
||||
i {
|
||||
-webkit-transition: color 100ms linear;
|
||||
-moz-transition: color 100ms linear;
|
||||
-ms-transition: color 100ms linear;
|
||||
-o-transition: color 100ms linear;
|
||||
transition: color 100ms linear;
|
||||
|
||||
&.icon-twitter-sign:hover {
|
||||
color: #4099FF;
|
||||
}
|
||||
|
||||
&.icon-facebook-sign:hover {
|
||||
color: #3b5999;
|
||||
}
|
||||
|
||||
&.icon-google-plus-sign:hover {
|
||||
color: #d34836;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#thread_active_users {
|
||||
float: right;
|
||||
color: rgb(153,153,153);
|
||||
|
||||
strong {
|
||||
color: rgb(100,100,100);
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
|
||||
.account-username-box{
|
||||
border-bottom:1px solid #e3e3e3;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.account-sub-links a{
|
||||
margin-left:10px;
|
||||
}
|
||||
|
||||
.account-username{
|
||||
font-size:20px;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
.account-picture-block{
|
||||
display:inline-block;
|
||||
vertical-align:top;
|
||||
}
|
||||
|
||||
.account-online-status {
|
||||
.icon-circle-blank {
|
||||
color:red;
|
||||
}
|
||||
.icon-circle {
|
||||
color:green;
|
||||
}
|
||||
}
|
||||
|
||||
.user-profile-picture {
|
||||
width:128px;
|
||||
margin-bottom:10px;
|
||||
}
|
||||
|
||||
.user-picture-label {
|
||||
font-size:20px;
|
||||
}
|
||||
|
||||
.account-bio-block{
|
||||
display:inline-block;
|
||||
vertical-align:top;
|
||||
}
|
||||
|
||||
.account-bio-label{
|
||||
display:inline-block;
|
||||
width:100px;
|
||||
}
|
||||
|
||||
.user-recent-posts {
|
||||
div {
|
||||
color: #333;
|
||||
margin-bottom: 10px;
|
||||
cursor: pointer;
|
||||
p {
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
span {
|
||||
padding-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
.category-icon {
|
||||
width: 100%;
|
||||
height: 90px;
|
||||
text-align: center;
|
||||
border-radius: 0px;
|
||||
margin: 0;
|
||||
padding-top:25px;
|
||||
cursor: pointer;
|
||||
margin-bottom: 20px;
|
||||
border-radius: 5px;
|
||||
overflow:hidden;
|
||||
}
|
||||
.category-row h4 {
|
||||
font-weight: 700;
|
||||
text-align: left;
|
||||
/*color: #555;*/ // NOTE: color for cat/topic header links should be grey in the default theme when we get around to it.
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
.category-row a {
|
||||
text-decoration: none;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.category-purple {
|
||||
@color: #ab1290;
|
||||
background: @color;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: lighten(@color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.category-darkblue {
|
||||
@color: #004C66;
|
||||
background: @color;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: lighten(@color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.category-blue {
|
||||
@color: #0059B2;
|
||||
background: @color;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: lighten(@color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.category-darkgreen {
|
||||
@color: #004000;
|
||||
background: @color;
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
background: lighten(@color, 10%);
|
||||
}
|
||||
}
|
||||
.category-orange {
|
||||
@color: #FF7A4D;
|
||||
color: white;
|
||||
background: @color;
|
||||
|
||||
&:hover {
|
||||
background: lighten(@color, 10%);
|
||||
}
|
||||
}
|
||||
|
||||
.category-list {
|
||||
li {
|
||||
.inline-block;
|
||||
.pointer;
|
||||
padding: 0.5em 0;
|
||||
text-align: center;
|
||||
margin: 0.5em;
|
||||
-webkit-border-radius: 5px;
|
||||
-moz-border-radius: 5px;
|
||||
border-radius: 5px;
|
||||
padding: 0.5em;
|
||||
}
|
||||
}
|
||||
|
||||
.hero-unit {
|
||||
background: #56BCDA;
|
||||
color: white;
|
||||
padding: 30px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.users-box{
|
||||
display: inline-block;
|
||||
margin-top: 20px;
|
||||
text-align: center;
|
||||
vertical-align: top;
|
||||
max-width: 104px;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
width:80px;
|
||||
height:80px;
|
||||
}
|
||||
|
||||
a {
|
||||
margin:5px;
|
||||
}
|
||||
}
|
||||
|
||||
a:hover, .btn-link:hover, .btn-link:active, .btn-link:focus {
|
||||
text-decoration:none;
|
||||
}
|
||||
|
||||
.formatting-bar {
|
||||
.no-select;
|
||||
|
||||
span:focus {
|
||||
outline: none;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.breadcrumb {
|
||||
li {
|
||||
max-width: 35%;
|
||||
white-space: nowrap;
|
||||
text-overflow:ellipsis;
|
||||
overflow: hidden;
|
||||
}
|
||||
}
|
||||
|
||||
body .navbar .nodebb-inline-block {
|
||||
display:inline-block;
|
||||
}
|
||||
|
||||
#right-menu{
|
||||
float:right;
|
||||
}
|
||||
|
||||
#admin-redis-info {
|
||||
span {
|
||||
display:inline-block;
|
||||
width:220px;
|
||||
}
|
||||
}
|
||||
|
||||
.post-signature {
|
||||
color: #666;
|
||||
font-size: 12px;
|
||||
border-top: 1px solid #ddd;
|
||||
display: inline-block;
|
||||
|
||||
img {
|
||||
max-width:200px;
|
||||
max-height:60px;
|
||||
}
|
||||
}
|
||||
|
||||
.username-field {
|
||||
.icon-circle {
|
||||
font-size: 12px;
|
||||
color: green;
|
||||
margin-right:3px;
|
||||
}
|
||||
.icon-circle-blank {
|
||||
font-size: 12px;
|
||||
color: red;
|
||||
margin-right:3px;
|
||||
}
|
||||
}
|
||||
|
||||
#chat-content {
|
||||
width:95%;
|
||||
height:200px;
|
||||
resize:none;
|
||||
}
|
||||
|
||||
#chat-message-input {
|
||||
width:95%;
|
||||
}
|
||||
|
||||
#content{
|
||||
padding-bottom:20px;
|
||||
}
|
||||
|
||||
.dropdown-toggle {
|
||||
i {
|
||||
font-size: 12px;
|
||||
|
||||
&.active {
|
||||
color: #558;
|
||||
text-shadow: 0 0 1em #aaf, 0 0 1em #aaf, 0 0 1em #aaf;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#notif-list {
|
||||
li {
|
||||
font-size: 12px;
|
||||
width: 300px;
|
||||
text-align: left;
|
||||
|
||||
a {
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
&.unread {
|
||||
background: #eceff5;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.taskbar {
|
||||
display: none;
|
||||
-moz-opacity: 0.5;
|
||||
opacity: 0.5;
|
||||
margin-top: 0;
|
||||
-webkit-transition: opacity 250ms ease-in;
|
||||
-moz-transition: opacity 250ms ease-in;
|
||||
-ms-transition: opacity 250ms ease-in;
|
||||
-o-transition: opacity 250ms ease-in;
|
||||
transition: opacity 250ms ease-in;
|
||||
|
||||
&[data-active="1"] {
|
||||
display: block;
|
||||
}
|
||||
|
||||
&:hover {
|
||||
-moz-opacity: 1;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
li {
|
||||
a > span {
|
||||
.inline-block;
|
||||
max-width: 200px;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 24px;
|
||||
max-height: 24px;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
&.pulse {
|
||||
-webkit-animation: pulsate 2500ms linear;
|
||||
-webkit-animation-iteration-count: infinite;
|
||||
}
|
||||
|
||||
@-webkit-keyframes pulsate {
|
||||
0% { background: none; }
|
||||
50% { background: #e5e5e5; }
|
||||
100% { background: none; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.post-window {
|
||||
position: fixed;
|
||||
display: none;
|
||||
height: 350px;
|
||||
visibility: hidden;
|
||||
|
||||
> div {
|
||||
position: absolute;
|
||||
height: 100%;
|
||||
background: rgba(64, 64, 64, 0.6);
|
||||
visibility: visible;
|
||||
|
||||
.btn-toolbar {
|
||||
width: 90%;
|
||||
margin: 0 auto;
|
||||
|
||||
span {
|
||||
color: white;
|
||||
|
||||
&:hover {
|
||||
text-decoration: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
input {
|
||||
width: 98%;
|
||||
text-align: center;
|
||||
border: none;
|
||||
padding: 0.5em 0;
|
||||
-webkit-border-radius: 0px;
|
||||
-moz-border-radius: 0px;
|
||||
border-radius: 0px;
|
||||
margin: 1% 1% 2% 1%;
|
||||
}
|
||||
|
||||
textarea {
|
||||
background: rgba(64, 64, 64, 0.95);
|
||||
border: none;
|
||||
padding: 0.5em;
|
||||
display: block;
|
||||
width: 90%;
|
||||
margin: 0.5em auto;
|
||||
resize: none;
|
||||
color: white;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
#imagedrop {
|
||||
text-align:center;
|
||||
color:white;
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
width: 100%;
|
||||
height:230px;
|
||||
line-height:230px;
|
||||
font-size:20px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#imagelist {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
left: 0px;
|
||||
padding-left:2em;
|
||||
|
||||
div {
|
||||
margin-right:5px;
|
||||
}
|
||||
span {
|
||||
line-height:20px;
|
||||
float:left;
|
||||
}
|
||||
button {
|
||||
padding-left:5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 979px) {
|
||||
.post-window {
|
||||
position: relative;
|
||||
bottom: 0px !important;
|
||||
|
||||
> div {
|
||||
position: static;
|
||||
width: 100% !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#mobile-menu {
|
||||
position: fixed;
|
||||
bottom: 0px;
|
||||
height: 50px;
|
||||
background: #333;
|
||||
width: 100%;
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
#mobile-menu, #mobile-menu-overlay {
|
||||
z-index: 999;
|
||||
|
||||
@media (min-width: 979px)
|
||||
{
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.btn-none,
|
||||
.btn-none:active,
|
||||
.btn-none[disabled] {
|
||||
background-color: transparent;
|
||||
background-image: none;
|
||||
-webkit-box-shadow: none;
|
||||
-moz-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.btn-none {
|
||||
cursor: pointer;
|
||||
border-color: transparent;
|
||||
-webkit-border-radius: 0;
|
||||
-moz-border-radius: 0;
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.btn-none:hover,
|
||||
.btn-none:focus {
|
||||
text-decoration: none;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.btn-none[disabled]:hover,
|
||||
.btn-none[disabled]:focus {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.btn-none {
|
||||
.icon-white {
|
||||
color: white;
|
||||
}
|
||||
}
|
||||
|
||||
#mobile-menu {
|
||||
button {
|
||||
color: #eee;
|
||||
padding: 10px;
|
||||
text-shadow: none;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
}
|
||||
}
|
||||
|
||||
#mobile-menu-overlay {
|
||||
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
position: fixed;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
//margin-top: 50px;
|
||||
padding-top: 20px;
|
||||
opacity: 0;
|
||||
|
||||
-webkit-transition: opacity 150ms ease;
|
||||
-moz-transition: opacity 150ms ease;
|
||||
-ms-transition: opacity 150ms ease;
|
||||
-o-transition: opacity 150ms ease;
|
||||
transition: opacity 150ms ease;
|
||||
z-index: -1;
|
||||
&.menu-visible {
|
||||
z-index: 99;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.mobile-menu-icon {
|
||||
color: white;
|
||||
width: 20%;
|
||||
min-width: 85px;
|
||||
height: auto;
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
-webkit-tap-highlight-color: rgba(0,0,0,0);
|
||||
|
||||
-webkit-transition: margin-top 250ms ease;
|
||||
-moz-transition: margin-top 250ms ease;
|
||||
-ms-transition: margin-top 250ms ease;
|
||||
-o-transition: margin-top 250ms ease;
|
||||
transition: margin-top 250ms ease;
|
||||
|
||||
margin-top: 20%;
|
||||
|
||||
|
||||
&.menu-visible {
|
||||
margin-top: 0%;
|
||||
}
|
||||
|
||||
i {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#mobile-sidebar {
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 100%;
|
||||
top: 0px;
|
||||
overflow: hidden;
|
||||
margin-top: 60px;
|
||||
}
|
||||
|
||||
.category-box {
|
||||
height:90px;
|
||||
|
||||
.post-preview {
|
||||
padding-left:10px;
|
||||
padding-right:10px;
|
||||
text-align:left;
|
||||
img {
|
||||
width:60px;
|
||||
height:60px;
|
||||
padding-right:5px;
|
||||
}
|
||||
|
||||
p {
|
||||
overflow: hidden;
|
||||
text-overflow:ellipsis;
|
||||
height:60px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#mark-allread-btn {
|
||||
margin-bottom:15px;
|
||||
}
|
||||
|
||||
@-webkit-keyframes scroll-2 /* Safari and Chrome */
|
||||
{
|
||||
0% {top: 0px;}
|
||||
25% {top: -90px;}
|
||||
50% {top: -180px;}
|
||||
75% {top: -270px;}
|
||||
100% {top: -360px;}
|
||||
}
|
||||
|
||||
@keyframes scroll-2
|
||||
{
|
||||
0% {top: 0px;}
|
||||
25% {top: -90px;}
|
||||
50% {top: -180px;}
|
||||
75% {top: -270px;}
|
||||
100% {top: -360px;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes scroll-1 /* Safari and Chrome */
|
||||
{
|
||||
0% {top: 0px;}
|
||||
33% {top: -90px;}
|
||||
66% {top: -180px;}
|
||||
100% {top: -270px;}
|
||||
}
|
||||
|
||||
@keyframes scroll-1
|
||||
{
|
||||
0% {top: 0px;}
|
||||
33% {top: -90px;}
|
||||
66% {top: -180px;}
|
||||
100% {top: -270px;}
|
||||
}
|
||||
|
||||
@-webkit-keyframes scroll-0 /* Safari and Chrome */
|
||||
{
|
||||
0% {top: 0px;}
|
||||
50% {top: -90px;}
|
||||
100% {top: -180px;}
|
||||
}
|
||||
|
||||
@keyframes scroll-0
|
||||
{
|
||||
0% {top: 0px;}
|
||||
50% {top: -90px;}
|
||||
100% {top: -180px;}
|
||||
}
|
||||
|
||||
.category-slider-2:hover {
|
||||
position:relative;
|
||||
|
||||
-webkit-animation: scroll-2 10s ease 0.5s infinite normal;
|
||||
animation: scroll-2 10s ease 0.5s infinite normal;/* Safari and Chrome: */
|
||||
}
|
||||
|
||||
.category-slider-1:hover {
|
||||
position:relative;
|
||||
|
||||
-webkit-animation: scroll-1 8s ease 0.5s infinite normal;
|
||||
animation: scroll-1 8s ease 0.5s infinite normal;/* Safari and Chrome: */
|
||||
}
|
||||
|
||||
.category-slider-0:hover {
|
||||
position:relative;
|
||||
|
||||
-webkit-animation: scroll-0 6s ease 0.5s infinite normal;
|
||||
animation: scroll-0 6s ease 0.5s infinite normal;/* Safari and Chrome: */
|
||||
}
|
||||
|
||||
.form-search {
|
||||
float: left;
|
||||
margin-top: 5px;
|
||||
margin-bottom:0px;
|
||||
}
|
||||
|
||||
.search-result-post {
|
||||
width: 100%;
|
||||
line-height: 16px;
|
||||
padding: 5px;
|
||||
overflow:hidden;
|
||||
img {
|
||||
display: block;
|
||||
float: left;
|
||||
width:48px;
|
||||
height:48px;
|
||||
padding-right:10px;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,193 +0,0 @@
|
||||
|
||||
.post-container {
|
||||
list-style-type: none;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
clear: both;
|
||||
|
||||
.profile-image-block {
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
font-size: 12px;
|
||||
|
||||
.stats {
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
|
||||
li {
|
||||
padding-bottom: 15px;
|
||||
|
||||
&.deleted {
|
||||
-moz-opacity: 0.30;
|
||||
opacity: 0.30;
|
||||
height:30px;
|
||||
overflow-y:hidden;
|
||||
}
|
||||
|
||||
&.deleted-expanded {
|
||||
height:100%;
|
||||
overflow-y:default;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-block, .post-block {
|
||||
border: 1px solid rgba(0, 0, 0, 0.06);
|
||||
border-radius: 5px;
|
||||
padding: 10px;
|
||||
|
||||
p {
|
||||
line-height: 1.75em;
|
||||
}
|
||||
}
|
||||
|
||||
.profile-block {
|
||||
background: rgba(0, 0, 0, 0.02);
|
||||
margin-right: -11px;
|
||||
margin-left: -11px;
|
||||
margin-bottom: -11px;
|
||||
margin-top: 15px;
|
||||
border-radius: 0 0 5px 5px;
|
||||
font-size: 10px;
|
||||
line-height: 18px;
|
||||
padding: 5px;
|
||||
padding-left: 10px;
|
||||
|
||||
img.hidden-desktop {
|
||||
max-width: 10px;
|
||||
max-height: 10px;
|
||||
padding-top: 5px;
|
||||
margin-right: 5px;
|
||||
|
||||
}
|
||||
}
|
||||
.post-content {
|
||||
min-height: 50px;
|
||||
padding: 2px 5px 0 5px;
|
||||
word-wrap: break-word;
|
||||
}
|
||||
|
||||
.post-images{
|
||||
padding: 2px 5px 0 5px;
|
||||
}
|
||||
|
||||
.post-block {
|
||||
.post-buttons {
|
||||
font-size: 12px;
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
|
||||
button, a {
|
||||
display: inline-block;
|
||||
padding: 0px 15px;
|
||||
border: none;
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.06);
|
||||
cursor: pointer;
|
||||
background: none;
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
|
||||
&:last-child {
|
||||
padding-right: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.icon-star {
|
||||
//theme this to make it yellow eventually
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.deleted {
|
||||
-moz-opacity: 0.30;
|
||||
opacity: 0.30;
|
||||
}
|
||||
/*http://stackoverflow.com/questions/11037517/bootstrap-making-responsive-changes-to-layout*/
|
||||
@media (max-width: 979px) {
|
||||
.span12-tablet {
|
||||
width: 100% !important;
|
||||
margin-left:0px;
|
||||
*width: 100% !important;
|
||||
}
|
||||
}
|
||||
|
||||
.main-post {
|
||||
h3 {
|
||||
margin: 0;
|
||||
|
||||
.topic-title {
|
||||
width: auto;
|
||||
white-space: nowrap;
|
||||
text-overflow:ellipsis;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-top: 5px;
|
||||
margin-bottom: -5px;
|
||||
padding-bottom: 5px;
|
||||
}
|
||||
}
|
||||
|
||||
.main-avatar {
|
||||
color: white;
|
||||
position: relative;
|
||||
float: left;
|
||||
margin-right: 25px;
|
||||
margin-bottom: 0px;
|
||||
padding-bottom: 0px;
|
||||
text-align: center;
|
||||
width:80px;
|
||||
|
||||
@media (max-width: 767px) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.main-avatar:hover .hover-overlay {
|
||||
opacity: 0.75;
|
||||
}
|
||||
|
||||
.hover-overlay {
|
||||
margin: 5px;
|
||||
position: absolute;
|
||||
bottom: 0px;
|
||||
height: 35px;
|
||||
padding-top: 2px;
|
||||
width: 80px;
|
||||
font-size: 13px;
|
||||
line-height: 16px;
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
.post-content {
|
||||
min-height: 80px;
|
||||
}
|
||||
hr {
|
||||
margin-top: 0;
|
||||
margin-right: 10px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.post-block {
|
||||
.post-buttons {
|
||||
div {
|
||||
border: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
.favourite {
|
||||
cursor: pointer;
|
||||
}
|
||||
.btn {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.topic-buttons {
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
3
public/language/TODO
Normal file
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"
|
||||
}
|
||||
18
public/language/en/global.json
Normal file
18
public/language/en/global.json
Normal file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"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.admin": "Admin",
|
||||
"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!"
|
||||
}
|
||||
3
public/language/en/notifications.json
Normal file
3
public/language/en/notifications.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"title": "Notifications"
|
||||
}
|
||||
5
public/language/en/recent.json
Normal file
5
public/language/en/recent.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"day": "Day",
|
||||
"week": "Week",
|
||||
"month": "Month"
|
||||
}
|
||||
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"
|
||||
}
|
||||
23
public/language/en/topic.json
Normal file
23
public/language/en/topic.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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",
|
||||
"disabled_categories_note": "Disabled Categories are greyed out",
|
||||
"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,8 +1,9 @@
|
||||
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 : ''),
|
||||
content = null;
|
||||
@@ -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,116 +21,149 @@ 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);
|
||||
if (event !== null && event.state && event.state.url !== undefined)
|
||||
ajaxify.go(event.state.url, null, null, true);
|
||||
};
|
||||
|
||||
ajaxify.go = function(url, callback, template, quiet) {
|
||||
// leave room and join global
|
||||
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');
|
||||
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];
|
||||
}
|
||||
|
||||
|
||||
} else if (templates[url]) {
|
||||
tpl_url = url;
|
||||
}
|
||||
|
||||
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);
|
||||
require(['forum/' + tpl_url], function(script) {
|
||||
if (script && script.init) script.init();
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
|
||||
app.process_page();
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function() {
|
||||
if(window.location.hash)
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||
if (window.location.hash)
|
||||
hash = window.location.hash;
|
||||
if(hash)
|
||||
if (hash)
|
||||
app.scrollToPost(hash.substr(1));
|
||||
});
|
||||
|
||||
}, url, template);
|
||||
utils.refreshTitle(url);
|
||||
|
||||
socket.emit('api:meta.buildTitle', url, function(title) {
|
||||
document.title = title;
|
||||
});
|
||||
}, url, template);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (this.href == window.location.href + "#") return;
|
||||
if(this.href.slice(-1) === "#") return;
|
||||
|
||||
$(document.body).on('click', 'a', function (e) {
|
||||
function hrefEmpty(href) {
|
||||
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
|
||||
}
|
||||
|
||||
var url = this.href.replace(rootUrl +'/', '');
|
||||
|
||||
if (this.target !== '') return;
|
||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
|
||||
return;
|
||||
|
||||
if(!window.location.pathname.match(/\/(403|404)$/g))
|
||||
app.previousUrl = window.location.href;
|
||||
|
||||
if (this.getAttribute('data-ajaxify') == 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!e.ctrlKey && e.which === 1) {
|
||||
if (ajaxify.go(url)) {
|
||||
e.preventDefault();
|
||||
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
|
||||
|
||||
if (config.useOutgoingLinksPage == true) {
|
||||
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) {
|
||||
@@ -139,18 +173,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).find('script'),
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -162,6 +196,6 @@ var ajaxify = {};
|
||||
}
|
||||
evalScript(scripts[i]);
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery));
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
|
||||
@@ -1,123 +1,154 @@
|
||||
var socket,
|
||||
config,
|
||||
app = {},
|
||||
API_URL = null;
|
||||
app = {
|
||||
'username': null,
|
||||
'uid': null
|
||||
};
|
||||
|
||||
|
||||
(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 : ''));
|
||||
|
||||
var reconnecting = false;
|
||||
var reconnectTries = 0;
|
||||
|
||||
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('event:consolelog', function(data) {
|
||||
console.log(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
|
||||
});
|
||||
|
||||
$('#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
|
||||
if(socket) {
|
||||
socket.disconnect();
|
||||
setTimeout(function() {
|
||||
socket.socket.connect();
|
||||
}, 200);
|
||||
} else {
|
||||
var max_reconnection_attemps = 5;
|
||||
var reconnection_delay = 200;
|
||||
socket = io.connect(RELATIVE_PATH, {
|
||||
'max reconnection attempts': max_reconnection_attemps,
|
||||
'reconnection delay': reconnection_delay
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:user.get_online_users', function(users) {
|
||||
jQuery('.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>');
|
||||
|
||||
var reconnecting = false,
|
||||
reconnectEl, reconnectTimer;
|
||||
|
||||
socket.on('event:connect', function (data) {
|
||||
app.username = data.username;
|
||||
app.uid = data.uid;
|
||||
|
||||
app.showLoginMessage();
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('event:alert', function (data) {
|
||||
app.alert(data);
|
||||
});
|
||||
|
||||
socket.on('connect', function (data) {
|
||||
if (reconnecting) {
|
||||
reconnectEl.html('<i class="icon-ok"></i> Connected!');
|
||||
reconnecting = false;
|
||||
|
||||
setTimeout(function() {
|
||||
reconnectEl.removeClass('active');
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
el.processed = true;
|
||||
});
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('event:disconnect', function() {
|
||||
socket.socket.connect();
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function (data, attempt) {
|
||||
if(attempt == max_reconnection_attemps) {
|
||||
socket.socket.reconnectionAttempts = 0;
|
||||
socket.socket.reconnectionDelay = reconnection_delay;
|
||||
return;
|
||||
}
|
||||
|
||||
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;
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('event:banned', function() {
|
||||
app.alert({
|
||||
title: 'Banned',
|
||||
message: 'You are banned you will be logged out!',
|
||||
type: 'warning',
|
||||
timeout: 1000
|
||||
});
|
||||
|
||||
setTimeout(app.logout, 1000);
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
}
|
||||
},
|
||||
async: false
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
app.logout = function() {
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: $('#csrf_token').val()
|
||||
}, function() {
|
||||
window.location.reload(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) {
|
||||
@@ -125,46 +156,46 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
|
||||
// use unique alert_id to have multiple alerts visible at a time, use the same alert_id to fade out the current instance
|
||||
// type : error, success, info, warning/notify
|
||||
// title = bolded title text
|
||||
// message = alert message content
|
||||
// timeout default = permanent
|
||||
// location : alert_window (default) or content
|
||||
app.alert = function(params) {
|
||||
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
|
||||
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);
|
||||
var title = params.title || '';
|
||||
|
||||
function startTimeout(div, timeout) {
|
||||
var timeoutId = setTimeout(function() {
|
||||
$(div).fadeOut(1000, function() {
|
||||
var timeoutId = setTimeout(function () {
|
||||
$(div).fadeOut(1000, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}, timeout);
|
||||
|
||||
|
||||
$(div).attr('timeoutId', timeoutId);
|
||||
}
|
||||
|
||||
if(alert.length > 0) {
|
||||
alert.find('strong').html(params.title);
|
||||
if (alert.length > 0) {
|
||||
alert.find('strong').html(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'),
|
||||
p = document.createElement('p');
|
||||
|
||||
p.innerHTML = params.message;
|
||||
strong.innerHTML = params.title;
|
||||
strong.innerHTML = title;
|
||||
|
||||
div.className = "alert toaster-alert " + "alert-" + params.type;
|
||||
|
||||
div.className = "alert toaster-alert " + ((params.type=='warning') ? '' : "alert-" + params.type);
|
||||
|
||||
div.setAttribute('id', alert_id);
|
||||
div.appendChild(button);
|
||||
div.appendChild(strong);
|
||||
@@ -172,23 +203,23 @@ var socket,
|
||||
|
||||
button.className = 'close';
|
||||
button.innerHTML = '×';
|
||||
button.onclick = function(ev) {
|
||||
button.onclick = function (ev) {
|
||||
div.parentNode.removeChild(div);
|
||||
}
|
||||
|
||||
if (params.location == null)
|
||||
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();
|
||||
});
|
||||
}
|
||||
@@ -196,8 +227,8 @@ var socket,
|
||||
}
|
||||
}
|
||||
|
||||
app.alertSuccess = function(message, timeout) {
|
||||
if(!timeout)
|
||||
app.alertSuccess = function (message, timeout) {
|
||||
if (!timeout)
|
||||
timeout = 2000;
|
||||
|
||||
app.alert({
|
||||
@@ -208,62 +239,55 @@ 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) {
|
||||
if (app.current_room === room)
|
||||
app.current_room = null;
|
||||
app.enter_room = function (room) {
|
||||
if (socket) {
|
||||
if (app.current_room === room)
|
||||
return;
|
||||
|
||||
|
||||
socket.emit('event:enter_room', {
|
||||
'enter': room,
|
||||
'leave': app.current_room
|
||||
});
|
||||
|
||||
|
||||
app.current_room = room;
|
||||
}
|
||||
};
|
||||
|
||||
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() {
|
||||
|
||||
// here is where all modules' onNavigate should be called, I think.
|
||||
require(['mobileMenu'], function(mobileMenu) {
|
||||
mobileMenu.onNavigate();
|
||||
});
|
||||
|
||||
app.process_page = function () {
|
||||
app.populate_online_users();
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
|
||||
var path = window.location.pathname,
|
||||
parts = path.split('/'),
|
||||
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');
|
||||
@@ -272,12 +296,15 @@ var socket,
|
||||
});
|
||||
}
|
||||
|
||||
setTimeout(function() {
|
||||
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
|
||||
$('span.timeago').timeago();
|
||||
$('.post-content img').addClass('img-responsive');
|
||||
|
||||
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',
|
||||
@@ -286,10 +313,10 @@ var socket,
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
|
||||
if(showWelcomeMessage) {
|
||||
|
||||
if (showWelcomeMessage) {
|
||||
showWelcomeMessage = false;
|
||||
if(document.readyState !== 'complete') {
|
||||
if (document.readyState !== 'complete') {
|
||||
$(document).ready(showAlert);
|
||||
} else {
|
||||
showAlert();
|
||||
@@ -297,137 +324,140 @@ 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) {
|
||||
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');
|
||||
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'));
|
||||
chat.center(chatModal);
|
||||
});
|
||||
}
|
||||
|
||||
for(var x=0,numPosts=data.posts.length;x<numPosts;x++) {
|
||||
socket.emit('api:post.privileges', data.posts[x].pid);
|
||||
}
|
||||
app.createNewPosts = function (data) {
|
||||
if (data.posts[0].uid !== app.uid) {
|
||||
data.posts[0].display_moderator_tools = 'none';
|
||||
}
|
||||
|
||||
tempContainer.replaceWith(tempContainer.contents());
|
||||
infiniteLoaderActive = false;
|
||||
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');
|
||||
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
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;
|
||||
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
$('span.timeago').timeago();
|
||||
$('.post-content img').addClass('img-responsive');
|
||||
});
|
||||
}
|
||||
|
||||
app.infiniteLoaderActive = false;
|
||||
|
||||
app.loadMorePosts = function(tid, callback) {
|
||||
if(app.infiniteLoaderActive)
|
||||
app.loadMorePosts = function (tid, callback) {
|
||||
if (app.infiniteLoaderActive)
|
||||
return;
|
||||
app.infiniteLoaderActive = true;
|
||||
socket.emit('api:topic.loadMore', {
|
||||
tid: tid,
|
||||
after: document.querySelectorAll('#post-container li[data-pid]').length
|
||||
}, function(data) {
|
||||
|
||||
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) {
|
||||
app.infiniteLoaderActive = false;
|
||||
if(data.posts.length) {
|
||||
if (data.posts.length) {
|
||||
$('#loading-indicator').attr('done', '0');
|
||||
app.createNewPosts(data);
|
||||
if(callback)
|
||||
callback();
|
||||
} else {
|
||||
$('#loading-indicator').attr('done', '1');
|
||||
}
|
||||
$('#loading-indicator').addClass('hide');
|
||||
if (callback)
|
||||
callback(data.posts);
|
||||
});
|
||||
}
|
||||
|
||||
app.scrollToPost = function(pid) {
|
||||
if(!pid)
|
||||
return;
|
||||
var container = $(document.body),
|
||||
scrollTo = $('#post_anchor_' + pid);
|
||||
app.scrollToTop = function () {
|
||||
$('body,html').animate({
|
||||
scrollTop: 0
|
||||
});
|
||||
};
|
||||
|
||||
if(!scrollTo.length) {
|
||||
var tid = $('#post-container').attr('data-tid');
|
||||
app.loadMorePosts(tid, function() {
|
||||
app.scrollToPost(pid);
|
||||
});
|
||||
app.scrollToBottom = function () {
|
||||
$('body,html').animate({
|
||||
scrollTop: $('html').height() - 100
|
||||
});
|
||||
}
|
||||
|
||||
app.scrollToPost = function (pid) {
|
||||
|
||||
if (!pid)
|
||||
return;
|
||||
|
||||
var container = $(document.body),
|
||||
scrollTo = $('#post_anchor_' + pid),
|
||||
tid = $('#post-container').attr('data-tid');
|
||||
|
||||
function animateScroll() {
|
||||
$('body,html').animate({
|
||||
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
|
||||
}, 400);
|
||||
//$('body,html').scrollTop(scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height());
|
||||
}
|
||||
|
||||
if (!scrollTo.length && tid) {
|
||||
|
||||
var intervalID = setInterval(function () {
|
||||
app.loadMorePosts(tid, function (posts) {
|
||||
scrollTo = $('#post_anchor_' + pid);
|
||||
|
||||
if (tid && scrollTo.length) {
|
||||
animateScroll();
|
||||
}
|
||||
|
||||
if (!posts.length || scrollTo.length)
|
||||
clearInterval(intervalID);
|
||||
});
|
||||
}, 100);
|
||||
|
||||
} else if (tid) {
|
||||
animateScroll();
|
||||
}
|
||||
|
||||
container.animate({
|
||||
scrollTop: scrollTo.offset().top - container.offset().top + container.scrollTop() - $('#header-menu').height()
|
||||
});
|
||||
}
|
||||
|
||||
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();
|
||||
});
|
||||
|
||||
}
|
||||
}());
|
||||
}());
|
||||
@@ -1,75 +1,92 @@
|
||||
(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Account = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
Account.init = function() {
|
||||
header.init();
|
||||
|
||||
var rep = $('#reputation');
|
||||
rep.html(app.addCommas(rep.html()));
|
||||
|
||||
var postcount = $('#postcount');
|
||||
postcount.html(app.addCommas(postcount.html()));
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
|
||||
if(yourid !== theirid) {
|
||||
if(isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
} else {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
}
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enter_room('user/' + theirid);
|
||||
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {uid: theirid}, function(success) {
|
||||
var username = $('.account-username a').html();
|
||||
if(success) {
|
||||
app.addCommasToNumbers();
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
|
||||
if (yourid !== theirid && yourid !== "0") {
|
||||
if (isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error following' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {uid: theirid}, function(success) {
|
||||
var username = $('.account-username a').html();
|
||||
if(success) {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.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) {
|
||||
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');
|
||||
followBtn.hide();
|
||||
unfollowBtn.hide();
|
||||
}
|
||||
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error following' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.user-recent-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
|
||||
socket.on('api:user.isOnline', Account.handleUserOnline);
|
||||
|
||||
socket.emit('api:user.isOnline', theirid, Account.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);
|
||||
};
|
||||
|
||||
});
|
||||
Account.handleUserOnline = function(data) {
|
||||
var onlineStatus = $('.account-online-status');
|
||||
|
||||
}());
|
||||
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');
|
||||
}
|
||||
};
|
||||
|
||||
return Account;
|
||||
});
|
||||
@@ -1,277 +1,225 @@
|
||||
define(['forum/accountheader', 'uploader'], function(header, uploader) {
|
||||
var AccountEdit = {};
|
||||
|
||||
AccountEdit.init = function() {
|
||||
header.init();
|
||||
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var selectedImageType = '';
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
$('#uploadForm').submit(function() {
|
||||
status('uploading the file ...');
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show();
|
||||
|
||||
if(!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
return false;
|
||||
}
|
||||
|
||||
$(this).find('#imageUploadCsrf').val($('#csrf_token').val());
|
||||
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
location: $('#inputLocation').val(),
|
||||
signature: $('#inputSignature').val()
|
||||
};
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
|
||||
error: function(xhr) {
|
||||
error('Error: ' + xhr.status);
|
||||
},
|
||||
|
||||
uploadProgress : function(event, position, total, percent) {
|
||||
$('#upload-progress-bar').css('width', percent+'%');
|
||||
},
|
||||
|
||||
|
||||
success: function(response) {
|
||||
if(response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
socket.emit('api:user.updateProfile', userData, function(err, data) {
|
||||
if (data.success) {
|
||||
app.alertSuccess('Your profile has been updated successfully!');
|
||||
if (data.picture) {
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
$('#user_label img').attr('src', data.picture);
|
||||
}
|
||||
if (data.gravatarpicture) {
|
||||
$('#user-gravatar-picture').attr('src', data.gravatarpicture);
|
||||
gravatarPicture = data.gravatarpicture;
|
||||
}
|
||||
} else {
|
||||
app.alertError('There was an error updating your profile! ' + err.error);
|
||||
}
|
||||
|
||||
var imageUrlOnServer = response.path;
|
||||
|
||||
$('#user-current-picture').attr('src', imageUrlOnServer);
|
||||
$('#user-uploaded-picture').attr('src', imageUrlOnServer);
|
||||
|
||||
uploadedPicture = imageUrlOnServer;
|
||||
|
||||
setTimeout(function() {
|
||||
hideAlerts();
|
||||
$('#upload-picture-modal').modal('hide');
|
||||
}, 750);
|
||||
|
||||
socket.emit('api:updateHeader', { fields: ['username', 'picture', 'userslug'] });
|
||||
success('File uploaded successfully!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
function hideAlerts() {
|
||||
$('#alert-status').hide();
|
||||
$('#alert-success').hide();
|
||||
$('#alert-error').hide();
|
||||
$('#upload-progress-box').hide();
|
||||
}
|
||||
|
||||
function status(message) {
|
||||
hideAlerts();
|
||||
$('#alert-status').text(message).show();
|
||||
}
|
||||
|
||||
function success(message) {
|
||||
hideAlerts();
|
||||
$('#alert-success').text(message).show();
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
hideAlerts();
|
||||
$('#alert-error').text(message).show();
|
||||
}
|
||||
|
||||
function changeUserPicture(type) {
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
AccountEdit.updateImages();
|
||||
|
||||
$('#change-picture-modal').modal('show');
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
$('#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) {
|
||||
AccountEdit.changeUserPicture(selectedImageType);
|
||||
|
||||
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() {
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
uploader.open(config.relative_path + '/user/uploadpicture', function(imageUrlOnServer) {
|
||||
$('#user-current-picture').attr('src', imageUrlOnServer);
|
||||
$('#user-uploaded-picture').attr('src', imageUrlOnServer);
|
||||
|
||||
uploadedPicture = imageUrlOnServer;
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
(function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
password_notify.html('Password too short');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else if (!passwordvalid) {
|
||||
password_notify.html('Invalid password');
|
||||
password_notify.attr('class', 'alert alert-danger');
|
||||
password_notify.removeClass('hide');
|
||||
} else {
|
||||
password_notify.html('OK!');
|
||||
password_notify.attr('class', 'alert alert-success');
|
||||
password_notify.removeClass('hide');
|
||||
}
|
||||
|
||||
onPasswordConfirmChanged();
|
||||
}
|
||||
|
||||
function onPasswordConfirmChanged() {
|
||||
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', 'alert alert-danger');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
password_confirm_notify.html('OK!');
|
||||
password_confirm_notify.attr('class', 'alert alert-success');
|
||||
password_confirm_notify.removeClass('hide');
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
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(err) {
|
||||
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
password_notify.addClass('hide');
|
||||
password_confirm_notify.addClass('hide');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if (err) {
|
||||
app.alertError(err.error);
|
||||
return;
|
||||
}
|
||||
|
||||
app.alertSuccess('Your password is updated!');
|
||||
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
};
|
||||
|
||||
|
||||
AccountEdit.changeUserPicture = function(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()
|
||||
};
|
||||
|
||||
socket.emit('api:user.updateProfile', userData, function(data) {
|
||||
if(data.success) {
|
||||
app.alertSuccess('Your profile has been updated successfully!');
|
||||
if(data.picture) {
|
||||
$('#user-current-picture').attr('src', data.picture);
|
||||
$('#user_label img').attr('src', data.picture);
|
||||
}
|
||||
if(data.gravatarpicture) {
|
||||
$('#user-gravatar-picture').attr('src', data.gravatarpicture);
|
||||
gravatarPicture = data.gravatarpicture;
|
||||
}
|
||||
} else {
|
||||
app.alertError('There was an error updating your profile!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
function updateImages() {
|
||||
AccountEdit.updateImages = function() {
|
||||
var currentPicture = $('#user-current-picture').attr('src');
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
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');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function(){
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
$('#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) {
|
||||
changeUserPicture(selectedImageType);
|
||||
|
||||
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(){
|
||||
|
||||
$('#change-picture-modal').modal('hide');
|
||||
$('#upload-picture-modal').modal('show');
|
||||
|
||||
hideAlerts();
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#pictureUploadSubmitBtn').on('click', function() {
|
||||
$('#uploadForm').submit();
|
||||
});
|
||||
|
||||
(function handlePasswordChange() {
|
||||
var currentPassword = $('#inputCurrentPassword');
|
||||
var password_notify = $('#password-notify');
|
||||
var password_confirm_notify = $('#password-confirm-notify');
|
||||
var password = $('#inputNewPassword');
|
||||
var password_confirm = $('#inputNewPasswordAgain');
|
||||
var passwordvalid = false;
|
||||
var passwordsmatch = false;
|
||||
|
||||
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < 6) {
|
||||
password_notify.html('Password too short');
|
||||
password_notify.attr('class', 'label label-important');
|
||||
} else if(!passwordvalid) {
|
||||
password_notify.html('Invalid password');
|
||||
password_notify.attr('class', 'label label-important');
|
||||
} else {
|
||||
password_notify.html('OK!');
|
||||
password_notify.attr('class', 'label label-success');
|
||||
}
|
||||
|
||||
onPasswordConfirmChanged();
|
||||
}
|
||||
|
||||
function onPasswordConfirmChanged() {
|
||||
if(password.val() !== password_confirm.val()) {
|
||||
password_confirm_notify.html('Passwords must match!');
|
||||
password_confirm_notify.attr('class', 'label label-important');
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
password_confirm_notify.html('OK!');
|
||||
password_confirm_notify.attr('class', 'label label-success');
|
||||
passwordsmatch = true;
|
||||
}
|
||||
}
|
||||
|
||||
password.on('keyup', onPasswordChanged);
|
||||
password_confirm.on('keyup', onPasswordConfirmChanged);
|
||||
|
||||
$('#changePasswordBtn').on('click', function() {
|
||||
|
||||
if(passwordvalid && passwordsmatch && currentPassword.val()) {
|
||||
socket.emit('api:user.changePassword', {'currentPassword': currentPassword.val(),'newPassword': password.val() }, function(data) {
|
||||
|
||||
currentPassword.val('');
|
||||
password.val('');
|
||||
password_confirm.val('');
|
||||
password_notify.html('');
|
||||
password_confirm_notify.html('');
|
||||
passwordsmatch = false;
|
||||
passwordvalid = false;
|
||||
|
||||
if(data.err) {
|
||||
app.alertError(data.err);
|
||||
return;
|
||||
}
|
||||
|
||||
app.alertSuccess('Your password is updated!');
|
||||
|
||||
});
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
return AccountEdit;
|
||||
});
|
||||
@@ -1,20 +1,43 @@
|
||||
(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid');
|
||||
define(function() {
|
||||
var AccountHeader = {};
|
||||
|
||||
AccountHeader.init = function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid');
|
||||
|
||||
AccountHeader.createMenu();
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
var editLink = $('#editLink');
|
||||
var settingsLink = $('#settingsLink');
|
||||
|
||||
if(yourid === "0") {
|
||||
editLink.hide();
|
||||
settingsLink.hide();
|
||||
}
|
||||
else if(yourid !== theirid) {
|
||||
editLink.hide();
|
||||
settingsLink.hide();
|
||||
}
|
||||
});
|
||||
var favouritesLink = $('#favouritesLink');
|
||||
|
||||
}());
|
||||
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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
AccountHeader.createMenu = function() {
|
||||
var userslug = $('.account-username-box').attr('data-userslug');
|
||||
var links = $('<div class="account-sub-links inline-block pull-right">\
|
||||
<span id="settingsLink" class="pull-right"><a href="/user/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
|
||||
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
|
||||
</div>');
|
||||
|
||||
$('.account-username-box').append(links);
|
||||
}
|
||||
|
||||
return AccountHeader;
|
||||
});
|
||||
@@ -1,22 +1,25 @@
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountSettings = {};
|
||||
|
||||
AccountSettings.init = function() {
|
||||
header.init();
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
$(document).ready(function() {
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked')?1:0
|
||||
};
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if(success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if (success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
return AccountSettings;
|
||||
});
|
||||
|
||||
@@ -1,103 +1,163 @@
|
||||
define(function() {
|
||||
var Categories = {};
|
||||
|
||||
var modified_categories = {};
|
||||
Categories.init = function() {
|
||||
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;
|
||||
}
|
||||
|
||||
function save() {
|
||||
socket.emit('api:admin.categories.update', modified_categories);
|
||||
modified_categories = {};
|
||||
}
|
||||
|
||||
function select_icon(el) {
|
||||
var selected = el.className.replace(' icon-2x', '');
|
||||
jQuery('#icons .selected').removeClass('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;
|
||||
|
||||
modified(el);
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('li').attr('data-cid');
|
||||
if(cid) {
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('.bootbox .span3').on('click', function() {
|
||||
jQuery('.bootbox .selected').removeClass('selected');
|
||||
jQuery(this).addClass('selected');
|
||||
});
|
||||
}
|
||||
function save() {
|
||||
socket.emit('api:admin.categories.update', modified_categories);
|
||||
modified_categories = {};
|
||||
}
|
||||
|
||||
function select_icon(el) {
|
||||
var selected = el.attr('class').replace(' icon-2x', '');
|
||||
jQuery('#icons .selected').removeClass('selected');
|
||||
if (selected)
|
||||
jQuery('#icons .' + selected).parent().addClass('selected');
|
||||
|
||||
|
||||
function update_blockclass(el) {
|
||||
el.parentNode.parentNode.className = 'entry-row ' + el.value;
|
||||
}
|
||||
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.attr('class', iconClass + ' icon-2x');
|
||||
el.val(iconClass);
|
||||
el.attr('value', iconClass);
|
||||
|
||||
jQuery('#entry-container').sortable();
|
||||
jQuery('.blockclass').each(function() {
|
||||
jQuery(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
modified(el);
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
//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() {
|
||||
jQuery('document').ready(function() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
function update_blockclass(el) {
|
||||
el.parentNode.parentNode.className = 'entry-row ' + el.value;
|
||||
}
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
function updateCategoryOrders() {
|
||||
var categories = $('.admin-categories #entry-container').children();
|
||||
for(var i=0; i<categories.length; ++i) {
|
||||
var input = $(categories[i]).find('input[data-name="order"]');
|
||||
|
||||
input.val(i+1).attr('data-value', i+1);
|
||||
modified(input);
|
||||
}
|
||||
}
|
||||
|
||||
jQuery('#entry-container').sortable({
|
||||
stop: function( event, ui ) {
|
||||
updateCategoryOrders();
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
|
||||
jQuery('.icon').on('click', function(ev) {
|
||||
select_icon(ev.target);
|
||||
jQuery('.blockclass').each(function() {
|
||||
jQuery(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
|
||||
jQuery('.blockclass').on('change', function(ev) {
|
||||
update_blockclass(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.category_name, .category_description, .blockclass').on('change', function(ev) {
|
||||
modified(ev.target);
|
||||
});
|
||||
function showCreateCategoryModal() {
|
||||
$('#new-category-modal').modal();
|
||||
}
|
||||
|
||||
jQuery('.entry-row button').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if(disabled == "0" || disabled == "")
|
||||
$(element).html('Disable');
|
||||
else
|
||||
$(element).html('Enable');
|
||||
function createNewCategory() {
|
||||
var category = {
|
||||
name: $('#inputName').val(),
|
||||
description: $('#inputDescription').val(),
|
||||
icon: $('#new-category-modal i').val(),
|
||||
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];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
jQuery('#addNew').on('click', showCreateCategoryModal);
|
||||
jQuery('#create-category-btn').on('click', createNewCategory);
|
||||
|
||||
jQuery('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('#new-category-modal').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
jQuery('.blockclass').on('change', function(ev) {
|
||||
update_blockclass(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.category_name, .category_description, .blockclass').on('change', function(ev) {
|
||||
modified(ev.target);
|
||||
});
|
||||
|
||||
jQuery('.entry-row button').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if (disabled == "0" || disabled == "")
|
||||
$(element).html('Disable');
|
||||
else
|
||||
$(element).html('Enable');
|
||||
|
||||
});
|
||||
|
||||
jQuery('#entry-container').on('click', '.disable-btn', function(ev) {
|
||||
var btn = jQuery(this);
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = btn.html() == "Disable" ? "1" : "0";
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
|
||||
save();
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
jQuery('.entry-row button').on('click', function(ev) {
|
||||
var btn = jQuery(this);
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = btn.html() == "Disable" ? "1":"0";
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
|
||||
save();
|
||||
return false;
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Categories;
|
||||
});
|
||||
@@ -1,122 +1,42 @@
|
||||
jQuery('document').ready(function() {
|
||||
// On menu click, change "active" state
|
||||
var menuEl = document.querySelector('.sidebar-nav'),
|
||||
liEls = menuEl.querySelectorAll('li')
|
||||
parentEl = null;
|
||||
|
||||
|
||||
|
||||
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) {
|
||||
setTimeout(function() {
|
||||
nodebb_admin.prepare();
|
||||
}, 500);
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the fields on the page from the config
|
||||
var fields = document.querySelectorAll('#content [data-field]'),
|
||||
numFields = fields.length,
|
||||
saveBtn = document.getElementById('save'),
|
||||
x, key, inputType;
|
||||
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) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
fields[x].value = nodebb_admin.config[key];
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
fields[x].checked = nodebb_admin.config[key] === '1' ? true : false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
if (nodebb_admin.config[key]) fields[x].value = nodebb_admin.config[key];
|
||||
menuEl.addEventListener('click', function(e) {
|
||||
parentEl = e.target.parentNode;
|
||||
if (parentEl.nodeName === 'LI') {
|
||||
for (var x = 0, numLis = liEls.length; x < numLis; x++) {
|
||||
if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active');
|
||||
else jQuery(parentEl).addClass('active');
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
saveBtn.addEventListener('click', function(e) {
|
||||
var key, value;
|
||||
e.preventDefault();
|
||||
socket.once('api:config.get', function(config) {
|
||||
app.config = config;
|
||||
});
|
||||
|
||||
for(x=0;x<numFields;x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
inputType = fields[x].getAttribute('type');
|
||||
switch(inputType) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
value = fields[x].value;
|
||||
break;
|
||||
socket.emit('api:config.get');
|
||||
|
||||
case 'checkbox':
|
||||
value = fields[x].checked ? '1' : '0';
|
||||
break;
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', { key: key, value: value });
|
||||
}
|
||||
socket.on('api:config.set', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Saved',
|
||||
message: 'Your changes to the NodeBB configuration have been saved.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'danger'
|
||||
});
|
||||
}
|
||||
|
||||
nodebb_admin.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
}
|
||||
|
||||
|
||||
jQuery('document').ready(function() {
|
||||
// On menu click, change "active" state
|
||||
var menuEl = document.querySelector('.sidebar-nav'),
|
||||
liEls = menuEl.querySelectorAll('li')
|
||||
parentEl = null;
|
||||
|
||||
menuEl.addEventListener('click', function(e) {
|
||||
parentEl = e.target.parentNode;
|
||||
if (parentEl.nodeName === 'LI') {
|
||||
for(var x=0,numLis=liEls.length;x<numLis;x++) {
|
||||
if (liEls[x] !== parentEl) jQuery(liEls[x]).removeClass('active');
|
||||
else jQuery(parentEl).addClass('active');
|
||||
}
|
||||
}
|
||||
}, false);
|
||||
});
|
||||
|
||||
socket.once('api:config.get', function(config) {
|
||||
nodebb_admin.config = config;
|
||||
});
|
||||
|
||||
socket.emit('api:config.get');
|
||||
|
||||
socket.on('api:config.set', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
app.alert({
|
||||
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.',
|
||||
type: 'success'
|
||||
});
|
||||
} else {
|
||||
app.alert({
|
||||
alert_id: 'config_status',
|
||||
timeout: 2500,
|
||||
title: 'Changes Not Saved',
|
||||
message: 'NodeBB encountered a problem saving your changes',
|
||||
type: 'error'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
|
||||
});
|
||||
202
public/src/forum/admin/groups.js
Normal file
202
public/src/forum/admin/groups.js
Normal file
@@ -0,0 +1,202 @@
|
||||
define(function() {
|
||||
var Groups = {};
|
||||
|
||||
Groups.init = 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.on('hidden.bs.modal', function() {
|
||||
ajaxify.go('admin/groups');
|
||||
});
|
||||
detailsModal.modal('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
return Groups;
|
||||
});
|
||||
@@ -1,25 +1,28 @@
|
||||
define(function() {
|
||||
var Admin = {};
|
||||
|
||||
(function() {
|
||||
|
||||
ajaxify.register_events(['api:get_all_rooms']);
|
||||
socket.on('api:get_all_rooms', function(data) {
|
||||
Admin.init = 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) {
|
||||
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>";
|
||||
var active_users = document.getElementById('active_users'),
|
||||
total = 0;
|
||||
active_users.innerHTML = '';
|
||||
|
||||
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>";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
document.getElementById('connections').innerHTML = total;
|
||||
});
|
||||
document.getElementById('connections').innerHTML = total;
|
||||
});
|
||||
|
||||
app.enter_room('admin');
|
||||
socket.emit('api:get_all_rooms');
|
||||
app.enter_room('admin');
|
||||
socket.emit('api:get_all_rooms');
|
||||
};
|
||||
|
||||
}());
|
||||
return Admin;
|
||||
});
|
||||
|
||||
@@ -1,38 +1,33 @@
|
||||
var nodebb_admin = nodebb_admin || {};
|
||||
define(function() {
|
||||
var Plugins = {
|
||||
init: function() {
|
||||
var pluginsList = $('.plugins'),
|
||||
numPlugins = pluginsList[0].querySelectorAll('li').length,
|
||||
pluginID, pluginTgl;
|
||||
|
||||
(function() {
|
||||
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;
|
||||
nodebb_admin.plugins.init();
|
||||
});
|
||||
})();
|
||||
return Plugins;
|
||||
});
|
||||
88
public/src/forum/admin/settings.js
Normal file
88
public/src/forum/admin/settings.js
Normal file
@@ -0,0 +1,88 @@
|
||||
define(['uploader'], function(uploader) {
|
||||
var Settings = {};
|
||||
|
||||
Settings.init = function() {
|
||||
Settings.prepare();
|
||||
};
|
||||
|
||||
Settings.prepare = function() {
|
||||
// Come back in 125ms if the config isn't ready yet
|
||||
if (!app.config) {
|
||||
setTimeout(function() {
|
||||
Settings.prepare();
|
||||
}, 125);
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate the fields on the page from the config
|
||||
var fields = document.querySelectorAll('#content [data-field]'),
|
||||
numFields = fields.length,
|
||||
saveBtn = document.getElementById('save'),
|
||||
x, key, inputType;
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
inputType = fields[x].getAttribute('type');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
if (app.config[key]) {
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'textarea':
|
||||
case 'number':
|
||||
fields[x].value = app.config[key];
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
fields[x].checked = app.config[key] === '1' ? true : false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
if (app.config[key]) fields[x].value = app.config[key];
|
||||
}
|
||||
}
|
||||
|
||||
saveBtn.addEventListener('click', function(e) {
|
||||
var key, value;
|
||||
e.preventDefault();
|
||||
|
||||
for (x = 0; x < numFields; x++) {
|
||||
key = fields[x].getAttribute('data-field');
|
||||
if (fields[x].nodeName === 'INPUT') {
|
||||
inputType = fields[x].getAttribute('type');
|
||||
switch (inputType) {
|
||||
case 'text':
|
||||
case 'number':
|
||||
value = fields[x].value;
|
||||
break;
|
||||
|
||||
case 'checkbox':
|
||||
value = fields[x].checked ? '1' : '0';
|
||||
break;
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', {
|
||||
key: key,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#uploadLogoBtn').on('click', function() {
|
||||
|
||||
uploader.open(config.relative_path + '/admin/uploadlogo', function(image) {
|
||||
$('#logoUrl').val(image);
|
||||
});
|
||||
|
||||
uploader.hideAlerts();
|
||||
});
|
||||
};
|
||||
|
||||
Settings.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
};
|
||||
|
||||
return Settings;
|
||||
});
|
||||
@@ -1,118 +1,132 @@
|
||||
define(function() {
|
||||
var Themes = {};
|
||||
|
||||
Themes.init = function() {
|
||||
var scriptEl = document.createElement('script');
|
||||
scriptEl.src = 'http://api.bootswatch.com/3/?callback=bootswatchListener';
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
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')) {
|
||||
case 'preview':
|
||||
var cssSrc = $(e.target).parents('li').attr('data-css'),
|
||||
cssEl = document.getElementById('base-theme');
|
||||
|
||||
var themes = {};
|
||||
cssEl.href = cssSrc;
|
||||
break;
|
||||
case 'use':
|
||||
var parentEl = $(e.target).parents('li'),
|
||||
themeType = parentEl.attr('data-type'),
|
||||
cssSrc = parentEl.attr('data-css'),
|
||||
themeId = parentEl.attr('data-theme');
|
||||
|
||||
themes.render = function(bootswatch) {
|
||||
socket.emit('api:admin.theme.set', {
|
||||
type: themeType,
|
||||
id: themeId,
|
||||
src: cssSrc
|
||||
}, function(err) {
|
||||
app.alert({
|
||||
alert_id: 'admin:theme',
|
||||
type: 'success',
|
||||
title: 'Theme Changed',
|
||||
message: 'You have successfully changed your NodeBB\'s theme. Please restart to see the changes.',
|
||||
timeout: 2500
|
||||
});
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
bootstrapThemeContainer.addEventListener('click', themeEvent);
|
||||
installedThemeContainer.addEventListener('click', themeEvent);
|
||||
|
||||
var revertEl = document.getElementById('revert_theme');
|
||||
revertEl.addEventListener('click', function() {
|
||||
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.theme.set', {
|
||||
type: 'local',
|
||||
id: 'nodebb-theme-cerulean'
|
||||
}, function(err) {
|
||||
app.alert({
|
||||
alert_id: 'admin:theme',
|
||||
type: 'success',
|
||||
title: 'Theme Changed',
|
||||
message: 'You have successfully reverted your NodeBB back to it\'s default theme. Please restart to see the changes.',
|
||||
timeout: 3500
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
|
||||
// Installed Themes
|
||||
socket.emit('api:admin.themes.getInstalled', function(themes) {
|
||||
var instListEl = document.getElementById('installed_themes'),
|
||||
themeFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
liEl.setAttribute('data-type', 'local');
|
||||
|
||||
if (themes.length > 0) {
|
||||
for (var x = 0, numThemes = themes.length; x < numThemes; x++) {
|
||||
liEl.setAttribute('data-theme', themes[x].id);
|
||||
liEl.innerHTML = '<img src="' + (themes[x].screenshot ? '/css/previews/' + themes[x].id : RELATIVE_PATH + '/images/themes/default.png') + '" />' +
|
||||
'<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 {
|
||||
// No themes found
|
||||
liEl.className = 'no-themes';
|
||||
liEl.innerHTML = 'No installed themes found';
|
||||
themeFrag.appendChild(liEl);
|
||||
}
|
||||
|
||||
instListEl.innerHTML = '';
|
||||
instListEl.appendChild(themeFrag);
|
||||
});
|
||||
}
|
||||
|
||||
Themes.render = function(bootswatch) {
|
||||
var themeFrag = document.createDocumentFragment(),
|
||||
themeEl = document.createElement('li'),
|
||||
themeContainer = document.querySelector('#bootstrap_themes'),
|
||||
numThemes = bootswatch.themes.length;
|
||||
|
||||
for(var x=0;x<numThemes;x++) {
|
||||
themeEl.setAttribute('data-type', 'bootswatch');
|
||||
|
||||
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';
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
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')) {
|
||||
case 'preview':
|
||||
var cssSrc = $(e.target).parents('li').attr('data-css'),
|
||||
cssEl = document.getElementById('base-theme');
|
||||
|
||||
cssEl.href = cssSrc;
|
||||
break;
|
||||
case 'use':
|
||||
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
|
||||
});
|
||||
socket.emit('api:config.set', {
|
||||
key: 'theme:src', value: cssSrc
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
bootstrapThemeContainer.addEventListener('click', themeEvent);
|
||||
installedThemeContainer.addEventListener('click', themeEvent);
|
||||
|
||||
var revertEl = document.getElementById('revert_theme');
|
||||
revertEl.addEventListener('click', function() {
|
||||
bootbox.confirm('Are you sure you wish to remove the custom theme and restore the NodeBB default theme?', function(confirm) {
|
||||
if (confirm) {
|
||||
nodebb_admin.remove('theme:id');
|
||||
nodebb_admin.remove('theme:src');
|
||||
}
|
||||
});
|
||||
}, false);
|
||||
|
||||
// Installed Themes
|
||||
socket.emit('api:admin.themes.getInstalled', function(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++) {
|
||||
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">';
|
||||
themeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
// No themes found
|
||||
liEl.className = 'no-themes';
|
||||
liEl.innerHTML = 'No installed themes found';
|
||||
themeFrag.appendChild(liEl);
|
||||
}
|
||||
|
||||
instListEl.innerHTML = '';
|
||||
instListEl.appendChild(themeFrag);
|
||||
});
|
||||
})();
|
||||
return Themes;
|
||||
});
|
||||
@@ -1,115 +1,153 @@
|
||||
$(document).ready(function() {
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
loadMoreEl = document.getElementById('topics_loadmore');
|
||||
define(function() {
|
||||
var Topics = {};
|
||||
|
||||
$(topicsListEl).on('click', '[data-action]', function() {
|
||||
var $this = $(this),
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('data-tid');
|
||||
Topics.init = function() {
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
loadMoreEl = document.getElementById('topics_loadmore');
|
||||
|
||||
switch(action) {
|
||||
case 'pin':
|
||||
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;
|
||||
case 'delete':
|
||||
if (!$this.hasClass('active')) socket.emit('api:topic.delete', { tid: tid });
|
||||
else socket.emit('api:topic.restore', { tid: tid });
|
||||
break;
|
||||
this.resolveButtonStates();
|
||||
|
||||
$(topicsListEl).on('click', '[data-action]', function() {
|
||||
var $this = $(this),
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('data-tid');
|
||||
|
||||
switch (action) {
|
||||
case 'pin':
|
||||
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;
|
||||
case 'delete':
|
||||
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]'),
|
||||
lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||
|
||||
this.innerHTML = '<i class="icon-refresh icon-spin"></i> Retrieving topics';
|
||||
socket.emit('api:admin.topics.getMore', {
|
||||
limit: 10,
|
||||
after: lastTid
|
||||
}, function(topics) {
|
||||
var btnEl = document.getElementById('topics_loadmore');
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
|
||||
// Fix relative paths
|
||||
html = html.replace(/\{relative_path\}/g, RELATIVE_PATH);
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
|
||||
Topics.resolveButtonStates();
|
||||
|
||||
btnEl.innerHTML = 'Load More Topics';
|
||||
$('span.timeago').timeago();
|
||||
} else {
|
||||
// Exhausted all topics
|
||||
btnEl.className += ' disabled';
|
||||
btnEl.innerHTML = 'No more topics';
|
||||
}
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
socket.on('api:topic.pin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unpin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.lock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unlock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.delete', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
$(btnEl).siblings('[data-action="lock"]').addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.restore', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
$(btnEl).siblings('[data-action="lock"]').removeClass('active');
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Topics.resolveButtonStates = function() {
|
||||
// Resolve proper button state for all topics
|
||||
var topicsListEl = document.querySelector('.topics'),
|
||||
topicEls = topicsListEl.querySelectorAll('li'),
|
||||
numTopics = topicEls.length;
|
||||
for (var x = 0; x < numTopics; x++) {
|
||||
if (topicEls[x].getAttribute('data-pinned') === '1') {
|
||||
topicEls[x].querySelector('[data-action="pin"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-pinned');
|
||||
}
|
||||
if (topicEls[x].getAttribute('data-locked') === '1') {
|
||||
topicEls[x].querySelector('[data-action="lock"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-locked');
|
||||
}
|
||||
if (topicEls[x].getAttribute('data-deleted') === '1') {
|
||||
topicEls[x].querySelector('[data-action="delete"]').className += ' active';
|
||||
topicEls[x].removeAttribute('data-deleted');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
loadMoreEl.addEventListener('click', function() {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
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';
|
||||
socket.emit('api:admin.topics.getMore', {
|
||||
limit: 10,
|
||||
after: lastTid
|
||||
}, function(topics) {
|
||||
var btnEl = document.getElementById('topics_loadmore');
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
btnEl.innerHTML = 'Load More Topics';
|
||||
} else {
|
||||
// Exhausted all topics
|
||||
btnEl.className += ' disabled';
|
||||
btnEl.innerHTML = 'No more topics';
|
||||
}
|
||||
});
|
||||
}
|
||||
}, false);
|
||||
|
||||
// Resolve proper button state for all topics
|
||||
var topicEls = topicsListEl.querySelectorAll('li'),
|
||||
numTopics = topicEls.length;
|
||||
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';
|
||||
topicEls[x].removeAttribute('data-pinned');
|
||||
topicEls[x].removeAttribute('data-locked');
|
||||
topicEls[x].removeAttribute('data-deleted');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.pin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unpin', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="pin"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.lock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.unlock', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="lock"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.delete', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).addClass('active');
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:topic.restore', function(response) {
|
||||
if (response.status === 'ok') {
|
||||
var btnEl = document.querySelector('li[data-tid="' + response.tid + '"] button[data-action="delete"]');
|
||||
|
||||
$(btnEl).removeClass('active');
|
||||
}
|
||||
return Topics;
|
||||
});
|
||||
@@ -1,183 +1,174 @@
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
(function() {
|
||||
|
||||
function initUsers() {
|
||||
Users.init = function() {
|
||||
var yourid = templates.get('yourid');
|
||||
|
||||
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 getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
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');
|
||||
function updateUserButtons() {
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
banBtn.addClass('disabled');
|
||||
else if (isUserBanned(banBtn))
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
jQuery('.delete-btn').each(function(index, element) {
|
||||
var deleteBtn = $(element);
|
||||
var isAdmin = isUserAdmin(deleteBtn);
|
||||
|
||||
if(isAdmin)
|
||||
deleteBtn.addClass('disabled');
|
||||
else
|
||||
deleteBtn.show();
|
||||
});
|
||||
function initUsers() {
|
||||
|
||||
jQuery('.admin-btn').on('click', function() {
|
||||
var adminBtn = $(this);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
var parent = adminBtn.parents('.users-box');
|
||||
var uid = getUID(adminBtn);
|
||||
updateUserButtons();
|
||||
|
||||
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);
|
||||
$('#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) {
|
||||
socket.emit('api:admin.user.unbanUser', uid);
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
} else {
|
||||
bootbox.confirm('Do you really want to ban "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.user.banUser', uid);
|
||||
banBtn.addClass('btn-warning');
|
||||
parent.attr('data-banned', 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
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('document').ready(function() {
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
timeoutId = 0,
|
||||
loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
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({
|
||||
jQuery('document').ready(function() {
|
||||
|
||||
var timeoutId = 0,
|
||||
loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
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
|
||||
}),
|
||||
userListEl = document.querySelector('.users');
|
||||
userListEl = document.querySelector('.users');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
userListEl.innerHTML = html;
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({ users: users });
|
||||
$('#users-container').append(html);
|
||||
}
|
||||
if (data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!')
|
||||
.show()
|
||||
.addClass('label-danger')
|
||||
.removeClass('label-success');
|
||||
} else {
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if(active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if(active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if(active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
initUsers();
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: users
|
||||
});
|
||||
$('#users-container').append(html);
|
||||
updateUserButtons();
|
||||
}
|
||||
}
|
||||
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if (active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
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);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Users;
|
||||
});
|
||||
@@ -1,133 +1,160 @@
|
||||
(function() {
|
||||
var cid = templates.get('category_id'),
|
||||
room = 'category_' + cid,
|
||||
twitterEl = document.getElementById('twitter-intent'),
|
||||
facebookEl = document.getElementById('facebook-share'),
|
||||
googleEl = document.getElementById('google-share'),
|
||||
twitter_url = templates.get('twitter-intent-url'),
|
||||
facebook_url = templates.get('facebook-share-url'),
|
||||
google_url = templates.get('google-share-url'),
|
||||
loadingMoreTopics = false;
|
||||
|
||||
app.enter_room(room);
|
||||
define(function () {
|
||||
var Category = {};
|
||||
|
||||
twitterEl.addEventListener('click', function() {
|
||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
facebookEl.addEventListener('click', function() {
|
||||
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
googleEl.addEventListener('click', function() {
|
||||
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
|
||||
return false;
|
||||
}, false);
|
||||
Category.init = function() {
|
||||
var cid = templates.get('category_id'),
|
||||
room = 'category_' + cid,
|
||||
twitterEl = jQuery('#twitter-intent'),
|
||||
facebookEl = jQuery('#facebook-share'),
|
||||
googleEl = jQuery('#google-share'),
|
||||
twitter_url = templates.get('twitter-intent-url'),
|
||||
facebook_url = templates.get('facebook-share-url'),
|
||||
google_url = templates.get('google-share-url'),
|
||||
loadingMoreTopics = false;
|
||||
|
||||
var new_post = document.getElementById('new_post');
|
||||
new_post.onclick = function() {
|
||||
require(['composer'], function(cmp) {
|
||||
cmp.push(0, cid);
|
||||
app.enter_room(room);
|
||||
|
||||
twitterEl.on('click', function () {
|
||||
window.open(twitter_url, '_blank', 'width=550,height=420,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
facebookEl.on('click', function () {
|
||||
window.open(facebook_url, '_blank', 'width=626,height=436,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
googleEl.on('click', function () {
|
||||
window.open(google_url, '_blank', 'width=500,height=570,scrollbars=no,status=no');
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
$('#new_post').on('click', function () {
|
||||
require(['composer'], function (cmp) {
|
||||
cmp.push(0, cid);
|
||||
});
|
||||
});
|
||||
|
||||
function onNewTopic(data) {
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
|
||||
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;
|
||||
socket.on('event:new_topic', Category.onNewTopic);
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.on('api:categories.getRecentReplies', function (posts) {
|
||||
if (!posts || posts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recent_replies = document.getElementById('category_recent_replies');
|
||||
|
||||
recent_replies.innerHTML = '';
|
||||
|
||||
var frag = document.createDocumentFragment(),
|
||||
li = document.createElement('li');
|
||||
for (var i = 0, numPosts = posts.length; i < numPosts; i++) {
|
||||
|
||||
li.setAttribute('data-pid', posts[i].pid);
|
||||
|
||||
|
||||
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 + '">' +
|
||||
'<strong><span>'+ posts[i].username + '</span></strong>' +
|
||||
'<p>' +
|
||||
posts[i].content +
|
||||
'</p>' +
|
||||
'</a>' +
|
||||
'<span class="timeago pull-right" title="' + posts[i].relativeTime + '"></span>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
});
|
||||
|
||||
$(window).off('scroll').on('scroll', function (ev) {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
Category.loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Category.onNewTopic = function(data) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: [data]
|
||||
}),
|
||||
topic = $(html),
|
||||
container = $('#topics-container'),
|
||||
topics = $('#topics-container').children('.category-item'),
|
||||
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', templates.get('category_id'));
|
||||
|
||||
addActiveUser(data);
|
||||
|
||||
$('#topics-container span.timeago').timeago();
|
||||
}
|
||||
|
||||
function addActiveUser(data) {
|
||||
var activeUser = $('.category-sidebar .active-users').find('a[data-uid="' + data.uid + '"]');
|
||||
if(!activeUser.length) {
|
||||
var newUser = templates.prepare(templates['category'].blocks['active_users']).parse({
|
||||
active_users: [{
|
||||
uid: data.uid,
|
||||
username: data.username,
|
||||
userslug: data.userslug,
|
||||
picture: data.teaser_userpicture
|
||||
}]
|
||||
});
|
||||
$(newUser).appendTo($('.category-sidebar .active-users'));
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('event:new_topic', onNewTopic);
|
||||
Category.onTopicsLoaded = function(topics) {
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.on('api:categories.getRecentReplies', function(posts) {
|
||||
if (!posts || posts.length === 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var recent_replies = document.getElementById('category_recent_replies');
|
||||
|
||||
recent_replies.innerHTML = '';
|
||||
|
||||
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);
|
||||
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>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
Category.loadMoreTopics = function(cid) {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:category.loadMore', {
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
}, function(data) {
|
||||
if(data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
cid: cid,
|
||||
after: $('#topics-container').children('.category-item').length
|
||||
}, function (data) {
|
||||
if (data.topics.length) {
|
||||
Category.onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function(ev) {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Category;
|
||||
});
|
||||
13
public/src/forum/favourites.js
Normal file
13
public/src/forum/favourites.js
Normal file
@@ -0,0 +1,13 @@
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountHeader = {};
|
||||
|
||||
AccountHeader.init = function() {
|
||||
header.init();
|
||||
|
||||
$('.user-favourite-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
};
|
||||
|
||||
return AccountHeader;
|
||||
});
|
||||
@@ -1,24 +1,20 @@
|
||||
(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Followers = {};
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followersCount = templates.get('followersCount');
|
||||
Followers.init = function() {
|
||||
header.init();
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if(parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').show();
|
||||
}
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followersCount = templates.get('followersCount');
|
||||
|
||||
}());
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
};
|
||||
|
||||
return Followers;
|
||||
});
|
||||
@@ -1,46 +1,17 @@
|
||||
(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Following = {};
|
||||
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followingCount = templates.get('followingCount');
|
||||
Following.init = function() {
|
||||
header.init();
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
if(parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').show();
|
||||
var followingCount = templates.get('followingCount');
|
||||
|
||||
if (parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
};
|
||||
|
||||
if(yourid !== theirid) {
|
||||
$('.unfollow-btn').hide();
|
||||
}
|
||||
else {
|
||||
$('.unfollow-btn').on('click',function() {
|
||||
var unfollowBtn = $(this);
|
||||
var followingUid = $(this).attr('followingUid');
|
||||
|
||||
socket.emit('api:user.unfollow', {uid: followingUid}, function(success) {
|
||||
var username = unfollowBtn.attr('data-username');
|
||||
if(success) {
|
||||
unfollowBtn.parent().remove();
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
}());
|
||||
return Following;
|
||||
});
|
||||
@@ -1,85 +1,65 @@
|
||||
(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');
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
num_users.innerHTML = "We currently have <b>" + data.count + "</b> registered users.";
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
|
||||
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>.";
|
||||
}
|
||||
});
|
||||
|
||||
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';
|
||||
});
|
||||
|
||||
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.on('api:updateHeader', function(data) {
|
||||
|
||||
var rightMenu = $('#right-menu');
|
||||
if (data.uid > 0) {
|
||||
jQuery('#search-button').on('click', function() {
|
||||
jQuery('#search-fields').removeClass('hide').show();
|
||||
jQuery(this).hide();
|
||||
jQuery('#search-fields input').focus()
|
||||
|
||||
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'])
|
||||
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();
|
||||
|
||||
$('#logged-out-menu').addClass('hide');
|
||||
$('#logged-in-menu').removeClass('hide');
|
||||
|
||||
$('#search-button').show();
|
||||
|
||||
var userLabel = loggedInMenu.find('#user_label');
|
||||
|
||||
if (userLabel.length) {
|
||||
if (data['userslug'])
|
||||
userLabel.find('#user-profile-link').attr('href', '/user/' + 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);
|
||||
|
||||
var logoutli = $('<li><a href="' + RELATIVE_PATH + '/logout">Log out</a></li>');
|
||||
rightMenu.append(logoutli);
|
||||
$('#logout-link').on('click', app.logout);
|
||||
}
|
||||
} else {
|
||||
rightMenu.html('');
|
||||
$('#search-button').hide();
|
||||
|
||||
var registerEl = document.createElement('li'),
|
||||
loginEl = document.createElement('li');
|
||||
jQuery('.nodebb-loggedin').hide();
|
||||
jQuery('.nodebb-loggedout').show();
|
||||
|
||||
registerEl.innerHTML = '<a href="/register">Register</a>';
|
||||
loginEl.innerHTML = '<a href="/login">Login</a>';
|
||||
$('#logged-out-menu').removeClass('hide');
|
||||
$('#logged-in-menu').addClass('hide');
|
||||
|
||||
right_menu.appendChild(registerEl);
|
||||
right_menu.appendChild(loginEl);
|
||||
}
|
||||
|
||||
$('#main-nav a,#user-control-list a,#logged-out-menu .dropdown-menu a').off('click').on('click', function() {
|
||||
if($('.navbar .navbar-collapse').hasClass('in'))
|
||||
$('.navbar-header button').click();
|
||||
});
|
||||
});
|
||||
|
||||
// Notifications dropdown
|
||||
@@ -90,75 +70,99 @@
|
||||
notifTrigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (notifContainer.className.indexOf('open') === -1) {
|
||||
socket.emit('api:notifications.get');
|
||||
socket.emit('api:notifications.mark_all_read', null, function() {
|
||||
notifIcon.className = 'icon-circle-blank';
|
||||
utils.refreshTitle();
|
||||
socket.emit('api:notifications.get', null, function(data) {
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
notifEl = document.createElement('li'),
|
||||
numRead = data.read.length,
|
||||
numUnread = data.unread.length,
|
||||
x;
|
||||
notifList.innerHTML = '';
|
||||
console.log(data);
|
||||
if ((data.read.length + data.unread.length) > 0) {
|
||||
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++) {
|
||||
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>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.className = 'no-notifs';
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
|
||||
// Add dedicated link to /notifications
|
||||
notifEl.removeAttribute('data-nid');
|
||||
notifEl.className = 'pagelink';
|
||||
notifEl.innerHTML = '<a href="' + RELATIVE_PATH + '/notifications">See all Notifications</a>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
|
||||
notifList.appendChild(notifFrag);
|
||||
|
||||
if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
|
||||
else notifIcon.className = 'icon-circle-blank';
|
||||
|
||||
socket.emit('api:notifications.mark_all_read', null, function() {
|
||||
notifIcon.className = 'icon-circle-blank';
|
||||
utils.refreshTitle();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
notifList.addEventListener('click', function(e) {
|
||||
var target;
|
||||
switch(e.target.nodeName) {
|
||||
case 'SPAN': target = e.target.parentNode.parentNode; break;
|
||||
case 'A': target = e.target.parentNode; break;
|
||||
case 'li': target = e.target; break;
|
||||
switch (e.target.nodeName) {
|
||||
case 'SPAN':
|
||||
target = e.target.parentNode.parentNode;
|
||||
break;
|
||||
case 'A':
|
||||
target = e.target.parentNode;
|
||||
break;
|
||||
case 'li':
|
||||
target = e.target;
|
||||
break;
|
||||
}
|
||||
if (target) {
|
||||
var nid = parseInt(target.getAttribute('data-nid'));
|
||||
if (nid > 0) socket.emit('api:notifications.mark_read', nid);
|
||||
}
|
||||
});
|
||||
socket.on('api:notifications.get', function(data) {
|
||||
var notifFrag = document.createDocumentFragment(),
|
||||
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++) {
|
||||
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++) {
|
||||
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>';
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl);
|
||||
}
|
||||
notifList.appendChild(notifFrag);
|
||||
|
||||
if (data.unread.length > 0) notifIcon.className = 'icon-circle active';
|
||||
else notifIcon.className = 'icon-circle-blank';
|
||||
});
|
||||
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: 'warning',
|
||||
timeout: 2000
|
||||
});
|
||||
utils.refreshTitle();
|
||||
});
|
||||
|
||||
|
||||
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);
|
||||
|
||||
chat.appendChatMessage(chatModal, message);
|
||||
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.load(modal.attr('UUID'));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
require(['mobileMenu'], function(mobileMenu) {
|
||||
mobileMenu.init();
|
||||
});
|
||||
|
||||
30
public/src/forum/home.js
Normal file
30
public/src/forum/home.js
Normal file
@@ -0,0 +1,30 @@
|
||||
define(function() {
|
||||
var home = {};
|
||||
|
||||
home.init = function() {
|
||||
|
||||
ajaxify.register_events([
|
||||
'user.count',
|
||||
'post.stats',
|
||||
'api:user.active.get'
|
||||
]);
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
$('#stats_users').html(utils.makeNumberHumanReadable(data.count)).attr('title', data.count);
|
||||
});
|
||||
|
||||
socket.emit('post.stats');
|
||||
socket.on('post.stats', function(data) {
|
||||
$('#stats_topics').html(utils.makeNumberHumanReadable(data.topics)).attr('title', data.topics);
|
||||
$('#stats_posts').html(utils.makeNumberHumanReadable(data.posts)).attr('title', data.posts);
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
$('#stats_online').html(data.users);
|
||||
});
|
||||
}
|
||||
|
||||
return home;
|
||||
});
|
||||
@@ -1,44 +1,69 @@
|
||||
define(function() {
|
||||
var Login = {};
|
||||
|
||||
|
||||
(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;
|
||||
}
|
||||
if (target) {
|
||||
document.location.href = target.getAttribute('data-url');
|
||||
}
|
||||
});
|
||||
|
||||
$('#login').on('click', function() {
|
||||
|
||||
var loginData = {
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'_csrf': $('#csrf-token').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').hide();
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
},
|
||||
error : function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show().delay(1000).fadeOut(250);
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 2000
|
||||
Login.init = 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;
|
||||
}
|
||||
if (target) {
|
||||
document.location.href = target.getAttribute('data-url');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
}());
|
||||
$('#login').on('click', function() {
|
||||
var loginData = {
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'_csrf': $('#csrf-token').val()
|
||||
};
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
|
||||
if(!app.previousUrl) { app.previousUrl = '/'; }
|
||||
|
||||
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) {
|
||||
$('#login-error-notify').show();
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 2000
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
};
|
||||
|
||||
return Login;
|
||||
});
|
||||
|
||||
31
public/src/forum/notifications.js
Normal file
31
public/src/forum/notifications.js
Normal file
@@ -0,0 +1,31 @@
|
||||
define(function() {
|
||||
var Notifications = {};
|
||||
|
||||
Notifications.init = function() {
|
||||
var listEl = $('.notifications-list'),
|
||||
markAllReadEl = document.getElementById('mark-all-notifs-read');
|
||||
|
||||
$('span.timeago').timeago();
|
||||
|
||||
// Allow the user to click anywhere in the LI
|
||||
listEl.on('click', 'li', function(e) {
|
||||
this.querySelector('a').click();
|
||||
});
|
||||
|
||||
// Mark all as read button
|
||||
$(markAllReadEl).click(function() {
|
||||
socket.emit('api:notifications.mark_all_read', {}, function() {
|
||||
ajaxify.go('notifications');
|
||||
app.alert({
|
||||
alert_id: "notifications:mark_all_read",
|
||||
title: "All Notifications Read",
|
||||
message: "Successfully marked all notifications read",
|
||||
type: 'success',
|
||||
timeout: 2500
|
||||
})
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
return Notifications;
|
||||
});
|
||||
@@ -1,80 +1,110 @@
|
||||
(function() {
|
||||
var loadingMoreTopics = false;
|
||||
define(function() {
|
||||
var Recent = {};
|
||||
|
||||
app.enter_room('recent_posts');
|
||||
Recent.newTopicCount = 0;
|
||||
Recent.newPostCount = 0;
|
||||
Recent.loadingMoreTopics = false;
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
var active = '';
|
||||
|
||||
var newTopicCount = 0, newPostCount = 0;
|
||||
Recent.init = function() {
|
||||
app.enter_room('recent_posts');
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
function updateAlertText() {
|
||||
|
||||
function getActiveSection() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
return active;
|
||||
}
|
||||
|
||||
active = getActiveSection();
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
jQuery(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).addClass('hide');
|
||||
});
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++Recent.newTopicCount;
|
||||
Recent.updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++Recent.newPostCount;
|
||||
Recent.updateAlertText();
|
||||
});
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !Recent.loadingMoreTopics) {
|
||||
Recent.loadMoreTopics();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
Recent.updateAlertText = function() {
|
||||
var text = '';
|
||||
|
||||
if(newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if(newTopicCount === 1)
|
||||
|
||||
if (Recent.newTopicCount > 1)
|
||||
text = 'There are ' + Recent.newTopicCount + ' new topics';
|
||||
else if (Recent.newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if(newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if(newPostCount === 1)
|
||||
|
||||
if (Recent.newPostCount > 1)
|
||||
text += ' and ' + Recent.newPostCount + ' new posts.';
|
||||
else if (Recent.newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#new-topics-alert').html(text).fadeIn('slow');
|
||||
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
|
||||
}
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
++newPostCount;
|
||||
updateAlertText();
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
Recent.onTopicsLoaded = function(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();
|
||||
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
}
|
||||
|
||||
function loadMoreTopics() {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreRecentTopics', {after:$('#topics-container').children().length}, function(data) {
|
||||
if(data.topics && data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
Recent.loadMoreTopics = function() {
|
||||
Recent.loadingMoreTopics = true;
|
||||
socket.emit('api:topics.loadMoreRecentTopics', {
|
||||
after: $('#topics-container').children('li').length,
|
||||
term: active
|
||||
}, function(data) {
|
||||
if (data.topics && data.topics.length) {
|
||||
Recent.onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
Recent.loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Recent;
|
||||
});
|
||||
|
||||
@@ -1,157 +1,159 @@
|
||||
(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;
|
||||
define(function() {
|
||||
var Register = {};
|
||||
|
||||
$(username).on('keyup change', function() {
|
||||
usernamevalid = utils.isUserNameValid(username.value);
|
||||
Register.init = function() {
|
||||
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>';
|
||||
|
||||
|
||||
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';
|
||||
} else {
|
||||
socket.emit('user.exists', {username: username.value});
|
||||
}
|
||||
});
|
||||
$('#referrer').val(app.previousUrl);
|
||||
|
||||
$(emailEl).on('keyup change', function() {
|
||||
emailvalid = utils.isEmailValid(email.value);
|
||||
|
||||
if(!emailvalid) {
|
||||
email_notify.innerHTML = 'Invalid email address';
|
||||
email_notify.className = 'label label-important';
|
||||
}
|
||||
else
|
||||
socket.emit('user.email.exists', { email: emailEl.value });
|
||||
});
|
||||
|
||||
$(password).on('keyup', function() {
|
||||
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';
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
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';
|
||||
} else {
|
||||
username_notify.innerHTML = 'OK!';
|
||||
username_notify.className = 'label label-success';
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
emailexists = data.exists;
|
||||
|
||||
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';
|
||||
}
|
||||
});
|
||||
|
||||
// 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');
|
||||
}
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
var validated = true;
|
||||
|
||||
if (username.value.length < 2 || !usernamevalid) {
|
||||
username_notify.innerHTML = 'Invalid username';
|
||||
username_notify.className = 'label label-important';
|
||||
validated = false;
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
}
|
||||
|
||||
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!';
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
if (!emailvalid) {
|
||||
email_notify.innerHTML = 'Invalid email address';
|
||||
validated = false;
|
||||
}
|
||||
|
||||
if(emailexists) {
|
||||
email_notify.innerHTML = 'Email Address exists';
|
||||
validated = false;
|
||||
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()
|
||||
});
|
||||
}
|
||||
|
||||
if(userexists || !passwordsmatch || !passwordvalid)
|
||||
validated = false;
|
||||
emailEl.on('blur', function() {
|
||||
validateEmail();
|
||||
});
|
||||
|
||||
return validated;
|
||||
}
|
||||
|
||||
register.addEventListener('click', function(e) {
|
||||
if (!validateForm()) e.preventDefault();
|
||||
}, false);
|
||||
|
||||
}());
|
||||
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()) || !utils.slugify(username.val())) {
|
||||
showError(username_notify, 'Invalid username!');
|
||||
} else {
|
||||
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();
|
||||
});
|
||||
|
||||
function validatePassword() {
|
||||
if (!password.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
}
|
||||
}
|
||||
|
||||
$(password).on('blur', function() {
|
||||
validatePassword();
|
||||
});
|
||||
|
||||
function validatePasswordConfirm() {
|
||||
if (!password.val() || password_notify.hasClass('alert-error')) {
|
||||
return;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (data.exists === true) {
|
||||
showError(username_notify, 'Username already taken!');
|
||||
} else {
|
||||
showSuccess(username_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(email_notify, 'Email address already taken!');
|
||||
} else {
|
||||
showSuccess(email_notify, successIcon);
|
||||
}
|
||||
});
|
||||
|
||||
// Alternate Logins
|
||||
$('.alt-logins li').on('click', function(e) {
|
||||
document.location.href = $(this).attr('data-url');
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
validationError = false;
|
||||
|
||||
validateEmail();
|
||||
validateUsername();
|
||||
validatePassword();
|
||||
validatePasswordConfirm();
|
||||
|
||||
return validationError;
|
||||
}
|
||||
|
||||
register.on('click', function(e) {
|
||||
if (validateForm()) e.preventDefault();
|
||||
});
|
||||
};
|
||||
|
||||
return Register;
|
||||
});
|
||||
@@ -1,39 +1,47 @@
|
||||
(function() {
|
||||
var inputEl = document.getElementById('email'),
|
||||
errorEl = document.getElementById('error'),
|
||||
errorTextEl = errorEl.querySelector('p');
|
||||
define(function() {
|
||||
var ResetPassword = {};
|
||||
|
||||
document.getElementById('reset').onclick = function() {
|
||||
if (inputEl.value.length > 0 && inputEl.value.indexOf('@') !== -1) {
|
||||
socket.emit('user:reset.send', { email: inputEl.value });
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
errorTextEl.innerHTML = 'Please enter a valid email';
|
||||
}
|
||||
ResetPassword.init = function() {
|
||||
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
|
||||
});
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
errorTextEl.innerHTML = 'Please enter a valid email';
|
||||
}
|
||||
};
|
||||
|
||||
ajaxify.register_events(['user.send_reset']);
|
||||
|
||||
socket.on('user.send_reset', function(data) {
|
||||
var submitEl = document.getElementById('reset');
|
||||
|
||||
if (data.status === 'ok') {
|
||||
jQuery('#error').hide();
|
||||
jQuery('#success').show();
|
||||
jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
|
||||
inputEl.value = '';
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
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;
|
||||
case 'send-failed':
|
||||
errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
ajaxify.register_events(['user.send_reset']);
|
||||
|
||||
socket.on('user.send_reset', function(data) {
|
||||
var submitEl = document.getElementById('reset');
|
||||
|
||||
if (data.status === 'ok') {
|
||||
jQuery('#error').hide();
|
||||
jQuery('#success').show();
|
||||
jQuery('#success p').html('An email has been dispatched to "' + data.email + '" with instructions on setting a new password.');
|
||||
inputEl.value = '';
|
||||
} else {
|
||||
jQuery('#success').hide();
|
||||
jQuery(errorEl).show();
|
||||
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;
|
||||
case 'send-failed':
|
||||
errorTextEl.innerHTML = 'There was a problem sending the reset code. Please try again later.';
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
}());
|
||||
return ResetPassword;
|
||||
});
|
||||
@@ -1,42 +1,58 @@
|
||||
(function() {
|
||||
var reset_code = templates.get('reset_code');
|
||||
define(function() {
|
||||
var ResetCode = {};
|
||||
|
||||
var resetEl = document.getElementById('reset'),
|
||||
password = document.getElementById('password'),
|
||||
repeat = document.getElementById('repeat'),
|
||||
noticeEl = document.getElementById('notice');
|
||||
ResetCode.init = function() {
|
||||
var reset_code = templates.get('reset_code');
|
||||
|
||||
resetEl.addEventListener('click', function() {
|
||||
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.style.display = 'block';
|
||||
} else if (password.value === repeat.value) {
|
||||
socket.emit('user:reset.commit', { code: reset_code, password: password.value });
|
||||
}
|
||||
}, false);
|
||||
var resetEl = document.getElementById('reset'),
|
||||
password = document.getElementById('password'),
|
||||
repeat = document.getElementById('repeat'),
|
||||
noticeEl = document.getElementById('notice');
|
||||
|
||||
// Enable the form if the code is valid
|
||||
socket.emit('user:reset.valid', { code: reset_code });
|
||||
resetEl.addEventListener('click', function() {
|
||||
if (password.value.length < 6) {
|
||||
$('#error').hide();
|
||||
noticeEl.querySelector('strong').innerHTML = 'Invalid 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) {
|
||||
$('#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
|
||||
});
|
||||
|
||||
|
||||
ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
|
||||
socket.on('user:reset.valid', function(data) {
|
||||
if (!!data.valid) resetEl.disabled = false;
|
||||
else {
|
||||
var formEl = document.getElementById('reset-form');
|
||||
// Show error message
|
||||
$('#error').show();
|
||||
formEl.parentNode.removeChild(formEl);
|
||||
}
|
||||
})
|
||||
ajaxify.register_events(['user:reset.valid', 'user:reset.commit']);
|
||||
socket.on('user:reset.valid', function(data) {
|
||||
if ( !! data.valid) resetEl.disabled = false;
|
||||
else {
|
||||
var formEl = document.getElementById('reset-form');
|
||||
// Show error message
|
||||
$('#error').show();
|
||||
formEl.parentNode.removeChild(formEl);
|
||||
}
|
||||
})
|
||||
|
||||
socket.on('user:reset.commit', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
$('#error').hide();
|
||||
$('#notice').hide();
|
||||
$('#success').show();
|
||||
}
|
||||
});
|
||||
}());
|
||||
socket.on('user:reset.commit', function(data) {
|
||||
if (data.status === 'ok') {
|
||||
$('#error').hide();
|
||||
$('#notice').hide();
|
||||
$('#success').show();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
return ResetCode;
|
||||
});
|
||||
@@ -1,14 +1,26 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Search = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init = 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;
|
||||
});
|
||||
};
|
||||
|
||||
return Search;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,102 +1,121 @@
|
||||
(function() {
|
||||
var loadingMoreTopics = false;
|
||||
define(function() {
|
||||
var Unread = {};
|
||||
|
||||
app.enter_room('recent_posts');
|
||||
Unread.init = function() {
|
||||
var loadingMoreTopics = false;
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
app.enter_room('recent_posts');
|
||||
|
||||
var newTopicCount = 0, newPostCount = 0;
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if(newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if(newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if(newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if(newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#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) {
|
||||
btn.remove();
|
||||
$('#topics-container').empty();
|
||||
$('#category-no-topics').removeClass('hidden');
|
||||
app.alertSuccess('All topics marked as read!');
|
||||
} else {
|
||||
app.alertError('There was an error marking topics read!');
|
||||
}
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).addClass('hide');
|
||||
});
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
var html = templates.prepare(templates['unread'].blocks['topics']).parse({ topics: topics }),
|
||||
container = $('#topics-container');
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
}
|
||||
|
||||
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) {
|
||||
onTopicsLoaded(data.topics);
|
||||
$('#topics-container').attr('data-next-start', data.nextStart);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if (newTopicCount > 1)
|
||||
text = 'There are ' + newTopicCount + ' new topics';
|
||||
else if (newTopicCount === 1)
|
||||
text = 'There is 1 new topic';
|
||||
else
|
||||
text = 'There are no new topics';
|
||||
|
||||
if (newPostCount > 1)
|
||||
text += ' and ' + newPostCount + ' new posts.';
|
||||
else if (newPostCount === 1)
|
||||
text += ' and 1 new post.';
|
||||
else
|
||||
text += ' and no new posts.';
|
||||
|
||||
text += ' Click here to reload.';
|
||||
|
||||
$('#new-topics-alert').html(text).removeClass('hide').fadeIn('slow');
|
||||
$('#category-no-topics').addClass('hidden');
|
||||
}
|
||||
});
|
||||
|
||||
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) {
|
||||
btn.remove();
|
||||
$('#topics-container').empty();
|
||||
$('#category-no-topics').removeClass('hidden');
|
||||
app.alertSuccess('All topics marked as read!');
|
||||
$('#numUnreadBadge')
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
.html('0');
|
||||
} else {
|
||||
app.alertError('There was an error marking topics read!');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
|
||||
var html = templates.prepare(templates['unread'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
container = $('#topics-container');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
}
|
||||
|
||||
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) {
|
||||
onTopicsLoaded(data.topics);
|
||||
$('#topics-container').attr('data-next-start', data.nextStart);
|
||||
} else {
|
||||
$('#load-more-btn').hide();
|
||||
}
|
||||
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(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)
|
||||
$('#load-more-btn').show();
|
||||
if ($("body").height() <= $(window).height() && $('#topics-container').children().length >= 20)
|
||||
$('#load-more-btn').show();
|
||||
|
||||
$('#load-more-btn').on('click', function() {
|
||||
loadMoreTopics();
|
||||
});
|
||||
$('#load-more-btn').on('click', function() {
|
||||
loadMoreTopics();
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
return Unread;
|
||||
});
|
||||
@@ -1,12 +1,22 @@
|
||||
(function() {
|
||||
|
||||
$(document).ready(function() {
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
Users.init = function() {
|
||||
var timeoutId = 0;
|
||||
var loadingMoreUsers = false;
|
||||
|
||||
var url = window.location.href,
|
||||
|
||||
function getActiveSection() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length-1];
|
||||
active = parts[parts.length - 1];
|
||||
return active;
|
||||
}
|
||||
|
||||
var active = getActiveSection();
|
||||
|
||||
var lastSearch = null;
|
||||
|
||||
app.addCommasToNumbers();
|
||||
|
||||
jQuery('.nav-pills li').removeClass('active');
|
||||
jQuery('.nav-pills li a').each(function() {
|
||||
@@ -15,106 +25,129 @@
|
||||
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 = $('#users-container');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
userListEl.html(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');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$('.reputation').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
$('.postcount').each(function(index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
|
||||
function onUsersLoaded(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') {
|
||||
set = 'users:joindate';
|
||||
} else if(active === 'users-sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if(active === 'users-sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
}
|
||||
|
||||
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);
|
||||
socket.on('api:user.isOnline', function(data) {
|
||||
if(getActiveSection() == 'online' && !loadingMoreUsers) {
|
||||
startLoading('users:online', 0, true);
|
||||
socket.emit('api:user.getOnlineAnonCount', {} , function(anonCount) {
|
||||
if(parseInt(anonCount, 10) > 0) {
|
||||
$('#users-container .anon-user').removeClass('hide');
|
||||
$('#online_anon_count').html(anonCount);
|
||||
} else {
|
||||
$('#load-more-users-btn').addClass('disabled');
|
||||
$('#users-container .anon-user').addClass('hide');
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = (document.body.offsetHeight - $(window).height()) * 0.9;
|
||||
});
|
||||
|
||||
if (document.body.scrollTop > bottom && !loadingMoreUsers) {
|
||||
function onUsersLoaded(users, emptyContainer) {
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({
|
||||
users: users
|
||||
});
|
||||
if(emptyContainer)
|
||||
$('#users-container .registered-user').remove();
|
||||
$('#users-container').append(html);
|
||||
$('#users-container .anon-user').appendTo($('#users-container'));
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if (active === 'latest' || active === 'users') {
|
||||
set = 'users:joindate';
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if (active === 'sort-reputation') {
|
||||
set = 'users:reputation';
|
||||
} else if (active === 'online') {
|
||||
set = 'users:online';
|
||||
}
|
||||
|
||||
if (set) {
|
||||
startLoading(set, $('#users-container').children('.registered-user').length);
|
||||
}
|
||||
}
|
||||
|
||||
function startLoading(set, after, emptyContainer) {
|
||||
loadingMoreUsers = true;
|
||||
socket.emit('api:users.loadMore', {
|
||||
set: set,
|
||||
after: after
|
||||
}, function(data) {
|
||||
if (data.users.length) {
|
||||
onUsersLoaded(data.users, emptyContainer);
|
||||
$('#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;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
return Users;
|
||||
});
|
||||
@@ -1,8 +1,7 @@
|
||||
define(['taskbar'], function(taskbar) {
|
||||
|
||||
|
||||
var module = {};
|
||||
|
||||
|
||||
module.bringModalToTop = function(chatModal) {
|
||||
var topZ = 0;
|
||||
$('.modal').each(function() {
|
||||
@@ -11,38 +10,85 @@ define(['taskbar'], function(taskbar) {
|
||||
topZ = thisZ;
|
||||
}
|
||||
});
|
||||
chatModal.css('zIndex', topZ+1);
|
||||
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.online = data.online;
|
||||
}
|
||||
if(callback)
|
||||
callback(data.online);
|
||||
});
|
||||
}
|
||||
|
||||
chatModal.find('.close').on('click',function(e){
|
||||
chatModal.hide();
|
||||
taskbar.discard('chat', uuid);
|
||||
});
|
||||
|
||||
chatModal.on('click', function(e) {
|
||||
module.bringModalToTop(chatModal);
|
||||
});
|
||||
|
||||
addSendHandler(chatModal, touid);
|
||||
function checkOnlineStatus(chatModal) {
|
||||
if(chatModal.intervalId === 0) {
|
||||
chatModal.intervalId = setInterval(function() {
|
||||
checkStatus(chatModal);
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {title:'chat with '+username});
|
||||
module.createModal = function(username, touid, callback) {
|
||||
|
||||
var chatModal = $('#chat-modal').clone(),
|
||||
uuid = utils.generateUUID();
|
||||
|
||||
chatModal.intervalId = 0;
|
||||
chatModal.touid = touid;
|
||||
chatModal.username = username;
|
||||
|
||||
chatModal.attr('id', 'chat-modal-' + touid);
|
||||
chatModal.attr('UUID', uuid);
|
||||
chatModal.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;
|
||||
}
|
||||
|
||||
module.center = function(chatModal) {
|
||||
chatModal.css("position", "fixed");
|
||||
chatModal.css("top", "100px");
|
||||
chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
@@ -50,43 +96,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 addSendHandler(chatModal, touid) {
|
||||
function getChatMessages(chatModal, callback) {
|
||||
socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) {
|
||||
for(var i = 0; i<messages.length; ++i) {
|
||||
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
|
||||
}
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
function addSendHandler(chatModal) {
|
||||
chatModal.find('#chat-message-input').off('keypress');
|
||||
chatModal.find('#chat-message-input').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,21 +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,
|
||||
};
|
||||
|
||||
var uploadsInProgress = [];
|
||||
|
||||
function createImagePlaceholder(img) {
|
||||
var text = $('.post-window textarea').val(),
|
||||
textarea = $('.post-window textarea'),
|
||||
imgText = "";
|
||||
|
||||
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();
|
||||
});
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
var reader = new FileReader();
|
||||
var dropDiv = $('#imagedrop');
|
||||
var imagelist = $('#imagelist');
|
||||
var uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
|
||||
var posts = composer.posts[uuid];
|
||||
|
||||
var reader = new FileReader(),
|
||||
dropDiv = $('.post-window .imagedrop'),
|
||||
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 = {
|
||||
@@ -23,60 +44,63 @@ define(['taskbar'], function(taskbar) {
|
||||
data: bin
|
||||
};
|
||||
|
||||
posts.images.push(img);
|
||||
createImagePlaceholder(img);
|
||||
|
||||
var imageLabel = $('<div class="label"><span>'+ file.name +'</span></div>');
|
||||
var closeButton = $('<button class="close">×</button>');
|
||||
closeButton.on('click', function(e) {
|
||||
|
||||
imageLabel.remove();
|
||||
var index = posts.images.indexOf(img);
|
||||
if(index !== -1) {
|
||||
posts.images.splice(index, 1);
|
||||
}
|
||||
});
|
||||
|
||||
imageLabel.append(closeButton);
|
||||
imagelist.append(imageLabel);
|
||||
dropDiv.hide();
|
||||
});
|
||||
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
function initializeFileReader() {
|
||||
jQuery.event.props.push( "dataTransfer" );
|
||||
|
||||
if(window.FileReader) {
|
||||
var drop = $('#imagedrop');
|
||||
|
||||
$(composer.postContainer).on('dragenter dragover', function() {
|
||||
drop.show();
|
||||
var draggingDocument = false;
|
||||
|
||||
if(window.FileReader) {
|
||||
var drop = $('.post-window .imagedrop'),
|
||||
textarea = $('.post-window textarea');
|
||||
|
||||
$(document).on('dragstart', function(e) {
|
||||
draggingDocument = true;
|
||||
}).on('dragend', function(e) {
|
||||
draggingDocument = false;
|
||||
});
|
||||
|
||||
|
||||
textarea.on('dragenter', function(e) {
|
||||
if(draggingDocument)
|
||||
return;
|
||||
drop.css('top', textarea.position().top + 'px');
|
||||
drop.show();
|
||||
|
||||
drop.on('dragleave', function(ev) {
|
||||
drop.hide();
|
||||
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');
|
||||
var posts = composer.posts[uuid];
|
||||
|
||||
var dt = e.dataTransfer;
|
||||
var files = dt.files;
|
||||
|
||||
var uuid = drop.parents('[data-uuid]').attr('data-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;
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,25 +109,22 @@ 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">' +
|
||||
'<div class="btn-group formatting-bar">' +
|
||||
'<div class="btn-toolbar formatting-bar">' +
|
||||
'<div class="btn-group">' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-bold"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-italic"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-list"></i></span>' +
|
||||
'<span class="btn btn-link" tabindex="-1"><i class="icon-link"></i></span>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<div style="position:relative;">'+
|
||||
'<div id="imagedrop" class=""><div>Drag and Drop Images Here</div></div>'+
|
||||
'<textarea tabIndex="2"></textarea>' +
|
||||
'<div id="imagelist"></div>'+
|
||||
'</div>'+
|
||||
'<div class="btn-toolbar">' +
|
||||
'<div class="btn-group action-bar" style="float: right; margin-right: -8px">' +
|
||||
'<button data-action="minimize" class="btn hidden-phone" tabIndex="4"><i class="icon-download-alt"></i> Minimize</button>' +
|
||||
'<textarea tabIndex="2"></textarea>' +
|
||||
'<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-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>' +
|
||||
@@ -112,7 +133,8 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
document.body.insertBefore(composer.postContainer, taskbar);
|
||||
|
||||
initializeFileReader();
|
||||
if(config.imgurClientIDSet)
|
||||
initializeFileReader();
|
||||
|
||||
socket.on('api:composer.push', function(threadData) {
|
||||
if (!threadData.error) {
|
||||
@@ -129,12 +151,12 @@ define(['taskbar'], function(taskbar) {
|
||||
pid: threadData.pid,
|
||||
title: threadData.title || '',
|
||||
body: threadData.body || '',
|
||||
images: []
|
||||
modified: false
|
||||
};
|
||||
composer.load(uuid);
|
||||
} else {
|
||||
app.alert({
|
||||
type: 'error',
|
||||
type: 'danger',
|
||||
timeout: 5000,
|
||||
alert_id: 'post_error',
|
||||
title: 'Please Log In to Post',
|
||||
@@ -152,21 +174,35 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
// Post Window events
|
||||
var jPostContainer = $(composer.postContainer),
|
||||
postContentEl = composer.postContainer.querySelector('textarea')
|
||||
postContentEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
jPostContainer.on('change', 'input, textarea', function() {
|
||||
var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
|
||||
if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
|
||||
else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
|
||||
|
||||
// Mark this post window as having been changed
|
||||
composer.posts[uuid].modified = true;
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.action-bar button', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
uuid = $(this).parents('.post-window').attr('data-uuid');
|
||||
switch(action) {
|
||||
case 'post': composer.post(uuid); break;
|
||||
case 'minimize': composer.minimize(uuid); break;
|
||||
case 'discard': composer.discard(uuid); break;
|
||||
case 'discard':
|
||||
if (composer.posts[uuid].modified) {
|
||||
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
|
||||
if (discard) composer.discard(uuid);
|
||||
});
|
||||
} else {
|
||||
composer.discard(uuid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.formatting-bar span', function() {
|
||||
var iconClass = this.querySelector('i').className,
|
||||
cursorEnd = postContentEl.value.length,
|
||||
@@ -242,16 +278,11 @@ define(['taskbar'], function(taskbar) {
|
||||
composer.load = function(post_uuid) {
|
||||
var post_data = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
bodyEl = composer.postContainer.querySelector('textarea'),
|
||||
dropDiv = $(composer.postContainer).find('#imagedrop'),
|
||||
imagelist = $(composer.postContainer).find('#imagelist');
|
||||
|
||||
dropDiv.hide();
|
||||
imagelist.empty();
|
||||
bodyEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
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;
|
||||
@@ -264,7 +295,8 @@ define(['taskbar'], function(taskbar) {
|
||||
titleEl.value = post_data.title;
|
||||
titleEl.readOnly = false;
|
||||
}
|
||||
bodyEl.value = post_data.body
|
||||
bodyEl.value = post_data.body;
|
||||
|
||||
|
||||
// Direct user focus to the correct element
|
||||
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
|
||||
@@ -277,7 +309,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(),
|
||||
@@ -292,16 +324,26 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
composer.post = function(post_uuid) {
|
||||
// Check title and post length
|
||||
var postData = composer.posts[post_uuid],
|
||||
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.",
|
||||
@@ -311,7 +353,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.",
|
||||
@@ -324,21 +366,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
|
||||
});
|
||||
}
|
||||
|
||||
@@ -347,8 +386,9 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
composer.discard = function(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$(composer.postContainer).find('#imagedrop').html('');
|
||||
$(composer.postContainer).find('.imagedrop').hide();
|
||||
delete composer.posts[post_uuid];
|
||||
uploadsInProgress.length = 0;
|
||||
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="#">' +
|
||||
|
||||
83
public/src/modules/uploader.js
Normal file
83
public/src/modules/uploader.js
Normal file
@@ -0,0 +1,83 @@
|
||||
define(function() {
|
||||
|
||||
var module = {};
|
||||
|
||||
module.open = function(route, callback) {
|
||||
$('#upload-picture-modal').modal('show').removeClass('hide');
|
||||
module.hideAlerts();
|
||||
|
||||
$('#uploadForm')[0].reset();
|
||||
$('#uploadForm').attr('action', route);
|
||||
|
||||
$('#pictureUploadSubmitBtn').off('click').on('click', function() {
|
||||
$('#uploadForm').submit();
|
||||
});
|
||||
|
||||
$('#uploadForm').off('submit').submit(function() {
|
||||
|
||||
function status(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-status').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function success(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-success').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
module.hideAlerts();
|
||||
$('#alert-error').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
status('uploading the file ...');
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show().removeClass('hide');
|
||||
|
||||
if (!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
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 + '%');
|
||||
},
|
||||
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
}
|
||||
callback(response.path);
|
||||
|
||||
success('File uploaded successfully!');
|
||||
setTimeout(function() {
|
||||
module.hideAlerts();
|
||||
$('#upload-picture-modal').modal('hide');
|
||||
}, 750);
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
module.hideAlerts = function() {
|
||||
$('#alert-status').addClass('hide');
|
||||
$('#alert-success').addClass('hide');
|
||||
$('#alert-error').addClass('hide');
|
||||
$('#upload-progress-box').addClass('hide');
|
||||
}
|
||||
|
||||
return module;
|
||||
});
|
||||
@@ -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,183 +48,185 @@
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
function loadTemplates(templatesToLoad, customTemplateDir) {
|
||||
function loadServer() {
|
||||
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() {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
function getTemplates(directory) {
|
||||
for (var t in templatesToLoad) {
|
||||
(function (file) {
|
||||
fs.readFile(directory + '/' + file + '.tpl', function (err, html) {
|
||||
var template = function () {
|
||||
this.toString = function () {
|
||||
return this.html;
|
||||
};
|
||||
}
|
||||
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
|
||||
global.templates[file] = new template;
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
|
||||
loaded--;
|
||||
if (loaded == 0) templates.ready();
|
||||
});
|
||||
}(templatesToLoad[t]));
|
||||
global.templates[file] = new template;
|
||||
|
||||
loaded--;
|
||||
if (loaded == 0) templates.ready();
|
||||
});
|
||||
}(templatesToLoad[t]));
|
||||
}
|
||||
}
|
||||
if (customTemplateDir) {
|
||||
fs.exists(customTemplateDir, function (exists) {
|
||||
var directory = (exists ? customTemplateDir : __dirname + '/../templates');
|
||||
getTemplates(directory);
|
||||
});
|
||||
} else {
|
||||
getTemplates(__dirname + '/../templates');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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) {
|
||||
loadTemplates(templates_to_load || []);
|
||||
templates.init = function (templates_to_load, custom_templates) {
|
||||
loadTemplates(templates_to_load || [], custom_templates || false);
|
||||
}
|
||||
|
||||
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) {
|
||||
if(data && data.status == 404) {
|
||||
ajaxify.go('404');
|
||||
return;
|
||||
}
|
||||
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'] = global.nconf.get('relative_path');
|
||||
else
|
||||
|
||||
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')) {
|
||||
case 'boolean':
|
||||
value = (element.value === 'true' || element.value === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt(element.value);
|
||||
break;
|
||||
default:
|
||||
value = element.value;
|
||||
break;
|
||||
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
|
||||
|
||||
$('#content').html(translatedTemplate);
|
||||
|
||||
jQuery('#content [template-variable]').each(function (index, element) {
|
||||
var value = null;
|
||||
|
||||
switch ($(element).attr('template-type')) {
|
||||
case 'boolean':
|
||||
value = ($(element).val() === 'true' || $(element).val() === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt($(element).val());
|
||||
break;
|
||||
default:
|
||||
value = $(element).val();
|
||||
break;
|
||||
}
|
||||
|
||||
templates.set($(element).attr('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 +235,15 @@
|
||||
}
|
||||
|
||||
function makeRegex(block) {
|
||||
return new RegExp("<!-- BEGIN " + block + " -->[^]*<!-- END " + block + " -->", 'g');
|
||||
return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
|
||||
}
|
||||
|
||||
function makeConditionalRegex(block) {
|
||||
return new RegExp("<!-- IF " + block + " -->[\\s\\S]*<!-- ENDIF " + 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];
|
||||
@@ -251,11 +255,25 @@
|
||||
return data;
|
||||
}
|
||||
|
||||
function getConditionalBlock(regex, block, template) {
|
||||
data = template.match(regex);
|
||||
if (data == null) return;
|
||||
|
||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
||||
|
||||
data = data[0]
|
||||
.replace("<!-- IF " + block + " -->", "")
|
||||
.replace("<!-- ENDIF " + block + " -->", "");
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
function setBlock(regex, block, template) {
|
||||
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) {
|
||||
@@ -264,20 +282,24 @@
|
||||
|
||||
for (var d in data) {
|
||||
if (data.hasOwnProperty(d)) {
|
||||
if (data[d] === null) {
|
||||
if (typeof data[d] === 'undefined') {
|
||||
continue;
|
||||
} else if (data[d] === null) {
|
||||
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 +309,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;
|
||||
@@ -295,18 +317,26 @@
|
||||
block = parse(data[d], namespace, block);
|
||||
template = setBlock(regex, block, template);
|
||||
} else {
|
||||
var conditional = makeConditionalRegex(d),
|
||||
block = getConditionalBlock(conditional, namespace, template);
|
||||
|
||||
if (block && !data[d]) {
|
||||
template = template.replace(conditional, '');
|
||||
}
|
||||
|
||||
|
||||
template = replace(namespace + d, data[d], template);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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 +345,8 @@
|
||||
templates.init();
|
||||
}
|
||||
|
||||
})('undefined' === typeof module ? {module:{exports:{}}} : module)
|
||||
|
||||
|
||||
|
||||
})('undefined' === typeof module ? {
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
|
||||
180
public/src/translator.js
Normal file
180
public/src/translator.js
Normal file
@@ -0,0 +1,180 @@
|
||||
(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];
|
||||
});
|
||||
};
|
||||
|
||||
translator.mget = function (keys, callback) {
|
||||
|
||||
var async = require('async');
|
||||
|
||||
function getKey(key, callback) {
|
||||
translator.get(key, function(value) {
|
||||
callback(null, value);
|
||||
});
|
||||
}
|
||||
|
||||
async.map(keys, getKey, callback);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* 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(':');
|
||||
if (!(parsedKey[0] && parsedKey[1])) continue;
|
||||
|
||||
var 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, languageFile, variables) {
|
||||
translator.load(languageFile, function (languageData) {
|
||||
data = insertLanguage(data, languageKey, languageData[parsedKey], variables);
|
||||
loading--;
|
||||
checkComplete();
|
||||
});
|
||||
}(keys[key], parsedKey, languageFile, variables));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,69 +1,91 @@
|
||||
(function (module) {
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var utils, fs;
|
||||
|
||||
try {
|
||||
if ('undefined' === typeof window) {
|
||||
fs = require('fs');
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
if (err) {
|
||||
return done(err);
|
||||
}
|
||||
var pending = list.length;
|
||||
if (!pending) return done(null, results);
|
||||
if (!pending) {
|
||||
return done(null, results);
|
||||
}
|
||||
list.forEach(function(file) {
|
||||
file = dir + '/' + file;
|
||||
fs.stat(file, function(err, stat) {
|
||||
if (stat && stat.isDirectory()) {
|
||||
utils.walk(file, function(err, res) {
|
||||
results = results.concat(res);
|
||||
if (!--pending) done(null, results);
|
||||
if (!--pending) {
|
||||
done(null, results);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
results.push(file.replace(main_dir, '').replace('.tpl', ''));
|
||||
if (!--pending) done(null, results);
|
||||
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' : '');
|
||||
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 24) return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 60) {
|
||||
return difference + (min ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 24) {
|
||||
return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 24);
|
||||
if (difference < 30) return difference + (min ? 'd' : ' day') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 30) {
|
||||
return difference + (min ? 'd' : ' day') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 30);
|
||||
if (difference < 12) return difference + (min ? 'mon' : ' month') + (difference !== 1 && !min ? 's' : '');
|
||||
if (difference < 12) {
|
||||
return difference + (min ? 'mon' : ' month') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
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 +93,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 +113,34 @@
|
||||
},
|
||||
|
||||
isUserNameValid: function(name) {
|
||||
return (name && name !== "" && (/^[a-zA-Z0-9 _-]{3,14}$/.test(name)));
|
||||
return (name && name !== "" && (/^['"\s\-.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.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++) {
|
||||
if (tags.length > 0) tags += "\n\t";
|
||||
for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
|
||||
if (tags.length > 0) {
|
||||
tags += "\n\t";
|
||||
}
|
||||
tag = '<meta';
|
||||
for(y in tagsArr[x]) {
|
||||
var y;
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
tag += ' />';
|
||||
@@ -126,36 +151,86 @@
|
||||
return tags;
|
||||
},
|
||||
|
||||
refreshTitle: function() {
|
||||
var a = document.createElement('a');
|
||||
buildLinkTags: function(tagsArr) {
|
||||
var tags = '',
|
||||
tag;
|
||||
for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
|
||||
if (tags.length > 0) tags += "\n\t";
|
||||
tag = '<link';
|
||||
var y;
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
tag += ' />';
|
||||
|
||||
a.href = document.location;
|
||||
socket.emit('api:meta.buildTitle', a.pathname.slice(1), function(title) {
|
||||
document.title = title;
|
||||
tags += tag;
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
|
||||
refreshTitle: function(url) {
|
||||
if (!url) {
|
||||
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;
|
||||
notificationIcon = notificationIcon || document.querySelector('.notifications a i');
|
||||
if (numNotifications > 0 && notificationIcon) {
|
||||
notificationIcon.className = 'icon-circle active';
|
||||
}
|
||||
});
|
||||
|
||||
jQuery.getJSON(RELATIVE_PATH + '/api/unread/total', function(data) {
|
||||
var badge = jQuery('#numUnreadBadge');
|
||||
badge.html(data.count > 20 ? '20+' : data.count);
|
||||
|
||||
if (data.count > 0) {
|
||||
badge
|
||||
.removeClass('badge-inverse')
|
||||
.addClass('badge-important');
|
||||
} else {
|
||||
badge
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
isRelativeUrl: function(url) {
|
||||
var firstChar = url.slice(0, 1);
|
||||
return (firstChar === '.' || firstChar === '/');
|
||||
},
|
||||
|
||||
makeNumberHumanReadable: function(num) {
|
||||
num = parseInt(num, 10);
|
||||
if (num > 999999) {
|
||||
return (num / 1000000).toFixed(1) + 'm';
|
||||
}
|
||||
else if(num > 999) {
|
||||
return (num / 1000).toFixed(1) + 'k';
|
||||
}
|
||||
return num;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
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,' ');};
|
||||
}
|
||||
|
||||
|
||||
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,79 +1,76 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div class="well account">
|
||||
|
||||
|
||||
<div class="account-username-box">
|
||||
<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 class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="row-fluid">
|
||||
<div class="span2" 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-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">
|
||||
<h4>profile</h4>
|
||||
|
||||
<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>
|
||||
<span>{email}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">full name</span>
|
||||
<span>{fullname}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">website</span>
|
||||
<span><a href="{website}">{website}</a></span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">location</span>
|
||||
<span>{location}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">age</span>
|
||||
<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>
|
||||
<span class="formatted-number">{profileviews}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">reputation</span>
|
||||
<span id='reputation'>{reputation}</span>
|
||||
<br/>
|
||||
|
||||
<span class="formatted-number">{reputation}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">posts</span>
|
||||
<span id='postcount'>{postcount}</span>
|
||||
<span class="formatted-number">{postcount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">followers</span>
|
||||
<span>{followerCount}</span>
|
||||
<span class="formatted-number">{followerCount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<span class="account-bio-label">following</span>
|
||||
<span>{followingCount}</span>
|
||||
<span class="formatted-number">{followingCount}</span>
|
||||
<br/>
|
||||
|
||||
|
||||
<hr/>
|
||||
<span class="account-bio-label">signature</span>
|
||||
<div class="post-signature">
|
||||
@@ -82,14 +79,13 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="span6 user-recent-posts">
|
||||
<h4>recent posts </h4>
|
||||
|
||||
<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>
|
||||
</div>
|
||||
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
|
||||
</div>
|
||||
<!-- END posts -->
|
||||
</div>
|
||||
</div>
|
||||
@@ -101,7 +97,4 @@
|
||||
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/account.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
<input type="hidden" template-type="boolean" template-variable="isFollowing" value="{isFollowing}" />
|
||||
@@ -1,174 +1,144 @@
|
||||
|
||||
<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 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>
|
||||
|
||||
<!-- 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">
|
||||
<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 class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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}" />
|
||||
<input type="hidden" template-variable="uploadedpicture" value="{uploadedpicture}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script>
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div class="account-username-box">
|
||||
|
||||
<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 class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div 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>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountsettings.js"></script>
|
||||
File diff suppressed because one or more lines are too long
@@ -2,20 +2,22 @@
|
||||
<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();
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -1,12 +1,50 @@
|
||||
|
||||
</div>
|
||||
</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="" 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 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 -->
|
||||
|
||||
<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>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript(RELATIVE_PATH + '/src/forum/admin/footer.js');
|
||||
require(['forum/admin/footer']);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
@@ -2,20 +2,22 @@
|
||||
<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();
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
98
public/templates/admin/groups.tpl
Normal file
98
public/templates/admin/groups.tpl
Normal file
@@ -0,0 +1,98 @@
|
||||
<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>
|
||||
@@ -7,7 +7,6 @@
|
||||
var RELATIVE_PATH = "{relative_path}";
|
||||
</script>
|
||||
<link id="base-theme" href="{relative_path}/vendor/bootstrap/css/bootstrap.min.css" 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 type="text/javascript" src="http://code.jquery.com/jquery.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/vendor/bootstrap/js/bootstrap.min.js"></script>
|
||||
@@ -16,88 +15,133 @@
|
||||
<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/jquery/js/jquery.form.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",
|
||||
waitSeconds: 3
|
||||
waitSeconds: 3,
|
||||
paths: {
|
||||
"forum": '../forum'
|
||||
}
|
||||
});
|
||||
</script>
|
||||
<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>
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/style.css" />
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/admin.css" />
|
||||
<script src="{relative_path}/src/utils.js"></script>
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="{relative_path}/css/theme.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="navbar navbar-inverse navbar-fixed-top header">
|
||||
<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="/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="/" target="_top"><i class="icon-book"></i> Forum</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="#" id="reconnect"></a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ul id="logged-in-menu" class="nav navbar-nav navbar-right">
|
||||
<li id="user_label" class="dropdown">
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#" id="user_dropdown">
|
||||
<img src="{userpicture}"/>
|
||||
</a>
|
||||
<ul id="user-control-list" class="dropdown-menu" aria-labelledby="user_dropdown">
|
||||
<li>
|
||||
<a id="user-profile-link" href="/user/{userslug}" target="_top"><span>Profile</span></a>
|
||||
</li>
|
||||
<li id="logout-link">
|
||||
<a href="#">Log out</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="alert_window"></div>
|
||||
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span3">
|
||||
<input id="csrf_token" type="hidden" template-variable="csrf" value="{csrf}" />
|
||||
|
||||
<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/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>
|
||||
<li class=''><a href='{relative_path}/admin/settings'><i class='icon-cogs'></i> Settings</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/redis'><i class='icon-hdd'></i> Redis</a></li>
|
||||
<li class=''><a href="{relative_path}/admin/motd"><i class="icon-comment"></i> MOTD</a></li>
|
||||
|
||||
<li class='active'>
|
||||
<a href='{relative_path}/admin/index'><i class='icon-home'></i> Home</a>
|
||||
</li>
|
||||
<li><a href='{relative_path}/admin/categories/active'><i class='icon-folder-close-alt'></i> Categories</a></li>
|
||||
<li><a href='{relative_path}/admin/users/latest'><i class='icon-user'></i> Users</a></li>
|
||||
<li><a href="{relative_path}/admin/groups"><i class="icon-group"></i> Groups</a></li>
|
||||
<li><a href='{relative_path}/admin/topics'><i class='icon-book'></i> Topics</a></li>
|
||||
<li><a href='{relative_path}/admin/themes'><i class='icon-th'></i> Themes</a></li>
|
||||
<li><a href='{relative_path}/admin/plugins'><i class='icon-code-fork'></i> Plugins</a></li>
|
||||
<li><a href='{relative_path}/admin/settings'><i class='icon-cogs'></i> Settings</a></li>
|
||||
<li><a href='{relative_path}/admin/redis'><i class='icon-hdd'></i> Redis</a></li>
|
||||
<li><a href='{relative_path}/admin/logger'><i class='icon-th'></i> Logger</a></li>
|
||||
<li><a href="{relative_path}/admin/motd"><i class="icon-comment"></i> MOTD</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Social Authentication</li>
|
||||
<li class=''><a href='{relative_path}/admin/twitter'><i class='icon-twitter-sign'></i> Twitter</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/facebook'><i class='icon-facebook-sign'></i> Facebook</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/gplus'><i class='icon-google-plus-sign'></i> Google+</a></li>
|
||||
<!--<li class="nav-header">Custom Modules</li>-->
|
||||
<!-- <li class=''><a href=''>Search</a></li> -->
|
||||
<li><a href='{relative_path}/admin/twitter'><i class='icon-twitter-sign'></i> Twitter</a></li>
|
||||
<li><a href='{relative_path}/admin/facebook'><i class='icon-facebook-sign'></i> Facebook</a></li>
|
||||
<li><a href='{relative_path}/admin/gplus'><i class='icon-google-plus-sign'></i> Google+</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Plugins</li>
|
||||
<!-- BEGIN plugins -->
|
||||
<li>
|
||||
<a href='{relative_path}/admin{plugins.route}'><i class="{plugins.icon}"></i> {plugins.name}</a>
|
||||
</li>
|
||||
<!-- END plugins -->
|
||||
</ul>
|
||||
</div>
|
||||
<div class="well sidebar-nav">
|
||||
<ul class="nav nav-list">
|
||||
<li class="nav-header">Unit Tests</li>
|
||||
<ul class="nav nav-list">
|
||||
<li class=''><a href='{relative_path}/admin/testing/categories'>Categories</a></li>
|
||||
<!--<li class=''><a href='{relative_path}/admin/testing/topics'>Topics</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/posts'>Posts</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/accounts'>Accounts</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/chat'>Chat</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/notifications'>Notifications</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/friends'>Friends</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/feed'>RSS Feed</a></li>
|
||||
<li class=''><a href='{relative_path}/admin/testing/emails'>Emails</a></li>-->
|
||||
<li><a href='{relative_path}/admin/testing/categories'>Categories</a></li>
|
||||
<!--<li><a href='{relative_path}/admin/testing/topics'>Topics</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/posts'>Posts</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/accounts'>Accounts</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/chat'>Chat</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/notifications'>Notifications</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/friends'>Friends</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/feed'>RSS Feed</a></li>
|
||||
<li><a href='{relative_path}/admin/testing/emails'>Emails</a></li>-->
|
||||
</ul>
|
||||
</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>
|
||||
|
||||
|
||||
@@ -18,5 +18,3 @@
|
||||
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/index.js"></script>
|
||||
46
public/templates/admin/logger.tpl
Normal file
46
public/templates/admin/logger.tpl
Normal file
@@ -0,0 +1,46 @@
|
||||
<h1>Logger</h1>
|
||||
<hr />
|
||||
|
||||
<h3>Logger Settings</h3>
|
||||
<div class="alert alert-warning">
|
||||
|
||||
<p>
|
||||
By enabling the check boxes, you will receive logs to your terminal. If you specify a path, logs will then be saved to a file instead. HTTP logging is useful for collecting statistics about who, when, and what people access on your forum. In addition to logging HTTP requests, we can also log socket.io events. Socket.io logging, in combination with redis-cli monitor, can be very helpful for learning NodeBB's internals.
|
||||
</p>
|
||||
<br/>
|
||||
<p>
|
||||
Simply check/uncheck the logging settings to enable or disable logging on the fly. No restart needed.
|
||||
</p>
|
||||
<br/>
|
||||
|
||||
<form>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" data-field="loggerStatus"> <strong>Enable HTTP logging</strong>
|
||||
</label>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<label>
|
||||
<input type="checkbox" data-field="loggerIOStatus"> <strong>Enable socket.io event logging</strong>
|
||||
</label>
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
|
||||
<label>Path to log file</label>
|
||||
<input class="form-control" type="text" placeholder="/path/to/log/file.log ::: leave blank to log to your terminal" data-field="loggerPath" />
|
||||
<br />
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -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,22 @@
|
||||
<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();
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -13,13 +13,11 @@
|
||||
<!-- 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>.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/plugins.js"></script>
|
||||
@@ -2,64 +2,94 @@
|
||||
<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>Site Keywords</label>
|
||||
<input type="text" class="form-control" placeholder="Keywords describing your community, comma-seperated" data-field="keywords" /><br />
|
||||
<label>Site Logo</label>
|
||||
<input id="logoUrl" type="text" class="form-control" placeholder="Path to a logo to display on forum header" data-field="brand:logo" /><br />
|
||||
<input id="uploadLogoBtn" type="button" class="btn btn-default" value="Upload"></input> <br />
|
||||
<label>Imgur Client ID</label>
|
||||
<input type="text" class="form-control" placeholder="Imgur ClientID for image uploads" data-field="imgurClientID" /><br />
|
||||
<label>Maximum User Image Size</label>
|
||||
<input type="text" class="form-control" placeholder="Maximum size of uploaded user images in kilobytes" data-field="maximumProfileImageSize" />
|
||||
</form>
|
||||
</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" data-field="privileges:manage_topic"><br />
|
||||
<strong>Manage Content</strong><br /> <input type="text" class="form-control" value="1000" data-field="privileges:manage_content"><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 class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-field="allowGuestPosting"> <strong>Allow guests to post without logging in</strong>
|
||||
</label>
|
||||
</div>
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input type="checkbox" data-field="useOutgoingLinksPage"> <strong>Use Outgoing Links Warning Page</strong>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<button class="btn btn-lg btn-primary" id="save">Save</button>
|
||||
|
||||
<script>
|
||||
nodebb_admin.prepare();
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
@@ -9,25 +9,25 @@ 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();
|
||||
|
||||
|
||||
for (var i = 0, ii = data.categories.length; i < ii; i++) {
|
||||
var category = data.categories[i],
|
||||
slug = 'category/' + category.slug;
|
||||
|
||||
asyncTest( "Loading Category '" + category.name + "' located at " + slug, function() {
|
||||
jQuery.get(config.api_url + slug, function(data) {
|
||||
jQuery.get(RELATIVE_PATH + '/api/' + slug, function(data) {
|
||||
ok( data.category_name, JSON.stringify(data) ); //todo: check this against data.categories
|
||||
start();
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
QUnit.start();
|
||||
});
|
||||
|
||||
@@ -23,4 +23,10 @@
|
||||
<button class="btn btn-warning" id="revert_theme">Revert</button> This will remove any custom theme applied to your NodeBB, and restore the base theme.
|
||||
</p>
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/themes.js"></script>
|
||||
<script>
|
||||
var bootswatchListener = function(data) {
|
||||
require(['forum/admin/themes'], function(t) {
|
||||
t.render(data);
|
||||
});
|
||||
}
|
||||
</script>
|
||||
@@ -11,7 +11,7 @@
|
||||
</div>
|
||||
<a target="_blank" href="{relative_path}/topic/{topics.slug}">{topics.title}</a>
|
||||
<ul>
|
||||
<li><i class="icon-time"></i> Posted {topics.relativeTime} ago by {topics.username}</li>
|
||||
<li><i class="icon-time"></i> Posted <span class="timeago" title="{topics.relativeTime}"></span> by {topics.username}</li>
|
||||
<li><i class="icon-comments"></i> {topics.postcount} post(s)</li>
|
||||
</ul>
|
||||
<div class="clear"></div>
|
||||
@@ -20,7 +20,5 @@
|
||||
</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,22 @@
|
||||
<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();
|
||||
require(['forum/admin/settings'], function(Settings) {
|
||||
Settings.prepare();
|
||||
});
|
||||
</script>
|
||||
|
||||
@@ -7,44 +7,38 @@
|
||||
<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}">
|
||||
<a href="/users/{users.userslug}">
|
||||
<img src="{users.picture}" class="img-polaroid"/>
|
||||
<div class="users-box" data-uid="{users.uid}" data-admin="{users.administrator}" data-username="{users.username}" data-banned="{users.banned}">
|
||||
<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>
|
||||
<i class='icon-star'></i>
|
||||
<span id='reputation'>{users.reputation}</span>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span id='postcount'>{users.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
<span id='postcount'>{users.postcount}</span>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn admin-btn">Admin</a>
|
||||
</div>
|
||||
<div>
|
||||
<a href="#" class="btn delete-btn btn-danger">Delete</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}" />
|
||||
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/admin/users.js"></script>
|
||||
|
||||
@@ -1,104 +1,128 @@
|
||||
<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>
|
||||
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
|
||||
<strong>There are no topics in this category.</strong><br />
|
||||
Why don't you try posting one?
|
||||
</div>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<hr class="{show_sidebar}" />
|
||||
<hr/>
|
||||
|
||||
<div class="alert alert-warning hide {no_topics_message}" id="category-no-topics">
|
||||
[[category:no_topics]]
|
||||
</div>
|
||||
|
||||
<div class="category row">
|
||||
<div class="{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>
|
||||
<ul id="topics-container" itemscope itemtype="http://www.schema.org/ItemList">
|
||||
<meta itemprop="itemListOrder" content="descending">
|
||||
<!-- BEGIN topics -->
|
||||
<li class="category-item {topics.deleted-class}" itemprop="itemListElement">
|
||||
<div class="row">
|
||||
<div class="col-md-12 topic-row">
|
||||
<div>
|
||||
<a href="../../topic/{topics.slug}" itemprop="url">
|
||||
<h3>
|
||||
<meta itemprop="name" content="{topics.title}">
|
||||
|
||||
<span class="topic-title">
|
||||
<strong><i class="{topics.pin-icon}"></i> <i class="{topics.lock-icon}"></i></strong>
|
||||
{topics.title}
|
||||
</span>
|
||||
</h3>
|
||||
</a>
|
||||
<small>
|
||||
<span class="topic-stats">
|
||||
posts
|
||||
<strong>{topics.postcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span class="topic-stats">
|
||||
views
|
||||
<strong>{topics.viewcount}</strong>
|
||||
</span>
|
||||
|
|
||||
<span>
|
||||
<a href="/user/{topics.userslug}">
|
||||
<img class="teaser-pic" src="{topics.picture}" title="{topics.username}"/>
|
||||
</a>
|
||||
posted <span class="timeago" title="{topics.relativeTime}"></span>
|
||||
</span>
|
||||
|
||||
<span class="pull-right hidden-xs">
|
||||
<a href="/user/{topics.teaser_userslug}">
|
||||
<img class="teaser-pic" src="{topics.teaser_userpicture}" title="{topics.teaser_username}"/>
|
||||
</a>
|
||||
<a href="../../topic/{topics.slug}#{topics.teaser_pid}">
|
||||
replied
|
||||
</a>
|
||||
<span class="timeago" title="{topics.teaser_timestamp}"></span>
|
||||
|
||||
</span>
|
||||
</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>
|
||||
</div>
|
||||
</div>
|
||||
</li></a>
|
||||
<!-- END topics -->
|
||||
</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">
|
||||
<div class="block-content active-users">
|
||||
<!-- BEGIN active_users -->
|
||||
<a href="/users/{active_users.userslug}"><img title="{active_users.username}" src="{active_users.picture}" class="img-polaroid" /></a>
|
||||
<a data-uid="{active_users.uid}" 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>
|
||||
<!-- BEGIN sidebars -->
|
||||
<div class="sidebar-block img-thumbnail {sidebars.block_class}">
|
||||
<div class="block-header">
|
||||
{sidebars.header}
|
||||
</div>
|
||||
<div class="block-content">
|
||||
{sidebars.content}
|
||||
</div>
|
||||
</div>
|
||||
<!-- END sidebars -->
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<input type="hidden" template-variable="category_id" value="{category_id}" />
|
||||
<input type="hidden" template-variable="twitter-intent-url" value="{twitter-intent-url}" />
|
||||
<input type="hidden" template-variable="facebook-share-url" value="{facebook-share-url}" />
|
||||
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/category.js"></script>
|
||||
<input type="hidden" template-variable="google-share-url" value="{google-share-url}" />
|
||||
@@ -1,39 +1,36 @@
|
||||
{
|
||||
"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/motd/?$": "admin/motd",
|
||||
"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/[^]*": "account",
|
||||
"^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/logger.*": "admin/logger",
|
||||
"^admin/gplus.*": "admin/gplus",
|
||||
"^admin/motd/?$": "admin/motd",
|
||||
"^admin/groups/?$": "admin/groups",
|
||||
"^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"
|
||||
"^recent/.*": "recent",
|
||||
|
||||
"reset/.*": "reset_code"
|
||||
},
|
||||
"force_refresh": {
|
||||
"logout": true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
25
public/templates/favourites.tpl
Normal file
25
public/templates/favourites.tpl
Normal file
@@ -0,0 +1,25 @@
|
||||
<div class="well favourites">
|
||||
<div class="account-username-box" data-userslug="{userslug}">
|
||||
<span class="account-username">
|
||||
<a href="/user/{userslug}">{username}</a> <i class="icon-chevron-right"></i>
|
||||
<a href="/user/{userslug}/favourites">favourites</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div id="no-favourites-notice" class="alert alert-warning {show_nofavourites}">You don't have any favourites, favourite some posts to see them here!</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 user-favourite-posts">
|
||||
<!-- BEGIN posts -->
|
||||
<div class="topic-row img-thumbnail clearfix" topic-url="topic/{posts.tid}/#{posts.pid}">
|
||||
<span><strong>{posts.username}</strong> : </span>
|
||||
<span>{posts.content}</span>
|
||||
<div>
|
||||
<span class="pull-right timeago" title="{posts.relativeTime}"></span>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<!-- END posts -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1,38 +1,33 @@
|
||||
|
||||
<div class="well">
|
||||
<div class="well users">
|
||||
|
||||
<div class="account-username-box">
|
||||
<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 class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- 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>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='reputation'>{followers.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='postcount'>{followers.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
<div class="user-info">
|
||||
<a href="/user/{followers.userslug}">{followers.username}</a>
|
||||
<br/>
|
||||
<div title="reputation" class="reputation">
|
||||
<span class='formatted-number'>{followers.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count" class="post-count">
|
||||
<span class='formatted-number'>{followers.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- END followers -->
|
||||
</div>
|
||||
<div id="no-followers-notice" class="alert alert-warning hide">This user doesn't have any followers :(</div>
|
||||
@@ -41,6 +36,3 @@
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-variable="followersCount" value="{followersCount}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/followers.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
@@ -1,40 +1,34 @@
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div class="well users">
|
||||
|
||||
|
||||
<div class="account-username-box">
|
||||
<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 class="account-sub-links inline-block pull-right">
|
||||
<span id="settingsLink" class="pull-right"><a href="/users/{userslug}/settings">settings</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/followers">followers</a></span>
|
||||
<span class="pull-right"><a href="/users/{userslug}/following">following</a></span>
|
||||
<span id="editLink" class="pull-right"><a href="/users/{userslug}/edit">edit</a></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<!-- 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>
|
||||
<br/>
|
||||
<div title="reputation">
|
||||
<span class='reputation'>{following.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
<div class="user-info">
|
||||
<a href="/user/{following.userslug}">{following.username}</a>
|
||||
<br/>
|
||||
<div title="reputation" class="reputation">
|
||||
<span class='formatted-number'>{following.reputation}</span>
|
||||
<i class='icon-star'></i>
|
||||
</div>
|
||||
<div title="post count" class="post-count">
|
||||
<span class='formatted-number'>{following.postcount}</span>
|
||||
<i class='icon-pencil'></i>
|
||||
</div>
|
||||
</div>
|
||||
<div title="post count">
|
||||
<span class='postcount'>{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>
|
||||
</div>
|
||||
|
||||
<!-- END following -->
|
||||
</div>
|
||||
<div id="no-following-notice" class="alert alert-warning hide">This user isn't following anyone :(</div>
|
||||
@@ -43,6 +37,3 @@
|
||||
<input type="hidden" template-variable="yourid" value="{yourid}" />
|
||||
<input type="hidden" template-variable="theirid" value="{theirid}" />
|
||||
<input type="hidden" template-variable="followingCount" value="{followingCount}" />
|
||||
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/following.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountheader.js"></script>
|
||||
|
||||
@@ -2,54 +2,70 @@
|
||||
|
||||
</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 chat-modal" tabindex="-1" role="dialog" aria-labelledby="Chat" aria-hidden="true" data-backdrop="none">
|
||||
<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="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="" 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 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 -->
|
||||
|
||||
<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>
|
||||
</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>
|
||||
<footer id="footer" class="container footer">
|
||||
<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>
|
||||
|
||||
<div id="mobile-menu">
|
||||
<button id="mobile-menu-btn" type="button" class="btn btn-none"><i class="icon-th icon-2x"></i></button>
|
||||
<button id="mobile-post-btn" type="button" class="btn btn-none"><i class="icon-plus icon-2x"></i></button>
|
||||
</div>
|
||||
|
||||
<!-- END Forum Info -->
|
||||
<script>
|
||||
$.getScript(RELATIVE_PATH + '/src/forum/footer.js');
|
||||
require(['forum/footer']);
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user