mirror of
https://github.com/NodeBB/NodeBB.git
synced 2025-12-16 21:40:23 +01:00
Compare commits
1044 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ab539b518 | ||
|
|
9078c2a536 | ||
|
|
fd20e4a400 | ||
|
|
1a64e40b21 | ||
|
|
cbfba4b45b | ||
|
|
f21a98f37f | ||
|
|
0edd6985ec | ||
|
|
98a646fa62 | ||
|
|
1d69436b44 | ||
|
|
d6c7551120 | ||
|
|
fb53e89023 | ||
|
|
d28beaa4dc | ||
|
|
ac06567617 | ||
|
|
d0a2c077ff | ||
|
|
ebf07626de | ||
|
|
9ef8bb4cdc | ||
|
|
bc8a53aadd | ||
|
|
03c4a1bc73 | ||
|
|
d93163896f | ||
|
|
08bdbc0bcc | ||
|
|
35d6a99775 | ||
|
|
e45e86fcc1 | ||
|
|
9589d340b9 | ||
|
|
91afbf106e | ||
|
|
aff11359ae | ||
|
|
aff8cef5f3 | ||
|
|
879855744d | ||
|
|
b5ab0c9097 | ||
|
|
2c398f81d2 | ||
|
|
520fcadd3f | ||
|
|
e8eb9f91b9 | ||
|
|
8bfb338eee | ||
|
|
bbb655abdd | ||
|
|
095e5527e3 | ||
|
|
4eb8c34855 | ||
|
|
e3185b9560 | ||
|
|
449adfae59 | ||
|
|
714e61b137 | ||
|
|
d0e4689907 | ||
|
|
1996e64c9b | ||
|
|
e2fb617cc0 | ||
|
|
fd88aff195 | ||
|
|
f7a1cca861 | ||
|
|
e3fb996a80 | ||
|
|
fe53037e53 | ||
|
|
438f90d859 | ||
|
|
742c8fb43c | ||
|
|
d43f3cebc6 | ||
|
|
9d452241ad | ||
|
|
c19a51e1b6 | ||
|
|
1b41a8f467 | ||
|
|
f933fc0167 | ||
|
|
8cfb239aac | ||
|
|
a974c6fa99 | ||
|
|
998f780fd2 | ||
|
|
b022d46d47 | ||
|
|
f7793e54b1 | ||
|
|
1e61033667 | ||
|
|
53caa5e422 | ||
|
|
40d20846d8 | ||
|
|
204913c63d | ||
|
|
6c30437c47 | ||
|
|
dd7fe47dfd | ||
|
|
50323c3d23 | ||
|
|
69e0aa338d | ||
|
|
6dcd06b63c | ||
|
|
42d77080f3 | ||
|
|
fbb4998999 | ||
|
|
14744a854f | ||
|
|
2c6afb4244 | ||
|
|
24907e456d | ||
|
|
2039885d96 | ||
|
|
daacdb50f3 | ||
|
|
21155b1b80 | ||
|
|
5d69167a64 | ||
|
|
5db27a835f | ||
|
|
c8e423e9cf | ||
|
|
be3465c5ca | ||
|
|
637e037e27 | ||
|
|
3e6bcd83cc | ||
|
|
dc4aeca427 | ||
|
|
9eb09f14cb | ||
|
|
a55fc364a0 | ||
|
|
2f90949560 | ||
|
|
a940219321 | ||
|
|
aaf6b11dc9 | ||
|
|
0f0913bfe5 | ||
|
|
5569337c40 | ||
|
|
ba2f47ead6 | ||
|
|
b52782deb8 | ||
|
|
2c6bf93eb5 | ||
|
|
4a11307b24 | ||
|
|
d8183c056c | ||
|
|
6ad28dadd4 | ||
|
|
2c489c600c | ||
|
|
abc782f5e6 | ||
|
|
a18e366493 | ||
|
|
12e95df068 | ||
|
|
a078f73e84 | ||
|
|
8f879cd3eb | ||
|
|
8385ceef79 | ||
|
|
83cc8f3ba8 | ||
|
|
cab6ab8e17 | ||
|
|
e9fbed71ae | ||
|
|
b4121f262d | ||
|
|
256a2fa9c6 | ||
|
|
0f362b7fac | ||
|
|
b504e2cd11 | ||
|
|
ea84fd70af | ||
|
|
09efb83ef3 | ||
|
|
b2aeb14094 | ||
|
|
0ed8fd6cbd | ||
|
|
2f1a3b9789 | ||
|
|
d068139d81 | ||
|
|
764e937c43 | ||
|
|
f72cf35348 | ||
|
|
fe1a75d1fd | ||
|
|
3dc9e2f4fa | ||
|
|
3bd8cf69a1 | ||
|
|
2c8725558e | ||
|
|
f68f02d346 | ||
|
|
8fd78199e2 | ||
|
|
1bde86a33f | ||
|
|
b6d4ae2732 | ||
|
|
680dbf138a | ||
|
|
3752a1c691 | ||
|
|
3a00c177d3 | ||
|
|
845e5e7986 | ||
|
|
57d2db36e6 | ||
|
|
8bfea656c4 | ||
|
|
ea2c03e28b | ||
|
|
f29b375ed4 | ||
|
|
07f1e0bcb5 | ||
|
|
0b94297c64 | ||
|
|
8655c2d2a6 | ||
|
|
da9c673ec4 | ||
|
|
1e6863ee19 | ||
|
|
00eb6b4efc | ||
|
|
002826d84f | ||
|
|
289e081e2e | ||
|
|
a717e9626e | ||
|
|
83f20c1cdb | ||
|
|
bc8adff70a | ||
|
|
c4623e2447 | ||
|
|
99fba3b83a | ||
|
|
2580caf864 | ||
|
|
f2d631e42d | ||
|
|
d86aefb518 | ||
|
|
3782ae1647 | ||
|
|
36e1a121ed | ||
|
|
2e52dd59ee | ||
|
|
d7a444d9ab | ||
|
|
f9e2b50826 | ||
|
|
32a32fcf5c | ||
|
|
9435acfa71 | ||
|
|
35ad3be969 | ||
|
|
9cb20c3886 | ||
|
|
a2c9867902 | ||
|
|
58a3f33200 | ||
|
|
25bac03bab | ||
|
|
770cea9329 | ||
|
|
30c11a8b42 | ||
|
|
27a01f6c61 | ||
|
|
bf27ade251 | ||
|
|
9d88b9eed3 | ||
|
|
04ea573caa | ||
|
|
d9eefd667c | ||
|
|
3ad98f3783 | ||
|
|
b89cf6f480 | ||
|
|
4397da144f | ||
|
|
cf8f0ca225 | ||
|
|
885242018f | ||
|
|
1abfe5de63 | ||
|
|
04dd1f9dac | ||
|
|
114294e24a | ||
|
|
dfa4cd4ae3 | ||
|
|
65d5a6cb81 | ||
|
|
64e87b761f | ||
|
|
1021615848 | ||
|
|
69c1ec97c9 | ||
|
|
45e7d64aeb | ||
|
|
b86a01ded1 | ||
|
|
e67af67180 | ||
|
|
9d03147f4e | ||
|
|
c313c4501b | ||
|
|
538356846d | ||
|
|
55d008d71f | ||
|
|
401a30e02c | ||
|
|
9816272b7b | ||
|
|
d72d2decd5 | ||
|
|
43c05d1d85 | ||
|
|
14f35a8c6b | ||
|
|
ffe1549cad | ||
|
|
3ca58a438d | ||
|
|
5da4cead67 | ||
|
|
0000a7f0b9 | ||
|
|
27ac24b1e3 | ||
|
|
95495926fc | ||
|
|
0f254c0b6c | ||
|
|
b27478876e | ||
|
|
a3734f2e15 | ||
|
|
1b843fba9c | ||
|
|
35f17db141 | ||
|
|
bcb364c4d4 | ||
|
|
6e16cb4b30 | ||
|
|
40e71299a1 | ||
|
|
0148cf06d0 | ||
|
|
ffa31ca0e7 | ||
|
|
ba3b8a21ae | ||
|
|
05209b01b9 | ||
|
|
4e0d0c2c20 | ||
|
|
225284073f | ||
|
|
ccef51095a | ||
|
|
cd77a1a457 | ||
|
|
6aeca98cd4 | ||
|
|
07a3b3f00b | ||
|
|
77e0cdcc3e | ||
|
|
e316dd3330 | ||
|
|
b511653e74 | ||
|
|
b5e37a6ce8 | ||
|
|
a34ed92fac | ||
|
|
4ff8509a0e | ||
|
|
e76936abfc | ||
|
|
9471fd8e46 | ||
|
|
2d4ceb8f9f | ||
|
|
4c40ee8e6e | ||
|
|
ddcf46fc73 | ||
|
|
7db234f958 | ||
|
|
f6f7959d28 | ||
|
|
7fa2f474fe | ||
|
|
d5e8044575 | ||
|
|
c0721e105f | ||
|
|
a475e38078 | ||
|
|
932b960aa9 | ||
|
|
24b12e23ea | ||
|
|
2a5d6e04fc | ||
|
|
4459d9d4e0 | ||
|
|
2be3158aff | ||
|
|
f1ad469861 | ||
|
|
0c05ee82b1 | ||
|
|
4f97275d24 | ||
|
|
4b9bfca767 | ||
|
|
202a4c4105 | ||
|
|
090bc2ad10 | ||
|
|
000c7efb1a | ||
|
|
90ad08a00d | ||
|
|
90e1e2436c | ||
|
|
21f5dad1d2 | ||
|
|
bc8bb352a8 | ||
|
|
274310e35a | ||
|
|
0bcc1642c7 | ||
|
|
d1d6605dcf | ||
|
|
da94d6214b | ||
|
|
67e49db797 | ||
|
|
de9100c489 | ||
|
|
4348e1efa4 | ||
|
|
6de3dba239 | ||
|
|
021cf9b8f6 | ||
|
|
ca087e6fa7 | ||
|
|
6c9e28232f | ||
|
|
ceac8e2dc9 | ||
|
|
98cf6eec71 | ||
|
|
5a00767370 | ||
|
|
4c90d22e43 | ||
|
|
e0e153eafb | ||
|
|
5c3c2623f2 | ||
|
|
34fc326a37 | ||
|
|
36745608bf | ||
|
|
090d35f306 | ||
|
|
347f5c132e | ||
|
|
d6f8162f17 | ||
|
|
b0b0f3640c | ||
|
|
4fd08332cc | ||
|
|
aaaffb823f | ||
|
|
76636b64db | ||
|
|
be6ed43223 | ||
|
|
9f2196abfb | ||
|
|
90a75ee045 | ||
|
|
2a21b4855e | ||
|
|
8f769d53a3 | ||
|
|
9ee250b597 | ||
|
|
074d7e7c8d | ||
|
|
870d48dc81 | ||
|
|
9ca10c25d4 | ||
|
|
8e2cc1c883 | ||
|
|
af09f4aca8 | ||
|
|
ab63ca6d92 | ||
|
|
400845ce6c | ||
|
|
351b07bb34 | ||
|
|
f861d44d55 | ||
|
|
2b7e4cbdf4 | ||
|
|
df10bde2db | ||
|
|
0da141e7bc | ||
|
|
6313a5eeb1 | ||
|
|
26de85c1de | ||
|
|
a54f464a13 | ||
|
|
bfbc596348 | ||
|
|
7508bd216e | ||
|
|
8cfc5dda37 | ||
|
|
b81737bc0f | ||
|
|
1c23be8911 | ||
|
|
5f86e31d1e | ||
|
|
746119bd45 | ||
|
|
385aa6df92 | ||
|
|
097810a057 | ||
|
|
3a7fcc2d3d | ||
|
|
95bb838699 | ||
|
|
81055523a0 | ||
|
|
e9fbab0f26 | ||
|
|
53ca7a1143 | ||
|
|
6c70d37f1c | ||
|
|
447073560f | ||
|
|
2c131f172a | ||
|
|
1564e3d530 | ||
|
|
3d9a732c4a | ||
|
|
4819bea378 | ||
|
|
030ce95dea | ||
|
|
2a1671ba9b | ||
|
|
a65c79cb02 | ||
|
|
dcbe4ffd4a | ||
|
|
215b919362 | ||
|
|
58df656c65 | ||
|
|
a8c91e2452 | ||
|
|
806a454b05 | ||
|
|
705754e823 | ||
|
|
fe527ff2a9 | ||
|
|
041e77f688 | ||
|
|
b927f6ce29 | ||
|
|
519d376071 | ||
|
|
d9ee9bf5e3 | ||
|
|
67d5ea83e7 | ||
|
|
7875138c08 | ||
|
|
43b012b32e | ||
|
|
c096656eff | ||
|
|
2dd295118c | ||
|
|
567997ef3c | ||
|
|
c698af17ae | ||
|
|
39b70a9e09 | ||
|
|
9b557cafd8 | ||
|
|
fe4aee177d | ||
|
|
7c4347736c | ||
|
|
16e07d475f | ||
|
|
2125bb2223 | ||
|
|
72a3ab1d6c | ||
|
|
639247a8b0 | ||
|
|
f0caac242c | ||
|
|
e066fbf36a | ||
|
|
b215dbde19 | ||
|
|
1325e4c501 | ||
|
|
2e2938616d | ||
|
|
53a7eab3e8 | ||
|
|
6bd4a34e69 | ||
|
|
d27f2eb214 | ||
|
|
b547d3577b | ||
|
|
4b5988c269 | ||
|
|
cfd3a7d126 | ||
|
|
0471a192ab | ||
|
|
5c6a7d4b94 | ||
|
|
ca01fb9f7d | ||
|
|
b176629b44 | ||
|
|
113cb85c46 | ||
|
|
5b6f5ebf9d | ||
|
|
21bbe68c97 | ||
|
|
28c75e09a9 | ||
|
|
69f453c73c | ||
|
|
dab0435d9b | ||
|
|
b5770be71f | ||
|
|
bf3822e8a5 | ||
|
|
cb6c42ea44 | ||
|
|
3dc3769088 | ||
|
|
3d18c4015a | ||
|
|
610d45bb32 | ||
|
|
66bc4184d8 | ||
|
|
b024d10185 | ||
|
|
e847c015a5 | ||
|
|
10474f8e2a | ||
|
|
8a99eef4fb | ||
|
|
da66efe7a5 | ||
|
|
1bd94b4e41 | ||
|
|
e862a1c4cc | ||
|
|
8b55920acf | ||
|
|
32df811765 | ||
|
|
91d6f83de4 | ||
|
|
7c8f857aaf | ||
|
|
1f970c3bdb | ||
|
|
700cab686b | ||
|
|
af06e0335a | ||
|
|
4a8dc82bd2 | ||
|
|
8a4e2b1ea0 | ||
|
|
1f52717f1e | ||
|
|
304285e874 | ||
|
|
d4eddc6e2c | ||
|
|
6d79521922 | ||
|
|
95db5f93cb | ||
|
|
b900bc9cce | ||
|
|
fc066c21bf | ||
|
|
32d5118266 | ||
|
|
3775c8e50a | ||
|
|
99fbc0dfd5 | ||
|
|
e32d230974 | ||
|
|
c9308efbec | ||
|
|
636551d2e9 | ||
|
|
347d6c2768 | ||
|
|
4f654fb489 | ||
|
|
80e7fd93c6 | ||
|
|
95efb2ae5e | ||
|
|
8d3a647d16 | ||
|
|
53afe6cb68 | ||
|
|
391b8098a3 | ||
|
|
190948336a | ||
|
|
fcda27e251 | ||
|
|
a48a07603c | ||
|
|
d930e2a1be | ||
|
|
7e2166903f | ||
|
|
d5c0ca4a9d | ||
|
|
7695e76494 | ||
|
|
78af47362c | ||
|
|
18a890ed51 | ||
|
|
5f731dd1f6 | ||
|
|
51990142d5 | ||
|
|
b840169a8c | ||
|
|
94fdeb2378 | ||
|
|
68e7ee7f07 | ||
|
|
77d5ecc82a | ||
|
|
437379413e | ||
|
|
ddb7896df1 | ||
|
|
66cdb9a067 | ||
|
|
f5741fd48b | ||
|
|
f405dec4e9 | ||
|
|
cc242ca667 | ||
|
|
2733198f9e | ||
|
|
52700fbe16 | ||
|
|
0f53749e70 | ||
|
|
1aa1ddb4ec | ||
|
|
71125fd1e2 | ||
|
|
5621fb8622 | ||
|
|
b9f1176ec1 | ||
|
|
b3b4d3c37d | ||
|
|
cdbca7d262 | ||
|
|
140f90f7f3 | ||
|
|
a905c6a084 | ||
|
|
bfe081f672 | ||
|
|
46a14715e3 | ||
|
|
c1da56ce45 | ||
|
|
ee63fae803 | ||
|
|
720711756f | ||
|
|
90a12c3253 | ||
|
|
6a00ab3782 | ||
|
|
9ccfa3f18c | ||
|
|
1660d75205 | ||
|
|
f6d57a241d | ||
|
|
bcfb4ca0e4 | ||
|
|
0159a43a20 | ||
|
|
86b019ec89 | ||
|
|
98a12cd1e1 | ||
|
|
e6452cbd4d | ||
|
|
cd44ead595 | ||
|
|
652fda1a6f | ||
|
|
30a7f1a816 | ||
|
|
6ca3d0c4af | ||
|
|
b2074c6dda | ||
|
|
d7c0e1c62e | ||
|
|
56ef05a0bf | ||
|
|
dd36cce329 | ||
|
|
2b2799dae9 | ||
|
|
293c176053 | ||
|
|
3cef5fce2b | ||
|
|
7d0f160c4c | ||
|
|
c5f9f896b2 | ||
|
|
db98b3db55 | ||
|
|
dba47e4bef | ||
|
|
2a46ead29f | ||
|
|
707179edf3 | ||
|
|
53164e96ab | ||
|
|
9702e28a07 | ||
|
|
fa56eca962 | ||
|
|
684839f04a | ||
|
|
0188ea9a3b | ||
|
|
85aa587749 | ||
|
|
48e36e3c31 | ||
|
|
0b6d018f8d | ||
|
|
f2bf65252d | ||
|
|
4335f8c5c6 | ||
|
|
ae82f57c67 | ||
|
|
168052bf45 | ||
|
|
a87ebb64d8 | ||
|
|
38da65ee58 | ||
|
|
e3e1a556cd | ||
|
|
56b618c915 | ||
|
|
7198110b57 | ||
|
|
b7aea63c53 | ||
|
|
78b65c0b12 | ||
|
|
0742590c0c | ||
|
|
c16f04bbcf | ||
|
|
51b38c4c55 | ||
|
|
79eddc9b06 | ||
|
|
21f63ac17f | ||
|
|
fdf5227c35 | ||
|
|
98f03a723e | ||
|
|
69427fa10e | ||
|
|
670986f7ef | ||
|
|
8b62041d28 | ||
|
|
89c5d01efa | ||
|
|
0a06f1ac7d | ||
|
|
df2e9b4b47 | ||
|
|
aed4e2792d | ||
|
|
c860df7975 | ||
|
|
2148f6ff95 | ||
|
|
2d05a06e37 | ||
|
|
e3da005780 | ||
|
|
f0e8633dcc | ||
|
|
590208f0c7 | ||
|
|
7d7ead3f47 | ||
|
|
974629ce85 | ||
|
|
72ef8c839f | ||
|
|
5ee5c8179a | ||
|
|
46d6d7637e | ||
|
|
62e2aa67d7 | ||
|
|
182659d0e1 | ||
|
|
4855131652 | ||
|
|
4d2469b4ce | ||
|
|
4e2326fc84 | ||
|
|
3a8dca6fb9 | ||
|
|
31635b92f3 | ||
|
|
a0c3de0273 | ||
|
|
32b191fa62 | ||
|
|
f616125d02 | ||
|
|
e1f6e064a9 | ||
|
|
633be7ff3c | ||
|
|
6b3863bfbf | ||
|
|
b66ca703b8 | ||
|
|
48a7f9058f | ||
|
|
8f80733563 | ||
|
|
aa1451cfbc | ||
|
|
13ef28118d | ||
|
|
34459e7cf1 | ||
|
|
798e17a954 | ||
|
|
5ad80218a2 | ||
|
|
e90e54cec1 | ||
|
|
bca1fde69c | ||
|
|
d70c688b65 | ||
|
|
541993c80a | ||
|
|
529c8acf6c | ||
|
|
e98d05b1a5 | ||
|
|
0afc7c9761 | ||
|
|
e5c1edd5f5 | ||
|
|
a5e78aab08 | ||
|
|
cec69c2be0 | ||
|
|
bc8815cb94 | ||
|
|
efac6272bb | ||
|
|
bdb30976b5 | ||
|
|
1d4ae8fe4d | ||
|
|
abdb4c34ef | ||
|
|
08d130893c | ||
|
|
b2fb9aa99f | ||
|
|
561ee9e4f1 | ||
|
|
3f70d45f3d | ||
|
|
c07783fa2c | ||
|
|
d4d0e8cd9b | ||
|
|
4be5ac2c23 | ||
|
|
070c95d8de | ||
|
|
8d12ecb758 | ||
|
|
81560c4698 | ||
|
|
390937a20b | ||
|
|
2a8a38b742 | ||
|
|
5a5c3c8c60 | ||
|
|
905e451455 | ||
|
|
69b8b47b15 | ||
|
|
b16e1a0113 | ||
|
|
0b39968a9c | ||
|
|
d8b3abb611 | ||
|
|
7fb0a616a1 | ||
|
|
d987e51a8b | ||
|
|
0f7f6cd0a3 | ||
|
|
0e8b33aa79 | ||
|
|
353b3047cd | ||
|
|
377de06eeb | ||
|
|
4013e27a8b | ||
|
|
181c6eb2e6 | ||
|
|
7955a5d53a | ||
|
|
c261babf17 | ||
|
|
b90eef6d19 | ||
|
|
5c597ca218 | ||
|
|
3dbcf8112d | ||
|
|
5357ad61db | ||
|
|
ff50917c29 | ||
|
|
48835d8c44 | ||
|
|
e9c66bb35a | ||
|
|
23eb7824ac | ||
|
|
494b9d23ac | ||
|
|
64ae9ac033 | ||
|
|
a72fc69997 | ||
|
|
a16f93cbd5 | ||
|
|
81e5cf0cf3 | ||
|
|
01102d5982 | ||
|
|
2174aec0e1 | ||
|
|
46bad118de | ||
|
|
2d7228fa40 | ||
|
|
a1839d90fd | ||
|
|
0cc136c3f6 | ||
|
|
cd1e26418d | ||
|
|
dab4f07258 | ||
|
|
501dc56fd3 | ||
|
|
253271127d | ||
|
|
f2da892b38 | ||
|
|
6dd72f480c | ||
|
|
3caf8b4a67 | ||
|
|
39f2efbef8 | ||
|
|
288341945d | ||
|
|
d02bd72764 | ||
|
|
7d3adb9275 | ||
|
|
156950ac2f | ||
|
|
83f18c1915 | ||
|
|
332730575f | ||
|
|
08ef67e824 | ||
|
|
7e71fb218c | ||
|
|
a7216caa3b | ||
|
|
87309601ce | ||
|
|
53db9db50f | ||
|
|
23628668b7 | ||
|
|
6ac685b194 | ||
|
|
db8c43ca97 | ||
|
|
bff0c8fdaf | ||
|
|
6f2b809385 | ||
|
|
455479bd54 | ||
|
|
03b34a449d | ||
|
|
08e51c8942 | ||
|
|
4aef5bfb72 | ||
|
|
34c74770ce | ||
|
|
da8d198676 | ||
|
|
33868804fd | ||
|
|
22a3794c51 | ||
|
|
1058d54c52 | ||
|
|
90ce539683 | ||
|
|
99c2fbd947 | ||
|
|
866d813218 | ||
|
|
f1df8c2479 | ||
|
|
6f1523c279 | ||
|
|
163cdaf70c | ||
|
|
a9ce8393e4 | ||
|
|
f04e30c4d4 | ||
|
|
9d0f8b4543 | ||
|
|
d631a4b2e5 | ||
|
|
2cf55dcf9f | ||
|
|
9fbb139e67 | ||
|
|
11e3b0da7d | ||
|
|
0b922d3f60 | ||
|
|
7e4faa3270 | ||
|
|
635fba1e45 | ||
|
|
7c950cc350 | ||
|
|
cc0fe66e3e | ||
|
|
b2d6ce59cf | ||
|
|
586a181e0a | ||
|
|
33150943df | ||
|
|
28dab60232 | ||
|
|
71ef76b108 | ||
|
|
64008ef5d8 | ||
|
|
1859154370 | ||
|
|
fa4067e885 | ||
|
|
c8c355b319 | ||
|
|
51355a53d9 | ||
|
|
1f3f672d3f | ||
|
|
7c3fa30c13 | ||
|
|
cbbb7a7c8e | ||
|
|
6893bd8b04 | ||
|
|
22eabf6620 | ||
|
|
a827888ee3 | ||
|
|
54d94f5988 | ||
|
|
7c1b6d6ad2 | ||
|
|
8c4f776122 | ||
|
|
84fa704b25 | ||
|
|
535379d9d7 | ||
|
|
d2927e2be2 | ||
|
|
bd04b2f921 | ||
|
|
27fce2363d | ||
|
|
01340c87bd | ||
|
|
77c2f551d3 | ||
|
|
18009ebb39 | ||
|
|
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 | ||
|
|
f5c4f98834 |
21
.gitignore
vendored
21
.gitignore
vendored
@@ -1,7 +1,3 @@
|
||||
#################
|
||||
## npm
|
||||
#################
|
||||
|
||||
npm-debug.log
|
||||
node_modules/
|
||||
sftp-config.json
|
||||
@@ -9,11 +5,18 @@ config.json
|
||||
public/src/nodebb.min.js
|
||||
public/config.json
|
||||
public/css/*.css
|
||||
public/themes/*
|
||||
!/public/themes/vanilla
|
||||
!/public/themes/cerulean
|
||||
!/public/themes/modern
|
||||
*.sublime-project
|
||||
*.sublime-workspace
|
||||
plugins/*
|
||||
.project
|
||||
.idea
|
||||
*.swp
|
||||
Vagrantfile
|
||||
.vagrant
|
||||
provision.sh
|
||||
*.komodoproject
|
||||
|
||||
feeds/recent.rss
|
||||
|
||||
# winston?
|
||||
error.log
|
||||
events.log
|
||||
|
||||
15
README.md
15
README.md
@@ -1,15 +1,16 @@
|
||||
|
||||
# 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")
|
||||
* [Join us on IRC](https://kiwiirc.com/client/irc.freenode.net/nodebb) - #nodebb on Freenode
|
||||
|
||||

|
||||

|
||||
|
||||

|
||||

|
||||
|
||||
## How can I follow along/contribute?
|
||||
|
||||
@@ -22,7 +23,7 @@
|
||||
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.
|
||||
* Redis, version 2.6 or greater or MongoDB, version 2.4 or greater
|
||||
* nginx, version 1.3.13 or greater (**only if** intending to use nginx to proxy requests to a NodeBB)
|
||||
|
||||
## Installation
|
||||
@@ -31,6 +32,8 @@ First, we install our base software stack:
|
||||
|
||||
# apt-get install git nodejs redis-server npm build-essential imagemagick
|
||||
|
||||
If you want to use MongoDB instead of Redis install it from http://www.mongodb.org/downloads and remove 'redis-server' from the above command. [MongoDB-Setup](https://github.com/designcreateplay/NodeBB/wiki/MongoDB-Setup)
|
||||
|
||||
**If your package manager only installed a version of Node.js that is less than 0.8 (e.g. Ubuntu 12.10, 13.04):**
|
||||
|
||||
# add-apt-repository ppa:chris-lea/node.js
|
||||
@@ -64,4 +67,4 @@ NodeBB can also be started with helper programs, such as `supervisor` and `forev
|
||||
|
||||
## Upgrading NodeBB
|
||||
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)
|
||||
Detailed upgrade instructions are listed in [Upgrading NodeBB](https://github.com/designcreateplay/NodeBB/wiki/Upgrading-NodeBB)
|
||||
|
||||
125
app.js
125
app.js
@@ -25,9 +25,11 @@
|
||||
|
||||
var fs = require('fs'),
|
||||
async = require('async'),
|
||||
semver = require('semver'),
|
||||
winston = require('winston'),
|
||||
pkg = require('./package.json'),
|
||||
path = require('path'),
|
||||
pkg = require('./package.json'),
|
||||
utils = require('./public/src/utils.js'),
|
||||
meta;
|
||||
|
||||
// Runtime environment
|
||||
@@ -48,6 +50,12 @@
|
||||
winston.error(err.stack);
|
||||
};
|
||||
|
||||
require('child_process').exec('/usr/bin/which convert', function(err, stdout, stderr) {
|
||||
if(err || !stdout) {
|
||||
winston.warn('Couldn\'t find convert. Did you install imagemagick?');
|
||||
}
|
||||
});
|
||||
|
||||
// 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.');
|
||||
@@ -55,65 +63,73 @@
|
||||
winston.info('');
|
||||
|
||||
|
||||
if (fs.existsSync(__dirname + '/config.json') && (!nconf.get('setup') && !nconf.get('upgrade'))) {
|
||||
if (!nconf.get('help') && !nconf.get('setup') && !nconf.get('install') && !nconf.get('upgrade') && fs.existsSync(__dirname + '/config.json')) {
|
||||
// Load server-side configs
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
meta = require('./src/meta.js');
|
||||
meta = require('./src/meta');
|
||||
|
||||
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/');
|
||||
nconf.set('url', nconf.get('base_url') + (nconf.get('use_port') ? ':' + nconf.get('port') : '') + nconf.get('relative_path') + path.sep);
|
||||
nconf.set('upload_url', path.join(path.sep, nconf.get('relative_path'), 'uploads', path.sep));
|
||||
nconf.set('base_dir', __dirname);
|
||||
|
||||
winston.info('Initializing NodeBB v' + pkg.version + ', on port ' + nconf.get('port') + ', using Redis store at ' + nconf.get('redis:host') + ':' + nconf.get('redis:port') + '.');
|
||||
winston.info('NodeBB instance bound to: ' + (nconf.get('bind_address') || 'Any address'));
|
||||
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.');
|
||||
}
|
||||
|
||||
meta.configs.init(function () {
|
||||
// Initial setup for Redis & Reds
|
||||
var reds = require('reds'),
|
||||
RDB = require('./src/redis.js');
|
||||
if (semver.gt(pkg.dependencies['nodebb-theme-cerulean'], require('./node_modules/nodebb-theme-cerulean/package.json').version)) {
|
||||
winston.error('nodebb-theme-cerulean is out of date - please run npm install.')
|
||||
}
|
||||
|
||||
reds.createClient = function () {
|
||||
return reds.client || (reds.client = RDB);
|
||||
};
|
||||
require('./src/database').init(function(err) {
|
||||
meta.configs.init(function () {
|
||||
|
||||
var templates = require('./public/src/templates.js'),
|
||||
translator = require('./public/src/translator.js'),
|
||||
webserver = require('./src/webserver.js'),
|
||||
SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket']}),
|
||||
websockets = require('./src/websockets.js'),
|
||||
plugins = require('./src/plugins'); // Don't remove this - plugins initializes itself
|
||||
var templates = require('./public/src/templates'),
|
||||
translator = require('./public/src/translator'),
|
||||
webserver = require('./src/webserver'),
|
||||
SocketIO = require('socket.io').listen(global.server, { log: false, transports: ['websocket', 'xhr-polling', 'jsonp-polling', 'flashsocket'], 'browser client minification': true}),
|
||||
websockets = require('./src/websockets'),
|
||||
plugins = require('./src/plugins'),
|
||||
notifications = require('./src/notifications'),
|
||||
upgrade = require('./src/upgrade');
|
||||
|
||||
websockets.init(SocketIO);
|
||||
upgrade.check(function(schema_ok) {
|
||||
if (schema_ok || nconf.get('check-schema') === false) {
|
||||
websockets.init(SocketIO);
|
||||
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
plugins.init();
|
||||
global.templates = {};
|
||||
global.translator = translator;
|
||||
|
||||
translator.loadServer();
|
||||
translator.loadServer();
|
||||
|
||||
// todo: replace below with read directory code, derp.
|
||||
templates.init([
|
||||
'header', 'footer', 'logout', 'outgoing', 'admin/header', 'admin/footer', 'admin/index',
|
||||
'emails/reset', 'emails/reset_plaintext', 'emails/email_confirm', 'emails/email_confirm_plaintext',
|
||||
'emails/header', 'emails/footer',
|
||||
var customTemplates = meta.config['theme:templates'] ? path.join(__dirname, 'node_modules', meta.config['theme:id'], meta.config['theme:templates']) : false;
|
||||
|
||||
'noscript/header', 'noscript/home', 'noscript/category', 'noscript/topic'
|
||||
]);
|
||||
|
||||
templates.ready(webserver.init);
|
||||
utils.walk(path.join(__dirname, 'public/templates'), function (err, tplsToLoad) {
|
||||
templates.init(tplsToLoad, customTemplates);
|
||||
});
|
||||
|
||||
plugins.ready(function() {
|
||||
templates.ready(webserver.init);
|
||||
});
|
||||
|
||||
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('upgrade')) {
|
||||
meta = require('./src/meta.js');
|
||||
|
||||
meta.configs.init(function () {
|
||||
require('./src/upgrade').upgrade();
|
||||
});
|
||||
} else {
|
||||
} else if (nconf.get('setup') || nconf.get('install') || !fs.existsSync(__dirname + '/config.json')) {
|
||||
// New install, ask setup questions
|
||||
if (nconf.get('setup')) {
|
||||
winston.info('NodeBB Setup Triggered via Command Line');
|
||||
@@ -121,6 +137,10 @@
|
||||
winston.warn('Configuration not found, starting NodeBB setup');
|
||||
}
|
||||
|
||||
nconf.file({
|
||||
file: __dirname + '/config.json'
|
||||
});
|
||||
|
||||
var install = require('./src/install');
|
||||
|
||||
winston.info('Welcome to NodeBB!');
|
||||
@@ -131,10 +151,31 @@
|
||||
if (err) {
|
||||
winston.error('There was a problem completing NodeBB setup: ', err.message);
|
||||
} else {
|
||||
winston.info('NodeBB Setup Completed.');
|
||||
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'
|
||||
});
|
||||
require('./src/database').init(function(err) {
|
||||
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
|
||||
@@ -2,73 +2,97 @@
|
||||
{
|
||||
"name": "Announcements",
|
||||
"description": "Announcements regarding our community",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-bullhorn"
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-bullhorn",
|
||||
"order": 1
|
||||
},
|
||||
{
|
||||
"name": "General Discussion",
|
||||
"description": "A place to talk about whateeeever you want",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-comment"
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-comment",
|
||||
"order": 2
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Development",
|
||||
"description": "NodeBB development news and announcements",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-github"
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-github",
|
||||
"order": 3
|
||||
},
|
||||
{
|
||||
"name": "Blogs",
|
||||
"description": "Blog posts from individual members",
|
||||
"blockclass": "category-blue",
|
||||
"icon" : "icon-pencil"
|
||||
"bgColor": "#0059B2",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-pencil",
|
||||
"order": 4
|
||||
},
|
||||
{
|
||||
"name": "Feature Requests",
|
||||
"description": "Got a feature request you'd like to see? Give us a shout here.",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-lightbulb"
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-lightbulb-o",
|
||||
"order": 5
|
||||
},
|
||||
{
|
||||
"name": "Bug Reports",
|
||||
"description": "Having trouble with NodeBB? Let us know...",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-cogs"
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-cogs",
|
||||
"order": 6
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Plugins",
|
||||
"description": "Enhance your NodeBB with plugins!",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-plus-sign"
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-plus-square",
|
||||
"order": 7
|
||||
},
|
||||
{
|
||||
"name": "NodeBB Link Exchange",
|
||||
"description": "Link exchange",
|
||||
"blockclass": "category-purple",
|
||||
"icon" : "icon-exchange"
|
||||
"bgColor": "#ab1290",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-exchange",
|
||||
"order": 8
|
||||
},
|
||||
{
|
||||
"name": "News",
|
||||
"description": "News from around the world",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-globe"
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-globe",
|
||||
"order": 9
|
||||
},
|
||||
{
|
||||
"name": "Movies",
|
||||
"description": "Discuss the latest movies here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-film"
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-film",
|
||||
"order": 10
|
||||
},
|
||||
{
|
||||
"name": "Games",
|
||||
"description": "Discuss the latest games here",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-screenshot"
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-crosshairs",
|
||||
"order": 11
|
||||
},
|
||||
{
|
||||
"name": "Random",
|
||||
"description": "Anything and (almost) everything welcome!",
|
||||
"blockclass": "category-darkblue",
|
||||
"icon" : "icon-beer"
|
||||
"bgColor": "#004C66",
|
||||
"color": "#fff",
|
||||
"icon" : "fa-beer",
|
||||
"order": 12
|
||||
}
|
||||
]
|
||||
81
mocks/databasemock.js
Normal file
81
mocks/databasemock.js
Normal file
@@ -0,0 +1,81 @@
|
||||
/**
|
||||
* Database Mock - wrapper for database.js, makes system use separate test db, instead of production
|
||||
* ATTENTION: testing db is flushed before every use!
|
||||
*/
|
||||
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var 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 dbType = nconf.get('database'),
|
||||
testDbConfig = nconf.get('test_database'),
|
||||
productionDbConfig = nconf.get(dbType);
|
||||
|
||||
if(!testDbConfig){
|
||||
errorText = 'test_database is not defined';
|
||||
winston.info(
|
||||
"\n===========================================================\n"+
|
||||
"Please, add parameters for test database in config.json\n"+
|
||||
"For example (redis):\n"+
|
||||
'"test_database": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "6379",' + '\n' +
|
||||
' "password": "",' + '\n' +
|
||||
' "database": "1"' + '\n' +
|
||||
'}\n'+
|
||||
" or (mongo):\n" +
|
||||
'"test_database": {' + '\n' +
|
||||
' "host": "127.0.0.1",' + '\n' +
|
||||
' "port": "27017",' + '\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 = 'test_database has the same config as production db';
|
||||
winston.error(errorText);
|
||||
throw new Error(errorText);
|
||||
}
|
||||
|
||||
nconf.set(dbType, testDbConfig);
|
||||
|
||||
db = require('../src/database');
|
||||
before(function(done) {
|
||||
|
||||
db.init(function(err) {
|
||||
//Clean up
|
||||
db.flushdb(function(err) {
|
||||
if(err){
|
||||
winston.error(err);
|
||||
throw new Error(err);
|
||||
} else {
|
||||
winston.info('test_database flushed');
|
||||
done();
|
||||
}
|
||||
|
||||
//TODO: data seeding, if needed at all
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
module.exports = db;
|
||||
|
||||
}(module));
|
||||
58
nodebb
58
nodebb
@@ -1,6 +1,54 @@
|
||||
#!/bin/bash
|
||||
clear
|
||||
echo "Launching NodeBB in \"development\" mode."
|
||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
|
||||
NODE_ENV=development supervisor --extensions 'node|js|tpl' -- app $1
|
||||
|
||||
# $0 script path
|
||||
# $1 action
|
||||
# $2 subaction
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
node app
|
||||
;;
|
||||
|
||||
dev)
|
||||
echo "Launching NodeBB in \"development\" mode."
|
||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
|
||||
NODE_ENV=development node app
|
||||
;;
|
||||
|
||||
watch)
|
||||
echo "Launching NodeBB in \"development\" mode."
|
||||
echo "To run the production build of NodeBB, please use \"forever\"."
|
||||
echo "More Information: https://github.com/designcreateplay/NodeBB/wiki/How-to-run-NodeBB"
|
||||
NODE_ENV=development supervisor -q --extensions 'node|js|tpl' -- app $1
|
||||
;;
|
||||
|
||||
language)
|
||||
case "$2" in
|
||||
check)
|
||||
node app --language="check"
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Language Settings"
|
||||
echo $"Usage: $0 language {check}"
|
||||
echo ''
|
||||
column -s ' ' -t <<< '
|
||||
check Compare language files against the /en directory
|
||||
'
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
*)
|
||||
echo "Welcome to NodeBB"
|
||||
echo $"Usage: $0 {start|dev|watch|language}"
|
||||
echo ''
|
||||
column -s ' ' -t <<< '
|
||||
start Start NodeBB in production mode
|
||||
dev Start NodeBB in development mode
|
||||
watch Start NodeBB in development mode and watch for changes
|
||||
language Language settings
|
||||
'
|
||||
exit 1
|
||||
esac
|
||||
|
||||
46
package.json
46
package.json
@@ -2,21 +2,22 @@
|
||||
"name": "nodebb",
|
||||
"license": "GPLv3 or later",
|
||||
"description": "NodeBB Forum",
|
||||
"version": "0.0.7",
|
||||
"version": "0.2.1",
|
||||
"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.16",
|
||||
"redis": "0.8.3",
|
||||
"express": "3.2.0",
|
||||
"express-namespace": "~0.1.1",
|
||||
"emailjs": "0.3.4",
|
||||
"cookie": "0.0.6",
|
||||
"connect-redis": "1.4.5",
|
||||
"passport": "0.1.17",
|
||||
"passport-local": "0.1.6",
|
||||
"passport-twitter": "0.1.5",
|
||||
@@ -25,25 +26,37 @@
|
||||
"less-middleware": "0.1.12",
|
||||
"marked": "0.2.8",
|
||||
"bcrypt": "0.7.5",
|
||||
"async": "0.2.8",
|
||||
"async": "~0.2.8",
|
||||
"node-imagemagick": "0.1.8",
|
||||
"gravatar": "1.0.6",
|
||||
"nconf": "~0.6.7",
|
||||
"sitemap": "~0.6.0",
|
||||
"cheerio": "~0.12.0",
|
||||
"request": "~2.25.0",
|
||||
"reds": "~0.2.4",
|
||||
"winston": "~0.7.2",
|
||||
"nodebb-plugin-mentions": "~0.1.0",
|
||||
"nodebb-plugin-markdown": "~0.1.0",
|
||||
"rss": "~0.2.0",
|
||||
"prompt": "~0.2.11",
|
||||
"uglify-js": "~2.4.0",
|
||||
"validator": "~1.5.1"
|
||||
"validator": "~1.5.1",
|
||||
"nodebb-plugin-mentions": "~0.1",
|
||||
"nodebb-plugin-markdown": "~0.3",
|
||||
"nodebb-theme-vanilla": "~0.0.12",
|
||||
"nodebb-theme-cerulean": "0.0.10",
|
||||
"cron": "~1.0.1",
|
||||
"semver": "~2.2.1",
|
||||
"string": "~1.7.0",
|
||||
"xregexp": "~2.0.0"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"redis": "0.8.3",
|
||||
"mongodb": "~1.3.19",
|
||||
"connect-redis": "1.4.5",
|
||||
"connect-mongo": "0.4.0",
|
||||
"hiredis": "~0.1.15"
|
||||
},
|
||||
"devDependencies": {
|
||||
"mocha": "~1.13.0"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/designcreateplay/NodeBB/issues"
|
||||
},
|
||||
@@ -53,15 +66,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",
|
||||
@@ -70,6 +90,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 +0,0 @@
|
||||
@import "../themes/cerulean/cerulean.less";
|
||||
14
public/language/de/category.json
Normal file
14
public/language/de/category.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"new_topic_button": "Neues Thema",
|
||||
"no_topics": "<strong>Es gibt noch keine Threads in dieser Kategorie.</strong><br />Warum beginnst du nicht den ersten?",
|
||||
"sidebar.recent_replies": "Neuste Antworten",
|
||||
"sidebar.active_participants": "Aktive Teilnehmer",
|
||||
"sidebar.moderators": "Moderatoren",
|
||||
"posts": "Posts",
|
||||
"views": "Aufrufe",
|
||||
"posted": "Geposted",
|
||||
"browsing": "Sieht zu",
|
||||
"no_replies": "Niemand hat geantwortet",
|
||||
"replied": "geantwortet",
|
||||
"last_edited_by": "zuletzt editiert durch"
|
||||
}
|
||||
10
public/language/de/footer.json
Normal file
10
public/language/de/footer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"chat.chatting_with": "Chatten mit <span id='chat-with-name'></span>",
|
||||
"chat.placeholder": "schreibe hier etwas, und drücke Enter zum absenden",
|
||||
"chat.send": "Senden",
|
||||
"stats.online": "Online",
|
||||
"stats.users": "Benutzer",
|
||||
"stats.topics": "Themen",
|
||||
"stats.posts": "Beiträge",
|
||||
"success": "erfolg"
|
||||
}
|
||||
31
public/language/de/global.json
Normal file
31
public/language/de/global.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"home": "Übersicht",
|
||||
"search": "Suche",
|
||||
"buttons.close": "Schließen",
|
||||
"403.title": "Zugriff Verweigert",
|
||||
"403.message": "Du bist nicht dazu berechtigt, diese Seite aufzurufen. <a href='/login'>Logge dich ein</a> und versuche es erneut.",
|
||||
"404.title": " Nicht Gefunden",
|
||||
"404.message": "Die abgefragte Seite wurde nicht gefunden. <a href='/''>Zurück zur Übersicht</a>.",
|
||||
"500.title": "Internal error.",
|
||||
"500.message": "Ooops! Looks like something went wrong!",
|
||||
|
||||
"register": "Registrierung",
|
||||
"login": "Login",
|
||||
|
||||
"logout": "Logout",
|
||||
"logout.title": "Du bist ausgeloggt.",
|
||||
"logout.message": "Du hast dich soeben erfolgreich aus dem Forum ausgeloggt.",
|
||||
|
||||
"save_changes": "Speichere Änderungen",
|
||||
"close": "Schließen",
|
||||
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Aktuell",
|
||||
"header.unread": "Ungelesen",
|
||||
"header.users": "Benutzer",
|
||||
"header.search": "Suche",
|
||||
"header.profile": "Profil",
|
||||
|
||||
"notifications.loading": "Benachrichtigungen laden",
|
||||
"chats.loading": "Nachrichten werden geladen"
|
||||
}
|
||||
10
public/language/de/login.json
Normal file
10
public/language/de/login.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"login": "Einloggen",
|
||||
"username": "Benutzername",
|
||||
"password": "Passwort",
|
||||
"remember_me": "Eingeloggt bleiben?",
|
||||
"forgot_password": "Passwort vergessen?",
|
||||
"alternative_logins": "Login Alternativen",
|
||||
"failed_login_attempt": " Anmeldeversuch fehlgeschlagen, versuche es erneut.",
|
||||
"login_successful": "Du hast dich erfolgreich eingeloggt!"
|
||||
}
|
||||
9
public/language/de/notifications.json
Normal file
9
public/language/de/notifications.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Benachrichtigungen",
|
||||
"back_to_home": "Zurück zur Startseite",
|
||||
"mark_all_as_read": "Alles als gelesen markieren",
|
||||
"outgoing_link": "Externer Link",
|
||||
"outgoing_link_message": "Du verlässt nun",
|
||||
"continue_to": "Gehe weiter zu",
|
||||
"return_to": "Kehre zurück zu"
|
||||
}
|
||||
5
public/language/de/recent.json
Normal file
5
public/language/de/recent.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"day": "Tag",
|
||||
"week": "Woche",
|
||||
"month": "Monat"
|
||||
}
|
||||
16
public/language/de/register.json
Normal file
16
public/language/de/register.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"register": "Registrieren",
|
||||
"help.email": "Deine E-Mail Adresse ist standardmäßig nicht öffentlich sichtbar.",
|
||||
"help.username_restrictions": "Einen einmaligen Benutzernamen. 3-16 Zeichen. Andere Benutzer können dich mit @<span id='yourUsername'>Benutzername</span> anschreiben.",
|
||||
"help.minimum_password_length": "Dein Passwort muss mindestens sechs Zeichen lang sein.",
|
||||
"email_address": "E-Mail",
|
||||
"email_address_placeholder": "E-Mail Adresse hier eingeben",
|
||||
"username": "Benutzername",
|
||||
"username_placeholder": "Benutzernamen eingeben",
|
||||
"password": "Passwort",
|
||||
"password_placeholder": "Passwort eingeben",
|
||||
"confirm_password": "Passwort bestätigen",
|
||||
"confirm_password_placeholder": "Passwort zur Bestätigung erneut eingeben",
|
||||
"register_now_button": "Jetzt registrieren",
|
||||
"alternative_registration": "Alternative Registrierung"
|
||||
}
|
||||
13
public/language/de/reset_password.json
Normal file
13
public/language/de/reset_password.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"reset_password": "Passwort zurücksetzen",
|
||||
"update_password": "Ändere Passwort",
|
||||
"password_change": "Passwort wurde geändert",
|
||||
"password_reset_successful": "<p>Das Passwort wurde erfolgreich zurückgesetzt. <a href=\"/login\">Log dich neu ein</a>.",
|
||||
"wrong_reset_code.title": "Der Reset-Code ist falsch.",
|
||||
"wrong_reset_code.message": "Der empfangene Reset-Code war falsch. Bitte versuche es erneut oder <a href=\"/reset\">fordere einen neuen Code an</a>.",
|
||||
"new_password": "Neues Passwort",
|
||||
"repeat_password": "Wiederhole das Passwort",
|
||||
"enter_email": "Bitte gib Deine <strong>E-Mail Adresse</strong> ein und wir senden Dir eine Anleitung, wie Du Dein Passwort zurücksetzen kannst.",
|
||||
"password_reset_sent": "Passwortzrücksetzung beantragt.",
|
||||
"invalid_email": "Ungültige E-Mail / Adresse existiert nicht!"
|
||||
}
|
||||
43
public/language/de/topic.json
Normal file
43
public/language/de/topic.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"topic": "Thema",
|
||||
"topics": "Themen",
|
||||
|
||||
"no_topics_found": "Keine passende Themen gefunden.",
|
||||
|
||||
"profile": "Profil",
|
||||
"posted_by": "geschrieben von",
|
||||
"chat": "Chat",
|
||||
"notify_me": "Werde bei neues Antworten auf dieses Thema benachrichtigt.",
|
||||
"quote": "zitieren",
|
||||
"reply": "antworten",
|
||||
"edit": "bearbeiten",
|
||||
"delete": "löschen",
|
||||
"banned": "gesperrt",
|
||||
"link": "Link",
|
||||
|
||||
"thread_tools.title": "Thread Tools",
|
||||
"thread_tools.pin": "Thread pinnen",
|
||||
"thread_tools.unpin": "Thread nicht mehr pinnen",
|
||||
"thread_tools.lock": "Thread sperren",
|
||||
"thread_tools.move": "Thread verschieben",
|
||||
"thread_tools.delete": "Thread löschen",
|
||||
|
||||
"load_categories": "Kategorien laden",
|
||||
"disabled_categories_note": "Deaktivierte Kategorien sind ausgegraut.",
|
||||
"confirm_move": "verschieben",
|
||||
|
||||
"favourite": "Favorit",
|
||||
"favourites": "Favoriten",
|
||||
"favourites.not_logged_in.title": "Nicht eingeloggt!",
|
||||
"favourites.not_logged_in.message": "Bitte logge dich ein, um diesen Beitrag favorisieren zu können.",
|
||||
"favourites.has_no_favourites":"Du hast noch keine Favoriten.",
|
||||
|
||||
"posted_by": "Geposted von",
|
||||
"loading": "Lade",
|
||||
"more_posts": "Mehr Posts",
|
||||
"move_topic": "Thema verschieben",
|
||||
"topic_will_be_moved_to": "Dieses Thema wird verschoben nach",
|
||||
|
||||
"reputation": "Reputation",
|
||||
"posts": "Posts"
|
||||
}
|
||||
5
public/language/de/unread.json
Normal file
5
public/language/de/unread.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"no_unread_topics": "Es gibt keine ungelesenen Themen.",
|
||||
"mark_all_read": "alle als gelesen markieren",
|
||||
"load_more": "mehr laden"
|
||||
}
|
||||
38
public/language/de/user.json
Normal file
38
public/language/de/user.json
Normal file
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"banned": "Gebannt",
|
||||
"offline": "offline",
|
||||
"email": "E-Mail",
|
||||
"fullname": "Kompletter Name",
|
||||
"website": "Homepage",
|
||||
"location": "Wohnort",
|
||||
"age": "Alter",
|
||||
"joined": "Beigetreten",
|
||||
"profile_views": "Profilaufrufe",
|
||||
"reputation": "Reputation",
|
||||
"posts": "Posts",
|
||||
"followers": "Follower",
|
||||
"following": "Folgt",
|
||||
"signature": "Signatur",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Geburtstag",
|
||||
|
||||
"change_picture": "Ändere Profilbild",
|
||||
"edit": "Ändern",
|
||||
"uploaded_picture": "Hochgeladene Bilder",
|
||||
"upload_new_picture": "Neues Bild hochladen",
|
||||
"change_password": "Ändere Passwort",
|
||||
"confirm_password": "Passwort wiederholen",
|
||||
"password": "Passwort",
|
||||
|
||||
"upload_picture": "Bild hochladen",
|
||||
"upload_a_picture": "Ein Bild hochladen",
|
||||
"image_spec": "Du solltest nur Dateien die PNG, JPG, oder GIF kleiner als 256kb hochladen.",
|
||||
|
||||
"settings": "Einstellungen",
|
||||
"show_email": "Zeige meine E-Mail Adresse an.",
|
||||
|
||||
"has_no_follower": "Dieser User hat noch keine Follower.",
|
||||
"follows_no_one": "Dieser User folgt noch niemanden."
|
||||
|
||||
|
||||
}
|
||||
9
public/language/de/users.json
Normal file
9
public/language/de/users.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"latest_users": "neuste Benutzer",
|
||||
"top_posters": "meiste Beiträge",
|
||||
"most_reputation": "höhstes Ansehen",
|
||||
"online": "Online",
|
||||
"search": "Suchen",
|
||||
"enter_username": "Benutzer durchsuchen",
|
||||
"load_more": "mehr laden"
|
||||
}
|
||||
@@ -3,5 +3,12 @@
|
||||
"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"
|
||||
}
|
||||
"sidebar.moderators": "Moderators",
|
||||
"posts": "posts",
|
||||
"views": "views",
|
||||
"posted": "posted",
|
||||
"browsing": "browsing",
|
||||
"no_replies": "No one has replied",
|
||||
"replied": "replied",
|
||||
"last_edited_by": "last edited by"
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"chat.chatting_with": "Chat with <span id='chat-with-name'></span>",
|
||||
"chat.placeholder": "type chat message, here press enter to send",
|
||||
"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"
|
||||
}
|
||||
"stats.posts": "Posts",
|
||||
"success": "success"
|
||||
}
|
||||
|
||||
@@ -5,13 +5,27 @@
|
||||
"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>.",
|
||||
"404.message": "You seem to have stumbled upon a page that does not exist. Return to the <a href='/'>home page</a>.",
|
||||
"500.title": "Internal error.",
|
||||
"500.message": "Oops! Looks like something went wrong!",
|
||||
|
||||
"register": "Register",
|
||||
"login": "Login",
|
||||
|
||||
"logout": "Logout",
|
||||
"logout.title": "You are now logged out.",
|
||||
"logout.message": "You have successfully logged out of NodeBB",
|
||||
|
||||
"save_changes": "Save Changes",
|
||||
"close": "Close",
|
||||
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Recent",
|
||||
"header.unread": "Unread",
|
||||
"header.users": "Users",
|
||||
"header.search": "Search",
|
||||
"notifications.loading": "Loading Notifications"
|
||||
}
|
||||
"header.profile": "Profile",
|
||||
|
||||
"notifications.loading": "Loading Notifications",
|
||||
"chats.loading": "Loading Chats"
|
||||
}
|
||||
|
||||
@@ -7,4 +7,4 @@
|
||||
"alternative_logins": "Alternative Logins",
|
||||
"failed_login_attempt": "Failed login attempt, please try again.",
|
||||
"login_successful": "You have successfully logged in!"
|
||||
}
|
||||
}
|
||||
|
||||
9
public/language/en/notifications.json
Normal file
9
public/language/en/notifications.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Notifications",
|
||||
"back_to_home": "back to NodeBB",
|
||||
"mark_all_as_read": "Mark All as Read",
|
||||
"outgoing_link": "Outgoing Link",
|
||||
"outgoing_link_message": "You are now leaving",
|
||||
"continue_to": "Continue to",
|
||||
"return_to": "Return to "
|
||||
}
|
||||
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"
|
||||
}
|
||||
13
public/language/en/reset_password.json
Normal file
13
public/language/en/reset_password.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"reset_password": "Reset Password",
|
||||
"update_password": "Update Password",
|
||||
"password_changed.title": "Password Changed",
|
||||
"password_changed.message": "<p>Password successfully reset, please <a href=\"/login\">log in again</a>.",
|
||||
"wrong_reset_code.title": "Incorrect Reset Code",
|
||||
"wrong_reset_code.message": "The reset code received was incorrect. Please try again, or <a href=\"/reset\">request a new reset code</a>.",
|
||||
"new_password": "New Password",
|
||||
"repeat_password": "Confirm Password",
|
||||
"enter_email": "Please enter your <strong>email address</strong> and we will send you an email with instructions on how to reset your account.",
|
||||
"password_reset_sent": "Password Reset Sent",
|
||||
"invalid_email": "Invalid Email / Email does not exist!"
|
||||
}
|
||||
@@ -1,22 +1,43 @@
|
||||
{
|
||||
"topic": "Topic",
|
||||
"topics": "Topics",
|
||||
|
||||
"no_topics_found": "No topics found!",
|
||||
|
||||
"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",
|
||||
"fork": "Fork",
|
||||
"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",
|
||||
|
||||
"favourite": "Favourite",
|
||||
"favourites": "Favorites",
|
||||
"favourites.not_logged_in.title": "Not Logged In",
|
||||
"favourites.not_logged_in.message": "Please log in in order to favourite this post"
|
||||
}
|
||||
"favourites.not_logged_in.message": "Please log in in order to favourite this post",
|
||||
"favourites.has_no_favourites": "You don't have any favourites, favourite some posts to see them here!",
|
||||
|
||||
"posted_by": "posted by",
|
||||
"loading": "Loading",
|
||||
"more_posts": "More Posts",
|
||||
"move_topic": "Move Topic",
|
||||
"topic_will_be_moved_to": "This topic will be moved to the category",
|
||||
|
||||
"reputation": "Reputation",
|
||||
"posts": "Posts"
|
||||
}
|
||||
|
||||
40
public/language/en/user.json
Normal file
40
public/language/en/user.json
Normal file
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"banned": "Banned",
|
||||
"offline": "Offline",
|
||||
"email": "Email",
|
||||
"fullname": "Full Name",
|
||||
"website": "Website",
|
||||
"location": "Location",
|
||||
"age": "Age",
|
||||
"joined": "Joined",
|
||||
"lastonline": "Last Online",
|
||||
"profile_views": "Profile views",
|
||||
"reputation": "Reputation",
|
||||
"posts": "Posts",
|
||||
"followers": "Followers",
|
||||
"following": "Following",
|
||||
"signature": "Signature",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Birthday",
|
||||
|
||||
"change_picture": "Change Picture",
|
||||
"edit": "Edit",
|
||||
"uploaded_picture": "Uploaded Picture",
|
||||
"upload_new_picture": "Upload New Picture",
|
||||
"change_password": "Change Password",
|
||||
"confirm_password": "Confirm Password",
|
||||
"password": "Password",
|
||||
|
||||
"upload_picture": "Upload picture",
|
||||
"upload_a_picture": "Upload a picture",
|
||||
"image_spec": "You may only upload PNG, JPG, or GIF files under 256kb.",
|
||||
|
||||
"settings": "settings",
|
||||
"show_email": "Show My Email",
|
||||
|
||||
"has_no_follower": "This user doesn't have any followers :(",
|
||||
"follows_no_one": "This user isn't following anyone :(",
|
||||
|
||||
"email_hidden": "Email Hidden",
|
||||
"hidden": "hidden"
|
||||
}
|
||||
14
public/language/es/category.json
Normal file
14
public/language/es/category.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"new_topic_button": "Nuevo Tema",
|
||||
"no_topics": "<strong>No hay temas en esta categoría.</strong><br />Por que no te animas y publicas uno?",
|
||||
"sidebar.recent_replies": "Respuestas recientes",
|
||||
"sidebar.active_participants": "Miembros más activos",
|
||||
"sidebar.moderators": "Moderadores",
|
||||
"posts": "respuestas",
|
||||
"views": "visitas",
|
||||
"posted": "posted",
|
||||
"browsing": "viendo ahora",
|
||||
"no_replies": "Nadie ha respondido aún",
|
||||
"replied": "respondio",
|
||||
"last_edited_by": "ultima edición por"
|
||||
}
|
||||
10
public/language/es/footer.json
Normal file
10
public/language/es/footer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"chat.chatting_with": "Chatear con <span id='chat-with-name'></span>",
|
||||
"chat.placeholder": "ingresa tu mensaje aqui, y presiona enter para enviar",
|
||||
"chat.send": "Enviar",
|
||||
"stats.online": "Online",
|
||||
"stats.users": "Usuarios",
|
||||
"stats.topics": "Temas",
|
||||
"stats.posts": "Posts",
|
||||
"success": "exito!"
|
||||
}
|
||||
31
public/language/es/global.json
Normal file
31
public/language/es/global.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"home": "Inicio",
|
||||
"search": "Buscar",
|
||||
"buttons.close": "Cerrar",
|
||||
"403.title": "Acceso denegado",
|
||||
"403.message": "Al parecer no tienes premisos necesarios para estar en este lugar. Tal vez puedes <a href='/login'>intentar conectarte</a>?",
|
||||
"404.title": "Ups... 404, no se encontra che!",
|
||||
"404.message": "Al parecer lo que estas buscando no existe. Te recomendamos que vuelvas al <a href='/''>inicio</a>.",
|
||||
"500.title": "Error Interno.",
|
||||
"500.message": "Ooops! Algo salio mal!, No te alarmes. Nuestros simios hiperinteligentes lo solucionarán",
|
||||
|
||||
"register": "Registrarse",
|
||||
"login": "Conectarse",
|
||||
|
||||
"logout": "Salir",
|
||||
"logout.title": "Te has desconectado.",
|
||||
"logout.message": "Haz sido desconectado correctamente",
|
||||
|
||||
"save_changes": "Guardar Cambios",
|
||||
"close": "Cerrar",
|
||||
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Recientes",
|
||||
"header.unread": "No Leeidos",
|
||||
"header.users": "Miembros",
|
||||
"header.search": "Buscar",
|
||||
"header.profile": "Perfil",
|
||||
|
||||
"notifications.loading": "Cargando Notificaciones",
|
||||
"chats.loading": "Cargando Chats"
|
||||
}
|
||||
10
public/language/es/login.json
Normal file
10
public/language/es/login.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"login": "Conectarse",
|
||||
"username": "Usuario",
|
||||
"password": "Contraseña",
|
||||
"remember_me": "Recordarme?",
|
||||
"forgot_password": "Olvidaste tu contraseña?",
|
||||
"alternative_logins": "Conexiones Alternativas",
|
||||
"failed_login_attempt": "Error al loguearte, intenta de nuevo.",
|
||||
"login_successful": "Te has conectado con exito!"
|
||||
}
|
||||
9
public/language/es/notifications.json
Normal file
9
public/language/es/notifications.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Notificaciones",
|
||||
"back_to_home": "volver al Inicio",
|
||||
"mark_all_as_read": "Marcar todo como leeido",
|
||||
"outgoing_link": "Link Externo",
|
||||
"outgoing_link_message": "Estas saliendo del sitio",
|
||||
"continue_to": "Continuar",
|
||||
"return_to": "Volver a "
|
||||
}
|
||||
5
public/language/es/recent.json
Normal file
5
public/language/es/recent.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"day": "Día",
|
||||
"week": "Semana",
|
||||
"month": "Mes"
|
||||
}
|
||||
16
public/language/es/register.json
Normal file
16
public/language/es/register.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"register": "Registrase",
|
||||
"help.email": "Por defecto, tu email será oculto al publico.",
|
||||
"help.username_restrictions": "El nombre de usuario debe tener entre %1 y %2 caracteres. Los miembros pueden responderte escribiendo @<span id='yourUsername'>usuario</span>.",
|
||||
"help.minimum_password_length": "Tu contraseña debe tener al menos %1 caracteres.",
|
||||
"email_address": "Email",
|
||||
"email_address_placeholder": "Escribe tu email",
|
||||
"username": "Usuario",
|
||||
"username_placeholder": "Escribe tu usuario",
|
||||
"password": "Contraseña",
|
||||
"password_placeholder": "Escribe tu Contraseña",
|
||||
"confirm_password": "Confirmar Contraseña",
|
||||
"confirm_password_placeholder": "Confirmar Contraseña",
|
||||
"register_now_button": "Registrarme ahora",
|
||||
"alternative_registration": "Otros metodos interesantes para registrarse"
|
||||
}
|
||||
13
public/language/es/reset_password.json
Normal file
13
public/language/es/reset_password.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"reset_password": "Resetear Contraseña",
|
||||
"update_password": "Actualizar contraseña",
|
||||
"password_changed.title": "Contraseña editada",
|
||||
"password_changed.message": "<p>La contraseña fue modificada con exito, por favor <a href=\"/login\">conectate de nuevo</a>.",
|
||||
"wrong_reset_code.title": "Código de Reseteo Incorrecto",
|
||||
"wrong_reset_code.message": "El código de reseteo ingresado no es correcto. Por favor intentalo de nuevo o <a href=\"/reset\">pide un nuevo código</a>.",
|
||||
"new_password": "Nueva Contraseña",
|
||||
"repeat_password": "Confirmar Contraseña",
|
||||
"enter_email": "Por favor ingresa tu <strong>email</strong> y te enviaremos un email de como resetear tu cuenta.",
|
||||
"password_reset_sent": "Resteo de contraseña enviado",
|
||||
"invalid_email": "Email Invalido o no existe!"
|
||||
}
|
||||
42
public/language/es/topic.json
Normal file
42
public/language/es/topic.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"topic": "Tema",
|
||||
"topics": "Temas",
|
||||
|
||||
"no_topics_found": "No se encontraron temas!",
|
||||
|
||||
"profile": "Perfil",
|
||||
"posted_by": "Publicado por",
|
||||
"chat": "Chat",
|
||||
"notify_me": "Seras notificado cuando haya nuevas respuestas en este tema",
|
||||
"quote": "Citar",
|
||||
"reply": "Responder",
|
||||
"edit": "Editar",
|
||||
"delete": "Borrar",
|
||||
"banned": "banneado",
|
||||
"link": "Link",
|
||||
|
||||
"thread_tools.title": "Herramientas del Tema",
|
||||
"thread_tools.pin": "Poner Sticky",
|
||||
"thread_tools.lock": "Cerrar Tema",
|
||||
"thread_tools.move": "Mover Tema",
|
||||
"thread_tools.delete": "Borrar Tema",
|
||||
|
||||
"load_categories": "Cargando Categorias",
|
||||
"disabled_categories_note": "Las categorías deshabilidas estan en gris",
|
||||
"confirm_move": "Mover",
|
||||
|
||||
"favourite": "Favorito",
|
||||
"favourites": "Favoritos",
|
||||
"favourites.not_logged_in.title": "No estas conectado :(",
|
||||
"favourites.not_logged_in.message": "Por favor, conectate para agregar a favorito este post.",
|
||||
"favourites.has_no_favourites": "No tienes favoritos, puedes agregar alguno y volver a verlos aqui!",
|
||||
|
||||
"posted_by": "Publicado por",
|
||||
"loading": "Cargando",
|
||||
"more_posts": "Más posts",
|
||||
"move_topic": "Mover Tema",
|
||||
"topic_will_be_moved_to": "Este tema sera movido a la categoría",
|
||||
|
||||
"reputation": "Reputación",
|
||||
"posts": "Posts"
|
||||
}
|
||||
5
public/language/es/unread.json
Normal file
5
public/language/es/unread.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"no_unread_topics": "No hay temas nuevos para leer.",
|
||||
"mark_all_read": "Marcar todo como leeido",
|
||||
"load_more": "Cargar más"
|
||||
}
|
||||
36
public/language/es/user.json
Normal file
36
public/language/es/user.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"banned": "Banneado",
|
||||
"offline": "Desconectado",
|
||||
"email": "Email",
|
||||
"fullname": "Nombre Completo",
|
||||
"website": "Website",
|
||||
"location": "Ubicación",
|
||||
"age": "Edad",
|
||||
"joined": "Registro",
|
||||
"profile_views": "Visitas en su perfil",
|
||||
"reputation": "Reputación",
|
||||
"posts": "Posts",
|
||||
"followers": "Seguidores",
|
||||
"following": "Siguiendo",
|
||||
"signature": "Firma",
|
||||
"gravatar": "Gravatar",
|
||||
"birthday": "Cumpleaños",
|
||||
|
||||
"change_picture": "Cambiar Foto",
|
||||
"edit": "Editar",
|
||||
"uploaded_picture": "Fotos Cargadas",
|
||||
"upload_new_picture": "Cargar Nueva Foto",
|
||||
"change_password": "Cambiar Contraseña",
|
||||
"confirm_password": "Confirmar Contraseña",
|
||||
"password": "Contraseña",
|
||||
|
||||
"upload_picture": "Cargar foto",
|
||||
"upload_a_picture": "Cargar una foto",
|
||||
"image_spec": "Solo puedes usar PNG, JPG, o GIF hasta 256kb.",
|
||||
|
||||
"settings": "Opciones",
|
||||
"show_email": "Mostrar mi Email",
|
||||
|
||||
"has_no_follower": "Este miembro no tiene seguidores :(",
|
||||
"follows_no_one": "Este miembro no sigue a nadie, que pena :("
|
||||
}
|
||||
9
public/language/es/users.json
Normal file
9
public/language/es/users.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"latest_users": "Ultimos Miembros",
|
||||
"top_posters": "Top Posteadores",
|
||||
"most_reputation": "Mayor Reputación",
|
||||
"online": "Conectados",
|
||||
"search": "Buscar",
|
||||
"enter_username": "Ingresa el nombre de usuario para buscar",
|
||||
"load_more": "Cargar más"
|
||||
}
|
||||
14
public/language/fr/category.json
Normal file
14
public/language/fr/category.json
Normal file
@@ -0,0 +1,14 @@
|
||||
{
|
||||
"new_topic_button": "Nouveau Sujet",
|
||||
"no_topics": "<strong>Il n'y a aucun topic dans cette catégorie.</strong><br />Pourquoi ne pas en créer un?",
|
||||
"sidebar.recent_replies": "Réponses Récentes",
|
||||
"sidebar.active_participants": "Participants Actifs",
|
||||
"sidebar.moderators": "Modérateurs",
|
||||
"posts": "messages",
|
||||
"views": "vues",
|
||||
"posted": "posté",
|
||||
"browsing": "naviguer",
|
||||
"no_replies": "Personne n'a répondu",
|
||||
"replied": "répondu",
|
||||
"last_edited_by": "dernière édition par"
|
||||
}
|
||||
10
public/language/fr/footer.json
Normal file
10
public/language/fr/footer.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"chat.chatting_with": "Chat avec <span id=\"chat-with-name\"></span>",
|
||||
"chat.placeholder": "taper le message ici, presser entrer pour envoyer",
|
||||
"chat.send": "Envoyer",
|
||||
"stats.online": "Online",
|
||||
"stats.users": "Utilisateurs",
|
||||
"stats.topics": "Sujets",
|
||||
"stats.posts": "Message",
|
||||
"success": "succès"
|
||||
}
|
||||
31
public/language/fr/global.json
Normal file
31
public/language/fr/global.json
Normal file
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"home": "Accueil",
|
||||
"search": "Recherche",
|
||||
"buttons.close": "Fermer",
|
||||
"403.title": "Accès Refusé",
|
||||
"403.message": "Il semble que vous vous soyez retrouvé sur une page dont vous n'avez pas accès. Peut-être devriez vous <a href='/login'>essayez de vous connecter</a>?",
|
||||
"404.title": "Introuvable",
|
||||
"404.message": "Il semble que vous vous soyez retrouvé sur une page qui n'existe pas. Retourner à <a href='/'>l'accueil</a>.",
|
||||
"500.title": "Erreur Interne.",
|
||||
"500.message": "Oops! Il semblerait que quelque chose se soit mal passé!",
|
||||
|
||||
"register": "S'inscrire",
|
||||
"login": "Connecter",
|
||||
|
||||
"logout": "Déconnection",
|
||||
"logout.title": "Vous êtes maintenant déconnecté.",
|
||||
"logout.message": "Vous vous êtes déconnecté de NodeBB avec succès",
|
||||
|
||||
"save_changes": "Enregistrer les changements",
|
||||
"close": "Fermer",
|
||||
|
||||
"header.admin": "Admin",
|
||||
"header.recent": "Récent",
|
||||
"header.unread": "Non Lu",
|
||||
"header.users": "Utilisateurs",
|
||||
"header.search": "Recherche",
|
||||
"header.profile": "Profile",
|
||||
|
||||
"notifications.loading": "Chargement des Notifications",
|
||||
"chats.loading": "Chargement des Chats"
|
||||
}
|
||||
10
public/language/fr/login.json
Normal file
10
public/language/fr/login.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"login": "Connexion",
|
||||
"username": "Identifiant",
|
||||
"password": "Mot de passe",
|
||||
"remember_me": "Se souvenir de moi?",
|
||||
"forgot_password": "Mot de passe oublié?",
|
||||
"alternative_logins": "Connexion Alternative",
|
||||
"failed_login_attempt": "Echèc d'authentification, veuillez réessayer.",
|
||||
"login_successful": "Vous êtes maintenant connecté!"
|
||||
}
|
||||
9
public/language/fr/notifications.json
Normal file
9
public/language/fr/notifications.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"title": "Notifications",
|
||||
"back_to_home": "retour à NodeBB",
|
||||
"mark_all_as_read": "Tout marquer comme lu",
|
||||
"outgoing_link": "Lien Sortant",
|
||||
"outgoing_link_message": "Vous quitter NodeBB",
|
||||
"continue_to": "Continuer vers",
|
||||
"return_to": "Retour vers"
|
||||
}
|
||||
5
public/language/fr/recent.json
Normal file
5
public/language/fr/recent.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"day": "Jour",
|
||||
"week": "Semaine",
|
||||
"month": "Mois"
|
||||
}
|
||||
16
public/language/fr/register.json
Normal file
16
public/language/fr/register.json
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"register": "S'inscrire",
|
||||
"help.email": "Par défault, votre email est masqué du public.",
|
||||
"help.username_restrictions": "Un identifiant unique entre %1 et %2 charactères. Les autres utilisateurs peuvent vous citer avec @<span id='yourUsername'>username</span>.",
|
||||
"help.minimum_password_length": "Votre mot de passe doit avoir au moins %1 charactères.",
|
||||
"email_address": "Adresse Email",
|
||||
"email_address_placeholder": "Entrer l'addresse Email",
|
||||
"username": "Nom d'utilisateur",
|
||||
"username_placeholder": "Entré le Nom d'utilisateur",
|
||||
"password": "Mot de passe",
|
||||
"password_placeholder": "Entrer le Mot de passe",
|
||||
"confirm_password": "Confirmer le Mot de passe",
|
||||
"confirm_password_placeholder": "Confirmer le Mot de passe",
|
||||
"register_now_button": "S'enregistrer maintenant",
|
||||
"alternative_registration": "Enregistrement Alternatif"
|
||||
}
|
||||
13
public/language/fr/reset_password.json
Normal file
13
public/language/fr/reset_password.json
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"reset_password": "Réinitialiser le Mot de passe",
|
||||
"update_password": "Mettre à jour le Mot de passe",
|
||||
"password_changed.title": "Mot de passe modifié",
|
||||
"password_changed.message": "<p>Mot de passe réinitialisé avec succès, veuillez vous <a href=\"/login\">reconnecter</a>.",
|
||||
"wrong_reset_code.title": "Code de Réinisialisation Incorrect",
|
||||
"wrong_reset_code.message": "Le Code de Réinisialisation est Incorrect. Veillez réessayer, ou <a href=\"/reset\">demander un nouveau Code de Réinisialisation</a>.",
|
||||
"new_password": "Nouveau Mot de passe",
|
||||
"repeat_password": "Confirmer le Mot de passe",
|
||||
"enter_email": "Veuillez entrer votre <strong>adresse email</strong> et vous recevrez un email avec les instruction pour réinitialiser votre compte.",
|
||||
"password_reset_sent": "Réinitialisation de Mot de Passe Envoyée",
|
||||
"invalid_email": "Email Invalide / L'Email n'existe pas!"
|
||||
}
|
||||
42
public/language/fr/topic.json
Normal file
42
public/language/fr/topic.json
Normal file
@@ -0,0 +1,42 @@
|
||||
{
|
||||
"topic": "Sujet",
|
||||
"topics": "Sujets",
|
||||
|
||||
"no_topics_found": "Aucun sujet trouvé!",
|
||||
|
||||
"profile": "Profile",
|
||||
"posted_by": "Envoyé by",
|
||||
"chat": "Chat",
|
||||
"notify_me": "Être notifié des réponses dans ce sujet",
|
||||
"quote": "Citer",
|
||||
"reply": "Répondre",
|
||||
"edit": "Editer",
|
||||
"delete": "Supprimer",
|
||||
"banned": "bannir",
|
||||
"link": "Lien",
|
||||
|
||||
"thread_tools.title": "Outils du Fil",
|
||||
"thread_tools.pin": "Epingler le fil",
|
||||
"thread_tools.lock": "Verrouiller le fil",
|
||||
"thread_tools.move": "Déplacer le fil",
|
||||
"thread_tools.delete": "Supprimer le fil",
|
||||
|
||||
"load_categories": "Chargement des Categories",
|
||||
"disabled_categories_note": "Les Catégories Désactivées sont grisées",
|
||||
"confirm_move": "Déplacer",
|
||||
|
||||
"favourite": "Favoris",
|
||||
"favourites": "Favoris",
|
||||
"favourites.not_logged_in.title": "Non Connecté",
|
||||
"favourites.not_logged_in.message": "Veuillez vous connecter avant de mettre ce message en Favoris",
|
||||
"favourites.has_no_favourites": "Vous n'avez aucun Favoris, mettre en favoris des messages pour les voir apparaître ici!",
|
||||
|
||||
"posted_by": "posté par",
|
||||
"loading": "Chargement",
|
||||
"more_posts": "d'autres Messages",
|
||||
"move_topic": "Déplacer le Sujet",
|
||||
"topic_will_be_moved_to": "Ce sujet sera déplacé vers la catégorie",
|
||||
|
||||
"reputation": "réputation",
|
||||
"posts": "messages"
|
||||
}
|
||||
5
public/language/fr/unread.json
Normal file
5
public/language/fr/unread.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"no_unread_topics": "Aucun sujet non lu.",
|
||||
"mark_all_read": "Marquer tout comme lu",
|
||||
"load_more": "Charger la suite"
|
||||
}
|
||||
36
public/language/fr/user.json
Normal file
36
public/language/fr/user.json
Normal file
@@ -0,0 +1,36 @@
|
||||
{
|
||||
"banned": "Banni",
|
||||
"offline": "Hors-ligne",
|
||||
"email": "email",
|
||||
"fullname": "Nom",
|
||||
"website": "Site Web",
|
||||
"location": "Emplacement",
|
||||
"age": "age",
|
||||
"joined": "adhésion",
|
||||
"profil_views": "vues du profil",
|
||||
"reputation": "réputation",
|
||||
"posts": "messages",
|
||||
"followers": "suiveurs",
|
||||
"following": "suivis",
|
||||
"signature": "signature",
|
||||
"gravatar": "gravatar",
|
||||
"birthday": "anniversaire",
|
||||
|
||||
"change_picture": "changer d'image",
|
||||
"edit": "editer",
|
||||
"uploaded_picture": "images uploadées",
|
||||
"upload_new_picture": "uploader une nouvelle image",
|
||||
"change_password": "chnger le mot de passe",
|
||||
"confirm_password": "confirmer le mot de passe",
|
||||
"password": "mot de passe",
|
||||
|
||||
"upload_picture": "Uploader un image",
|
||||
"upload_a_picture": "Uploader un image",
|
||||
"image_spec": "Vous pouvez uploader seulement des fichiers de types PNG, JPG, ou GIF en dessous de 256kb.",
|
||||
|
||||
"settings": "paramètres",
|
||||
"show_my_email": "montrer mon email",
|
||||
|
||||
"has_no_follower": "Cet utilisateur n'a aucun suiver :(",
|
||||
"follows_no_one": "Cet utilisateur ne suit personne :("
|
||||
}
|
||||
9
public/language/fr/users.json
Normal file
9
public/language/fr/users.json
Normal file
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"latest_users": "Derniers Utilisateurs",
|
||||
"top_posters": "Meilleurs Publieur",
|
||||
"most_reputation": "Meilleur Réputation",
|
||||
"online": "En Ligne",
|
||||
"search": "Rechercher",
|
||||
"enter_username": "Entrer un nom d'utilisateur pour rechercher",
|
||||
"load_more": "Charger la suite"
|
||||
}
|
||||
BIN
public/logo.png
Normal file
BIN
public/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.1 KiB |
@@ -1,5 +1,6 @@
|
||||
var ajaxify = {};
|
||||
"use strict";
|
||||
|
||||
var ajaxify = {};
|
||||
|
||||
(function ($) {
|
||||
/*global app, templates, utils*/
|
||||
@@ -23,18 +24,27 @@ var ajaxify = {};
|
||||
|
||||
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, true);
|
||||
}
|
||||
};
|
||||
|
||||
var pagination;
|
||||
var pagination, paginator_bar;
|
||||
|
||||
ajaxify.go = function (url, callback, template, quiet) {
|
||||
ajaxify.currentPage = null;
|
||||
|
||||
ajaxify.go = function (url, callback, quiet) {
|
||||
// start: the following should be set like so: ajaxify.onchange(function(){}); where the code actually belongs
|
||||
$(window).off('scroll');
|
||||
app.enter_room('global');
|
||||
app.enterRoom('global');
|
||||
|
||||
pagination = pagination || document.getElementById('pagination');
|
||||
if (pagination) pagination.parentNode.style.display = 'none';
|
||||
paginator_bar = pagination ? document.body.querySelector('.progress-container') : undefined;
|
||||
if (pagination) {
|
||||
pagination.parentNode.style.display = 'none';
|
||||
paginator_bar.style.display = 'none';
|
||||
}
|
||||
|
||||
window.onscroll = null;
|
||||
// end
|
||||
|
||||
@@ -61,38 +71,61 @@ var ajaxify = {};
|
||||
}
|
||||
|
||||
if (templates.is_available(tpl_url) && !templates.force_refresh(tpl_url)) {
|
||||
if (quiet !== true) {
|
||||
if (window.history && window.history.pushState) {
|
||||
window.history.pushState({
|
||||
"url": url
|
||||
}, url, RELATIVE_PATH + "/" + url);
|
||||
}
|
||||
ajaxify.currentPage = tpl_url;
|
||||
|
||||
if (window.history && window.history.pushState) {
|
||||
window.history[!quiet ? 'pushState' : 'replaceState']({
|
||||
url: url
|
||||
}, url, RELATIVE_PATH + '/' + url);
|
||||
|
||||
$.ajax(RELATIVE_PATH + '/plugins/fireHook', {
|
||||
type: 'PUT',
|
||||
data: {
|
||||
_csrf: $('#csrf_token').val(),
|
||||
hook: 'page.load',
|
||||
args: {
|
||||
template: tpl_url,
|
||||
url: url,
|
||||
uid: app.uid
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
translator.load(tpl_url);
|
||||
|
||||
jQuery('#footer, #content').fadeOut(100);
|
||||
jQuery('#footer, #content').removeClass('hide').addClass('ajaxifying');
|
||||
|
||||
templates.flush();
|
||||
templates.load_template(function () {
|
||||
exec_body_scripts(content);
|
||||
require(['forum/' + tpl_url], function(script) {
|
||||
if (script && script.init) {
|
||||
script.init();
|
||||
}
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
|
||||
jQuery('#content, #footer').stop(true, true).fadeIn(200, function () {
|
||||
app.processPage();
|
||||
|
||||
app.process_page();
|
||||
if (window.location.hash)
|
||||
hash = window.location.hash;
|
||||
if (hash)
|
||||
app.scrollToPost(hash.substr(1));
|
||||
});
|
||||
jQuery('#content, #footer').stop(true, true).removeClass('ajaxifying');
|
||||
|
||||
utils.refreshTitle(url);
|
||||
if (window.location.hash) {
|
||||
hash = window.location.hash;
|
||||
}
|
||||
|
||||
}, url, template);
|
||||
if (hash) {
|
||||
require(['forum/topic'], function(topic) {
|
||||
topic.scrollToPost(hash.substr(1));
|
||||
});
|
||||
}
|
||||
|
||||
app.refreshTitle(url);
|
||||
|
||||
}, url);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -100,23 +133,36 @@ var ajaxify = {};
|
||||
return false;
|
||||
};
|
||||
|
||||
ajaxify.refresh = function() {
|
||||
ajaxify.go(ajaxify.currentPage);
|
||||
};
|
||||
|
||||
$('document').ready(function () {
|
||||
if (!window.history || !window.history.pushState) return; // no ajaxification for old browsers
|
||||
if (!window.history || !window.history.pushState) {
|
||||
return; // no ajaxification for old browsers
|
||||
}
|
||||
|
||||
content = content || document.getElementById('content');
|
||||
|
||||
// Enhancing all anchors to ajaxify...
|
||||
$(document.body).on('click', 'a', function (e) {
|
||||
function hrefEmpty(href) {
|
||||
return href == 'javascript:;' || href == window.location.href + "#" || href.slice(-1) === "#";
|
||||
return href === 'javascript:;' || href === window.location.href + "#" || href.slice(-1) === "#";
|
||||
}
|
||||
|
||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:')
|
||||
if (hrefEmpty(this.href) || this.target !== '' || this.protocol === 'javascript:') {
|
||||
return;
|
||||
}
|
||||
|
||||
app.previousUrl = window.location.href;
|
||||
if(!window.location.pathname.match(/\/(403|404)$/g)) {
|
||||
app.previousUrl = window.location.href;
|
||||
}
|
||||
|
||||
if (!e.ctrlKey && e.which === 1) {
|
||||
if (this.getAttribute('data-ajaxify') === 'false') {
|
||||
return;
|
||||
}
|
||||
|
||||
if ((!e.ctrlKey && !e.shiftKey) && e.which === 1) {
|
||||
if (this.host === window.location.host) {
|
||||
// Internal link
|
||||
var url = this.href.replace(rootUrl + '/', '');
|
||||
@@ -127,8 +173,11 @@ var ajaxify = {};
|
||||
}
|
||||
} else if (window.location.pathname !== '/outgoing') {
|
||||
// External Link
|
||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
||||
e.preventDefault();
|
||||
|
||||
if (config.useOutgoingLinksPage == true) {
|
||||
ajaxify.go('outgoing?url=' + encodeURIComponent(this.href));
|
||||
e.preventDefault();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -165,7 +214,7 @@ var ajaxify = {};
|
||||
|
||||
var scripts = [],
|
||||
script,
|
||||
children_nodes = $(body_el).children(),
|
||||
children_nodes = $(body_el).find('script'),
|
||||
child,
|
||||
i;
|
||||
|
||||
@@ -186,4 +235,4 @@ var ajaxify = {};
|
||||
}
|
||||
}
|
||||
|
||||
}(jQuery));
|
||||
}(jQuery));
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
var socket,
|
||||
config,
|
||||
app = {};
|
||||
app = {
|
||||
"username": null,
|
||||
"uid": null,
|
||||
"isFocused": true,
|
||||
"currentRoom": null
|
||||
};
|
||||
|
||||
(function () {
|
||||
var showWelcomeMessage = false;
|
||||
|
||||
app.loadConfig = function() {
|
||||
|
||||
$.ajax({
|
||||
url: RELATIVE_PATH + '/api/config',
|
||||
success: function (data) {
|
||||
@@ -17,13 +21,20 @@ var socket,
|
||||
socket.socket.connect();
|
||||
}, 200);
|
||||
} else {
|
||||
socket = io.connect(config.socket.address);
|
||||
var max_reconnection_attemps = 5;
|
||||
var reconnection_delay = 200;
|
||||
socket = io.connect(RELATIVE_PATH, {
|
||||
'max reconnection attempts': max_reconnection_attemps,
|
||||
'reconnection delay': reconnection_delay
|
||||
});
|
||||
|
||||
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']
|
||||
@@ -36,11 +47,40 @@ var socket,
|
||||
|
||||
socket.on('connect', function (data) {
|
||||
if (reconnecting) {
|
||||
reconnectEl.html('<i class="icon-ok"></i> Connected!');
|
||||
reconnectEl.tooltip('destroy');
|
||||
reconnectEl.html('<i class="fa fa-check"></i>');
|
||||
reconnecting = false;
|
||||
|
||||
// Rejoin room that was left when we disconnected
|
||||
var url_parts = document.location.pathname.slice(RELATIVE_PATH.length).split('/').slice(1),
|
||||
room;
|
||||
switch(url_parts[0]) {
|
||||
case 'user':
|
||||
room = 'user/' + templates.get('theirid');
|
||||
case 'topic':
|
||||
room = 'topic_' + url_parts[1];
|
||||
break;
|
||||
case 'category':
|
||||
room = 'category_' + url_parts[1];
|
||||
break;
|
||||
case 'recent': // intentional fall-through
|
||||
case 'unread':
|
||||
room = 'recent_posts';
|
||||
break;
|
||||
case 'admin':
|
||||
room = 'admin';
|
||||
break;
|
||||
|
||||
default:
|
||||
room = 'global';
|
||||
break;
|
||||
}
|
||||
app.enterRoom(room, true);
|
||||
|
||||
socket.emit('reconnected');
|
||||
|
||||
setTimeout(function() {
|
||||
reconnectEl.removeClass('active');
|
||||
reconnectEl.removeClass('active').addClass("hide");
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
@@ -53,12 +93,22 @@ var socket,
|
||||
socket.socket.connect();
|
||||
});
|
||||
|
||||
socket.on('reconnecting', function (data) {
|
||||
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...');
|
||||
if (!reconnectEl.hasClass('active')) reconnectEl.html('<i class="fa fa-spinner fa-spin"></i>');
|
||||
reconnectEl.addClass('active').removeClass("hide");
|
||||
|
||||
reconnectEl.tooltip({
|
||||
placement: 'bottom'
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('api:user.get_online_users', function (users) {
|
||||
@@ -71,10 +121,10 @@ var socket,
|
||||
|
||||
if (uid && jQuery.inArray(uid, users) !== -1) {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle"></i>');
|
||||
el.prepend('<i class="fa fa-circle"></i>');
|
||||
} else {
|
||||
el.find('i').remove();
|
||||
el.prepend('<i class="icon-circle-blank"></i>');
|
||||
el.prepend('<i class="fa fa-circle-o"></i>');
|
||||
}
|
||||
|
||||
el.processed = true;
|
||||
@@ -97,28 +147,36 @@ var socket,
|
||||
});
|
||||
});
|
||||
|
||||
app.enter_room('global');
|
||||
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.enterRoom('global');
|
||||
}
|
||||
},
|
||||
async: false
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.logout = function() {
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: $('#csrf_token').val()
|
||||
}, function() {
|
||||
window.location.href = RELATIVE_PATH + '/';
|
||||
});
|
||||
};
|
||||
|
||||
// takes a string like 1000 and returns 1,000
|
||||
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) {
|
||||
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,
|
||||
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
|
||||
|
||||
return input.replace(commentsAndPhpTags, '').replace(tags, function ($0, $1) {
|
||||
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
@@ -130,6 +188,7 @@ var socket,
|
||||
var alert_id = 'alert_button_' + ((params.alert_id) ? params.alert_id : new Date().getTime());
|
||||
|
||||
var alert = $('#' + alert_id);
|
||||
var title = params.title || '';
|
||||
|
||||
function startTimeout(div, timeout) {
|
||||
var timeoutId = setTimeout(function () {
|
||||
@@ -142,53 +201,45 @@ var socket,
|
||||
}
|
||||
|
||||
if (alert.length > 0) {
|
||||
alert.find('strong').html(params.title);
|
||||
alert.find('strong').html(title);
|
||||
alert.find('p').html(params.message);
|
||||
alert.attr('class', "alert toaster-alert " + "alert-" + params.type);
|
||||
|
||||
clearTimeout(alert.attr('timeoutId'));
|
||||
startTimeout(alert, params.timeout);
|
||||
} else {
|
||||
var div = document.createElement('div'),
|
||||
button = document.createElement('button'),
|
||||
strong = document.createElement('strong'),
|
||||
p = document.createElement('p');
|
||||
var div = $('<div id="' + alert_id + '" class="alert toaster-alert alert-' + params.type +'"></div>'),
|
||||
button = $('<button class="close">×</button>'),
|
||||
strong = $('<strong>' + title + '</strong>'),
|
||||
p = $('<p>' + params.message + '</p>');
|
||||
|
||||
p.innerHTML = params.message;
|
||||
strong.innerHTML = params.title;
|
||||
div.append(button)
|
||||
.append(strong)
|
||||
.append(p);
|
||||
|
||||
div.className = "alert toaster-alert " + "alert-" + params.type;
|
||||
|
||||
div.setAttribute('id', alert_id);
|
||||
div.appendChild(button);
|
||||
div.appendChild(strong);
|
||||
div.appendChild(p);
|
||||
|
||||
button.className = 'close';
|
||||
button.innerHTML = '×';
|
||||
button.onclick = function (ev) {
|
||||
div.parentNode.removeChild(div);
|
||||
}
|
||||
button.on('click', function () {
|
||||
div.remove();
|
||||
});
|
||||
|
||||
if (params.location == null)
|
||||
params.location = 'alert_window';
|
||||
|
||||
jQuery('#' + params.location).prepend(jQuery(div).fadeIn('100'));
|
||||
$('#' + params.location).prepend(div.fadeIn('100'));
|
||||
|
||||
if (params.timeout) {
|
||||
startTimeout(div, params.timeout);
|
||||
}
|
||||
|
||||
if (params.clickfn) {
|
||||
div.onclick = function () {
|
||||
div.on('click', function () {
|
||||
params.clickfn();
|
||||
jQuery(div).fadeOut(500, function () {
|
||||
this.remove();
|
||||
div.fadeOut(500, function () {
|
||||
$(this).remove();
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.alertSuccess = function (message, timeout) {
|
||||
if (!timeout)
|
||||
@@ -200,7 +251,7 @@ var socket,
|
||||
type: 'success',
|
||||
timeout: timeout
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.alertError = function (message, timeout) {
|
||||
if (!timeout)
|
||||
@@ -212,24 +263,24 @@ var socket,
|
||||
type: 'danger',
|
||||
timeout: timeout
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.current_room = null;
|
||||
app.enter_room = function (room) {
|
||||
app.enterRoom = function (room, force) {
|
||||
if (socket) {
|
||||
if (app.current_room === room)
|
||||
if (app.currentRoom === room && !force) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.emit('event:enter_room', {
|
||||
'enter': room,
|
||||
'leave': app.current_room
|
||||
'leave': app.currentRoom
|
||||
});
|
||||
|
||||
app.current_room = room;
|
||||
app.currentRoom = room;
|
||||
}
|
||||
};
|
||||
|
||||
app.populate_online_users = function () {
|
||||
app.populateOnlineUsers = function () {
|
||||
var uids = [];
|
||||
|
||||
jQuery('.post-row').each(function () {
|
||||
@@ -237,19 +288,11 @@ var socket,
|
||||
});
|
||||
|
||||
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.populate_online_users();
|
||||
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
function highlightNavigationLink() {
|
||||
var path = window.location.pathname,
|
||||
parts = path.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
jQuery('#main-nav li').removeClass('active');
|
||||
@@ -264,14 +307,39 @@ var socket,
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.createUserTooltips = function() {
|
||||
$('img[title].teaser-pic,img[title].user-img').each(function() {
|
||||
$(this).tooltip({
|
||||
placement: 'top',
|
||||
title: $(this).attr('title')
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
app.makeNumbersHumanReadable = function(elements) {
|
||||
elements.each(function() {
|
||||
$(this).html(utils.makeNumberHumanReadable($(this).attr('title')));
|
||||
});
|
||||
};
|
||||
|
||||
app.processPage = function () {
|
||||
app.populateOnlineUsers();
|
||||
|
||||
highlightNavigationLink();
|
||||
|
||||
$('span.timeago').timeago();
|
||||
$('.post-content img').addClass('img-responsive');
|
||||
|
||||
app.makeNumbersHumanReadable($('.human-readable-number'));
|
||||
|
||||
app.createUserTooltips();
|
||||
|
||||
setTimeout(function () {
|
||||
window.scrollTo(0, 1); // rehide address bar on mobile after page load completes.
|
||||
}, 100);
|
||||
}
|
||||
};
|
||||
|
||||
app.showLoginMessage = function () {
|
||||
function showAlert() {
|
||||
@@ -291,15 +359,37 @@ var socket,
|
||||
showAlert();
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
app.addCommasToNumbers = function () {
|
||||
$('.formatted-number').each(function (index, element) {
|
||||
$(element).html(app.addCommas($(element).html()));
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.openChat = function (username, touid) {
|
||||
if (username === app.username) {
|
||||
app.alert({
|
||||
type: 'warning',
|
||||
title: 'Invalid Chat',
|
||||
message: "You can't chat with yourself!",
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!app.username) {
|
||||
app.alert({
|
||||
type: 'danger',
|
||||
title: 'Not Logged In',
|
||||
message: 'Please log in to chat with <strong>' + username + '</strong>',
|
||||
timeout: 5000
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
require(['chat'], function (chat) {
|
||||
var chatModal;
|
||||
if (!chat.modalExists(touid)) {
|
||||
@@ -308,59 +398,9 @@ var socket,
|
||||
chatModal = chat.getModal(touid);
|
||||
}
|
||||
chat.load(chatModal.attr('UUID'));
|
||||
chat.center(chatModal);
|
||||
});
|
||||
}
|
||||
|
||||
app.createNewPosts = function (data) {
|
||||
data.posts[0].display_moderator_tools = 'none';
|
||||
var html = templates.prepare(templates['topic'].blocks['posts']).parse(data);
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var uniqueid = new Date().getTime(),
|
||||
tempContainer = jQuery('<div id="' + uniqueid + '"></div>')
|
||||
.appendTo("#post-container")
|
||||
.hide()
|
||||
.append(translatedHTML)
|
||||
.fadeIn('slow');
|
||||
|
||||
for (var x = 0, numPosts = data.posts.length; x < numPosts; x++) {
|
||||
socket.emit('api:post.privileges', data.posts[x].pid);
|
||||
}
|
||||
|
||||
tempContainer.replaceWith(tempContainer.contents());
|
||||
infiniteLoaderActive = false;
|
||||
|
||||
app.populate_online_users();
|
||||
app.addCommasToNumbers();
|
||||
$('span.timeago').timeago();
|
||||
});
|
||||
}
|
||||
|
||||
app.infiniteLoaderActive = false;
|
||||
|
||||
app.loadMorePosts = function (tid, callback) {
|
||||
if (app.infiniteLoaderActive)
|
||||
return;
|
||||
app.infiniteLoaderActive = true;
|
||||
|
||||
if ($('#loading-indicator').attr('done') === '0')
|
||||
$('#loading-indicator').removeClass('hide');
|
||||
|
||||
socket.emit('api:topic.loadMore', {
|
||||
tid: tid,
|
||||
after: document.querySelectorAll('#post-container li[data-pid]').length
|
||||
}, function (data) {
|
||||
app.infiniteLoaderActive = false;
|
||||
if (data.posts.length) {
|
||||
$('#loading-indicator').attr('done', '0');
|
||||
app.createNewPosts(data);
|
||||
} else {
|
||||
$('#loading-indicator').attr('done', '1');
|
||||
}
|
||||
$('#loading-indicator').addClass('hide');
|
||||
if (callback)
|
||||
callback(data.posts);
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.scrollToTop = function () {
|
||||
$('body,html').animate({
|
||||
@@ -372,56 +412,66 @@ var socket,
|
||||
$('body,html').animate({
|
||||
scrollTop: $('html').height() - 100
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
app.scrollToPost = function (pid) {
|
||||
var titleObj = {
|
||||
active: false,
|
||||
interval: undefined,
|
||||
titles: []
|
||||
};
|
||||
app.alternatingTitle = function (title) {
|
||||
if (typeof title !== 'string') return;
|
||||
|
||||
if (!pid)
|
||||
return;
|
||||
if (title.length > 0) {
|
||||
titleObj.titles[1] = title;
|
||||
if (titleObj.interval) {
|
||||
clearInterval(titleObj.interval);
|
||||
}
|
||||
titleObj.interval = setInterval(function() {
|
||||
window.document.title = titleObj.titles[titleObj.titles.indexOf(window.document.title) ^ 1];
|
||||
}, 2000);
|
||||
} else {
|
||||
if (titleObj.interval) {
|
||||
clearInterval(titleObj.interval);
|
||||
}
|
||||
if (titleObj.titles[0]) window.document.title = titleObj.titles[0];
|
||||
}
|
||||
};
|
||||
|
||||
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());
|
||||
app.refreshTitle = function(url) {
|
||||
if (!url) {
|
||||
var a = document.createElement('a');
|
||||
a.href = document.location;
|
||||
url = a.pathname.slice(1);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
}
|
||||
socket.emit('api:meta.buildTitle', url, function(title, numNotifications) {
|
||||
titleObj.titles[0] = (numNotifications > 0 ? '(' + numNotifications + ') ' : '') + title;
|
||||
app.alternatingTitle('');
|
||||
});
|
||||
};
|
||||
|
||||
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());
|
||||
input.val('');
|
||||
return false;
|
||||
});
|
||||
|
||||
$(window).blur(function(){
|
||||
app.isFocused = false;
|
||||
});
|
||||
|
||||
$(window).focus(function(){
|
||||
app.isFocused = true;
|
||||
|
||||
app.alternatingTitle('');
|
||||
});
|
||||
});
|
||||
|
||||
showWelcomeMessage = location.href.indexOf('loggedin') !== -1;
|
||||
|
||||
app.loadConfig();
|
||||
|
||||
app.alternatingTitle('');
|
||||
}());
|
||||
@@ -1,85 +1,101 @@
|
||||
(function() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var Account = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enter_room('user/' + theirid);
|
||||
Account.init = function() {
|
||||
header.init();
|
||||
|
||||
app.addCommasToNumbers();
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
isFollowing = templates.get('isFollowing');
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
$(document).ready(function() {
|
||||
var username = $('.account-username a').html();
|
||||
app.enterRoom('user/' + theirid);
|
||||
|
||||
if (yourid !== theirid) {
|
||||
if (isFollowing) {
|
||||
followBtn.hide();
|
||||
unfollowBtn.show();
|
||||
app.addCommasToNumbers();
|
||||
$('.user-recent-posts img').addClass('img-responsive');
|
||||
|
||||
var followBtn = $('#follow-btn');
|
||||
var unfollowBtn = $('#unfollow-btn');
|
||||
var chatBtn = $('#chat-btn');
|
||||
|
||||
if (yourid !== theirid && yourid !== "0") {
|
||||
if (isFollowing) {
|
||||
followBtn.addClass('hide');
|
||||
unfollowBtn.removeClass('hide');
|
||||
} else {
|
||||
followBtn.removeClass('hide');
|
||||
unfollowBtn.addClass('hide');
|
||||
}
|
||||
chatBtn.removeClass('hide');
|
||||
} else {
|
||||
followBtn.show();
|
||||
unfollowBtn.hide();
|
||||
followBtn.addClass('hide');
|
||||
unfollowBtn.addClass('hide');
|
||||
chatBtn.addClass('hide');
|
||||
}
|
||||
} else {
|
||||
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 + '!');
|
||||
}
|
||||
followBtn.on('click', function() {
|
||||
socket.emit('api:user.follow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.addClass('hide');
|
||||
unfollowBtn.removeClass('hide');
|
||||
app.alertSuccess('You are now following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error following' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
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 + '!');
|
||||
}
|
||||
unfollowBtn.on('click', function() {
|
||||
socket.emit('api:user.unfollow', {
|
||||
uid: theirid
|
||||
}, function(success) {
|
||||
if (success) {
|
||||
followBtn.removeClass('hide');
|
||||
unfollowBtn.addClass('hide');
|
||||
app.alertSuccess('You are no longer following ' + username + '!');
|
||||
} else {
|
||||
app.alertError('There was an error unfollowing ' + username + '!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.user-recent-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
chatBtn.on('click', function() {
|
||||
app.openChat(username, theirid);
|
||||
});
|
||||
|
||||
$('.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);
|
||||
$('.user-recent-posts span.timeago').timeago();
|
||||
});
|
||||
|
||||
});
|
||||
};
|
||||
|
||||
Account.handleUserOnline = function(data) {
|
||||
var onlineStatus = $('.account-online-status');
|
||||
|
||||
function handleUserOnline(data) {
|
||||
if (data.online) {
|
||||
onlineStatus.find('span span').text('online');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle');
|
||||
} else {
|
||||
onlineStatus.find('span span').text('offline');
|
||||
onlineStatus.find('i').attr('class', 'icon-circle-blank');
|
||||
}
|
||||
if (data.online) {
|
||||
onlineStatus.find('span span').text('online');
|
||||
onlineStatus.find('i').attr('class', 'fa fa-circle');
|
||||
} else {
|
||||
onlineStatus.find('span span').text('offline');
|
||||
onlineStatus.find('i').attr('class', 'fa fa-circle-o');
|
||||
}
|
||||
};
|
||||
|
||||
socket.on('api:user.isOnline', handleUserOnline);
|
||||
|
||||
socket.emit('api:user.isOnline', theirid, handleUserOnline);
|
||||
|
||||
socket.on('event:new_post', function(data) {
|
||||
var html = templates.prepare(templates['account'].blocks['posts']).parse(data);
|
||||
$('.user-recent-posts').prepend(html);
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
}());
|
||||
return Account;
|
||||
});
|
||||
|
||||
@@ -1,88 +1,202 @@
|
||||
var gravatarPicture = templates.get('gravatarpicture');
|
||||
var uploadedPicture = templates.get('uploadedpicture');
|
||||
define(['forum/accountheader', 'uploader'], function(header, uploader) {
|
||||
var AccountEdit = {},
|
||||
gravatarPicture = '',
|
||||
uploadedPicture = '';
|
||||
|
||||
$(document).ready(function() {
|
||||
AccountEdit.init = function() {
|
||||
header.init();
|
||||
|
||||
gravatarPicture = templates.get('gravatarpicture');
|
||||
uploadedPicture = templates.get('uploadedpicture');
|
||||
|
||||
var selectedImageType = '';
|
||||
|
||||
$('#uploadForm').submit(function() {
|
||||
status('uploading the file ...');
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
$('#upload-progress-bar').css('width', '0%');
|
||||
$('#upload-progress-box').show();
|
||||
$('#upload-progress-box').removeClass('hide');
|
||||
var userData = {
|
||||
uid: $('#inputUID').val(),
|
||||
email: $('#inputEmail').val(),
|
||||
fullname: $('#inputFullname').val(),
|
||||
website: $('#inputWebsite').val(),
|
||||
birthday: $('#inputBirthday').val(),
|
||||
location: $('#inputLocation').val(),
|
||||
signature: $('#inputSignature').val()
|
||||
};
|
||||
|
||||
if (!$('#userPhotoInput').val()) {
|
||||
error('select an image to upload!');
|
||||
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);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
|
||||
function getSignatureCharsLeft() {
|
||||
return '(' + $('#inputSignature').val().length + '/' + config.maximumSignatureLength + ')';
|
||||
}
|
||||
|
||||
$(this).find('#imageUploadCsrf').val($('#csrf_token').val());
|
||||
$('#signatureCharCountLeft').html(getSignatureCharsLeft());
|
||||
|
||||
$('#inputSignature').on('keyup change', function(ev) {
|
||||
$('#signatureCharCountLeft').html(getSignatureCharsLeft());
|
||||
});
|
||||
|
||||
$(this).ajaxSubmit({
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
AccountEdit.updateImages();
|
||||
|
||||
error: function(xhr) {
|
||||
error('Error: ' + xhr.status);
|
||||
},
|
||||
$('#change-picture-modal').modal('show');
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
uploadProgress: function(event, position, total, percent) {
|
||||
console.log(percent);
|
||||
$('#upload-progress-bar').css('width', percent + '%');
|
||||
},
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .fa-check').show();
|
||||
$('#uploaded-box .fa-check').hide();
|
||||
selectedImageType = 'gravatar';
|
||||
});
|
||||
|
||||
success: function(response) {
|
||||
if (response.error) {
|
||||
error(response.error);
|
||||
return;
|
||||
}
|
||||
$('#uploaded-box').on('click', function() {
|
||||
$('#gravatar-box .fa-check').hide();
|
||||
$('#uploaded-box .fa-check').show();
|
||||
selectedImageType = 'uploaded';
|
||||
});
|
||||
|
||||
var imageUrlOnServer = response.path;
|
||||
$('#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(RELATIVE_PATH + '/user/uploadpicture', {}, function(imageUrlOnServer) {
|
||||
imageUrlOnServer = imageUrlOnServer + '?' + new Date().getTime();
|
||||
|
||||
$('#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 showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
}
|
||||
|
||||
function hideAlerts() {
|
||||
$('#alert-status').addClass('hide');
|
||||
$('#alert-success').addClass('hide');
|
||||
$('#alert-error').addClass('hide');
|
||||
$('#upload-progress-box').addClass('hide');
|
||||
}
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
function status(message) {
|
||||
hideAlerts();
|
||||
$('#alert-status').text(message).removeClass('hide');
|
||||
}
|
||||
(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;
|
||||
var successIcon = '<i class="fa fa-check"></i>';
|
||||
|
||||
function success(message) {
|
||||
hideAlerts();
|
||||
$('#alert-success').text(message).removeClass('hide');
|
||||
}
|
||||
|
||||
function error(message) {
|
||||
hideAlerts();
|
||||
$('#alert-error').text(message).removeClass('hide');
|
||||
}
|
||||
function onPasswordChanged() {
|
||||
passwordvalid = utils.isPasswordValid(password.val());
|
||||
if (password.val().length < config.minimumPasswordLength) {
|
||||
showError(password_notify, 'Password too short!');
|
||||
} else if (!passwordvalid) {
|
||||
showError(password_notify, 'Invalid password!');
|
||||
} else {
|
||||
showSuccess(password_notify, successIcon);
|
||||
}
|
||||
}
|
||||
|
||||
function changeUserPicture(type) {
|
||||
function onPasswordConfirmChanged() {
|
||||
if(password.val()) {
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, 'Passwords must match!')
|
||||
passwordsmatch = false;
|
||||
} else {
|
||||
showSuccess(password_confirm_notify, successIcon);
|
||||
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('');
|
||||
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
|
||||
};
|
||||
@@ -94,198 +208,36 @@ $(document).ready(function() {
|
||||
});
|
||||
}
|
||||
|
||||
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(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);
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
|
||||
function updateImages() {
|
||||
AccountEdit.updateImages = function() {
|
||||
var currentPicture = $('#user-current-picture').attr('src');
|
||||
|
||||
if (gravatarPicture) {
|
||||
$('#user-gravatar-picture').attr('src', gravatarPicture);
|
||||
$('#gravatar-box').show();
|
||||
} else
|
||||
} else {
|
||||
$('#gravatar-box').hide();
|
||||
}
|
||||
|
||||
if (uploadedPicture) {
|
||||
$('#user-uploaded-picture').attr('src', uploadedPicture);
|
||||
$('#uploaded-box').show();
|
||||
} else
|
||||
} else {
|
||||
$('#uploaded-box').hide();
|
||||
}
|
||||
|
||||
|
||||
if (currentPicture == gravatarPicture)
|
||||
$('#gravatar-box .icon-ok').show();
|
||||
else
|
||||
$('#gravatar-box .icon-ok').hide();
|
||||
if (currentPicture == gravatarPicture) {
|
||||
$('#gravatar-box .fa-check').show();
|
||||
} else {
|
||||
$('#gravatar-box .fa-check').hide();
|
||||
}
|
||||
|
||||
if (currentPicture == uploadedPicture)
|
||||
$('#uploaded-box .icon-ok').show();
|
||||
else
|
||||
$('#uploaded-box .icon-ok').hide();
|
||||
if (currentPicture == uploadedPicture) {
|
||||
$('#uploaded-box .fa-check').show();
|
||||
} else {
|
||||
$('#uploaded-box .fa-check').hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
$('#changePictureBtn').on('click', function() {
|
||||
selectedImageType = '';
|
||||
updateImages();
|
||||
|
||||
$('#change-picture-modal').modal('show');
|
||||
$('#change-picture-modal').removeClass('hide');
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#gravatar-box').on('click', function() {
|
||||
$('#gravatar-box .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');
|
||||
$('#upload-picture-modal').removeClass('hide');
|
||||
|
||||
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 < 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;
|
||||
});
|
||||
|
||||
}());
|
||||
return AccountEdit;
|
||||
});
|
||||
@@ -1,24 +1,11 @@
|
||||
(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');
|
||||
|
||||
function createMenu() {
|
||||
var userslug = $('.account-username-box').attr('data-userslug');
|
||||
var links = $('<div class="account-sub-links inline-block pull-right">\
|
||||
<span id="settingsLink" class="pull-right"><a href="/user/' + userslug + '/settings">settings</a></span>\
|
||||
<span id="favouritesLink" class="pull-right"><a href="/user/' + userslug + '/favourites">favourites</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/followers">followers</a></span>\
|
||||
<span class="pull-right"><a href="/user/' + userslug + '/following">following</a></span>\
|
||||
<span id="editLink" class="pull-right"><a href="/user/' + userslug + '/edit">edit</a></span>\
|
||||
</div>');
|
||||
|
||||
$('.account-username-box').append(links);
|
||||
}
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
createMenu();
|
||||
AccountHeader.createMenu();
|
||||
|
||||
var editLink = $('#editLink');
|
||||
var settingsLink = $('#settingsLink');
|
||||
@@ -37,6 +24,20 @@
|
||||
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,19 +1,25 @@
|
||||
$(document).ready(function() {
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountSettings = {};
|
||||
|
||||
$('#submitBtn').on('click', function() {
|
||||
AccountSettings.init = function() {
|
||||
header.init();
|
||||
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
$('#submitBtn').on('click', function() {
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(success) {
|
||||
if (success) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
var settings = {
|
||||
showemail: $('#showemailCheckBox').is(':checked') ? 1 : 0
|
||||
};
|
||||
|
||||
socket.emit('api:user.saveSettings', settings, function(err) {
|
||||
if (!err) {
|
||||
app.alertSuccess('Settings saved!');
|
||||
} else {
|
||||
app.alertError('There was an error saving settings!');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
});
|
||||
return false;
|
||||
});
|
||||
};
|
||||
|
||||
});
|
||||
return AccountSettings;
|
||||
});
|
||||
|
||||
@@ -1,139 +1,350 @@
|
||||
var modified_categories = {};
|
||||
define(['uploader'], function(uploader) {
|
||||
var Categories = {};
|
||||
|
||||
function modified(el) {
|
||||
var cid = $(el).parents('li').attr('data-cid');
|
||||
Categories.init = function() {
|
||||
var modified_categories = {};
|
||||
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid][$(el).attr('data-name')] = $(el).val();
|
||||
}
|
||||
|
||||
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');
|
||||
|
||||
|
||||
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);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
function save() {
|
||||
socket.emit('api:admin.categories.update', modified_categories);
|
||||
modified_categories = {};
|
||||
}
|
||||
|
||||
function select_icon(el) {
|
||||
var selected = el.attr('class').replace(' fa-2x', '');
|
||||
$('#icons .selected').removeClass('selected');
|
||||
if (selected)
|
||||
$('#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 = $('.bootbox .selected').children(':first').attr('class');
|
||||
|
||||
jQuery('#entry-container').sortable();
|
||||
jQuery('.blockclass').each(function() {
|
||||
jQuery(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
el.attr('class', iconClass + ' fa-2x');
|
||||
|
||||
// remove the 'fa ' from the class name, just need the icon name itself
|
||||
var categoryIconClass = iconClass.replace('fa ', '');
|
||||
el.val(categoryIconClass);
|
||||
el.attr('value', categoryIconClass);
|
||||
|
||||
//DRY Failure. this needs to go into an ajaxify onready style fn. Currently is copy pasted into every single function so after ACP is off the ground fix asap
|
||||
(function() {
|
||||
function showCreateCategoryModal() {
|
||||
$('#new-category-modal').modal();
|
||||
}
|
||||
modified(el);
|
||||
}
|
||||
});
|
||||
|
||||
function createNewCategory() {
|
||||
var category = {
|
||||
name: $('#inputName').val(),
|
||||
description: $('#inputDescription').val(),
|
||||
icon: $('#new-category-modal i').attr('value'),
|
||||
blockclass: $('#inputBlockclass').val()
|
||||
};
|
||||
|
||||
socket.emit('api:admin.categories.create', category, function(err, data) {
|
||||
if (!err) {
|
||||
app.alert({
|
||||
alert_id: 'category_created',
|
||||
title: 'Created',
|
||||
message: 'Category successfully created!',
|
||||
type: 'success',
|
||||
timeout: 2000
|
||||
setTimeout(function() { //bootbox was rewritten for BS3 and I had to add this timeout for the previous code to work. TODO: to look into
|
||||
$('.bootbox .col-md-3').on('click', function() {
|
||||
$('.bootbox .selected').removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
});
|
||||
}, 500);
|
||||
}
|
||||
|
||||
var html = templates.prepare(templates['admin/categories'].blocks['categories']).parse({
|
||||
categories: [data]
|
||||
});
|
||||
$('#entry-container').append(html);
|
||||
|
||||
$('#new-category-modal').modal('hide');
|
||||
function update_blockclass(el) {
|
||||
el.parentNode.parentNode.className = 'entry-row ' + el.value;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
$('#entry-container').sortable({
|
||||
stop: function( event, ui ) {
|
||||
updateCategoryOrders();
|
||||
}
|
||||
});
|
||||
}
|
||||
$('.blockclass').each(function() {
|
||||
$(this).val(this.getAttribute('data-value'));
|
||||
});
|
||||
|
||||
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');
|
||||
function showCreateCategoryModal() {
|
||||
$('#new-category-modal').modal();
|
||||
}
|
||||
|
||||
function createNewCategory() {
|
||||
var category = {
|
||||
name: $('#inputName').val(),
|
||||
description: $('#inputDescription').val(),
|
||||
icon: $('#new-category-modal i').val(),
|
||||
bgColor: '#0059b2',
|
||||
color: '#fff',
|
||||
order: $('.admin-categories #entry-container').children().length + 1
|
||||
};
|
||||
|
||||
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');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$('document').ready(function() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
|
||||
$('.nav-pills li').removeClass('active');
|
||||
$('.nav-pills li a').each(function() {
|
||||
if (this.getAttribute('href').match(active)) {
|
||||
$(this.parentNode).addClass('active');
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$('#save').on('click', save);
|
||||
$('#addNew').on('click', showCreateCategoryModal);
|
||||
$('#create-category-btn').on('click', createNewCategory);
|
||||
|
||||
$('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
$('#new-category-modal').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
});
|
||||
|
||||
$('.admin-categories form input').on('change', function(ev) {
|
||||
modified(ev.target);
|
||||
});
|
||||
|
||||
$('.dropdown li[data-disabled]').each(function(index, element) {
|
||||
var disabled = $(element).attr('data-disabled');
|
||||
if (disabled == "0" || disabled == "") {
|
||||
$(element).html('<a href="#"><i class="fa fa-power-off"></i> Disable</a>');
|
||||
} else {
|
||||
$(element).html('<a href="#"><i class="fa fa-power-off"></i> Enable</a>');
|
||||
}
|
||||
});
|
||||
|
||||
$('.dropdown').on('click', '[data-disabled]', function(ev) {
|
||||
var btn = $(this);
|
||||
var categoryRow = btn.parents('li');
|
||||
var cid = categoryRow.attr('data-cid');
|
||||
|
||||
var disabled = this.getAttribute('data-disabled') === '0' ? '1' : '0';
|
||||
categoryRow.remove();
|
||||
modified_categories[cid] = modified_categories[cid] || {};
|
||||
modified_categories[cid]['disabled'] = disabled;
|
||||
|
||||
save();
|
||||
return false;
|
||||
});
|
||||
|
||||
// Colour Picker
|
||||
$('[data-name="bgColor"], [data-name="color"]').each(function(idx, inputEl) {
|
||||
var jinputEl = $(this),
|
||||
previewEl = jinputEl.parents('[data-cid]').find('.preview-box');
|
||||
|
||||
jinputEl.ColorPicker({
|
||||
color: this.value || '#000',
|
||||
onChange: function(hsb, hex) {
|
||||
jinputEl.val('#' + hex);
|
||||
if (inputEl.getAttribute('data-name') === 'bgColor') previewEl.css('background', '#' + hex);
|
||||
else if (inputEl.getAttribute('data-name') === 'color') previewEl.css('color', '#' + hex);
|
||||
modified(inputEl);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// Permissions modal
|
||||
$('.permissions').on('click', function() {
|
||||
var cid = $(this).parents('li[data-cid]').attr('data-cid');
|
||||
Categories.launchPermissionsModal(cid);
|
||||
});
|
||||
|
||||
|
||||
$('.upload-button').on('click', function() {
|
||||
var inputEl = this;
|
||||
var cid = $(this).parents('li[data-cid]').attr('data-cid');
|
||||
uploader.open(RELATIVE_PATH + '/admin/category/uploadpicture', {cid:cid}, function(imageUrlOnServer) {
|
||||
inputEl.value = imageUrlOnServer;
|
||||
$(inputEl).parents('li[data-cid]').find('.preview-box').css('background', 'url(' + imageUrlOnServer + '?' + new Date().getTime() + ')');
|
||||
modified(inputEl);
|
||||
});
|
||||
});
|
||||
|
||||
$('.admin-categories').delegate('.delete-image', 'click', function() {
|
||||
var parent = $(this).parents('li[data-cid]'),
|
||||
inputEl = parent.find('.upload-button'),
|
||||
preview = parent.find('.preview-box'),
|
||||
bgColor = parent.find('.category_bgColor').val();
|
||||
|
||||
inputEl.value = '';
|
||||
modified(inputEl);
|
||||
|
||||
preview.css('background', bgColor);
|
||||
|
||||
$(this).hide();
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
Categories.launchPermissionsModal = function(cid) {
|
||||
var modal = $('#category-permissions-modal'),
|
||||
searchEl = modal.find('#permission-search'),
|
||||
resultsEl = modal.find('.search-results'),
|
||||
groupsResultsEl = modal.find('.groups-results'),
|
||||
searchDelay;
|
||||
|
||||
searchEl.off().on('keyup', function() {
|
||||
var searchEl = this,
|
||||
resultsFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
clearTimeout(searchDelay);
|
||||
|
||||
searchDelay = setTimeout(function() {
|
||||
socket.emit('api:admin.categories.search', searchEl.value, cid, function(err, results) {
|
||||
var numResults = results.length,
|
||||
resultObj;
|
||||
for(var x=0;x<numResults;x++) {
|
||||
resultObj = results[x];
|
||||
|
||||
liEl.setAttribute('data-uid', resultObj.uid);
|
||||
liEl.innerHTML = '<div class="pull-right">' +
|
||||
'<div class="btn-group">' +
|
||||
'<button type="button" data-priv="+r" class="btn btn-default' + (resultObj.privileges['+r'] ? ' active' : '') + '">Read</button>' +
|
||||
'<button type="button" data-priv="+w" class="btn btn-default' + (resultObj.privileges['+w'] ? ' active' : '') + '">Write</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
'<img src="' + resultObj.picture + '" /> ' + resultObj.username;
|
||||
|
||||
resultsFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
|
||||
resultsEl.html(resultsFrag);
|
||||
});
|
||||
}, 250);
|
||||
});
|
||||
|
||||
Categories.refreshPrivilegeList(cid);
|
||||
|
||||
resultsEl.off().on('click', '[data-priv]', function(e) {
|
||||
var btnEl = $(this),
|
||||
uid = btnEl.parents('li[data-uid]').attr('data-uid'),
|
||||
privilege = this.getAttribute('data-priv');
|
||||
e.preventDefault();
|
||||
|
||||
socket.emit('api:admin.categories.setPrivilege', cid, uid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
|
||||
btnEl.toggleClass('active', privileges[privilege]);
|
||||
|
||||
Categories.refreshPrivilegeList(cid);
|
||||
});
|
||||
});
|
||||
|
||||
modal.off().on('click', '.members li > img', function() {
|
||||
searchEl.val(this.getAttribute('title'));
|
||||
searchEl.keyup();
|
||||
});
|
||||
|
||||
// User Groups and privileges
|
||||
socket.emit('api:admin.categories.groupsearch', cid, function(err, results) {
|
||||
var groupsFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li');
|
||||
var numResults = results.length,
|
||||
resultObj;
|
||||
|
||||
for(var x=0;x<numResults;x++) {
|
||||
resultObj = results[x];
|
||||
liEl.setAttribute('data-gid', resultObj.gid);
|
||||
liEl.innerHTML = '<div class="pull-right">' +
|
||||
'<div class="btn-group">' +
|
||||
'<button type="button" data-gpriv="+gr" class="btn btn-default' + (resultObj.privileges['+gr'] ? ' active' : '') + '">Read</button>' +
|
||||
'<button type="button" data-gpriv="+gw" class="btn btn-default' + (resultObj.privileges['+gw'] ? ' active' : '') + '">Write</button>' +
|
||||
'</div>' +
|
||||
'</div>' +
|
||||
' '+resultObj.name;
|
||||
|
||||
groupsFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
|
||||
groupsResultsEl.html(groupsFrag);
|
||||
});
|
||||
|
||||
jQuery('#save').on('click', save);
|
||||
jQuery('#addNew').on('click', showCreateCategoryModal);
|
||||
jQuery('#create-category-btn').on('click', createNewCategory);
|
||||
groupsResultsEl.off().on('click', '[data-gpriv]', function(e) {
|
||||
var btnEl = $(this),
|
||||
gid = btnEl.parents('li[data-gid]').attr('data-gid'),
|
||||
privilege = this.getAttribute('data-gpriv');
|
||||
e.preventDefault();
|
||||
socket.emit('api:admin.categories.setGroupPrivilege', cid, gid, privilege, !btnEl.hasClass('active'), function(err, privileges) {
|
||||
btnEl.toggleClass('active', privileges[privilege]);
|
||||
});
|
||||
})
|
||||
|
||||
jQuery('#entry-container').on('click', '.icon', function(ev) {
|
||||
select_icon($(this).find('i'));
|
||||
modal.modal();
|
||||
};
|
||||
|
||||
Categories.refreshPrivilegeList = function (cid) {
|
||||
var modalEl = $('#category-permissions-modal'),
|
||||
readMembers = modalEl.find('#category-permissions-read'),
|
||||
writeMembers = modalEl.find('#category-permissions-write');
|
||||
socket.emit('api:admin.categories.getPrivilegeSettings', cid, function(err, privilegeList) {
|
||||
var readLength = privilegeList['+r'].length,
|
||||
writeLength = privilegeList['+w'].length,
|
||||
readFrag = document.createDocumentFragment(),
|
||||
writeFrag = document.createDocumentFragment(),
|
||||
liEl = document.createElement('li'),
|
||||
x, userObj;
|
||||
|
||||
if (readLength > 0) {
|
||||
for(x=0;x<readLength;x++) {
|
||||
userObj = privilegeList['+r'][x];
|
||||
liEl.setAttribute('data-uid', userObj.uid);
|
||||
|
||||
liEl.innerHTML = '<img src="' + userObj.picture + '" title="' + userObj.username + '" />';
|
||||
readFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
liEl.className = 'empty';
|
||||
liEl.innerHTML = 'No users are in this list';
|
||||
readFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
|
||||
if (writeLength > 0) {
|
||||
for(x=0;x<writeLength;x++) {
|
||||
userObj = privilegeList['+w'][x];
|
||||
liEl.setAttribute('data-uid', userObj.uid);
|
||||
|
||||
liEl.innerHTML = '<img src="' + userObj.picture + '" title="' + userObj.username + '" />';
|
||||
writeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
liEl.className = 'empty';
|
||||
liEl.innerHTML = 'No users are in this list';
|
||||
writeFrag.appendChild(liEl.cloneNode(true));
|
||||
}
|
||||
|
||||
readMembers.html(readFrag);
|
||||
writeMembers.html(writeFrag);
|
||||
});
|
||||
};
|
||||
|
||||
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-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,121 +1,42 @@
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
jQuery('document').ready(function() {
|
||||
// On menu click, change "active" state
|
||||
var menuEl = document.querySelector('.sidebar-nav'),
|
||||
liEls = menuEl.querySelectorAll('li')
|
||||
parentEl = null;
|
||||
|
||||
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.',
|
||||
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'
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
});
|
||||
@@ -1,194 +1,204 @@
|
||||
$(document).ready(function() {
|
||||
var createEl = document.getElementById('create'),
|
||||
createModal = $('#create-modal'),
|
||||
createSubmitBtn = document.getElementById('create-modal-go'),
|
||||
createNameEl = $('#create-group-name'),
|
||||
detailsModal = $('#group-details-modal'),
|
||||
detailsSearch = detailsModal.find('#group-details-search'),
|
||||
searchResults = detailsModal.find('#group-details-search-results'),
|
||||
groupMembersEl = detailsModal.find('ul.current_members'),
|
||||
detailsModalSave = detailsModal.find('.btn-primary'),
|
||||
searchDelay = undefined,
|
||||
listEl = $('#groups-list');
|
||||
define(function() {
|
||||
var Groups = {};
|
||||
|
||||
createEl.addEventListener('click', function() {
|
||||
createModal.modal('show');
|
||||
setTimeout(function() {
|
||||
createNameEl.focus();
|
||||
}, 250);
|
||||
}, false);
|
||||
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');
|
||||
|
||||
createSubmitBtn.addEventListener('click', function() {
|
||||
var submitObj = {
|
||||
name: createNameEl.val(),
|
||||
description: $('#create-group-desc').val()
|
||||
},
|
||||
errorEl = $('#create-modal-error'),
|
||||
errorText;
|
||||
createEl.addEventListener('click', function() {
|
||||
createModal.modal('show');
|
||||
setTimeout(function() {
|
||||
createNameEl.focus();
|
||||
}, 250);
|
||||
}, false);
|
||||
|
||||
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;
|
||||
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 {
|
||||
errorEl.addClass('hide');
|
||||
createNameEl.val('');
|
||||
createModal.on('hidden.bs.modal', function() {
|
||||
ajaxify.go('admin/groups');
|
||||
});
|
||||
createModal.modal('hide');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
listEl.on('click', 'button[data-action]', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
gid = $(this).parents('li[data-gid]').attr('data-gid');
|
||||
detailsSearch.on('keyup', function() {
|
||||
var searchEl = this;
|
||||
|
||||
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;
|
||||
if (searchDelay) clearTimeout(searchDelay);
|
||||
|
||||
searchDelay = setTimeout(function() {
|
||||
var searchText = searchEl.value,
|
||||
resultsEl = document.getElementById('group-details-search-results'),
|
||||
foundUser = document.createElement('li'),
|
||||
foundUserImg, foundUserLabel;
|
||||
|
||||
nameEl.val(groupObj.name);
|
||||
descEl.val(groupObj.description);
|
||||
foundUser.innerHTML = '<img /><span></span>';
|
||||
foundUserImg = foundUser.getElementsByTagName('img')[0];
|
||||
foundUserLabel = foundUser.getElementsByTagName('span')[0];
|
||||
|
||||
// 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));
|
||||
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));
|
||||
}
|
||||
groupMembersEl.html('');
|
||||
groupMembersEl[0].appendChild(membersFrag);
|
||||
}
|
||||
|
||||
detailsModal.attr('data-gid', groupObj.gid);
|
||||
detailsModal.modal('show');
|
||||
resultsEl.innerHTML = '';
|
||||
resultsEl.appendChild(resultsSlug);
|
||||
} else resultsEl.innerHTML = '<li>No Users Found</li>';
|
||||
});
|
||||
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')));
|
||||
}, 200);
|
||||
});
|
||||
|
||||
if (members.indexOf(uid) === -1) {
|
||||
socket.emit('api:groups.join', {
|
||||
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.append(userLabel.cloneNode(true));
|
||||
groupMembersEl.find('li[data-uid="' + uid + '"]').remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
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');
|
||||
detailsModalSave.on('click', function() {
|
||||
var formEl = detailsModal.find('form'),
|
||||
nameEl = formEl.find('#change-group-name'),
|
||||
descEl = formEl.find('#change-group-desc'),
|
||||
gid = detailsModal.attr('data-gid');
|
||||
|
||||
socket.emit('api:groups.update', {
|
||||
gid: gid,
|
||||
values: {
|
||||
name: nameEl.val(),
|
||||
description: descEl.val()
|
||||
}
|
||||
}, function(err) {
|
||||
if (!err) {
|
||||
detailsModal.modal('hide');
|
||||
ajaxify.go('admin/groups');
|
||||
}
|
||||
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,36 @@
|
||||
define(function() {
|
||||
var Admin = {};
|
||||
|
||||
(function() {
|
||||
Admin.init = function() {
|
||||
ajaxify.register_events(['api:get_all_rooms']);
|
||||
socket.on('api:get_all_rooms', function(data) {
|
||||
|
||||
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 = '';
|
||||
|
||||
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>";
|
||||
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.enterRoom('admin');
|
||||
socket.emit('api:get_all_rooms');
|
||||
|
||||
}());
|
||||
$('#logout-link').on('click', function() {
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: $('#csrf_token').val()
|
||||
}, function() {
|
||||
window.location.href = RELATIVE_PATH + '/';
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
return Admin;
|
||||
});
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
var nodebb_admin = nodebb_admin || {};
|
||||
|
||||
(function() {
|
||||
var plugins = {
|
||||
define(function() {
|
||||
var Plugins = {
|
||||
init: function() {
|
||||
var pluginsList = $('.plugins'),
|
||||
numPlugins = pluginsList[0].querySelectorAll('li').length,
|
||||
@@ -15,7 +13,7 @@ var nodebb_admin = nodebb_admin || {};
|
||||
|
||||
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';
|
||||
pluginTgl.innerHTML = '<i class="fa fa-power-off"></i> ' + (status.active ? 'Dea' : 'A') + 'ctivate';
|
||||
|
||||
app.alert({
|
||||
alert_id: 'plugin_toggled_' + status.id,
|
||||
@@ -31,8 +29,5 @@ var nodebb_admin = nodebb_admin || {};
|
||||
}
|
||||
};
|
||||
|
||||
jQuery(document).ready(function() {
|
||||
nodebb_admin.plugins = plugins;
|
||||
nodebb_admin.plugins.init();
|
||||
});
|
||||
})();
|
||||
return Plugins;
|
||||
});
|
||||
99
public/src/forum/admin/settings.js
Normal file
99
public/src/forum/admin/settings.js
Normal file
@@ -0,0 +1,99 @@
|
||||
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 = parseInt(app.config[key], 10) === 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else if (fields[x].nodeName === 'TEXTAREA') {
|
||||
if (app.config[key]) fields[x].value = app.config[key];
|
||||
} else if (fields[x].nodeName === 'SELECT') {
|
||||
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;
|
||||
} else if (fields[x].nodeName === 'SELECT') {
|
||||
value = fields[x].value;
|
||||
}
|
||||
|
||||
socket.emit('api:config.set', {
|
||||
key: key,
|
||||
value: value
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
$('#uploadLogoBtn').on('click', function() {
|
||||
uploader.open(RELATIVE_PATH + '/admin/uploadlogo', {}, function(image) {
|
||||
$('#logoUrl').val(image);
|
||||
});
|
||||
|
||||
uploader.hideAlerts();
|
||||
});
|
||||
|
||||
$('#uploadFaviconBtn').on('click', function() {
|
||||
uploader.open(RELATIVE_PATH + '/admin/uploadfavicon', {}, function(icon) {
|
||||
$('#faviconUrl').val(icon);
|
||||
});
|
||||
|
||||
uploader.hideAlerts();
|
||||
});
|
||||
};
|
||||
|
||||
Settings.remove = function(key) {
|
||||
socket.emit('api:config.remove', key);
|
||||
};
|
||||
|
||||
return Settings;
|
||||
});
|
||||
@@ -1,13 +1,113 @@
|
||||
var nodebb_admin = (function(nodebb_admin) {
|
||||
define(function() {
|
||||
var Themes = {};
|
||||
|
||||
var themes = {};
|
||||
Themes.init = function() {
|
||||
var scriptEl = document.createElement('script');
|
||||
scriptEl.src = 'http://api.bootswatch.com/3/?callback=bootswatchListener';
|
||||
document.body.appendChild(scriptEl);
|
||||
|
||||
themes.render = function(bootswatch) {
|
||||
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'),
|
||||
themeType = parentEl.attr('data-type'),
|
||||
cssSrc = parentEl.attr('data-css'),
|
||||
themeId = parentEl.attr('data-theme');
|
||||
|
||||
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;
|
||||
|
||||
themeEl.setAttribute('data-type', 'bootswatch');
|
||||
|
||||
for (var x = 0; x < numThemes; x++) {
|
||||
var theme = bootswatch.themes[x];
|
||||
themeEl.setAttribute('data-css', theme.cssMin);
|
||||
@@ -28,91 +128,5 @@ var nodebb_admin = (function(nodebb_admin) {
|
||||
themeContainer.appendChild(themeFrag);
|
||||
}
|
||||
|
||||
nodebb_admin.themes = themes;
|
||||
|
||||
return nodebb_admin;
|
||||
|
||||
}(nodebb_admin || {}));
|
||||
|
||||
|
||||
(function() {
|
||||
var scriptEl = document.createElement('script');
|
||||
scriptEl.src = 'http://api.bootswatch.com/3/?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 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);
|
||||
});
|
||||
})();
|
||||
return Themes;
|
||||
});
|
||||
@@ -1,127 +1,158 @@
|
||||
$(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();
|
||||
|
||||
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'));
|
||||
$(topicsListEl).on('click', '[data-action]', function() {
|
||||
var $this = $(this),
|
||||
action = this.getAttribute('data-action'),
|
||||
tid = $this.parents('[data-tid]').attr('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');
|
||||
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;
|
||||
}
|
||||
});
|
||||
|
||||
topics = JSON.parse(topics);
|
||||
if (topics.length > 0) {
|
||||
var html = templates.prepare(templates['admin/topics'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
topicsListEl = document.querySelector('.topics');
|
||||
loadMoreEl.addEventListener('click', function() {
|
||||
if (this.className.indexOf('disabled') === -1) {
|
||||
var topics = document.querySelectorAll('.topics li[data-tid]');
|
||||
|
||||
topicsListEl.innerHTML += html;
|
||||
btnEl.innerHTML = 'Load More Topics';
|
||||
} else {
|
||||
// Exhausted all topics
|
||||
btnEl.className += ' disabled';
|
||||
btnEl.innerHTML = 'No more topics';
|
||||
if(!topics.length) {
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
var lastTid = parseInt(topics[topics.length - 1].getAttribute('data-tid'));
|
||||
|
||||
this.innerHTML = '<i class="fa fa-refresh fa-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');
|
||||
}
|
||||
}
|
||||
}, 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,170 +1,271 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
var yourid = templates.get('yourid');
|
||||
Users.init = function() {
|
||||
var yourid = templates.get('yourid');
|
||||
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-admin') !== "0");
|
||||
}
|
||||
function isUserAdmin(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-admin') !== "0");
|
||||
}
|
||||
|
||||
function isUserBanned(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
|
||||
}
|
||||
function isUserBanned(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return (parent.attr('data-banned') !== "" && parent.attr('data-banned') !== "0");
|
||||
}
|
||||
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
}
|
||||
function getUID(element) {
|
||||
var parent = $(element).parents('.users-box');
|
||||
return parent.attr('data-uid');
|
||||
}
|
||||
|
||||
function updateUserButtons() {
|
||||
jQuery('.ban-btn').each(function(index, element) {
|
||||
var banBtn = $(element);
|
||||
var uid = getUID(banBtn);
|
||||
if (isUserAdmin(banBtn) || uid === yourid)
|
||||
banBtn.addClass('disabled');
|
||||
else if (isUserBanned(banBtn))
|
||||
banBtn.addClass('btn-warning');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initUsers() {
|
||||
|
||||
updateUserButtons();
|
||||
|
||||
$('#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);
|
||||
function updateUserBanButtons() {
|
||||
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 if (!isUserAdmin(banBtn))
|
||||
banBtn.removeClass('disabled');
|
||||
else
|
||||
banBtn.removeClass('btn-warning');
|
||||
parent.attr('data-banned', 0);
|
||||
updateUserAdminButtons();
|
||||
});
|
||||
}
|
||||
|
||||
function updateUserAdminButtons() {
|
||||
jQuery('.admin-btn').each(function(index, element) {
|
||||
var adminBtn = $(element);
|
||||
var uid = getUID(adminBtn);
|
||||
if (isUserAdmin(adminBtn)) {
|
||||
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
|
||||
if (uid === yourid) {
|
||||
adminBtn.addClass('disabled');
|
||||
}
|
||||
}
|
||||
else if (isUserBanned(adminBtn))
|
||||
adminBtn.addClass('disabled');
|
||||
else if (!isUserBanned(adminBtn))
|
||||
adminBtn.removeClass('disabled');
|
||||
else
|
||||
adminBtn.removeClass('btn-warning');
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function initUsers() {
|
||||
updateUserBanButtons();
|
||||
updateUserAdminButtons();
|
||||
|
||||
$('#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);
|
||||
updateUserAdminButtons();
|
||||
} 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);
|
||||
updateUserAdminButtons();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#users-container').on('click', '.admin-btn', function() {
|
||||
var adminBtn = $(this);
|
||||
var isAdmin = isUserAdmin(adminBtn);
|
||||
var parent = adminBtn.parents('.users-box');
|
||||
var isBanned = isUserBanned(adminBtn);
|
||||
var uid = getUID(adminBtn);
|
||||
|
||||
if(uid === yourid){
|
||||
app.alert({
|
||||
title: 'Error',
|
||||
message: 'You can\'t remove yourself as Administrator!',
|
||||
type: 'danger',
|
||||
timeout: 5000
|
||||
});
|
||||
}
|
||||
else if (!isAdmin) {
|
||||
socket.emit('api:admin.user.makeAdmin', uid);
|
||||
adminBtn.attr('value', 'UnMake Admin').html('Remove Admin');
|
||||
parent.attr('data-admin', 1);
|
||||
updateUserBanButtons();
|
||||
|
||||
} else if(uid !== yourid) {
|
||||
bootbox.confirm('Do you really want to remove this user as admin "' + parent.attr('data-username') + '"?', function(confirm) {
|
||||
if (confirm) {
|
||||
socket.emit('api:admin.user.removeAdmin', uid);
|
||||
adminBtn.attr('value', 'Make Admin').html('Make Admin');
|
||||
parent.attr('data-admin', 0);
|
||||
updateUserBanButtons();
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
|
||||
function handleUserCreate() {
|
||||
$('#createUser').on('click', function() {
|
||||
$('#create-modal').modal('show');
|
||||
});
|
||||
|
||||
$('#create-modal-go').on('click', function() {
|
||||
var username = $('#create-user-name').val(),
|
||||
email = $('#create-user-email').val(),
|
||||
password = $('#create-user-password').val(),
|
||||
passwordAgain = $('#create-user-password-again').val(),
|
||||
errorEl = $('#create-modal-error');
|
||||
|
||||
if(password !== passwordAgain) {
|
||||
return errorEl.html('<strong>Error</strong><p>Passwords must match!</p>').removeClass('hide');
|
||||
}
|
||||
|
||||
var user = {
|
||||
username: username,
|
||||
email: email,
|
||||
password: password
|
||||
};
|
||||
|
||||
socket.emit('api:admin.user.createUser', user, function(err, data) {
|
||||
if(err) {
|
||||
return errorEl.html('<strong>Error</strong><p>' + err + '</p>').removeClass('hide');
|
||||
}
|
||||
$('#create-modal').modal('hide');
|
||||
app.alertSuccess('User created!');
|
||||
ajaxify.go('admin/users');
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
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('.fa-spinner').removeClass('none');
|
||||
socket.emit('api:admin.user.search', username);
|
||||
|
||||
}, 250);
|
||||
});
|
||||
|
||||
initUsers();
|
||||
|
||||
handleUserCreate();
|
||||
|
||||
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.innerHTML = html;
|
||||
jQuery('.fa-spinner').addClass('none');
|
||||
|
||||
if (data && data.length === 0) {
|
||||
$('#user-notfound-notify').html('User not found!')
|
||||
.show()
|
||||
.addClass('label-danger')
|
||||
.removeClass('label-success');
|
||||
} 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);
|
||||
$('#user-notfound-notify').html(data.length + ' user' + (data.length > 1 ? 's' : '') + ' found!')
|
||||
.show()
|
||||
.addClass('label-success')
|
||||
.removeClass('label-danger');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: users
|
||||
});
|
||||
$('#users-container').append(html);
|
||||
updateUserButtons();
|
||||
}
|
||||
|
||||
function loadMoreUsers() {
|
||||
var set = '';
|
||||
if (active === 'latest') {
|
||||
set = 'users:joindate';
|
||||
} else if (active === 'sort-posts') {
|
||||
set = 'users:postcount';
|
||||
} else if (active === '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);
|
||||
}
|
||||
loadingMoreUsers = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
}
|
||||
$('#load-more-users-btn').on('click', loadMoreUsers);
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
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.innerHTML = html;
|
||||
jQuery('.icon-spinner').addClass('none');
|
||||
|
||||
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');
|
||||
}
|
||||
|
||||
initUsers();
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
var html = templates.prepare(templates['admin/users'].blocks['users']).parse({
|
||||
users: users
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreUsers) {
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
$('#users-container').append(html);
|
||||
updateUserButtons();
|
||||
}
|
||||
|
||||
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';
|
||||
}
|
||||
|
||||
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,138 +1,171 @@
|
||||
(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'),
|
||||
define(['composer'], function(composer) {
|
||||
var Category = {},
|
||||
loadingMoreTopics = false;
|
||||
|
||||
app.enter_room(room);
|
||||
Category.init = function() {
|
||||
var cid = templates.get('category_id'),
|
||||
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');
|
||||
|
||||
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);
|
||||
app.enterRoom('category_' + cid);
|
||||
|
||||
var new_post = document.getElementById('new_post');
|
||||
new_post.onclick = function () {
|
||||
require(['composer'], function (cmp) {
|
||||
cmp.push(0, cid);
|
||||
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;
|
||||
});
|
||||
|
||||
$('#new_post').on('click', function () {
|
||||
composer.newTopic(cid);
|
||||
});
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
|
||||
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 user-img" 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();
|
||||
app.createUserTooltips();
|
||||
});
|
||||
|
||||
$(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]
|
||||
});
|
||||
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var topic = $(translatedHTML),
|
||||
container = $('#topics-container'),
|
||||
topics = $('#topics-container').children('.category-item'),
|
||||
numTopics = topics.length;
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
jQuery('#category-no-topics').remove();
|
||||
|
||||
if (numTopics > 0) {
|
||||
for (var x = 0; x < numTopics; x++) {
|
||||
if ($(topics[x]).find('.fa-thumb-tack').length) {
|
||||
if(x === numTopics - 1) {
|
||||
topic.insertAfter(topics[x]);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
topic.insertBefore(topics[x]);
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
container.append(topic);
|
||||
}
|
||||
|
||||
topic.hide().fadeIn('slow');
|
||||
socket.emit('api:categories.getRecentReplies', templates.get('category_id'));
|
||||
|
||||
addActiveUser(data);
|
||||
|
||||
$('#topics-container span.timeago').timeago();
|
||||
});
|
||||
}
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic'
|
||||
]);
|
||||
|
||||
function onNewTopic(data) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: [data]
|
||||
}),
|
||||
topic = $(html),
|
||||
container = $('#topics-container'),
|
||||
topics = $('#topics-container').children(),
|
||||
numTopics = topics.length;
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
jQuery('#category-no-topics').remove();
|
||||
|
||||
if (numTopics > 0) {
|
||||
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.append(topic);
|
||||
topic.hide().fadeIn('slow');
|
||||
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.emit('api:categories.getRecentReplies', cid);
|
||||
$('#topics-container span.timeago').timeago();
|
||||
}
|
||||
|
||||
socket.on('event:new_topic', onNewTopic);
|
||||
Category.onTopicsLoaded = function(topics) {
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
});
|
||||
|
||||
socket.emit('api:categories.getRecentReplies', cid);
|
||||
socket.on('api:categories.getRecentReplies', function (posts) {
|
||||
if (!posts || posts.length === 0) {
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var container = $('#topics-container');
|
||||
|
||||
jQuery('#topics-container, .category-sidebar').removeClass('hidden');
|
||||
jQuery('#category-no-topics').remove();
|
||||
|
||||
html = $(translatedHTML);
|
||||
container.append(html);
|
||||
|
||||
$('#topics-container span.timeago').timeago();
|
||||
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
|
||||
});
|
||||
}
|
||||
|
||||
Category.loadMoreTopics = function(cid) {
|
||||
if (loadingMoreTopics) {
|
||||
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 + '">' +
|
||||
'<p>' +
|
||||
posts[i].content +
|
||||
'</p>' +
|
||||
'<p class="meta"><strong>' + posts[i].username + '</strong></span> -<span class="timeago" title="' + posts[i].relativeTime + '"></span></p>' +
|
||||
'</a>';
|
||||
|
||||
frag.appendChild(li.cloneNode(true));
|
||||
recent_replies.appendChild(frag);
|
||||
}
|
||||
$('#category_recent_replies span.timeago').timeago();
|
||||
});
|
||||
|
||||
function onTopicsLoaded(topics) {
|
||||
|
||||
var html = templates.prepare(templates['category'].blocks['topics']).parse({
|
||||
topics: topics
|
||||
}),
|
||||
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) {
|
||||
loadingMoreTopics = true;
|
||||
socket.emit('api:category.loadMore', {
|
||||
cid: cid,
|
||||
after: $('#topics-container').children().length
|
||||
after: $('#topics-container').children('.category-item').length
|
||||
}, function (data) {
|
||||
if (data.topics.length) {
|
||||
onTopicsLoaded(data.topics);
|
||||
Category.onTopicsLoaded(data.topics);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function (ev) {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics(cid);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Category;
|
||||
});
|
||||
@@ -1,7 +1,9 @@
|
||||
(function() {
|
||||
$(document).ready(function() {
|
||||
$('.user-favourite-posts .topic-row').on('click', function() {
|
||||
ajaxify.go($(this).attr('topic-url'));
|
||||
});
|
||||
});
|
||||
}());
|
||||
define(['forum/accountheader'], function(header) {
|
||||
var AccountHeader = {};
|
||||
|
||||
AccountHeader.init = function() {
|
||||
header.init();
|
||||
};
|
||||
|
||||
return AccountHeader;
|
||||
});
|
||||
@@ -1,18 +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() {
|
||||
var yourid = templates.get('yourid'),
|
||||
theirid = templates.get('theirid'),
|
||||
followersCount = templates.get('followersCount');
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
if (parseInt(followersCount, 10) === 0) {
|
||||
$('#no-followers-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
}());
|
||||
return Followers;
|
||||
});
|
||||
@@ -1,40 +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() {
|
||||
var followingCount = templates.get('followingCount');
|
||||
|
||||
if (parseInt(followingCount, 10) === 0) {
|
||||
$('#no-following-notice').removeClass('hide');
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
});
|
||||
}
|
||||
|
||||
app.addCommasToNumbers();
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
}());
|
||||
return Following;
|
||||
});
|
||||
@@ -1,39 +1,19 @@
|
||||
(function() {
|
||||
var stats_users = document.getElementById('stats_users'),
|
||||
stats_topics = document.getElementById('stats_topics'),
|
||||
stats_posts = document.getElementById('stats_posts'),
|
||||
stats_online = document.getElementById('stats_online'),
|
||||
user_label = document.getElementById('user_label');
|
||||
|
||||
socket.emit('user.count', {});
|
||||
socket.on('user.count', function(data) {
|
||||
stats_users.innerHTML = data.count;
|
||||
});
|
||||
|
||||
socket.emit('post.stats');
|
||||
socket.on('post.stats', function(data) {
|
||||
stats_topics.innerHTML = data.topics;
|
||||
stats_posts.innerHTML = data.posts;
|
||||
});
|
||||
|
||||
socket.emit('api:user.active.get');
|
||||
socket.on('api:user.active.get', function(data) {
|
||||
stats_online.innerHTML = data.users;
|
||||
});
|
||||
|
||||
socket.emit('api:updateHeader', {
|
||||
fields: ['username', 'picture', 'userslug']
|
||||
});
|
||||
|
||||
socket.on('api:updateHeader', function(data) {
|
||||
jQuery('#search-button').on('click', function() {
|
||||
jQuery('#search-fields').removeClass('hide').show();
|
||||
jQuery(this).hide();
|
||||
jQuery('#search-fields input').focus()
|
||||
|
||||
jQuery('#search-form').on('submit', function() {
|
||||
jQuery('#search-fields').hide();
|
||||
jQuery('#search-button').show();
|
||||
$('#search-button').on('click', function() {
|
||||
$('#search-fields').removeClass('hide').show();
|
||||
$(this).hide();
|
||||
$('#search-fields input').focus();
|
||||
|
||||
$('#search-form').on('submit', function() {
|
||||
$('#search-fields').hide();
|
||||
$('#search-button').show();
|
||||
});
|
||||
|
||||
$('#search-fields input').on('blur', function() {
|
||||
@@ -43,48 +23,46 @@
|
||||
});
|
||||
|
||||
var loggedInMenu = $('#logged-in-menu'),
|
||||
isLoggedIn = data.uid > 0;
|
||||
isLoggedIn = data.uid > 0,
|
||||
allowGuestSearching = (data.config || {}).allowGuestSearching === '1';
|
||||
|
||||
if (isLoggedIn) {
|
||||
jQuery('.nodebb-loggedin').show();
|
||||
jQuery('.nodebb-loggedout').hide();
|
||||
$('.nodebb-loggedin').show();
|
||||
$('.nodebb-loggedout').hide();
|
||||
|
||||
$('#logged-out-menu').addClass('hide');
|
||||
$('#logged-in-menu').removeClass('hide');
|
||||
|
||||
$('#search-button').show();
|
||||
$('#search-button').removeClass("hide").show();
|
||||
|
||||
var userLabel = loggedInMenu.find('#user_label');
|
||||
|
||||
if (userLabel.length) {
|
||||
if (data['userslug'])
|
||||
userLabel.attr('href', '/user/' + 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']);
|
||||
|
||||
$('#logout-link').on('click', function() {
|
||||
var csrf_token = $('#csrf_token').val();
|
||||
|
||||
$.post(RELATIVE_PATH + '/logout', {
|
||||
_csrf: csrf_token
|
||||
}, function() {
|
||||
window.location = RELATIVE_PATH + '/';
|
||||
});
|
||||
});
|
||||
$('#logout-link').on('click', app.logout);
|
||||
}
|
||||
} else {
|
||||
$('#search-button').hide();
|
||||
if (allowGuestSearching) {
|
||||
$('#search-button').removeClass("hide").show();
|
||||
} else {
|
||||
$('#search-button').addClass("hide").hide();
|
||||
}
|
||||
|
||||
jQuery('.nodebb-loggedin').hide();
|
||||
jQuery('.nodebb-loggedout').show();
|
||||
$('.nodebb-loggedin').hide();
|
||||
$('.nodebb-loggedout').show();
|
||||
|
||||
$('#logged-out-menu').removeClass('hide');
|
||||
$('#logged-in-menu').addClass('hide');
|
||||
|
||||
}
|
||||
|
||||
$('#main-nav a,#right-menu a').off('click').on('click', function() {
|
||||
$('#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();
|
||||
});
|
||||
@@ -94,7 +72,8 @@
|
||||
var notifContainer = document.getElementsByClassName('notifications')[0],
|
||||
notifTrigger = notifContainer.querySelector('a'),
|
||||
notifList = document.getElementById('notif-list'),
|
||||
notifIcon = document.querySelector('.notifications a i');
|
||||
notifIcon = $('.notifications a');
|
||||
|
||||
notifTrigger.addEventListener('click', function(e) {
|
||||
e.preventDefault();
|
||||
if (notifContainer.className.indexOf('open') === -1) {
|
||||
@@ -119,17 +98,32 @@
|
||||
notifFrag.appendChild(notifEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
notifEl.className = 'no-notifs';
|
||||
notifEl.innerHTML = '<a>You have no notifications</a>';
|
||||
notifFrag.appendChild(notifEl);
|
||||
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';
|
||||
if (data.unread.length > 0) {
|
||||
notifIcon.toggleClass('active', true);
|
||||
} else {
|
||||
notifIcon.toggleClass('active', false);
|
||||
}
|
||||
|
||||
socket.emit('api:notifications.mark_all_read', null, function() {
|
||||
notifIcon.className = 'icon-circle-blank';
|
||||
utils.refreshTitle();
|
||||
notifIcon.toggleClass('active', false);
|
||||
app.refreshTitle();
|
||||
|
||||
// Update favicon + local count
|
||||
Tinycon.setBubble(0);
|
||||
localStorage.setItem('notifications:count', 0);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -154,8 +148,29 @@
|
||||
}
|
||||
});
|
||||
|
||||
var updateNotifCount = function(count) {
|
||||
// Update notification icon, if necessary
|
||||
if (count > 0) {
|
||||
notifIcon.toggleClass('active', true);
|
||||
} else {
|
||||
notifIcon.toggleClass('active', false);
|
||||
}
|
||||
|
||||
// Update the favicon + saved local count
|
||||
Tinycon.setBubble(count);
|
||||
localStorage.setItem('notifications:count', count);
|
||||
};
|
||||
|
||||
socket.emit('api:notifications.getCount', function(err, count) {
|
||||
if (!err) {
|
||||
updateNotifCount(count);
|
||||
} else {
|
||||
updateNotifCount(0);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('event:new_notification', function() {
|
||||
document.querySelector('.notifications a i').className = 'icon-circle active';
|
||||
notifIcon.toggleClass('active', true);
|
||||
app.alert({
|
||||
alert_id: 'new_notif',
|
||||
title: 'New notification',
|
||||
@@ -163,26 +178,93 @@
|
||||
type: 'warning',
|
||||
timeout: 2000
|
||||
});
|
||||
utils.refreshTitle();
|
||||
app.refreshTitle();
|
||||
|
||||
if (ajaxify.currentPage === 'notifications') {
|
||||
ajaxify.refresh();
|
||||
}
|
||||
|
||||
// Update the favicon + local storage
|
||||
var savedCount = parseInt(localStorage.getItem('notifications:count'),10) || 0;
|
||||
updateNotifCount(savedCount+1);
|
||||
});
|
||||
socket.on('event:notifications.updateCount', function(count) {
|
||||
updateNotifCount(count);
|
||||
});
|
||||
|
||||
// Chats Dropdown
|
||||
var chatsToggleEl = $('#chat_dropdown'),
|
||||
chatsListEl = $('#chat-list'),
|
||||
chatDropdownEl = chatsToggleEl.parent();
|
||||
chatsToggleEl.on('click', function() {
|
||||
if (chatDropdownEl.hasClass('open')) {
|
||||
return;
|
||||
}
|
||||
|
||||
socket.on('chatMessage', function(data) {
|
||||
socket.emit('api:chats.list', function(chats) {
|
||||
var chatsFrag = document.createDocumentFragment(),
|
||||
chatEl = document.createElement('li'),
|
||||
numChats = chats.length,
|
||||
x, userObj;
|
||||
|
||||
if (numChats > 0) {
|
||||
for(x=0;x<numChats;x++) {
|
||||
userObj = chats[x];
|
||||
chatEl.setAttribute('data-uid', userObj.uid);
|
||||
chatEl.innerHTML = '<a href="javascript:app.openChat(\'' + userObj.username + '\', ' + userObj.uid + ');"><img src="' + userObj.picture + '" title="' + userObj.username + '" />' + userObj.username + '</a>';
|
||||
|
||||
chatsFrag.appendChild(chatEl.cloneNode(true));
|
||||
}
|
||||
} else {
|
||||
chatEl.innerHTML = '<a href="#">No Recent Chats</a>';
|
||||
chatsFrag.appendChild(chatEl.cloneNode(true));
|
||||
}
|
||||
|
||||
chatsListEl.empty();
|
||||
chatsListEl.html(chatsFrag);
|
||||
});
|
||||
});
|
||||
|
||||
socket.on('event:chats.receive', function(data) {
|
||||
require(['chat'], function(chat) {
|
||||
var modal = null;
|
||||
if (chat.modalExists(data.fromuid)) {
|
||||
modal = chat.getModal(data.fromuid);
|
||||
chat.appendChatMessage(modal, data.message, data.timestamp);
|
||||
|
||||
if (modal.is(":visible")) {
|
||||
chat.load(modal.attr('UUID'));
|
||||
} else {
|
||||
chat.toggleNew(modal.attr('UUID'), true);
|
||||
}
|
||||
|
||||
if (!modal.is(":visible") || !app.isFocused) {
|
||||
app.alternatingTitle(data.username + ' has messaged you');
|
||||
}
|
||||
} else {
|
||||
modal = chat.createModal(data.username, data.fromuid);
|
||||
chat.toggleNew(modal.attr('UUID'), true);
|
||||
app.alternatingTitle(data.username + ' has messaged you');
|
||||
}
|
||||
|
||||
chat.load(modal.attr('UUID'));
|
||||
});
|
||||
});
|
||||
|
||||
require(['mobileMenu'], function(mobileMenu) {
|
||||
mobileMenu.init();
|
||||
});
|
||||
}());
|
||||
function updateUnreadCount(count) {
|
||||
var badge = $('#numUnreadBadge');
|
||||
badge.html(count > 20 ? '20+' : count);
|
||||
|
||||
if (count > 0) {
|
||||
badge
|
||||
.removeClass('badge-inverse')
|
||||
.addClass('badge-important');
|
||||
} else {
|
||||
badge
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse');
|
||||
}
|
||||
}
|
||||
|
||||
socket.on('event:unread.updateCount', updateUnreadCount);
|
||||
socket.emit('api:unread.count', updateUnreadCount);
|
||||
|
||||
}());
|
||||
|
||||
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,60 +1,64 @@
|
||||
(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');
|
||||
}
|
||||
});
|
||||
define(function() {
|
||||
var Login = {};
|
||||
|
||||
$('#login').on('click', function() {
|
||||
var loginData = {
|
||||
'username': $('#username').val(),
|
||||
'password': $('#password').val(),
|
||||
'_csrf': $('#csrf-token').val()
|
||||
};
|
||||
Login.init = function() {
|
||||
$('#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').attr('disabled', 'disabled').html('Logging in...');
|
||||
$('#login-error-notify').hide();
|
||||
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: RELATIVE_PATH + '/login',
|
||||
data: loginData,
|
||||
success: function(data, textStatus, jqXHR) {
|
||||
|
||||
if (!data.success) {
|
||||
$('#login-error-notify').show();
|
||||
$('#login').removeAttr('disabled').html('Login');
|
||||
} else {
|
||||
$('#login').html('Redirecting...');
|
||||
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();
|
||||
} else {
|
||||
$('#login-error-notify').hide();
|
||||
if(app.previousUrl.indexOf('/reset/') != -1)
|
||||
window.location.replace(RELATIVE_PATH + "/?loggedin");
|
||||
else
|
||||
window.location.replace(app.previousUrl + "?loggedin");
|
||||
$('#login').removeAttr('disabled').html('Login');
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true
|
||||
});
|
||||
|
||||
app.loadConfig();
|
||||
}
|
||||
},
|
||||
error: function(data, textStatus, jqXHR) {
|
||||
$('#login-error-notify').show();
|
||||
},
|
||||
dataType: 'json',
|
||||
async: true,
|
||||
timeout: 2000
|
||||
return false;
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
|
||||
$('#login-error-notify button').on('click', function() {
|
||||
$('#login-error-notify').hide();
|
||||
return false;
|
||||
});
|
||||
document.querySelector('#content input').focus();
|
||||
|
||||
document.querySelector('#content input').focus();
|
||||
}());
|
||||
if(!config.emailSetup)
|
||||
$('#reset-link').addClass('hide');
|
||||
else
|
||||
$('#reset-link').removeClass('hide');
|
||||
|
||||
};
|
||||
|
||||
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,85 +1,114 @@
|
||||
(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.enterRoom('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 getActiveSection() {
|
||||
var url = window.location.href,
|
||||
parts = url.split('/'),
|
||||
active = parts[parts.length - 1];
|
||||
return active;
|
||||
}
|
||||
|
||||
});
|
||||
active = getActiveSection();
|
||||
|
||||
function updateAlertText() {
|
||||
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
|
||||
}),
|
||||
container = $('#topics-container');
|
||||
});
|
||||
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var container = $('#topics-container');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
container.append(html);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
loadingMoreTopics = false;
|
||||
html = $(html);
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
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);
|
||||
}
|
||||
Recent.loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
})();
|
||||
return Recent;
|
||||
});
|
||||
|
||||
@@ -1,154 +1,157 @@
|
||||
(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>';
|
||||
define(function() {
|
||||
var Register = {};
|
||||
|
||||
$('#referrer').val(app.previousUrl);
|
||||
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="fa fa-check"></i>';
|
||||
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
}
|
||||
$('#referrer').val(app.previousUrl);
|
||||
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
function validateEmail() {
|
||||
if (!emailEl.val()) {
|
||||
function showError(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-success')
|
||||
.addClass('alert-danger');
|
||||
element.show();
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (!utils.isEmailValid(emailEl.val())) {
|
||||
showError(email_notify, 'Invalid email address.');
|
||||
} else
|
||||
socket.emit('user.email.exists', {
|
||||
email: emailEl.val()
|
||||
});
|
||||
}
|
||||
|
||||
emailEl.on('blur', function() {
|
||||
validateEmail();
|
||||
});
|
||||
|
||||
function validateUsername() {
|
||||
if (!username.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
function showSuccess(element, msg) {
|
||||
element.html(msg);
|
||||
element.parent()
|
||||
.removeClass('alert-danger')
|
||||
.addClass('alert-success');
|
||||
element.show();
|
||||
}
|
||||
|
||||
if (username.val().length < config.minimumUsernameLength) {
|
||||
showError(username_notify, 'Username too short!');
|
||||
} else if (username.val().length > config.maximumUsernameLength) {
|
||||
showError(username_notify, 'Username too long!');
|
||||
} else if (!utils.isUserNameValid(username.val())) {
|
||||
showError(username_notify, 'Invalid username!');
|
||||
} else {
|
||||
socket.emit('user.exists', {
|
||||
username: username.val()
|
||||
});
|
||||
}
|
||||
}
|
||||
function validateEmail() {
|
||||
if (!emailEl.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
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 (!utils.isEmailValid(emailEl.val())) {
|
||||
showError(email_notify, 'Invalid email address.');
|
||||
} else {
|
||||
socket.emit('user.email.exists', {
|
||||
email: emailEl.val()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
emailEl.on('blur', function() {
|
||||
validateEmail();
|
||||
});
|
||||
|
||||
function validateUsername() {
|
||||
if (!username.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (username.val().length < config.minimumUsernameLength) {
|
||||
showError(username_notify, 'Username too short!');
|
||||
} else if (username.val().length > config.maximumUsernameLength) {
|
||||
showError(username_notify, 'Username too long!');
|
||||
} else if (!utils.isUserNameValid(username.val()) || !utils.slugify(username.val())) {
|
||||
showError(username_notify, 'Invalid username!');
|
||||
} else {
|
||||
socket.emit('user.exists', {
|
||||
username: username.val()
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val() && password_confirm.val() !== '') {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
}
|
||||
}
|
||||
username.on('keyup', function() {
|
||||
jQuery('#yourUsername').html(this.value.length > 0 ? this.value : 'username');
|
||||
});
|
||||
username.on('blur', function() {
|
||||
validateUsername();
|
||||
});
|
||||
|
||||
$(password).on('blur', function() {
|
||||
validatePassword();
|
||||
});
|
||||
function validatePassword() {
|
||||
if (!password.val()) {
|
||||
validationError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
function validatePasswordConfirm() {
|
||||
if (!password.val() || password_notify.hasClass('alert-error')) {
|
||||
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!');
|
||||
}
|
||||
}
|
||||
|
||||
if (password.val() !== password_confirm.val()) {
|
||||
showError(password_confirm_notify, 'Passwords must match!');
|
||||
} else {
|
||||
showSuccess(password_confirm_notify, successIcon);
|
||||
$(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();
|
||||
});
|
||||
$(password_confirm).on('blur', function() {
|
||||
validatePasswordConfirm();
|
||||
});
|
||||
|
||||
ajaxify.register_events(['user.exists', 'user.email.exists']);
|
||||
ajaxify.register_events(['user.exists', 'user.email.exists']);
|
||||
|
||||
socket.on('user.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(username_notify, 'Username already taken!');
|
||||
} else {
|
||||
showSuccess(username_notify, successIcon);
|
||||
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);
|
||||
}
|
||||
});
|
||||
|
||||
function validateForm() {
|
||||
validationError = false;
|
||||
|
||||
validateEmail();
|
||||
validateUsername();
|
||||
validatePassword();
|
||||
validatePasswordConfirm();
|
||||
|
||||
return validationError;
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('user.email.exists', function(data) {
|
||||
if (data.exists === true) {
|
||||
showError(email_notify, 'Email address already taken!');
|
||||
} else {
|
||||
showSuccess(email_notify, successIcon);
|
||||
}
|
||||
});
|
||||
register.on('click', function(e) {
|
||||
if (validateForm()) {
|
||||
e.preventDefault();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// 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,41 +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,52 +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 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);
|
||||
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,6 +1,7 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Search = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
Search.init = function() {
|
||||
var searchQuery = $('#topics-container').attr('data-search-query');
|
||||
|
||||
$('.search-result-text').each(function() {
|
||||
@@ -19,6 +20,7 @@
|
||||
input.val('');
|
||||
return false;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
})();
|
||||
return Search;
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,114 +1,126 @@
|
||||
(function() {
|
||||
var loadingMoreTopics = false;
|
||||
define(function() {
|
||||
var Unread = {},
|
||||
loadingMoreTopics = false;
|
||||
|
||||
app.enter_room('recent_posts');
|
||||
Unread.init = function() {
|
||||
app.enterRoom('recent_posts');
|
||||
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
ajaxify.register_events([
|
||||
'event:new_topic',
|
||||
'event:new_post'
|
||||
]);
|
||||
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
var newTopicCount = 0,
|
||||
newPostCount = 0;
|
||||
|
||||
$('#new-topics-alert').on('click', function() {
|
||||
$(this).hide();
|
||||
});
|
||||
|
||||
socket.on('event:new_topic', function(data) {
|
||||
|
||||
++newTopicCount;
|
||||
updateAlertText();
|
||||
|
||||
});
|
||||
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if (newTopicCount > 1)
|
||||
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!');
|
||||
$('#numUnreadBadge')
|
||||
.removeClass('badge-important')
|
||||
.addClass('badge-inverse')
|
||||
.html('0');
|
||||
} 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);
|
||||
} else {
|
||||
$('#load-more-btn').hide();
|
||||
}
|
||||
|
||||
loadingMoreTopics = false;
|
||||
});
|
||||
}
|
||||
|
||||
$(window).off('scroll').on('scroll', function() {
|
||||
var bottom = ($(document).height() - $(window).height()) * 0.9;
|
||||
function updateAlertText() {
|
||||
var text = '';
|
||||
|
||||
if ($(window).scrollTop() > bottom && !loadingMoreTopics) {
|
||||
loadMoreTopics();
|
||||
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
|
||||
});
|
||||
|
||||
translator.translate(html, function(translatedHTML) {
|
||||
var container = $('#topics-container');
|
||||
|
||||
$('#category-no-topics').remove();
|
||||
|
||||
html = $(translatedHTML);
|
||||
container.append(html);
|
||||
$('span.timeago').timeago();
|
||||
app.makeNumbersHumanReadable(html.find('.human-readable-number'));
|
||||
});
|
||||
}
|
||||
|
||||
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 >= 20)
|
||||
$('#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,18 @@
|
||||
(function() {
|
||||
define(function() {
|
||||
var Users = {};
|
||||
|
||||
$(document).ready(function() {
|
||||
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];
|
||||
return active;
|
||||
}
|
||||
|
||||
var active = getActiveSection();
|
||||
|
||||
var lastSearch = null;
|
||||
|
||||
@@ -30,7 +36,7 @@
|
||||
var username = $('#search-user').val();
|
||||
|
||||
if (username == '') {
|
||||
jQuery('#user-notfound-notify').html('<i class="icon icon-circle-blank"></i>');
|
||||
jQuery('#user-notfound-notify').html('<i class="fa fa-circle-o"></i>');
|
||||
jQuery('#user-notfound-notify').parent().removeClass('btn-warning label-warning btn-success label-success');
|
||||
return;
|
||||
}
|
||||
@@ -38,7 +44,7 @@
|
||||
if (lastSearch === username) return;
|
||||
lastSearch = username;
|
||||
|
||||
jQuery('#user-notfound-notify').html('<i class="icon-spinner icon-spin"></i>');
|
||||
jQuery('#user-notfound-notify').html('<i class="fa fa-spinner fa-spin"></i>');
|
||||
|
||||
setTimeout(function() {
|
||||
socket.emit('api:admin.user.search', username);
|
||||
@@ -59,9 +65,9 @@
|
||||
var html = templates.prepare(templates['users'].blocks['users']).parse({
|
||||
users: data
|
||||
}),
|
||||
userListEl = document.querySelector('#users-container');
|
||||
userListEl = $('#users-container');
|
||||
|
||||
userListEl.innerHTML = html;
|
||||
userListEl.html(html);
|
||||
|
||||
|
||||
if (data && data.length === 0) {
|
||||
@@ -75,17 +81,27 @@
|
||||
});
|
||||
|
||||
socket.on('api:user.isOnline', function(data) {
|
||||
if(active == 'online' && !loadingMoreUsers) {
|
||||
$('#users-container').empty();
|
||||
startLoading('users:online', 0);
|
||||
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 {
|
||||
$('#users-container .anon-user').addClass('hide');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
function onUsersLoaded(users) {
|
||||
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() {
|
||||
@@ -101,18 +117,18 @@
|
||||
}
|
||||
|
||||
if (set) {
|
||||
startLoading(set, $('#users-container').children().length);
|
||||
startLoading(set, $('#users-container').children('.registered-user').length);
|
||||
}
|
||||
}
|
||||
|
||||
function startLoading(set, after) {
|
||||
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);
|
||||
onUsersLoaded(data.users, emptyContainer);
|
||||
$('#load-more-users-btn').removeClass('disabled');
|
||||
} else {
|
||||
$('#load-more-users-btn').addClass('disabled');
|
||||
@@ -131,6 +147,7 @@
|
||||
loadMoreUsers();
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
||||
return Users;
|
||||
});
|
||||
@@ -1,4 +1,4 @@
|
||||
define(['taskbar'], function(taskbar) {
|
||||
define(['taskbar', 'string'], function(taskbar, S) {
|
||||
|
||||
var module = {};
|
||||
|
||||
@@ -20,7 +20,7 @@ define(['taskbar'], function(taskbar) {
|
||||
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) {
|
||||
@@ -35,7 +35,7 @@ define(['taskbar'], function(taskbar) {
|
||||
callback(data.online);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function checkOnlineStatus(chatModal) {
|
||||
if(chatModal.intervalId === 0) {
|
||||
chatModal.intervalId = setInterval(function() {
|
||||
@@ -43,12 +43,12 @@ define(['taskbar'], function(taskbar) {
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.createModal = function(username, touid, callback) {
|
||||
|
||||
|
||||
var chatModal = $('#chat-modal').clone(),
|
||||
uuid = utils.generateUUID();
|
||||
|
||||
|
||||
chatModal.intervalId = 0;
|
||||
chatModal.touid = touid;
|
||||
chatModal.username = username;
|
||||
@@ -61,27 +61,38 @@ define(['taskbar'], function(taskbar) {
|
||||
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();
|
||||
chatModal.remove();
|
||||
chatModal.data('modal', null);
|
||||
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});
|
||||
taskbar.push('chat', chatModal.attr('UUID'), {
|
||||
title:'<i class="fa fa-comment"></i> ' + username,
|
||||
state: ''
|
||||
});
|
||||
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
module.center = function(chatModal) {
|
||||
chatModal.css("position", "fixed");
|
||||
chatModal.css("left", Math.max(0, (($(window).width() - $(chatModal).outerWidth()) / 2) + $(window).scrollLeft()) + "px");
|
||||
return chatModal;
|
||||
}
|
||||
|
||||
@@ -90,6 +101,9 @@ define(['taskbar'], function(taskbar) {
|
||||
chatModal.show();
|
||||
module.bringModalToTop(chatModal);
|
||||
checkOnlineStatus(chatModal);
|
||||
taskbar.updateActive(uuid);
|
||||
scrollToBottom(chatModal.find('#chat-content'));
|
||||
chatModal.find('#chat-message-input').focus();
|
||||
}
|
||||
|
||||
module.minimize = function(uuid) {
|
||||
@@ -101,7 +115,7 @@ define(['taskbar'], function(taskbar) {
|
||||
}
|
||||
|
||||
function getChatMessages(chatModal, callback) {
|
||||
socket.emit('getChatMessages', {touid:chatModal.touid}, function(messages) {
|
||||
socket.emit('api:chats.get', {touid:chatModal.touid}, function(messages) {
|
||||
for(var i = 0; i<messages.length; ++i) {
|
||||
module.appendChatMessage(chatModal, messages[i].content, messages[i].timestamp);
|
||||
}
|
||||
@@ -125,10 +139,10 @@ define(['taskbar'], function(taskbar) {
|
||||
}
|
||||
|
||||
function sendMessage(chatModal) {
|
||||
var msg = app.strip_tags(chatModal.find('#chat-message-input').val());
|
||||
var msg = S(chatModal.find('#chat-message-input').val()).stripTags().s;
|
||||
if(msg.length) {
|
||||
msg = msg +'\n';
|
||||
socket.emit('sendChatMessage', { touid:chatModal.touid, message:msg});
|
||||
socket.emit('api:chats.send', { touid:chatModal.touid, message:msg});
|
||||
chatModal.find('#chat-message-input').val('');
|
||||
}
|
||||
}
|
||||
@@ -139,10 +153,18 @@ define(['taskbar'], function(taskbar) {
|
||||
var date = new Date(parseInt(timestamp, 10));
|
||||
|
||||
chatContent.append('[' + date.toLocaleTimeString() + '] ' + message);
|
||||
scrollToBottom(chatContent);
|
||||
};
|
||||
|
||||
function scrollToBottom(chatContent) {
|
||||
chatContent.scrollTop(
|
||||
chatContent[0].scrollHeight - chatContent.height()
|
||||
);
|
||||
}
|
||||
|
||||
module.toggleNew = function(uuid, state) {
|
||||
taskbar.toggleNew(uuid, state);
|
||||
};
|
||||
|
||||
return module;
|
||||
});
|
||||
@@ -1,76 +1,444 @@
|
||||
define(['taskbar'], function(taskbar) {
|
||||
var composer = {
|
||||
initialized: false,
|
||||
active: undefined,
|
||||
taskbar: taskbar,
|
||||
posts: {},
|
||||
postContainer: undefined,
|
||||
posts: {}
|
||||
};
|
||||
|
||||
var uploadsInProgress = [];
|
||||
function allowed() {
|
||||
if(!(parseInt(app.uid, 10) > 0 || parseInt(config.allowGuestPosting, 10) === 1)) {
|
||||
app.alert({
|
||||
type: 'danger',
|
||||
timeout: 5000,
|
||||
alert_id: 'post_error',
|
||||
title: 'Please Log In to Post',
|
||||
message: 'Posting is currently restricted to registered members only, click here to log in',
|
||||
clickfn: function() {
|
||||
ajaxify.go('login');
|
||||
}
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function createImagePlaceholder(img) {
|
||||
var text = $('.post-window textarea').val(),
|
||||
textarea = $('.post-window textarea'),
|
||||
imgText = "";
|
||||
composer.newTopic = function(cid) {
|
||||
if(allowed()) {
|
||||
push({
|
||||
cid: cid,
|
||||
title: '',
|
||||
body: '',
|
||||
modified: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
text += imgText;
|
||||
textarea.val(text + " ");
|
||||
uploadsInProgress.push(1);
|
||||
socket.emit("api:posts.uploadImage", img, function(err, data) {
|
||||
composer.newReply = function(tid, title, text) {
|
||||
if(allowed()) {
|
||||
push({
|
||||
tid: tid,
|
||||
title: title,
|
||||
body: text,
|
||||
modified: false
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
var currentText = textarea.val();
|
||||
imgText = "";
|
||||
composer.editPost = function(pid) {
|
||||
if(allowed()) {
|
||||
socket.emit('api:composer.push', {
|
||||
pid: pid
|
||||
}, function(threadData) {
|
||||
push({
|
||||
pid: pid,
|
||||
title: threadData.title,
|
||||
body: threadData.body,
|
||||
modified: false
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if(!err)
|
||||
textarea.val(currentText.replace(imgText, ""));
|
||||
else
|
||||
textarea.val(currentText.replace(imgText, ""));
|
||||
uploadsInProgress.pop();
|
||||
function push(post) {
|
||||
var uuid = utils.generateUUID();
|
||||
|
||||
taskbar.push('composer', uuid, {
|
||||
title: post.title ? post.title : 'New Topic',
|
||||
icon: post.picture
|
||||
});
|
||||
|
||||
composer.posts[uuid] = post;
|
||||
|
||||
composer.load(uuid);
|
||||
}
|
||||
|
||||
composer.load = function(post_uuid) {
|
||||
if($('#cmp-uuid-' + post_uuid).length) {
|
||||
composer.activateReposition(post_uuid);
|
||||
} else {
|
||||
composer.createNewComposer(post_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
composer.createNewComposer = function(post_uuid) {
|
||||
|
||||
templates.preload_template('composer', function() {
|
||||
|
||||
var composerTemplate = templates['composer'].parse({});
|
||||
composerTemplate = $(composerTemplate);
|
||||
|
||||
composerTemplate.attr('id', 'cmp-uuid-' + post_uuid);
|
||||
|
||||
$(document.body).append(composerTemplate);
|
||||
|
||||
composer.activateReposition(post_uuid);
|
||||
|
||||
var postContainer = $(composerTemplate[0]);
|
||||
|
||||
if(config.allowFileUploads || config.imgurClientIDSet)
|
||||
initializeFileReader(post_uuid);
|
||||
|
||||
var postData = composer.posts[post_uuid],
|
||||
titleEl = postContainer.find('.title'),
|
||||
bodyEl = postContainer.find('textarea');
|
||||
|
||||
if (parseInt(postData.tid) > 0) {
|
||||
titleEl.val('Replying to: ' + postData.title);
|
||||
titleEl.prop('readOnly', true);
|
||||
} else if (parseInt(postData.pid) > 0) {
|
||||
titleEl.val(postData.title);
|
||||
titleEl.prop('readOnly', true);
|
||||
socket.emit('api:composer.editCheck', postData.pid, function(editCheck) {
|
||||
if (editCheck.titleEditable) {
|
||||
postContainer.find('input').prop('readonly', false);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
titleEl.val(postData.title);
|
||||
titleEl.prop('readOnly', false);
|
||||
}
|
||||
|
||||
bodyEl.val(postData.body);
|
||||
|
||||
postContainer.on('change', 'input, textarea', function() {
|
||||
composer.posts[post_uuid].modified = true;
|
||||
});
|
||||
|
||||
postContainer.on('click', '.action-bar button', function() {
|
||||
var action = $(this).attr('data-action');
|
||||
|
||||
switch(action) {
|
||||
case 'post':
|
||||
composer.post(post_uuid);
|
||||
break;
|
||||
case 'discard':
|
||||
if (composer.posts[post_uuid].modified) {
|
||||
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
|
||||
if (discard) {
|
||||
composer.discard(post_uuid);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
composer.discard(post_uuid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
postContainer.on('click', '.formatting-bar span', function() {
|
||||
var postContentEl = postContainer.find('textarea'),
|
||||
iconClass = $(this).find('i').attr('class'),
|
||||
cursorEnd = postContentEl.val().length,
|
||||
selectionStart = postContentEl[0].selectionStart,
|
||||
selectionEnd = postContentEl[0].selectionEnd,
|
||||
selectionLength = selectionEnd - selectionStart;
|
||||
|
||||
|
||||
function insertIntoInput(element, value) {
|
||||
var start = postContentEl[0].selectionStart;
|
||||
element.val(element.val().slice(0, start) + value + element.val().slice(start, element.val().length));
|
||||
postContentEl[0].selectionStart = postContentEl[0].selectionEnd = start + value.length;
|
||||
}
|
||||
|
||||
switch(iconClass) {
|
||||
case 'fa fa-bold':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
insertIntoInput(postContentEl, "**bolded text**");
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '**' + postContentEl.val().slice(selectionStart, selectionEnd) + '**' + postContentEl.val().slice(selectionEnd));
|
||||
postContentEl[0].selectionStart = selectionStart + 2;
|
||||
postContentEl[0].selectionEnd = selectionEnd + 2;
|
||||
}
|
||||
break;
|
||||
case 'fa fa-italic':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
insertIntoInput(postContentEl, "*italicised text*");
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '*' + postContentEl.val().slice(selectionStart, selectionEnd) + '*' + postContentEl.val().slice(selectionEnd));
|
||||
postContentEl[0].selectionStart = selectionStart + 1;
|
||||
postContentEl[0].selectionEnd = selectionEnd + 1;
|
||||
}
|
||||
break;
|
||||
case 'fa fa-list':
|
||||
// Nothing selected
|
||||
insertIntoInput(postContentEl, "\n\n* list item");
|
||||
break;
|
||||
case 'fa fa-link':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
insertIntoInput(postContentEl, "[link text](link url)");
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.val(postContentEl.val().slice(0, selectionStart) + '[' + postContentEl.val().slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.val().slice(selectionEnd));
|
||||
postContentEl[0].selectionStart = selectionStart + selectionLength + 3;
|
||||
postContentEl[0].selectionEnd = selectionEnd + 11;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
postContainer.on('click', '.formatting-bar span .fa-picture-o, .formatting-bar span .fa-upload', function() {
|
||||
$('#files').click();
|
||||
});
|
||||
|
||||
$('#files').on('change', function(e) {
|
||||
var files = e.target.files;
|
||||
if(files) {
|
||||
for (var i=0; i<files.length; i++) {
|
||||
loadFile(post_uuid, files[i]);
|
||||
}
|
||||
}
|
||||
$('#fileForm')[0].reset();
|
||||
});
|
||||
|
||||
|
||||
var resizeActive = false,
|
||||
resizeCenterX = 0,
|
||||
resizeOffset = 0,
|
||||
resizeStart = function(e) {
|
||||
bodyRect = document.body.getBoundingClientRect();
|
||||
resizeRect = resizeEl.getBoundingClientRect();
|
||||
resizeCenterX = resizeRect.left + (resizeRect.width/2);
|
||||
resizeOffset = resizeCenterX - e.clientX;
|
||||
resizeSnaps.half = bodyRect.width / 2;
|
||||
resizeSnaps.none = bodyRect.width;
|
||||
resizeActive = true;
|
||||
|
||||
$(document.body).on('mousemove', resizeAction);
|
||||
document.body.addEventListener('touchmove', resizeTouchAction);
|
||||
},
|
||||
resizeStop = function() {
|
||||
resizeActive = false;
|
||||
$(document.body).off('mousemove', resizeAction);
|
||||
document.body.removeEventListener('touchmove', resizeTouchAction);
|
||||
},
|
||||
resizeTouchAction = function(e) {
|
||||
e.preventDefault();
|
||||
resizeAction(e.touches[0]);
|
||||
},
|
||||
resizeAction = function(e) {
|
||||
if (resizeActive) {
|
||||
position = (e.clientX + resizeOffset);
|
||||
if (Math.abs(position - resizeSnaps.half) <= 15) {
|
||||
// Half snap
|
||||
postContainer.css('width', resizeSnaps.half);
|
||||
resizeSavePosition(resizeSnaps.half);
|
||||
} else if (Math.abs(position - resizeSnaps.none) <= 30) {
|
||||
// Minimize snap
|
||||
postContainer.css('width', bodyRect.width - resizeSnaps.none + 15);
|
||||
resizeSavePosition(resizeSnaps.none);
|
||||
} else if (position <= 30) {
|
||||
// Full snap
|
||||
postContainer.css('width', bodyRect.width - 15);
|
||||
resizeSavePosition(bodyRect.width - 15);
|
||||
} else {
|
||||
// OH SNAP, NO SNAPS!
|
||||
postContainer.css('width', bodyRect.width - position);
|
||||
resizeSavePosition(bodyRect.width - position);
|
||||
}
|
||||
}
|
||||
},
|
||||
resizeSavePosition = function(px) {
|
||||
var percentage = px/bodyRect.width;
|
||||
localStorage.setItem('composer:resizePercentage', percentage);
|
||||
},
|
||||
resizeSnaps = {
|
||||
none: 0,
|
||||
half: 0,
|
||||
full: 0
|
||||
},
|
||||
resizeRect, bodyRect;
|
||||
|
||||
var resizeEl = postContainer.find('.resizer')[0];
|
||||
|
||||
resizeEl.addEventListener('mousedown', resizeStart);
|
||||
resizeEl.addEventListener('mouseup', resizeStop);
|
||||
resizeEl.addEventListener('touchstart', function(e) {
|
||||
e.preventDefault();
|
||||
resizeStart(e.touches[0]);
|
||||
});
|
||||
resizeEl.addEventListener('touchend', function(e) {
|
||||
e.preventDefault();
|
||||
resizeStop();
|
||||
});
|
||||
// .on('mousedown touchstart', resizeStart)
|
||||
// .on('mouseup touchend', resizeStop)
|
||||
|
||||
window.addEventListener('resize', function() {
|
||||
if (composer.active !== undefined) {
|
||||
composer.activateReposition(composer.active);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function loadFile(file) {
|
||||
var reader = new FileReader(),
|
||||
dropDiv = $('.post-window .imagedrop'),
|
||||
uuid = dropDiv.parents('[data-uuid]').attr('data-uuid');
|
||||
composer.activateReposition = function(post_uuid) {
|
||||
|
||||
$(reader).on('loadend', function(e) {
|
||||
var bin = this.result;
|
||||
bin = bin.split(',')[1];
|
||||
if(composer.active && composer.active !== post_uuid) {
|
||||
composer.minimize(composer.active);
|
||||
}
|
||||
|
||||
var img = {
|
||||
name: file.name,
|
||||
data: bin
|
||||
};
|
||||
var percentage = localStorage.getItem('composer:resizePercentage'),
|
||||
bodyRect = document.body.getBoundingClientRect(),
|
||||
postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
|
||||
createImagePlaceholder(img);
|
||||
composer.active = post_uuid;
|
||||
|
||||
dropDiv.hide();
|
||||
});
|
||||
if (percentage) {
|
||||
if (bodyRect.width >= 768) {
|
||||
postContainer.css('width', Math.floor(bodyRect.width * percentage) + 'px');
|
||||
} else {
|
||||
postContainer.css('width', '100%');
|
||||
}
|
||||
}
|
||||
|
||||
reader.readAsDataURL(file);
|
||||
if(config.imgurClientIDSet) {
|
||||
postContainer.find('.upload-instructions').removeClass('hide');
|
||||
postContainer.find('.img-upload-btn').removeClass('hide');
|
||||
}
|
||||
|
||||
if(config.allowFileUploads) {
|
||||
postContainer.find('.file-upload-btn').removeClass('hide');
|
||||
}
|
||||
|
||||
postContainer.css('visibility', 'visible');
|
||||
|
||||
composer.focusElements(post_uuid);
|
||||
}
|
||||
|
||||
function initializeFileReader() {
|
||||
jQuery.event.props.push( "dataTransfer" );
|
||||
composer.focusElements = function(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
postData = composer.posts[post_uuid],
|
||||
titleEl = postContainer.find('.title'),
|
||||
bodyEl = postContainer.find('textarea');
|
||||
|
||||
if ((parseInt(postData.tid) || parseInt(postData.pid)) > 0) {
|
||||
bodyEl.focus();
|
||||
bodyEl.selectionStart = bodyEl.val().length;
|
||||
bodyEl.selectionEnd = bodyEl.val().length;
|
||||
} else if (parseInt(postData.cid) > 0) {
|
||||
titleEl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
composer.post = function(post_uuid) {
|
||||
var postData = composer.posts[post_uuid],
|
||||
postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
titleEl = postContainer.find('.title'),
|
||||
bodyEl = postContainer.find('textarea');
|
||||
|
||||
titleEl.val(titleEl.val().trim());
|
||||
bodyEl.val(bodyEl.val().trim());
|
||||
|
||||
if(postData.uploadsInProgress && postData.uploadsInProgress.length) {
|
||||
return composerAlert('Still uploading', 'Please wait for uploads to complete.');
|
||||
} else if (titleEl.val().length < parseInt(config.minimumTitleLength, 10)) {
|
||||
return composerAlert('Title too short', 'Please enter a longer title. At least ' + config.minimumTitleLength+ ' characters.');
|
||||
} else if (titleEl.val().length > parseInt(config.maximumTitleLength, 10)) {
|
||||
return composerAlert('Title too long', 'Please enter a shorter title. Titles can\'t be longer than ' + config.maximumTitleLength + ' characters.');
|
||||
} else if (bodyEl.val().length < parseInt(config.minimumPostLength, 10)) {
|
||||
return composerAlert('Content too short', 'Please enter a longer post. At least ' + config.minimumPostLength + ' characters.');
|
||||
}
|
||||
|
||||
// Still here? Let's post.
|
||||
if (parseInt(postData.cid) > 0) {
|
||||
socket.emit('api:topics.post', {
|
||||
'title' : titleEl.val(),
|
||||
'content' : bodyEl.val(),
|
||||
'category_id' : postData.cid
|
||||
});
|
||||
} else if (parseInt(postData.tid) > 0) {
|
||||
socket.emit('api:posts.reply', {
|
||||
'topic_id' : postData.tid,
|
||||
'content' : bodyEl.val()
|
||||
});
|
||||
} else if (parseInt(postData.pid) > 0) {
|
||||
socket.emit('api:posts.edit', {
|
||||
pid: postData.pid,
|
||||
content: bodyEl.val(),
|
||||
title: titleEl.val()
|
||||
});
|
||||
}
|
||||
|
||||
composer.discard(post_uuid);
|
||||
}
|
||||
|
||||
function composerAlert(title, message) {
|
||||
app.alert({
|
||||
type: 'danger',
|
||||
timeout: 2000,
|
||||
title: title,
|
||||
message: message,
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
composer.discard = function(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$('#cmp-uuid-' + post_uuid).remove();
|
||||
delete composer.posts[post_uuid];
|
||||
composer.active = undefined;
|
||||
taskbar.discard('composer', post_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
composer.minimize = function(post_uuid) {
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid);
|
||||
postContainer.css('visibility', 'hidden');
|
||||
composer.active = undefined;
|
||||
taskbar.minimize('composer', post_uuid);
|
||||
}
|
||||
|
||||
function initializeFileReader(post_uuid) {
|
||||
if(jQuery.event.props.indexOf('dataTransfer') === -1) {
|
||||
jQuery.event.props.push('dataTransfer');
|
||||
}
|
||||
|
||||
var draggingDocument = false;
|
||||
|
||||
if(window.FileReader) {
|
||||
var drop = $('.post-window .imagedrop'),
|
||||
textarea = $('.post-window textarea');
|
||||
var postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
drop = postContainer.find('.imagedrop'),
|
||||
textarea = postContainer.find('textarea');
|
||||
|
||||
$(document).on('dragstart', function(e) {
|
||||
$(document).off('dragstart').on('dragstart', function(e) {
|
||||
draggingDocument = true;
|
||||
}).on('dragend', function(e) {
|
||||
}).off('dragend').on('dragend', function(e) {
|
||||
draggingDocument = false;
|
||||
});
|
||||
|
||||
textarea.on('dragenter', function(e) {
|
||||
if(draggingDocument)
|
||||
if(draggingDocument) {
|
||||
return;
|
||||
}
|
||||
drop.css('top', textarea.position().top + 'px');
|
||||
drop.css('height', textarea.height());
|
||||
drop.css('line-height', textarea.height() + 'px');
|
||||
drop.show();
|
||||
|
||||
drop.on('dragleave', function(ev) {
|
||||
@@ -89,316 +457,85 @@ define(['taskbar'], function(taskbar) {
|
||||
|
||||
drop.on('drop', function(e) {
|
||||
e.preventDefault();
|
||||
var uuid = drop.parents('[data-uuid]').attr('data-uuid'),
|
||||
dt = e.dataTransfer,
|
||||
var dt = e.dataTransfer,
|
||||
files = dt.files;
|
||||
|
||||
for (var i=0; i<files.length; i++) {
|
||||
loadFile(files[i]);
|
||||
loadFile(post_uuid, files[i]);
|
||||
}
|
||||
|
||||
if(!files.length)
|
||||
if(!files.length) {
|
||||
drop.hide();
|
||||
}
|
||||
return false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
composer.init = function() {
|
||||
if (!composer.initialized) {
|
||||
var taskbar = document.getElementById('taskbar');
|
||||
function loadFile(post_uuid, file) {
|
||||
var reader = new FileReader(),
|
||||
dropDiv = $('#cmp-uuid-' + post_uuid).find('.imagedrop');
|
||||
|
||||
composer.postContainer = document.createElement('div');
|
||||
composer.postContainer.className = 'post-window row';
|
||||
composer.postContainer.innerHTML = '<div class="col-md-5">' +
|
||||
'<input type="text" tabIndex="1" placeholder="Enter your topic title here..." />' +
|
||||
'<div class="btn-toolbar formatting-bar">' +
|
||||
'<div class="btn-group">' +
|
||||
'<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>' +
|
||||
'<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>' +
|
||||
'</div>' +
|
||||
'</div>';
|
||||
$(reader).on('loadend', function(e) {
|
||||
var regex = /^data:.*;base64,(.*)$/;
|
||||
var matches = this.result.match(regex);
|
||||
|
||||
document.body.insertBefore(composer.postContainer, taskbar);
|
||||
var fileData = {
|
||||
name: file.name,
|
||||
data: matches[1]
|
||||
};
|
||||
|
||||
if(config.imgurClientIDSet)
|
||||
initializeFileReader();
|
||||
dropDiv.hide();
|
||||
|
||||
socket.on('api:composer.push', function(threadData) {
|
||||
if (!threadData.error) {
|
||||
var uuid = utils.generateUUID();
|
||||
|
||||
composer.taskbar.push('composer', uuid, {
|
||||
title: (!threadData.cid ? (threadData.title || '') : 'New Topic'),
|
||||
icon: threadData.picture
|
||||
});
|
||||
|
||||
composer.posts[uuid] = {
|
||||
tid: threadData.tid,
|
||||
cid: threadData.cid,
|
||||
pid: threadData.pid,
|
||||
title: threadData.title || '',
|
||||
body: threadData.body || ''
|
||||
};
|
||||
composer.load(uuid);
|
||||
} else {
|
||||
app.alert({
|
||||
type: 'danger',
|
||||
timeout: 5000,
|
||||
alert_id: 'post_error',
|
||||
title: 'Please Log In to Post',
|
||||
message: 'Posting is currently restricted to registered members only, click here to log in',
|
||||
clickfn: function() {
|
||||
ajaxify.go('login');
|
||||
}
|
||||
});
|
||||
if(file.type.match('image.*')) {
|
||||
uploadFile('api:posts.uploadImage', post_uuid, fileData);
|
||||
} else {
|
||||
if(file.size > parseInt(config.maximumFileSize, 10) * 1024) {
|
||||
return composerAlert('File too big', 'Maximum allowed file size is ' + config.maximumFileSize + 'kbs');
|
||||
}
|
||||
});
|
||||
uploadFile('api:posts.uploadFile', post_uuid, fileData);
|
||||
}
|
||||
});
|
||||
|
||||
socket.on('api:composer.editCheck', function(editCheck) {
|
||||
if (editCheck.titleEditable === true) composer.postContainer.querySelector('input').readOnly = false;
|
||||
});
|
||||
|
||||
// Post Window events
|
||||
var jPostContainer = $(composer.postContainer),
|
||||
postContentEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
jPostContainer.on('change', 'input, textarea', function() {
|
||||
var uuid = $(this).parents('.post-window')[0].getAttribute('data-uuid');
|
||||
if (this.nodeName === 'INPUT') composer.posts[uuid].title = this.value;
|
||||
else if (this.nodeName === 'TEXTAREA') composer.posts[uuid].body = this.value;
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.action-bar button', function() {
|
||||
var action = this.getAttribute('data-action'),
|
||||
uuid = $(this).parents('.post-window').attr('data-uuid');
|
||||
switch(action) {
|
||||
case 'post': composer.post(uuid); break;
|
||||
case 'minimize': composer.minimize(uuid); break;
|
||||
case 'discard':
|
||||
if (postContentEl.value.length > 0) {
|
||||
bootbox.confirm('Are you sure you wish to discard this post?', function(discard) {
|
||||
if (discard) composer.discard(uuid);
|
||||
});
|
||||
} else {
|
||||
composer.discard(uuid);
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
jPostContainer.on('click', '.formatting-bar span', function() {
|
||||
var iconClass = this.querySelector('i').className,
|
||||
cursorEnd = postContentEl.value.length,
|
||||
selectionStart = postContentEl.selectionStart,
|
||||
selectionEnd = postContentEl.selectionEnd,
|
||||
selectionLength = selectionEnd - selectionStart;
|
||||
|
||||
switch(iconClass) {
|
||||
case 'icon-bold':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
postContentEl.value = postContentEl.value + '**bolded text**';
|
||||
postContentEl.selectionStart = cursorEnd+2;
|
||||
postContentEl.selectionEnd = postContentEl.value.length - 2;
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '**' + postContentEl.value.slice(selectionStart, selectionEnd) + '**' + postContentEl.value.slice(selectionEnd);
|
||||
postContentEl.selectionStart = selectionStart + 2;
|
||||
postContentEl.selectionEnd = selectionEnd + 2;
|
||||
}
|
||||
break;
|
||||
case 'icon-italic':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
postContentEl.value = postContentEl.value + '*italicised text*';
|
||||
postContentEl.selectionStart = cursorEnd+1;
|
||||
postContentEl.selectionEnd = postContentEl.value.length - 1;
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '*' + postContentEl.value.slice(selectionStart, selectionEnd) + '*' + postContentEl.value.slice(selectionEnd);
|
||||
postContentEl.selectionStart = selectionStart + 1;
|
||||
postContentEl.selectionEnd = selectionEnd + 1;
|
||||
}
|
||||
break;
|
||||
case 'icon-list':
|
||||
// Nothing selected
|
||||
postContentEl.value = postContentEl.value + "\n\n* list item";
|
||||
postContentEl.selectionStart = cursorEnd+4;
|
||||
postContentEl.selectionEnd = postContentEl.value.length;
|
||||
break;
|
||||
case 'icon-link':
|
||||
if (selectionStart === selectionEnd) {
|
||||
// Nothing selected
|
||||
postContentEl.value = postContentEl.value + '[link text](link url)';
|
||||
postContentEl.selectionStart = cursorEnd+12;
|
||||
postContentEl.selectionEnd = postContentEl.value.length - 1;
|
||||
} else {
|
||||
// Text selected
|
||||
postContentEl.value = postContentEl.value.slice(0, selectionStart) + '[' + postContentEl.value.slice(selectionStart, selectionEnd) + '](link url)' + postContentEl.value.slice(selectionEnd);
|
||||
postContentEl.selectionStart = selectionStart + selectionLength + 3;
|
||||
postContentEl.selectionEnd = selectionEnd + 11;
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
window.addEventListener('resize', function() {
|
||||
if (composer.active !== undefined) composer.reposition(composer.active);
|
||||
});
|
||||
|
||||
composer.initialized = true;
|
||||
}
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
composer.push = function(tid, cid, pid, text) {
|
||||
socket.emit('api:composer.push', {
|
||||
tid: tid, // Replying
|
||||
cid: cid, // Posting
|
||||
pid: pid, // Editing
|
||||
body: text // Predefined text
|
||||
|
||||
function uploadFile(method, post_uuid, img) {
|
||||
var linkStart = method === 'api:posts.uploadImage' ? '!' : '',
|
||||
postContainer = $('#cmp-uuid-' + post_uuid),
|
||||
textarea = postContainer.find('textarea'),
|
||||
text = textarea.val(),
|
||||
imgText = linkStart + '[' + img.name + '](uploading...)';
|
||||
|
||||
text += imgText;
|
||||
textarea.val(text + " ");
|
||||
|
||||
if(!composer.posts[post_uuid].uploadsInProgress) {
|
||||
composer.posts[post_uuid].uploadsInProgress = [];
|
||||
}
|
||||
|
||||
composer.posts[post_uuid].uploadsInProgress.push(1);
|
||||
|
||||
socket.emit(method, img, function(err, data) {
|
||||
|
||||
var currentText = textarea.val();
|
||||
|
||||
if(err) {
|
||||
textarea.val(currentText.replace(imgText, linkStart + '[' + img.name + '](upload error)'));
|
||||
return app.alertError(err.message);
|
||||
}
|
||||
|
||||
textarea.val(currentText.replace(imgText, linkStart + '[' + data.name + '](' + data.url + ')'));
|
||||
|
||||
composer.posts[post_uuid].uploadsInProgress.pop();
|
||||
});
|
||||
}
|
||||
|
||||
composer.load = function(post_uuid) {
|
||||
var post_data = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
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;
|
||||
titleEl.readOnly = true;
|
||||
} else if (parseInt(post_data.pid) > 0) {
|
||||
titleEl.value = post_data.title;
|
||||
titleEl.readOnly = true;
|
||||
socket.emit('api:composer.editCheck', post_data.pid);
|
||||
} else {
|
||||
titleEl.value = post_data.title;
|
||||
titleEl.readOnly = false;
|
||||
}
|
||||
bodyEl.value = post_data.body;
|
||||
|
||||
|
||||
// Direct user focus to the correct element
|
||||
if ((parseInt(post_data.tid) || parseInt(post_data.pid)) > 0) {
|
||||
bodyEl.focus();
|
||||
bodyEl.selectionStart = bodyEl.value.length;
|
||||
bodyEl.selectionEnd = bodyEl.value.length;
|
||||
} else if (parseInt(post_data.cid) > 0) {
|
||||
titleEl.focus();
|
||||
}
|
||||
}
|
||||
|
||||
composer.reposition = function(post_uuid) {
|
||||
var postWindowEl = composer.postContainer.querySelector('.col-md-5'),
|
||||
taskbarBtn = document.querySelector('#taskbar [data-uuid="' + post_uuid + '"]'),
|
||||
btnRect = taskbarBtn.getBoundingClientRect(),
|
||||
taskbarRect = document.getElementById('taskbar').getBoundingClientRect(),
|
||||
windowRect, leftPos;
|
||||
|
||||
composer.postContainer.style.display = 'block';
|
||||
windowRect = postWindowEl.getBoundingClientRect();
|
||||
leftPos = btnRect.left + btnRect.width - windowRect.width;
|
||||
postWindowEl.style.left = (leftPos > 0 ? leftPos : 0) + 'px';
|
||||
composer.postContainer.style.bottom = taskbarRect.height + "px";
|
||||
}
|
||||
|
||||
composer.post = function(post_uuid) {
|
||||
// Check title and post length
|
||||
var postData = composer.posts[post_uuid],
|
||||
titleEl = composer.postContainer.querySelector('input'),
|
||||
bodyEl = composer.postContainer.querySelector('textarea');
|
||||
|
||||
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: 'danger',
|
||||
timeout: 2000,
|
||||
title: 'Title too short',
|
||||
message: "Please enter a longer title. At least " + config.minimumTitleLength+ " characters.",
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
if (bodyEl.value.length < config.minimumPostLength) {
|
||||
return app.alert({
|
||||
type: 'danger',
|
||||
timeout: 2000,
|
||||
title: 'Content too short',
|
||||
message: "Please enter a longer post. At least " + config.minimumPostLength + " characters.",
|
||||
alert_id: 'post_error'
|
||||
});
|
||||
}
|
||||
|
||||
// Still here? Let's post.
|
||||
if (parseInt(postData.cid) > 0) {
|
||||
socket.emit('api:topics.post', {
|
||||
'title' : titleEl.value,
|
||||
'content' : bodyEl.value,
|
||||
'category_id' : postData.cid
|
||||
});
|
||||
} else if (parseInt(postData.tid) > 0) {
|
||||
socket.emit('api:posts.reply', {
|
||||
'topic_id' : postData.tid,
|
||||
'content' : bodyEl.value
|
||||
});
|
||||
} else if (parseInt(postData.pid) > 0) {
|
||||
socket.emit('api:posts.edit', {
|
||||
pid: postData.pid,
|
||||
content: bodyEl.value,
|
||||
title: titleEl.value
|
||||
});
|
||||
}
|
||||
|
||||
composer.discard(post_uuid);
|
||||
}
|
||||
|
||||
composer.discard = function(post_uuid) {
|
||||
if (composer.posts[post_uuid]) {
|
||||
$(composer.postContainer).find('.imagedrop').hide();
|
||||
delete composer.posts[post_uuid];
|
||||
composer.minimize();
|
||||
taskbar.discard('composer', post_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
composer.minimize = function(uuid) {
|
||||
composer.postContainer.style.display = 'none';
|
||||
composer.active = undefined;
|
||||
taskbar.minimize('composer', uuid);
|
||||
}
|
||||
|
||||
composer.init();
|
||||
|
||||
return {
|
||||
push: composer.push,
|
||||
newTopic: composer.newTopic,
|
||||
newReply: composer.newReply,
|
||||
editPost: composer.editPost,
|
||||
load: composer.load,
|
||||
minimize: composer.minimize
|
||||
};
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
define(function() {
|
||||
var mobileMenu = {};
|
||||
|
||||
|
||||
var categories = null,
|
||||
overlay = null,
|
||||
menuBtn = null,
|
||||
postBtn = null,
|
||||
initialized = false;
|
||||
|
||||
|
||||
function loadCategories(callback) {
|
||||
if (categories) {
|
||||
callback(true);
|
||||
return;
|
||||
}
|
||||
|
||||
jQuery.getJSON(RELATIVE_PATH+'/api/home', function(data) {
|
||||
categories = data.categories;
|
||||
initialized = true;
|
||||
|
||||
if (callback) {
|
||||
callback(true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function displayCategories() {
|
||||
var baseIcon = document.createElement('a'),
|
||||
baseImage = document.createElement('i'),
|
||||
baseName = document.createElement('span');
|
||||
|
||||
baseIcon.className = 'mobile-menu-icon';
|
||||
|
||||
for (var i=0, ii=categories.length; i<ii; i++) {
|
||||
var icon = baseIcon.cloneNode(true),
|
||||
image = baseImage.cloneNode(true),
|
||||
name = baseName.cloneNode(true);
|
||||
|
||||
var category = categories[i];
|
||||
|
||||
image.className = category.icon + ' icon-3x';
|
||||
name.innerHTML = '<br />' + category.name;
|
||||
icon.appendChild(image);
|
||||
icon.appendChild(name);
|
||||
icon.src = 'category/' + category.slug;
|
||||
|
||||
icon.onclick = function() {
|
||||
jQuery('.mobile-menu-icon').toggleClass('menu-visible');
|
||||
|
||||
setTimeout(function() {
|
||||
jQuery(overlay).toggleClass('menu-visible');
|
||||
}, 200);
|
||||
|
||||
ajaxify.go(this.src);
|
||||
}
|
||||
|
||||
overlay.appendChild(icon);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function animateIcons() {
|
||||
jQuery(overlay).toggleClass('menu-visible');
|
||||
setTimeout(function() {
|
||||
jQuery('.mobile-menu-icon').toggleClass('menu-visible');
|
||||
}, 100);
|
||||
}
|
||||
|
||||
|
||||
mobileMenu.onNavigate = function() {
|
||||
if (initialized == false) return false;
|
||||
|
||||
var cid = templates.get('category_id'),
|
||||
tid = templates.get('topic_id');
|
||||
|
||||
if (cid) {
|
||||
postBtn.style.display = 'inline-block';
|
||||
postBtn.onclick = function() {
|
||||
require(['composer'], function(cmp) {
|
||||
cmp.push(0, cid);
|
||||
});
|
||||
};
|
||||
postBtn.children[0].className = 'icon-plus icon-2x';
|
||||
} else if (tid) {
|
||||
postBtn.style.display = 'inline-block';
|
||||
postBtn.onclick = function() {
|
||||
require(['composer'], function(cmp) {
|
||||
cmp.push(tid);
|
||||
});
|
||||
};
|
||||
postBtn.children[0].className = 'icon-reply icon-2x'
|
||||
} else {
|
||||
postBtn.style.display = 'none';
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
loadCategories(function() {
|
||||
displayCategories();
|
||||
mobileMenu.onNavigate();
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
return {
|
||||
init: mobileMenu.init,
|
||||
onNavigate: mobileMenu.onNavigate
|
||||
}
|
||||
});
|
||||
3
public/src/modules/string.js
Normal file
3
public/src/modules/string.js
Normal file
File diff suppressed because one or more lines are too long
@@ -25,6 +25,8 @@ define(function() {
|
||||
if (_btn.className.indexOf('active') === -1) {
|
||||
taskbar.minimizeAll();
|
||||
module.load(uuid);
|
||||
taskbar.toggleNew(uuid, false);
|
||||
app.alternatingTitle('');
|
||||
|
||||
// Highlight the button
|
||||
$(taskbar.tasklist).removeClass('active');
|
||||
@@ -69,7 +71,7 @@ define(function() {
|
||||
'</a>';
|
||||
btnEl.setAttribute('data-module', module);
|
||||
btnEl.setAttribute('data-uuid', uuid);
|
||||
btnEl.className = options.state || 'active';
|
||||
btnEl.className = options.state !== undefined ? options.state : 'active';
|
||||
|
||||
if (!options.state || options.state === 'active') taskbar.minimizeAll();
|
||||
taskbar.tasklist.appendChild(btnEl);
|
||||
@@ -82,14 +84,27 @@ define(function() {
|
||||
},
|
||||
minimizeAll: function() {
|
||||
$(taskbar.tasklist.querySelectorAll('.active')).removeClass('active');
|
||||
},
|
||||
toggleNew: function(uuid, state) {
|
||||
var btnEl = $(taskbar.tasklist.querySelector('[data-uuid="' + uuid + '"]'));
|
||||
btnEl.toggleClass('new', state);
|
||||
},
|
||||
updateActive: function(uuid) {
|
||||
var tasks = $(taskbar.tasklist).find('li');
|
||||
tasks.removeClass('active');
|
||||
tasks.filter('[data-uuid="' + uuid + '"]').addClass('active');
|
||||
}
|
||||
}
|
||||
|
||||
if (!taskbar.initialized) taskbar.init();
|
||||
if (!taskbar.initialized) {
|
||||
taskbar.init();
|
||||
}
|
||||
|
||||
return {
|
||||
push: taskbar.push,
|
||||
discard: taskbar.discard,
|
||||
minimize: taskbar.minimize
|
||||
minimize: taskbar.minimize,
|
||||
toggleNew: taskbar.toggleNew,
|
||||
updateActive: taskbar.updateActive
|
||||
}
|
||||
});
|
||||
84
public/src/modules/uploader.js
Normal file
84
public/src/modules/uploader.js
Normal file
@@ -0,0 +1,84 @@
|
||||
define(function() {
|
||||
|
||||
var module = {};
|
||||
|
||||
module.open = function(route, params, callback) {
|
||||
$('#upload-picture-modal').modal('show').removeClass('hide');
|
||||
module.hideAlerts();
|
||||
|
||||
$('#uploadForm')[0].reset();
|
||||
$('#uploadForm').attr('action', route);
|
||||
$('#uploadForm').find('#params').val(JSON.stringify(params));
|
||||
|
||||
$('#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;
|
||||
});
|
||||
@@ -48,7 +48,7 @@
|
||||
}
|
||||
};
|
||||
|
||||
templates.prepare = function (raw_tpl, data) {
|
||||
templates.prepare = function (raw_tpl) {
|
||||
var template = {};
|
||||
template.html = raw_tpl;
|
||||
template.parse = parse;
|
||||
@@ -57,30 +57,43 @@
|
||||
return template;
|
||||
};
|
||||
|
||||
function loadTemplates(templatesToLoad) {
|
||||
function loadTemplates(templatesToLoad, customTemplateDir) {
|
||||
function loadServer() {
|
||||
var loaded = templatesToLoad.length;
|
||||
|
||||
for (var t in templatesToLoad) {
|
||||
(function (file) {
|
||||
fs.readFile(__dirname + '/../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);
|
||||
template.prototype.file = file;
|
||||
template.prototype.parse = parse;
|
||||
template.prototype.html = String(html);
|
||||
|
||||
global.templates[file] = new template;
|
||||
global.templates[file] = new template;
|
||||
|
||||
loaded--;
|
||||
if (loaded == 0) templates.ready();
|
||||
});
|
||||
}(templatesToLoad[t]));
|
||||
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() {
|
||||
@@ -96,8 +109,8 @@
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
@@ -111,12 +124,37 @@
|
||||
return '';
|
||||
}
|
||||
|
||||
templates.preload_template = function(tpl_name, callback) {
|
||||
|
||||
if(templates[tpl_name]) {
|
||||
return callback();
|
||||
}
|
||||
|
||||
// TODO: This should be "load_template", and the current load_template
|
||||
// should be named something else
|
||||
// TODO: The "Date.now()" in the line below is only there for development purposes.
|
||||
// It should be removed at some point.
|
||||
jQuery.get(RELATIVE_PATH + '/templates/' + tpl_name + '.tpl?v=' + Date.now(), 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_name] = new template;
|
||||
|
||||
callback();
|
||||
});
|
||||
}
|
||||
|
||||
templates.load_template = function (callback, url, template) {
|
||||
var location = document.location || window.location,
|
||||
api_url = (url === '' || url === '/') ? 'home' : url,
|
||||
tpl_url = templates.get_custom_map(api_url.split('?')[0]),
|
||||
trimmed = api_url;
|
||||
tpl_url = templates.get_custom_map(api_url.split('?')[0]);
|
||||
|
||||
if (!tpl_url) {
|
||||
tpl_url = templates.getTemplateNameFromUrl(api_url);
|
||||
@@ -127,19 +165,7 @@
|
||||
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;
|
||||
|
||||
templates.preload_template(tpl_url, function() {
|
||||
parse_template();
|
||||
});
|
||||
} else {
|
||||
@@ -156,7 +182,13 @@
|
||||
template_data = data;
|
||||
parse_template();
|
||||
}).fail(function (data) {
|
||||
app.alertError("Can't load template data!");
|
||||
if (data && data.status == 404) {
|
||||
return ajaxify.go('404');
|
||||
} else if (data && data.status === 403) {
|
||||
return ajaxify.go('403');
|
||||
} else {
|
||||
app.alertError(data.responseJSON.error);
|
||||
}
|
||||
});
|
||||
|
||||
function parse_template() {
|
||||
@@ -168,25 +200,26 @@
|
||||
template_data['relative_path'] = RELATIVE_PATH;
|
||||
|
||||
translator.translate(templates[tpl_url].parse(template_data), function (translatedTemplate) {
|
||||
document.getElementById('content').innerHTML = translatedTemplate;
|
||||
|
||||
$('#content').html(translatedTemplate);
|
||||
|
||||
jQuery('#content [template-variable]').each(function (index, element) {
|
||||
var value = null;
|
||||
|
||||
switch (element.getAttribute('template-type')) {
|
||||
case 'boolean':
|
||||
value = (element.value === 'true' || element.value === '1') ? true : false;
|
||||
break;
|
||||
case 'int': // Intentional fall-through
|
||||
case 'integer':
|
||||
value = parseInt(element.value);
|
||||
break;
|
||||
default:
|
||||
value = element.value;
|
||||
break;
|
||||
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.getAttribute('template-variable'), value);
|
||||
templates.set($(element).attr('template-variable'), value);
|
||||
});
|
||||
|
||||
if (callback) {
|
||||
@@ -219,7 +252,11 @@
|
||||
}
|
||||
|
||||
function makeRegex(block) {
|
||||
return new RegExp("<!-- BEGIN " + block + " -->[\\s\\S]*<!-- END " + block + " -->", 'g');
|
||||
return new RegExp("<!--[\\s]*BEGIN " + block + "[\\s]*-->[\\s\\S]*<!--[\\s]*END " + block + "[\\s]*-->", 'g');
|
||||
}
|
||||
|
||||
function makeConditionalRegex(block) {
|
||||
return new RegExp("<!--[\\s]*IF " + block + "[\\s]*-->([\\s\\S]*?)<!--[\\s]*ENDIF " + block + "[\\s]*-->", 'g');
|
||||
}
|
||||
|
||||
function getBlock(regex, block, template) {
|
||||
@@ -228,9 +265,12 @@
|
||||
|
||||
if (self.blocks && block !== undefined) self.blocks[block] = data[0];
|
||||
|
||||
var begin = new RegExp("(\r\n)*<!-- BEGIN " + block + " -->(\r\n)*", "g"),
|
||||
end = new RegExp("(\r\n)*<!-- END " + block + " -->(\r\n)*", "g"),
|
||||
|
||||
data = data[0]
|
||||
.replace("<!-- BEGIN " + block + " -->", "")
|
||||
.replace("<!-- END " + block + " -->", "");
|
||||
.replace(begin, "")
|
||||
.replace(end, "");
|
||||
|
||||
return data;
|
||||
}
|
||||
@@ -242,14 +282,16 @@
|
||||
var template = this.html,
|
||||
regex, block;
|
||||
|
||||
return (function parse(data, namespace, template) {
|
||||
return (function parse(data, namespace, template, blockInfo) {
|
||||
if (!data || data.length == 0) {
|
||||
template = '';
|
||||
}
|
||||
|
||||
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 + '.';
|
||||
@@ -267,21 +309,51 @@
|
||||
result = "";
|
||||
|
||||
do {
|
||||
result += parse(data[d][i], namespace, block);
|
||||
result += parse(data[d][i], namespace, block, {iterator: i, total: numblocks});
|
||||
} while (i++ < numblocks);
|
||||
|
||||
namespace = namespace.replace(d + '.', '');
|
||||
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;
|
||||
|
||||
block = parse(data[d], namespace, block);
|
||||
template = setBlock(regex, block, template);
|
||||
template = parse(data[d], d + '.', template);
|
||||
} else {
|
||||
function checkConditional(key, value) {
|
||||
var conditional = makeConditionalRegex(key),
|
||||
matches = template.match(conditional);
|
||||
|
||||
if (matches !== null) {
|
||||
for (var i = 0, ii = matches.length; i < ii; i++) {
|
||||
var conditionalBlock = matches[i].split(/<!-- ELSE -->/);
|
||||
|
||||
if (conditionalBlock[1]) {
|
||||
// there is an else statement
|
||||
if (!value) {
|
||||
template = template.replace(matches[i], conditionalBlock[1].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
|
||||
} else {
|
||||
template = template.replace(matches[i], conditionalBlock[0].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
|
||||
}
|
||||
} else {
|
||||
// regular if statement
|
||||
if (!value) {
|
||||
template = template.replace(matches[i], '');
|
||||
} else {
|
||||
template = template.replace(matches[i], matches[i].replace(/<!-- ((\IF\b)|(\bENDIF\b))([^@]*?)-->/gi, ''));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkConditional(namespace + d, data[d]);
|
||||
checkConditional('!' + namespace + d, !data[d]);
|
||||
|
||||
if (blockInfo) {
|
||||
checkConditional('@first', blockInfo.iterator === 0);
|
||||
checkConditional('!@first', blockInfo.iterator !== 0);
|
||||
checkConditional('@last', blockInfo.iterator === blockInfo.total);
|
||||
checkConditional('!@last', blockInfo.iterator !== blockInfo.total);
|
||||
}
|
||||
|
||||
template = replace(namespace + d, data[d], template);
|
||||
}
|
||||
}
|
||||
@@ -290,8 +362,12 @@
|
||||
if (namespace) {
|
||||
var regex = new RegExp("{" + namespace + "[\\s\\S]*?}", 'g');
|
||||
template = template.replace(regex, '');
|
||||
namespace = '';
|
||||
}
|
||||
|
||||
// clean up all undefined conditionals
|
||||
template = template.replace(/<!-- IF([^@]*?)ENDIF([^@]*?)-->/gi, '');
|
||||
|
||||
return template;
|
||||
|
||||
})(data, "", template);
|
||||
@@ -306,4 +382,4 @@
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
} : module)
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
languageFile = parsedKey[0];
|
||||
|
||||
parsedKey = parsedKey[1];
|
||||
|
||||
translator.load(languageFile, function (languageData) {
|
||||
if (callback) {
|
||||
callback(languageData[parsedKey]);
|
||||
@@ -39,6 +40,20 @@
|
||||
});
|
||||
};
|
||||
|
||||
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.
|
||||
@@ -46,7 +61,7 @@
|
||||
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++) {
|
||||
@@ -56,7 +71,7 @@
|
||||
|
||||
text = text.replace(key, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
return text;
|
||||
}
|
||||
@@ -65,23 +80,23 @@
|
||||
if (keys.hasOwnProperty(key)) {
|
||||
var variables = keys[key].split(/[,][?\s+]/);
|
||||
|
||||
var parsedKey = keys[key].replace('[[', '').replace(']]', '').split(':'),
|
||||
languageFile = parsedKey[0];
|
||||
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) {
|
||||
(function (languageKey, parsedKey, languageFile, variables) {
|
||||
translator.load(languageFile, function (languageData) {
|
||||
data = insertLanguage(data, languageKey, languageData[parsedKey], variables);
|
||||
loading--;
|
||||
checkComplete();
|
||||
});
|
||||
}(keys[key], parsedKey));
|
||||
}(keys[key], parsedKey, languageFile, variables));
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,19 @@
|
||||
(function(module) {
|
||||
'use strict';
|
||||
|
||||
var utils, fs;
|
||||
var utils, fs, XRegExp;
|
||||
|
||||
try {
|
||||
if ('undefined' === typeof window) {
|
||||
fs = require('fs');
|
||||
} catch (e) {}
|
||||
XRegExp = require('xregexp').XRegExp;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
v = c === 'x' ? r : (r & 0x3 | 0x8);
|
||||
return v.toString(16);
|
||||
});
|
||||
},
|
||||
@@ -23,20 +25,28 @@
|
||||
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);
|
||||
if (!--pending) {
|
||||
done(null, results);
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -47,21 +57,35 @@
|
||||
var now = +new Date(),
|
||||
difference = now - Math.floor(parseFloat(timestamp));
|
||||
|
||||
if(difference < 0) {
|
||||
difference = 0;
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 1000);
|
||||
|
||||
if (difference < 60) return difference + (min ? 's' : ' second') + (difference !== 1 && !min ? 's' : '');
|
||||
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 ? 'm' : ' minute') + (difference !== 1 && !min ? 's' : '');
|
||||
}
|
||||
|
||||
difference = Math.floor(difference / 60);
|
||||
if (difference < 24) return difference + (min ? 'h' : ' hour') + (difference !== 1 && !min ? 's' : '');
|
||||
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' : '');
|
||||
@@ -69,19 +93,13 @@
|
||||
|
||||
//http://dense13.com/blog/2009/05/03/converting-string-to-slug-javascript/
|
||||
slugify: function(str) {
|
||||
var invalidChars = XRegExp('[^\\p{L} 0-9\-]', 'g');
|
||||
|
||||
str = str.replace(/^\s+|\s+$/g, ''); // trim
|
||||
str = str.toLowerCase();
|
||||
|
||||
// remove accents, swap ñ for n, etc
|
||||
var from = "àáäâèéëêìíïîıòóöôùúüûñçşğ·/_,:;";
|
||||
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
|
||||
str = XRegExp.replace(str, invalidChars, '');
|
||||
str = str.replace(/\s+/g, '-') // collapse whitespace and replace by -
|
||||
str = str.replace(/-+/g, '-'); // collapse dashes
|
||||
|
||||
return str;
|
||||
},
|
||||
@@ -94,30 +112,21 @@
|
||||
},
|
||||
|
||||
isUserNameValid: function(name) {
|
||||
return (name && name !== "" && (/^[a-zA-Z0-9 _-]+$/.test(name)));
|
||||
return (name && name !== "" && (/^['"\s\-.*0-9\u00BF-\u1FFF\u2C00-\uD7FF\w]+$/.test(name)));
|
||||
},
|
||||
|
||||
isPasswordValid: function(password) {
|
||||
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,
|
||||
commentsAndPhpTags = /<!--[\s\S]*?-->|<\?(?:php)?[\s\S]*?\?>/gi;
|
||||
|
||||
return input.replace(commentsAndPhpTags, '').replace(tags, function($0, $1) {
|
||||
return allowed.indexOf('<' + $1.toLowerCase() + '>') > -1 ? $0 : '';
|
||||
});
|
||||
},
|
||||
|
||||
buildMetaTags: function(tagsArr) {
|
||||
var tags = '',
|
||||
tag;
|
||||
for (var x = 0, numTags = tagsArr.length; x < numTags; x++) {
|
||||
if (tags.length > 0) tags += "\n\t";
|
||||
if (tags.length > 0) {
|
||||
tags += "\n\t";
|
||||
}
|
||||
tag = '<meta';
|
||||
var y;
|
||||
for (y in tagsArr[x]) {
|
||||
tag += ' ' + y + '="' + tagsArr[x][y] + '"';
|
||||
}
|
||||
@@ -129,41 +138,43 @@
|
||||
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')
|
||||
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 += ' />';
|
||||
|
||||
tags += tag;
|
||||
}
|
||||
|
||||
return tags;
|
||||
},
|
||||
|
||||
isRelativeUrl: function(url) {
|
||||
var firstChar = url.slice(0, 1);
|
||||
return (firstChar === '.' || firstChar === '/');
|
||||
},
|
||||
|
||||
makeNumberHumanReadable: function(num) {
|
||||
var n = parseInt(num, 10);
|
||||
if(!n) {
|
||||
return num;
|
||||
}
|
||||
if (n > 999999) {
|
||||
return (n / 1000000).toFixed(1) + 'm';
|
||||
}
|
||||
else if(n > 999) {
|
||||
return (n / 1000).toFixed(1) + 'k';
|
||||
}
|
||||
return n;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
if (!String.prototype.trim) {
|
||||
@@ -172,25 +183,6 @@
|
||||
};
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -199,4 +191,4 @@
|
||||
module: {
|
||||
exports: {}
|
||||
}
|
||||
} : module)
|
||||
} : module);
|
||||
5
public/templates/500.tpl
Normal file
5
public/templates/500.tpl
Normal file
@@ -0,0 +1,5 @@
|
||||
<div class="alert alert-danger">
|
||||
<strong>[[global:500.title]]</strong>
|
||||
<p>[[global:500.message]]</p>
|
||||
<p>{errorMessage}</p>
|
||||
</div>
|
||||
@@ -13,69 +13,96 @@
|
||||
<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>
|
||||
<span><i class="fa fa-circle-o"></i> <span>[[user:offline]]</span></span>
|
||||
</div>
|
||||
<div class="{show_banned}">
|
||||
<span class="label label-danger">banned</span>
|
||||
<!-- IF banned -->
|
||||
<div>
|
||||
<span class="label label-danger">[[user:banned]]</span>
|
||||
</div>
|
||||
<!-- ENDIF banned -->
|
||||
<div>
|
||||
<a id="chat-btn" href="#" class="btn btn-default hide">Chat</a>
|
||||
</div>
|
||||
<div id="user-actions">
|
||||
<a id="follow-btn" href="#" class="btn btn-default">Follow</a>
|
||||
<a id="unfollow-btn" href="#" class="btn btn-default">Unfollow</a>
|
||||
<a id="follow-btn" href="#" class="btn btn-default hide">Follow</a>
|
||||
<a id="unfollow-btn" href="#" class="btn btn-default hide">Unfollow</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<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 class="account-bio-label">[[user:email]]</span><i class="fa fa-eye-slash {emailClass}" title="[[user:email_hidden]]"></i>
|
||||
<!-- IF email -->
|
||||
<span>{email}</span>
|
||||
<!-- ELSE -->
|
||||
<i class="fa fa-eye-slash" title="[[user:email_hidden]]"></i> [[user:hidden]]
|
||||
<!-- ENDIF email -->
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">full name</span>
|
||||
|
||||
<!-- IF fullname -->
|
||||
<span class="account-bio-label">[[user:fullname]]</span>
|
||||
<span>{fullname}</span>
|
||||
<br/>
|
||||
<!-- ENDIF fullname -->
|
||||
|
||||
<span class="account-bio-label">website</span>
|
||||
<span><a href="{website}">{website}</a></span>
|
||||
<!-- IF website -->
|
||||
<span class="account-bio-label">[[user:website]]</span>
|
||||
<span><a href="{website}">{websiteName}</a></span>
|
||||
<br/>
|
||||
<!-- ENDIF website -->
|
||||
|
||||
<span class="account-bio-label">location</span>
|
||||
<!-- IF location -->
|
||||
<span class="account-bio-label">[[user:location]]</span>
|
||||
<span>{location}</span>
|
||||
<br/>
|
||||
<!-- ENDIF location -->
|
||||
|
||||
<span class="account-bio-label">age</span>
|
||||
<!-- IF age -->
|
||||
<span class="account-bio-label">[[user:age]]</span>
|
||||
<span>{age}</span>
|
||||
<br/>
|
||||
<!-- ENDIF age -->
|
||||
|
||||
<hr/>
|
||||
<span class="account-bio-label">joined</span>
|
||||
<span class="account-bio-label">[[user:joined]]</span>
|
||||
<span class="timeago" title="{joindate}"></span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">profile views</span>
|
||||
<span class="account-bio-label">[[user:lastonline]]</span>
|
||||
<span class="timeago" title="{lastonline}"></span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">[[user:profile_views]]</span>
|
||||
<span class="formatted-number">{profileviews}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">reputation</span>
|
||||
<span class="account-bio-label">[[user:reputation]]</span>
|
||||
<span class="formatted-number">{reputation}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">posts</span>
|
||||
<span class="account-bio-label">[[user:posts]]</span>
|
||||
<span class="formatted-number">{postcount}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">followers</span>
|
||||
<span class="account-bio-label">[[user:followers]]</span>
|
||||
<span class="formatted-number">{followerCount}</span>
|
||||
<br/>
|
||||
|
||||
<span class="account-bio-label">following</span>
|
||||
<span class="account-bio-label">[[user:following]]</span>
|
||||
<span class="formatted-number">{followingCount}</span>
|
||||
<br/>
|
||||
|
||||
<hr/>
|
||||
<span class="account-bio-label">signature</span>
|
||||
<!-- IF !disableSignatures -->
|
||||
<!-- IF signature -->
|
||||
<span class="account-bio-label">[[user:signature]]</span>
|
||||
<div class="post-signature">
|
||||
<span id='signature'>{signature}</span>
|
||||
</div>
|
||||
<!-- ENDIF signature -->
|
||||
<!-- ENDIF !disableSignatures -->
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -98,6 +125,3 @@
|
||||
<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>
|
||||
@@ -1,27 +1,26 @@
|
||||
<div class="well account">
|
||||
|
||||
<div class="well">
|
||||
|
||||
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Change Picture" aria-hidden="true">
|
||||
<div id="change-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="[[user: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>
|
||||
<h3 id="myModalLabel">[[user: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>
|
||||
<span class="user-picture-label">[[user:gravatar]]</span>
|
||||
<i class='fa fa-check fa-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>
|
||||
<span class="user-picture-label">[[user:uploaded_picture]]</span>
|
||||
<i class='fa fa-check fa-2x'></i>
|
||||
</div>
|
||||
|
||||
<a id="uploadPictureBtn" href="#">Upload new picture</a>
|
||||
<a id="uploadPictureBtn" href="#">[[user:upload_new_picture]]</a>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" data-dismiss="modal" aria-hidden="true">Close</button>
|
||||
@@ -31,45 +30,10 @@
|
||||
</div><!-- /.modal-dialog -->
|
||||
</div><!-- /.modal -->
|
||||
|
||||
<div id="upload-picture-modal" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="Upload Picture" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>
|
||||
<h3 id="myModalLabel">Upload Picture</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<form id="uploadForm" action="{relative_path}/user/uploadpicture" method="post" enctype="multipart/form-data">
|
||||
<div class="form-group">
|
||||
<label for="userPhoto">Upload a picture</label>
|
||||
<input type="file" id="userPhotoInput" name="userPhoto">
|
||||
<p class="help-block">You may only upload PNG, JPG, or GIF files under 256kb.</p>
|
||||
</div>
|
||||
<input id="imageUploadCsrf" type="hidden" name="_csrf" value="" />
|
||||
</form>
|
||||
|
||||
<div 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 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}/edit">edit</a>
|
||||
<a href="/user/{userslug}">{username}</a> <i class="fa fa-chevron-right"></i>
|
||||
<a href="/user/{userslug}/edit">[[user:edit]]</a>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@@ -77,7 +41,7 @@
|
||||
<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-thumbnail" src="{picture}" /><br /><br />
|
||||
<a id="changePictureBtn" href="#" class="btn btn-primary">change picture</a>
|
||||
<a id="changePictureBtn" href="#" class="btn btn-primary">[[user:change_picture]]</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -85,51 +49,53 @@
|
||||
<div>
|
||||
<form class='form-horizontal'>
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputEmail">Email</label>
|
||||
<label class="control-label" for="inputEmail">[[user:email]]</label>
|
||||
<div class="controls">
|
||||
<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>
|
||||
<label class="control-label" for="inputFullname">[[user:fullname]]</label>
|
||||
<div class="controls">
|
||||
<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>
|
||||
<label class="control-label" for="inputWebsite">[[user:website]]</label>
|
||||
<div class="controls">
|
||||
<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>
|
||||
<label class="control-label" for="inputLocation">[[user:location]]</label>
|
||||
<div class="controls">
|
||||
<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>
|
||||
<label class="control-label" for="inputBirthday">[[user:birthday]]</label>
|
||||
<div class="controls">
|
||||
<input class="form-control" type="date" id="inputBirthday" placeholder="mm/dd/yyyy" value="{birthday}">
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- IF !disableSignatures -->
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputSignature">Signature</label>
|
||||
<label class="control-label" for="inputSignature">[[user:signature]]</label> <small><label id="signatureCharCountLeft"></label></small>
|
||||
<div class="controls">
|
||||
<textarea class="form-control" id="inputSignature" placeholder="max 150 chars" rows="5">{signature}</textarea>
|
||||
<textarea class="form-control" id="inputSignature" rows="5">{signature}</textarea>
|
||||
</div>
|
||||
</div>
|
||||
<!-- ENDIF !disableSignatures -->
|
||||
|
||||
<input type="hidden" id="inputUID" value="{uid}"><br />
|
||||
|
||||
<div class="form-actions">
|
||||
<a id="submitBtn" href="#" class="btn btn-primary">Save changes</a>
|
||||
<a id="submitBtn" href="#" class="btn btn-primary">[[global:save_changes]]</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -149,23 +115,27 @@
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputNewPassword">Password</label>
|
||||
<div class="controls">
|
||||
<label class="control-label" for="inputNewPassword">[[user:password]]</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="password" id="inputNewPassword" placeholder="New Password" value="">
|
||||
<div id="password-notify" class="alert alert-danger hide"></div>
|
||||
<span class="input-group-addon">
|
||||
<span id="password-notify"><i class="fa fa-circle-o"></i></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="control-group">
|
||||
<label class="control-label" for="inputNewPasswordAgain">Confirm Password</label>
|
||||
<div class="controls">
|
||||
<label class="control-label" for="inputNewPasswordAgain">[[user:confirm_password]]</label>
|
||||
<div class="input-group">
|
||||
<input class="form-control" type="password" id="inputNewPasswordAgain" placeholder="Confirm Password" value="">
|
||||
<div id="password-confirm-notify" class="alert alert-danger hide"></div>
|
||||
<span class="input-group-addon">
|
||||
<span id="password-confirm-notify"><i class="fa fa-circle-o"></i></span>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<div class="form-actions">
|
||||
<a id="changePasswordBtn" href="#" class="btn btn-primary">Change Password</a>
|
||||
<a id="changePasswordBtn" href="#" class="btn btn-primary">[[user:change_password]]</a>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
@@ -177,6 +147,3 @@
|
||||
|
||||
<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/accountheader.js"></script>
|
||||
<script type="text/javascript" src="{relative_path}/src/forum/accountedit.js"></script>
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user