├── .gitattributes
├── .gitignore
├── LICENSE
├── README.md
├── conf
└── chatbox.conf
├── index.js
├── package.json
├── public
├── AdminHelp.md
├── admin.css
├── admin.html
├── admin.js
├── chat-log.txt
├── client.css
├── client.js
├── index.html
├── jquery-1.9.0.min.js
└── robots.txt
├── screenshots
└── Screenshot.png
└── wordpress
├── README.md
├── client.js
├── client.min.js
└── index.js
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
4 | # Custom for Visual Studio
5 | *.cs diff=csharp
6 |
7 | # Standard to msysgit
8 | *.doc diff=astextplain
9 | *.DOC diff=astextplain
10 | *.docx diff=astextplain
11 | *.DOCX diff=astextplain
12 | *.dot diff=astextplain
13 | *.DOT diff=astextplain
14 | *.pdf diff=astextplain
15 | *.PDF diff=astextplain
16 | *.rtf diff=astextplain
17 | *.RTF diff=astextplain
18 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | #################
2 | ## Eclipse
3 | #################
4 |
5 | *.pydevproject
6 | .project
7 | .metadata
8 | bin/
9 | tmp/
10 | *.tmp
11 | *.bak
12 | *.swp
13 | *~.nib
14 | local.properties
15 | .classpath
16 | .settings/
17 | .loadpath
18 |
19 | # External tool builders
20 | .externalToolBuilders/
21 |
22 | # Locally stored "Eclipse launch configurations"
23 | *.launch
24 |
25 | # CDT-specific
26 | .cproject
27 |
28 | # PDT-specific
29 | .buildpath
30 |
31 |
32 | #################
33 | ## Visual Studio
34 | #################
35 |
36 | ## Ignore Visual Studio temporary files, build results, and
37 | ## files generated by popular Visual Studio add-ons.
38 |
39 | # User-specific files
40 | *.suo
41 | *.user
42 | *.sln.docstates
43 |
44 | # Build results
45 |
46 | [Dd]ebug/
47 | [Rr]elease/
48 | x64/
49 | build/
50 | [Bb]in/
51 | [Oo]bj/
52 |
53 | # MSTest test Results
54 | [Tt]est[Rr]esult*/
55 | [Bb]uild[Ll]og.*
56 |
57 | *_i.c
58 | *_p.c
59 | *.ilk
60 | *.meta
61 | *.obj
62 | *.pch
63 | *.pdb
64 | *.pgc
65 | *.pgd
66 | *.rsp
67 | *.sbr
68 | *.tlb
69 | *.tli
70 | *.tlh
71 | *.tmp
72 | *.tmp_proj
73 | *.log
74 | *.vspscc
75 | *.vssscc
76 | .builds
77 | *.pidb
78 | *.log
79 | *.scc
80 |
81 | # Visual C++ cache files
82 | ipch/
83 | *.aps
84 | *.ncb
85 | *.opensdf
86 | *.sdf
87 | *.cachefile
88 |
89 | # Visual Studio profiler
90 | *.psess
91 | *.vsp
92 | *.vspx
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 |
101 | # TeamCity is a build add-in
102 | _TeamCity*
103 |
104 | # DotCover is a Code Coverage Tool
105 | *.dotCover
106 |
107 | # NCrunch
108 | *.ncrunch*
109 | .*crunch*.local.xml
110 |
111 | # Installshield output folder
112 | [Ee]xpress/
113 |
114 | # DocProject is a documentation generator add-in
115 | DocProject/buildhelp/
116 | DocProject/Help/*.HxT
117 | DocProject/Help/*.HxC
118 | DocProject/Help/*.hhc
119 | DocProject/Help/*.hhk
120 | DocProject/Help/*.hhp
121 | DocProject/Help/Html2
122 | DocProject/Help/html
123 |
124 | # Click-Once directory
125 | publish/
126 |
127 | # Publish Web Output
128 | *.Publish.xml
129 | *.pubxml
130 | *.publishproj
131 |
132 | # NuGet Packages Directory
133 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
134 | #packages/
135 |
136 | # Windows Azure Build Output
137 | csx
138 | *.build.csdef
139 |
140 | # Windows Store app package directory
141 | AppPackages/
142 |
143 | # Others
144 | sql/
145 | *.Cache
146 | ClientBin/
147 | [Ss]tyle[Cc]op.*
148 | ~$*
149 | *~
150 | *.dbmdl
151 | *.[Pp]ublish.xml
152 | *.pfx
153 | *.publishsettings
154 |
155 | # RIA/Silverlight projects
156 | Generated_Code/
157 |
158 | # Backup & report files from converting an old project file to a newer
159 | # Visual Studio version. Backup files are not needed, because we have git ;-)
160 | _UpgradeReport_Files/
161 | Backup*/
162 | UpgradeLog*.XML
163 | UpgradeLog*.htm
164 |
165 | # SQL Server files
166 | App_Data/*.mdf
167 | App_Data/*.ldf
168 |
169 | #############
170 | ## Windows detritus
171 | #############
172 |
173 | # Windows image file caches
174 | Thumbs.db
175 | ehthumbs.db
176 |
177 | # Folder config file
178 | Desktop.ini
179 |
180 | # Recycle Bin used on file shares
181 | $RECYCLE.BIN/
182 |
183 | # Mac crap
184 | .DS_Store
185 |
186 |
187 | #############
188 | ## Python
189 | #############
190 |
191 | *.py[cod]
192 |
193 | # Packages
194 | *.egg
195 | *.egg-info
196 | dist/
197 | build/
198 | eggs/
199 | parts/
200 | var/
201 | sdist/
202 | develop-eggs/
203 | .installed.cfg
204 |
205 | # Installer logs
206 | pip-log.txt
207 |
208 | # Unit test / coverage reports
209 | .coverage
210 | .tox
211 |
212 | #Translations
213 | *.mo
214 |
215 | #Mr Developer
216 | .mr.developer.cfg
217 | node_modules/
218 | chat-log.txt
219 |
220 |
221 | public/socket.io.js
222 |
223 | #Chat Log
224 | public/chat-log.txt
225 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 ArchITech
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # English description
2 |
3 |
4 |
5 | ## Chatbox
6 |
7 | A simple and fast chatbox app based on Node.js and Socket.io that features a powerful control panel for admin to use.
8 |
9 |
10 | ##### How to use
11 |
12 | ```
13 | $ cd chatbox
14 | $ npm install
15 | $ node index.js
16 | ```
17 |
18 | And point your browser to `http://localhost:4321` to go to chatbox page.
19 |
20 | If you want the application to run in the background, just do:
21 | ```
22 | $ nohup node index.js > /dev/null &
23 | ```
24 | Or use tools like `forever` or `pm2` to run it.
25 |
26 | Admin page is at `http://localhost:4321/admin.html`, default token is '12345'.
27 |
28 | Edit the token in `index.js` and put the same value in token field in Admin page then you are good to go.
29 |
30 | If you want hide the port like `localhost` (Not `localhost:4321`) in front page, just change the `index.js` port to 4321, and setting `public/client.js` port to 80(or 443).
31 |
32 | Quick way to change the port(e.g., change to 2231):
33 | ```
34 | $ sed -i 's/var port =.*/var port = 2231;/g' ./public/client.js
35 | $ sed -i 's/var port =.*/var port = 2231;/g' ./index.js
36 | ```
37 |
38 | Quick way to change the token(e.g., change to 54321):
39 | ```
40 | $ sed -i 's/var token =.*/var token = "54321";/g' ./index.js
41 | ```
42 |
43 | If you are using reverse proxy, setting the `using_reverse_proxy` to 1 in `index.js`. In your reverse proxy server, add the real ip address to `X-Real-IP` header.
44 |
45 | To embed this chatbox into a web page, just copy paste the content in public/index.html to the page you want to have chatbox, then change all included css file and JavaScript file path correctly. This app works great with light box library, I recommend using fancybox.
46 |
47 | When embedding this chatbox to Wordpress, you can see [this page](/wordpress/README.md) to know how to auto-sync the comment author name with chatbox visitor's nickname, so they don't need to enter nickname again.
48 |
49 | If you are get error in front page:
50 | ```
51 | failed: Error during WebSocket handshake: Unexpected response code: 400
52 | ```
53 | This is almost always due to not using https (SSL). Websocket over plain http is vulnerable to proxies in the middle (often transparent) operating at the http layer breaking the connection.The only way to avoid this is to use SSL all the time - this gives websocket the best chance of working.
54 |
55 |
56 | More info at [subsection 4.2.1 of the WebSockets RFC 6455](http://tools.ietf.org/html/rfc6455#section-4.2.1).
57 |
58 |
59 | ##### Demo
60 |
61 | The chatbox is usually minimized at left bottom by default.
62 |
63 | [http://lifeislikeaboat.com](http://lifeislikeaboat.com).
64 |
65 | [https://kn007.net/](https://kn007.net/)
66 |
67 |
68 |
69 | -----------------------------------------------------------
70 | # 中文介绍
71 |
72 |
73 |
74 | ## 聊天盒
75 |
76 | 该聊天盒基于Node.js与Socket.io,充分利用了HTML5 Websocket双向通讯技术,在方便网站游客高速实时聊天的同时也提供网站管理员强大的控制面板,管理员可对全体游客或者特定游客进行各种操作。
77 |
78 |
79 | ##### 如何使用
80 |
81 | ```
82 | $ cd chatbox
83 | $ npm install
84 | $ node index.js
85 | ```
86 |
87 | 在本地安装则直接访问`http://localhost:4321`即可进入聊天盒。
88 |
89 | 如果你想让聊天盒在后台运行,可以用下面语句启动聊天盒:
90 | ```
91 | $ nohup node index.js > /dev/null &
92 | ```
93 | 也可以使用`forever`或`pm2`工具来运行。
94 |
95 | 控制台的访问地址为`http://localhost:4321/admin.html`, 默认的密码为“12345”。
96 |
97 | 您可以通过修改index.js文件里的Token值来改掉默认的管理员密码。
98 |
99 | 如果你想让访问的地址隐藏端口号(如localhost,而非源代码中默认的localhost:4321),请修改`index.js`文件中的端口号为服务器后端的监听端口(如4321),其次修改`public/client.js`的端口为80(或443)即可。
100 |
101 | 通过Shell快速修改端口(比如改成2231):
102 | ```
103 | $ sed -i 's/var port =.*/var port = 2231;/g' ./public/client.js
104 | $ sed -i 's/var port =.*/var port = 2231;/g' ./index.js
105 | ```
106 |
107 | 同理,可以快速修改管理员密码(比如改成54321):
108 | ```
109 | $ sed -i 's/var token =.*/var token = "54321";/g' ./index.js
110 | ```
111 |
112 | 如果你使用反向代理,请将`index.js`的`using_reverse_proxy`值修改为1,并在反向代理服务器添加X-Real-IP 头指向源IP。
113 |
114 | 如果想把聊天盒嵌入网站中,只要将`public/index.html`文件的内容复制粘贴到想要显示聊天盒的网页里,同时`public/index.html`中所有引入css和JavaScript文件地址需要修改正确。推荐配合fancybox插件使用来放大聊天盒里的图片。
115 |
116 | 嵌入Wordpress后,同步评论者用户名,可参照[此说明](/wordpress/README.md) 。
117 |
118 | 如果你在调试时出现:
119 | ```
120 | failed: Error during WebSocket handshake: Unexpected response code: 400
121 | ```
122 | 比较大的可能是在前端隐藏了端口并使用了http,具体可以看下面传送门的解释。简单来说Websocket通信在使用80端口转发时,80端口只负责连接,握手及通信在反代转发到后端端口通讯时可能会出错(在HTTP层被断开)。使用https可以避免这个问题,握手通讯皆用443端口。如果你不想使用https,那么建议你通过使用`http://localhost:4321`的方式来使用,而不隐藏端口;或是直接让nodejs监听80端口,而不通过反代。
123 |
124 | 更多资料可以参照WebSockets RFC 6455协议中的4.2.1章,[传送门](http://tools.ietf.org/html/rfc6455#section-4.2.1)。
125 |
126 |
127 | ##### 示例
128 |
129 | 聊天盒一般默认最小化于网页左下角
130 |
131 | [http://lifeislikeaboat.com](http://lifeislikeaboat.com)
132 |
133 | [https://kn007.net/](https://kn007.net/)
134 |
135 |
136 | ##### 截图
137 |
138 | 
139 |
--------------------------------------------------------------------------------
/conf/chatbox.conf:
--------------------------------------------------------------------------------
1 | server {
2 | listen 443 ssl http2;
3 | server_name chatbox.kn007.net;
4 | include kn007_net_security.conf;
5 | location ~ ^/(admin\.html|chat-log.txt) {
6 | proxy_set_header Upgrade $http_upgrade;
7 | proxy_set_header Connection "upgrade";
8 | proxy_set_header Host $host;
9 | proxy_set_header X-Real-IP $remote_addr;
10 | proxy_http_version 1.1;
11 | proxy_pass http://127.0.0.1:2231;
12 | auth_basic "kn007's Auth System";
13 | auth_basic_user_file /usr/local/nginx/passwd_chatbox.db;
14 | }
15 | location / {
16 | proxy_set_header Upgrade $http_upgrade;
17 | proxy_set_header Connection "upgrade";
18 | proxy_set_header Host $host;
19 | proxy_set_header X-Real-IP $remote_addr;
20 | proxy_http_version 1.1;
21 | proxy_pass http://127.0.0.1:2231;
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/index.js:
--------------------------------------------------------------------------------
1 | // Setup basic express server
2 | var express = require('express');
3 | var app = express();
4 | var server = require('http').createServer(app);
5 | var io = require('socket.io')(server);
6 |
7 | //set chat history log file
8 | var fs = require('fs');
9 | var filePath = __dirname+"/public/chat-log.txt";
10 |
11 | //set timeout, default is 1 min
12 | //io.set("heartbeat timeout", 3*60*1000);
13 |
14 | //set which port this app runs on
15 | var port = 4321;
16 | //set admin password
17 | var token = "12345";
18 | //set 1 if you using reverse proxy
19 | var using_reverse_proxy = 0;
20 |
21 |
22 | var socketList = [];
23 | // users are grouped by browser base on cookie's uuid implementation,
24 | // therefore 1 connection is the smallest unique unit and 1 user is not.
25 | // 1 user may contain multiple connections when he opens multiple tabs in same browser.
26 | var userDict = {};
27 | var userCount = 0;
28 |
29 | var adminUser;
30 |
31 | var chatboxUpTime = (new Date()).toString();
32 | var totalUsers = 0;
33 | var totalSockets = 0;
34 | var totalMsg = 0;
35 | server.listen(port, function () {
36 | console.log('Server listening at port %d', port);
37 | });
38 |
39 | // Routing
40 | app.use(express.static(__dirname + '/public'));
41 |
42 |
43 |
44 | // Chatbox
45 |
46 | // log to console, if admin is online, send to admin as well
47 | function log(str) {
48 | console.log(str);
49 | if (adminUser && adminUser.id in userDict) {
50 | for(var i = 0; i < adminUser.socketList.length; i++) {
51 | var s = adminUser.socketList[i];
52 | s.emit('server log', {log: str});
53 | }
54 | }
55 | }
56 |
57 | // set username, avoid no name
58 | function setName(name) {
59 |
60 | if (typeof name != 'undefined' && name!=='')
61 | return name;
62 | return "no name";
63 | }
64 |
65 |
66 | function getCookie(cookie, cname) {
67 | var name = cname + "=";
68 | var ca = cookie.split(';');
69 | for(var i=0; i socket
184 | user.socketList.push(socket);
185 | socket.user = user;
186 |
187 |
188 | recordActionTime(socket);
189 | var action = {};
190 | action.type = 'Join';
191 | action.time = getTime();
192 | action.url = socket.url;
193 | action.detail = socket.remoteAddress;
194 | user.actionList.push(action);
195 |
196 | });
197 |
198 | // when the user disconnects..
199 | socket.on('disconnect', function () {
200 | var user = socket.user;
201 |
202 |
203 | // remove from socket list
204 | var socketIndex = socketList.indexOf(socket);
205 | if (socketIndex != -1) {
206 | socketList.splice(socketIndex, 1);
207 | }
208 |
209 |
210 | // the user only exist after login
211 | if(user.notLoggedIn){
212 | log('Socket disconnected before logging in.');
213 | log('socket.id: '+socket.id);
214 | return;
215 | }
216 |
217 | log(user.username + ' closed a connection ('+(user.socketList.length-1)+').');
218 |
219 | // also need to remove socket from user's socketlist
220 | // when a user has 0 socket connection, remove the user
221 | var socketIndexInUser = user.socketList.indexOf(socket);
222 | if (socketIndexInUser != -1) {
223 | user.socketList.splice(socketIndexInUser, 1);
224 | if(user.socketList.length === 0){
225 | log("It's his last connection, he's gone.");
226 | delete userDict[user.id];
227 | userCount--;
228 | // echo globally that this user has left
229 | socket.broadcast.emit('user left', {
230 | username: socket.user.username,
231 | numUsers: userCount
232 | });
233 |
234 | }else{
235 | var action = {};
236 | action.type = 'Left';
237 | action.time = getTime();
238 | action.url = socket.url;
239 | action.detail = socket.remoteAddress;
240 | user.actionList.push(action);
241 | }
242 | }
243 |
244 | });
245 |
246 | // this is when one user want to change his name
247 | // enforce that all his socket connections change name too
248 | socket.on('user edits name', function (data) {
249 | recordActionTime(socket);
250 |
251 | var oldName = socket.user.username;
252 | var newName = data.newName;
253 | socket.user.username = newName;
254 |
255 | if (newName === oldName) return;
256 |
257 | // sync name change
258 | var socketsToChangeName = socket.user.socketList;
259 | for (var i = 0; i< socketsToChangeName.length; i++) {
260 |
261 | socketsToChangeName[i].emit('change username', { username: newName });
262 |
263 | }
264 |
265 |
266 | // echo globally that this client has changed name, including user himself
267 | io.sockets.emit('log change name', {
268 | username: socket.user.username,
269 | oldname: oldName
270 | });
271 |
272 |
273 | var action = {};
274 | action.type = 'change name';
275 | action.time = getTime();
276 | action.url = socket.url;
277 | action.detail = 'Changed name from' + oldName + ' to ' + newName;
278 | socket.user.actionList.push(action);
279 |
280 | });
281 |
282 | socket.on('report', function (data) {
283 | log(data.username + ": " + data.msg);
284 | });
285 |
286 | // when the client emits 'new message', this listens and executes
287 | socket.on('new message', function (data) {
288 | totalMsg++;
289 | recordActionTime(socket, data.msg);
290 |
291 | socket.msgCount++;
292 | socket.user.msgCount++;
293 |
294 | // socket.broadcast.emit('new message', {//send to everybody but sender
295 | io.sockets.emit('new message', {//send to everybody including sender
296 | username: socket.user.username,
297 | message: data.msg
298 | });
299 |
300 |
301 | // log the message in chat history file
302 | var chatMsg = socket.user.username+": "+data.msg+'\n';
303 | console.log(chatMsg);
304 |
305 | fs.appendFile(filePath, new Date() + "\t"+ chatMsg, function(err) {
306 | if(err) {
307 | return log(err);
308 | }
309 | console.log("The message is saved to log file!");
310 | });
311 |
312 | var action = {};
313 | action.type = 'message';
314 | action.time = getTime();
315 | action.url = socket.url;
316 | action.detail = data.msg;
317 | socket.user.actionList.push(action);
318 |
319 | });
320 |
321 | socket.on('base64 file', function (data) {
322 | recordActionTime(socket);
323 |
324 | log('received base64 file from' + data.username);
325 |
326 | // socket.broadcast.emit('base64 image', //exclude sender
327 | io.sockets.emit('base64 file',
328 |
329 | {
330 | username: socket.user.username,
331 | file: data.file,
332 | fileName: data.fileName
333 | }
334 |
335 | );
336 |
337 | var action = {};
338 | action.type = 'send file';
339 | action.time = getTime();
340 | action.url = socket.url;
341 | action.detail = data.fileName;
342 | socket.user.actionList.push(action);
343 | });
344 |
345 |
346 | // when the client emits 'typing', we broadcast it to others
347 | socket.on('typing', function (data) {
348 | return;
349 | recordActionTime(socket);
350 |
351 | socket.broadcast.emit('typing', {
352 | username: socket.user.username
353 | });
354 | });
355 |
356 | // when the client emits 'stop typing', we broadcast it to others
357 | socket.on('stop typing', function (data) {
358 | return;
359 | recordActionTime(socket);
360 |
361 | socket.broadcast.emit('stop typing', {
362 | username: socket.user.username
363 | });
364 | });
365 |
366 | // for New Message Received Notification callback
367 | socket.on('reset2origintitle', function (data) {
368 | var socketsToResetTitle = socket.user.socketList;
369 | for (var i = 0; i< socketsToResetTitle.length; i++) {
370 | socketsToResetTitle[i].emit('reset2origintitle', {});
371 | }
372 | });
373 |
374 |
375 |
376 | //==========================================================================
377 | //==========================================================================
378 | // code below are for admin only, so we always want to verify token first
379 | //==========================================================================
380 | //==========================================================================
381 |
382 |
383 | // change username
384 | socket.on('admin change username', function (data) {
385 |
386 | if(data.token === token) {
387 |
388 | var user = userDict[data.userID];
389 | var newName = data.newName;
390 | var oldName = user.username;
391 | user.username = newName;
392 |
393 | if (newName === oldName) return;
394 |
395 | // sync name change
396 | var socketsToChangeName = user.socketList;
397 | for (var i = 0; i< socketsToChangeName.length; i++) {
398 |
399 | socketsToChangeName[i].emit('change username', { username: newName });
400 |
401 | }
402 |
403 |
404 | // echo globally that this client has changed name, including user himself
405 | io.sockets.emit('log change name', {
406 | username: user.username,
407 | oldname: oldName
408 | });
409 |
410 |
411 | }
412 |
413 | });
414 |
415 |
416 | // send script to target users
417 | socket.on('script', function (data) {
418 |
419 | if(data.token === token) {
420 |
421 | // handle individual sockets
422 | for (var i = 0; i < data.socketKeyList.length; i++) {
423 | var sid = data.socketKeyList[i];
424 | io.to(sid).emit('script', {script: data.script});
425 | }
426 |
427 |
428 | // handle users and all their sockets
429 | for (var i = 0; i < data.userKeyList.length; i++) {
430 | var userKey = data.userKeyList[i];
431 | if(userKey in userDict) { // in case is already gone
432 | var user = userDict[userKey];
433 | for (var j = 0; j< user.socketList.length; j++) {
434 | s = user.socketList[j];
435 | s.emit('script', {script: data.script});
436 | }
437 | }
438 | }
439 | }
440 |
441 | });
442 |
443 | socket.on('getServerStat', function (data) {
444 | socket.emit('server stat', {
445 | chatboxUpTime: chatboxUpTime,
446 | totalUsers: totalUsers,
447 | totalSockets: totalSockets,
448 | totalMsg: totalMsg
449 | });
450 | });
451 |
452 | // send real time data statistic to admin
453 | // this callback is currently also used for authentication
454 | socket.on('getUserList', function (data) {
455 |
456 | if(data.token === token) {
457 |
458 | adminUser = socket.user;
459 |
460 | // Don't send the original user object or socket object to browser!
461 | // create simple models for socket and user to send to browser
462 | var simpleUserDict = {};
463 |
464 | for (var key in userDict) {
465 | var user = userDict[key];
466 |
467 | // create simpleUser model
468 | var simpleUser = {};
469 | // is there a way to reduce code below?
470 | simpleUser.id = user.id; // key = user.id
471 | simpleUser.username = user.username;
472 | simpleUser.lastMsg = user.lastMsg;
473 | simpleUser.msgCount = user.msgCount;
474 | simpleUser.count = user.socketList.length;
475 | simpleUser.ip = user.ip;
476 | simpleUser.url = user.url;
477 | simpleUser.referrer = user.referrer;
478 | simpleUser.joinTime = user.joinTime;
479 | simpleUser.lastActive = user.lastActive;
480 | simpleUser.userAgent = user.userAgent;
481 | simpleUser.actionList = user.actionList;
482 |
483 | var simpleSocketList = [];
484 | for (var i = 0; i < user.socketList.length; i++) {
485 | var s = user.socketList[i];
486 |
487 | // create simpleSocket model
488 | var simpleSocket = {};
489 | simpleSocket.id = s.id;
490 | simpleSocket.ip = s.remoteAddress;
491 | simpleSocket.msgCount = s.msgCount;
492 | simpleSocket.lastMsg = s.lastMsg;
493 | simpleSocket.lastActive = s.lastActive;
494 | simpleSocket.url = s.url;
495 | simpleSocket.referrer = s.referrer;
496 | simpleSocket.joinTime = s.joinTime;
497 |
498 | simpleSocketList.push(simpleSocket);
499 | }
500 |
501 | simpleUser.socketList = simpleSocketList;
502 |
503 | simpleUserDict[simpleUser.id] = simpleUser;
504 | }
505 |
506 |
507 |
508 | socket.emit('listUsers', {
509 | userdict: simpleUserDict,
510 | success: true
511 | });
512 |
513 | // getUserList might still be called when token is wrong
514 | }else {
515 |
516 | if (adminUser && adminUser.id === socket.user.id) {
517 | adminUser = undefined;
518 | }
519 |
520 |
521 | socket.emit('listUsers', {
522 | success: false
523 | });
524 | }
525 |
526 | });
527 |
528 |
529 | });
530 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "socket.io-chatbox",
3 | "version": "stable@160216",
4 | "description": "A simple chatbox using socket.io",
5 | "main": "index.js",
6 | "contributors": [
7 | {
8 | "name": "Grant Timmerman"
9 | },
10 | {
11 | "name": "ArchiTech",
12 | "website": "lifeislikeaboat.com"
13 | }
14 | ],
15 | "homepage": "https://arch1tect.github.io/Chatbox",
16 | "repository": {
17 | "type": "git",
18 | "url": "git://github.com/Arch1tect/Chatbox.git"
19 | },
20 | "license": "BSD",
21 | "dependencies": {
22 | "express": "3.4.8",
23 | "socket.io": "^1.3.7"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/public/AdminHelp.md:
--------------------------------------------------------------------------------
1 | Script Example
2 |
3 | //show chatbox
4 | show()
5 | //hide chatbox
6 | hide()
7 | //change page background color
8 | color('black')
9 | //make user say 'I admire you' publicly
10 | say('I admire you!')
11 | //make user type 'I love you' in his input bar(won't send)
12 | type('I love you')
13 | //make user send whatever is in his input bar publicly
14 | send()
15 | //play user join sound
16 | beep()
17 | //play new message sound
18 | newMsgBeep()
19 | //Redirect user to "www.example.com"
20 | window.location = "http://www.example.com"
21 |
22 | $.getScript("https://cdn.jsdelivr.net/jquery.jrumble/1.3/jquery.jrumble.min.js", function(data, textStatus, jqxhr) {
23 |
24 | $topbar.jrumble();
25 | $topbar.trigger('startRumble');
26 |
27 | })
28 |
29 | //Make user load a 3rd party library then use it
30 |
31 | //Any JavaScript code can be ran on user's end, don't be evil.
32 |
--------------------------------------------------------------------------------
/public/admin.css:
--------------------------------------------------------------------------------
1 | /* ======================================================= */
2 | /* ================ Chatbox Admin Style =================*/
3 | /* ======================================================= */
4 |
5 | html {
6 | background: whitesmoke;
7 | font-family: Arial, Helvetica, sans-serif;
8 | /* font-family: "Times New Roman", Times, serif; */
9 | }
10 |
11 | .socketchatbox-admin-title h1 {
12 |
13 | color: #3D3D3D;
14 |
15 | margin: 50px;
16 |
17 | font-size: 55px;
18 | }
19 | .socketchatbox-admin-instruction li{
20 | margin:10px
21 | }
22 | #wrapper {
23 | /*min-height: 1000px; *//* we always want to show scroll bar */
24 | }
25 |
26 |
27 | #sendScript {
28 | width: 100%;
29 | outline: none;
30 | padding: 10px;
31 | margin: 0px;
32 | }
33 |
34 | .socketchatbox-info-admin {
35 | right: 5%;
36 | width: 50%;
37 | position: absolute;
38 | /* max-width: 618px; */
39 | word-wrap: break-word;
40 | /* background: rgba(255, 255, 255, 0.5); */
41 | }
42 |
43 |
44 | .username-info-viewmore {
45 | margin-right: 10px;
46 | cursor: pointer;
47 | }
48 | .username-info-viewmore:hover{
49 | background: lightgray;
50 | }
51 | #socketchatbox-checkAdmin {
52 | background: white;
53 | border: 1px solid lightgray;
54 | padding: 20px;
55 | }
56 | #socketchatbox-online {
57 | border: 1px solid lightgray;
58 | padding: 20px;
59 | background: white;
60 | }
61 |
62 | div#socketchatbox-online {}
63 | .socketchatbox-scriptHistory {
64 | border: 1px solid lightgray;
65 | padding: 20px;
66 | background: #EDEDED;
67 | }
68 | .socketchatbox-admin-leftside {
69 | width: 36%;
70 | margin-left: 5%;
71 | position: absolute;
72 | margin-bottom: 50px;
73 | }
74 | .socketchatbox-admin-server {
75 | border: 1px solid lightgray;
76 | padding-left: 5px;
77 | background: rgba(0, 0, 0, 0.75);
78 | word-wrap: break-word;
79 | font-size: small;
80 | overflow-y:scroll;
81 | height: 250px;
82 | }
83 | .log-time {
84 | width: 112px;
85 | margin-right: 21px;
86 | }
87 | .server-log-message {
88 | color: white;:;
89 | margin: 3px;
90 | margin-left: 7px;
91 | }
92 | .socketchatbox-admin-instruction {
93 | border: 1px solid lightgray;
94 | background: #FCFF8C;
95 | padding: 20px;
96 | }
97 | .socketchatbox-scriptHistoryScript {
98 | margin-top: 20px;
99 | max-height: 100px;
100 | overflow-y: scroll;
101 | }
102 | .username-info {
103 | margin-right: 5px;
104 | cursor: pointer;
105 | /* border: 1px solid white; */
106 | }
107 |
108 | .selected {
109 | border: 1px solid lightgray;
110 | background: yellow;
111 | }
112 |
113 | .partially-selected {
114 | border: 1px solid lightgray;
115 | background: linear-gradient(90deg, yellow 50%, white 50%);
116 | }
117 |
118 |
119 | /* User detail popup */
120 |
121 | .selectedSocket {
122 | background: yellow !important;
123 | }
124 |
125 | .socketchatbox-userdetail {
126 | border: 1px solid lightgray;
127 | background: #F9F9F9;
128 | margin: 10px;
129 | display:none;
130 | font-size: small;
131 | z-index: 9999;
132 | padding: 15px;
133 | }
134 | .socketchatbox-admin-userprofile {
135 | margin: 20px;
136 | }
137 |
138 | .socketchatbox-admin-userprofile p{
139 | margin: 7px;
140 | }
141 |
142 | .socketchatbox-userdetail-actions {
143 | margin: 20px;
144 | margin-top:0px;
145 | }
146 | .socketchatbox-userdetail-actions-each {
147 | border: 1px solid lightgray;
148 | padding: 10px;
149 |
150 | }
151 |
152 | .socketchatbox-userdetail-actions-each:nth-child(odd) {
153 | background: #ededed;
154 | }
155 |
156 | .socketchatbox-userdetail-actions-each:nth-child(even) {
157 | background: #fafafa;
158 | }
159 |
160 | .socketchatbox-userdetail-name {
161 | font-size: large;
162 | font-weight: bold;
163 | }
164 |
165 | .socketchatbox-userdetail-sockets {
166 | /*max-height: 300px;
167 | overflow-y: scroll;*/
168 | margin: 10px;
169 | }
170 |
171 | .socketchatbox-socketdetail {
172 | margin:5px;
173 | }
174 |
175 | .socketchatbox-socketdetail-each {
176 | padding: 15px;
177 | cursor: pointer;
178 | border: 1px solid lightgray;
179 | }
180 |
181 | .socketchatbox-socketdetail-each p{
182 | margin:5px
183 | }
184 |
185 | .socketchatbox-socketdetail-each:nth-child(odd) {
186 | background: #ededed;
187 | }
188 |
189 | .socketchatbox-socketdetail-each:nth-child(even) {
190 | background: #fafafa;
191 | }
192 |
193 | .socketchatbox-actionhistory-url {
194 | float: right;
195 | }
196 |
197 |
198 |
199 | #socketchatbox-scriptSentStatus {
200 | margin: 10px;
201 | }
202 |
203 | .socketchatbox-admin-input {
204 |
205 | font-size: 100%;
206 | }
207 |
208 | .socketchatbox-admin-input textarea{
209 | height: 150px;
210 | width: 100%;
211 | outline: none;
212 | padding-right: 5px;
213 | border: 1px solid lightgray;
214 | }
215 |
216 | button {
217 | border: 1px solid lightgray;
218 | margin-left: 10px;
219 | cursor: pointer;
220 | background: beige;
221 | }
222 |
223 | .socketchatbox-admin-changeUserName {
224 | /*margin:0px;*/
225 | }
226 |
227 | .socketchatbox-refresh-interval {
228 | margin-left:30px;
229 |
230 | }
231 |
232 | .blue {
233 | background: #EDEDED;
234 | }
235 |
236 | .error {
237 | background: red !important;
238 | }
239 | .green {
240 | background: #C0FF3F;
241 | }
242 | .redFont {
243 | color: red;
244 | }
245 |
246 |
247 |
--------------------------------------------------------------------------------
/public/admin.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Chatbox Admin
6 |
7 |
8 |
9 |
10 | Chatbox Control Panel
11 |
12 |
13 |
14 |
15 |
16 |
21 |
22 |
25 |
26 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
Instructions
51 |
52 | The first thing you need to do is to enter your token and click update, the
53 | default token is "12345", you can change the token in index.js.
54 |
Once the admin validation is successful, you can see all users online at the moment.
55 |
Click on the '[ ↓ ]' after user name to see more details, including all his sockets information.
56 |
To run JavaScript on user's browser, you need to select the user as target before sending your script.
57 |
58 | You can see some example scripts in
AdminHelp.md.
59 |
60 | And here's the
chat log file.
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 | Token Verification: Waiting...
69 |
70 | Token:
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
Users Online
80 |
81 |
82 |
83 |
Refresh Interval: 5 sec
84 |
85 |
86 | Waiting...
87 |
88 |
89 |
90 |
91 |
Name:
92 |
93 |
94 |
Last Message:
95 |
Total Messages:
96 |
Landing Page:
97 |
Referrer:
98 |
Idle Time:
99 |
Join Time:
100 |
101 |
IP:
102 |
103 |
104 |
105 |
User-Agent:
106 |
107 |
108 |
Live Connection
109 |
110 |
111 |
112 |
113 |
114 |
Action History
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
--------------------------------------------------------------------------------
/public/admin.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | var socket = chatboxClient.socket;
4 | var verified = false;
5 |
6 |
7 | var scriptHist = [];
8 | var scriptPointer = -1;
9 | var refreshInterval = 5; // unit is second not milisecond
10 | var refreshIntervalID;
11 | var token = "";
12 | var selectedUsers = {}; // user with all sockets selected
13 | var partiallyselectedUsers = {}; // user with some of sockets selected
14 | var selectedSockets = {}; // a simple array of socket's ID
15 | var userDict = {}; // similar to the userDict on server, but store simpleUser/simpleSocket objects
16 | var socketDict = {}; // similar ...
17 |
18 | var openedUserID;
19 |
20 | var $inputScriptMessage = $('.socketchatbox-admin-input textarea'); // admin script message input box
21 |
22 | adminInit();
23 |
24 |
25 | function countKeys(myObj) {
26 | var count = 0;
27 | for (var k in myObj) {
28 | if (myObj.hasOwnProperty(k)) {
29 | ++count;
30 | }
31 | }
32 | return count;
33 | }
34 |
35 | // Send a script (Admin only)
36 | function sendScript() {
37 | var script = $inputScriptMessage.val();
38 | var userCount = countKeys(selectedUsers);
39 | var socketCount = countKeys(selectedSockets);
40 |
41 | if (userCount + socketCount > 0) {
42 | // empty the input field
43 | $inputScriptMessage.val('');
44 |
45 | var userKeyList = [];
46 | var socketKeyList = [];
47 | for(var userKey in selectedUsers){
48 | userKeyList.push(userKey);
49 | }
50 | for(var socketKey in selectedSockets){
51 | socketKeyList.push(socketKey);
52 | }
53 |
54 | var data = {};
55 | data.token = token;
56 | data.script = script;
57 | data.userKeyList = userKeyList;
58 | data.socketKeyList = socketKeyList;
59 | socket.emit('script', data);
60 |
61 | // save script to local array
62 | scriptHist.push(script);
63 | scriptPointer = scriptHist.length-1;
64 | setHistoryScript();
65 |
66 | var msg = 'Script is sent to ';
67 | if (userCount > 0)
68 | msg += userCount+' users ';
69 | if (socketCount > 0)
70 | msg += socketCount+' sockets.';
71 |
72 | $('#socketchatbox-scriptSentStatus').text(msg);
73 | $('#socketchatbox-scriptSentStatus').removeClass('redFont');
74 |
75 | }
76 | else{
77 | $('#socketchatbox-scriptSentStatus').text('Must select at least one user to send script to.');
78 | $('#socketchatbox-scriptSentStatus').addClass('redFont');
79 |
80 | }
81 |
82 | // need to scroll down to really see this message
83 | window.scrollTo(0,document.body.scrollHeight);
84 |
85 | }
86 |
87 |
88 | $('.socketchatbox-admin-blockIP').click(function() {
89 | console.log("not available in this version, please wait and update.");
90 | });
91 |
92 |
93 | $('.socketchatbox-admin-lookupIP').click(function() {
94 | window.open("https://geoiptool.com/en/?ip=");
95 | });
96 |
97 |
98 | $('#sendScript').click(function() {
99 | sendScript();
100 | });
101 |
102 | $('#selectAll').click(function() {
103 | selectNoSocketNorUser();
104 |
105 | for(var userKey in userDict) {
106 | var user = userDict[userKey];
107 | user.selectedSocketCount = user.count;
108 | selectedUsers[userKey] = user;
109 | }
110 |
111 | syncHightlightGUI();
112 |
113 | });
114 |
115 | $('#selectNone').click(function() {
116 | selectNoSocketNorUser();
117 |
118 | syncHightlightGUI();
119 |
120 | });
121 |
122 | // doesn't update GUI
123 | function selectNoSocketNorUser() {
124 | selectedUsers = [];
125 | selectedSockets = [];
126 | partiallyselectedUsers = {};
127 | for(var userKey in userDict) {
128 | var user = userDict[userKey];
129 | user.selectedSocketCount = 0;
130 | }
131 | }
132 |
133 | function removeUserSocketsFromSelectedSockets(user) {
134 | for(var i=0; i 0) {
207 | //console.log('add to partiallyselectedUsers');
208 |
209 | partiallyselectedUsers[user.id] = user;
210 | for(var i = 0; i < user.socketList.length; i++) {
211 | var ss = user.socketList[i];
212 | if(ss.id!=s.id) {
213 | selectedSockets[ss.id] = ss;
214 | //console.log('add socket to selectedSockets, sid: '+ ss.id);
215 |
216 | }
217 | }
218 | }
219 | }else{
220 | //console.log('the user was not selected.');
221 |
222 |
223 | if (socketID in selectedSockets) { // user must be in the partiallySelectedUserList
224 | // socket previously selected, now deselect
225 | delete selectedSockets[socketID];
226 | user.selectedSocketCount--;
227 | if(user.selectedSocketCount<0) {
228 | console.log(user.selectedSocketCount<0);
229 | }
230 | if(user.selectedSocketCount===0)
231 | delete partiallyselectedUsers[user.id];
232 |
233 |
234 | }else{ // user not in partially selected user list nor in the selected user list
235 | // socket previously not selected, now select it unless this user is getting into selected user list
236 | user.selectedSocketCount++; // should equal to 1
237 | if(user.selectedSocketCount!=1)
238 | console.log("user.selectedSocketCount should be one, but it's "+user.selectedSocketCount);
239 | if(user.selectedSocketCount == user.count){
240 | selectedUsers[user.id] = user;
241 | for(var i = 0; i < user.socketList.length; i++) {
242 | var ss = user.socketList[i];
243 | delete selectedSockets[ss.id];
244 |
245 | }
246 |
247 |
248 | }else{
249 |
250 | // ensure in partiallyselecteduserlist, maybe already in
251 | partiallyselectedUsers[user.id] = user;
252 | selectedSockets[socketID] = s;
253 | }
254 | }
255 | }
256 |
257 |
258 | //console.log(user.selectedSocketCount);
259 | syncHightlightGUI();
260 |
261 |
262 | });
263 |
264 | // update GUI to sync with data, call this every time you change value of user.selectedSocketCount
265 | function syncHightlightGUI() {
266 | // sync user highlight
267 | for(var key in userDict) {
268 | var user = userDict[key];
269 | // check to see what status username selection should be in
270 | if (user.selectedSocketCount === 0) {
271 | // deselect
272 | user.jqueryObj.removeClass('selected');
273 | user.jqueryObj.removeClass('partially-selected');
274 |
275 |
276 | }else if (user.selectedSocketCount < user.count) {
277 | // partial select
278 | if(user.jqueryObj) {
279 | user.jqueryObj.removeClass('selected');
280 | user.jqueryObj.addClass('partially-selected');
281 | }
282 |
283 | }else {
284 | // full select
285 | if(user.jqueryObj) {
286 | user.jqueryObj.removeClass('partially-selected');
287 | user.jqueryObj.addClass('selected');
288 | }
289 | }
290 |
291 | if (user.id == openedUserID) {
292 | for(var i = 0; i < user.socketList.length; i++) {
293 | var s = user.socketList[i];
294 | if(user.id in selectedUsers || s.id in selectedSockets){
295 |
296 | s.jqueryObj.addClass('selectedSocket');
297 |
298 | }else{
299 | s.jqueryObj.removeClass('selectedSocket');
300 | }
301 | }
302 | }else {
303 |
304 | for(var i = 0; i < user.socketList.length; i++) {
305 | var s = user.socketList[i];
306 | if(s.jqueryObj)
307 | s.jqueryObj.removeClass('selectedSocket');
308 |
309 | }
310 |
311 | }
312 |
313 | }
314 | }
315 |
316 |
317 | function loadUserDetail (user) {
318 |
319 | // user info
320 |
321 | $('.socketchatbox-userdetail-name').text(user.username);
322 |
323 | // don't refresh the element if value is the same, we don't want to interrupt editing name
324 | if ($('.socketchatbox-userdetail-name-edit').data('name') !==user.username){
325 |
326 | $('.socketchatbox-userdetail-name-edit').val(user.username);
327 | $('.socketchatbox-userdetail-name-edit').data('name',user.username);
328 | }
329 | $('.socketchatbox-admin-changeUserName').data('id',user.id);
330 | $('.socketchatbox-userdetail-landingpage').text(user.url);
331 | $('.socketchatbox-userdetail-referrer').text(user.referrer);
332 | $('.socketchatbox-userdetail-ip').text(user.ip);
333 | $('.socketchatbox-userdetail-jointime').text(getTimeElapsed(user.joinTime));
334 | $('.socketchatbox-userdetail-totalmsg').text(user.msgCount);
335 | if(!user.lastMsg)
336 | user.lastMsg = "";
337 | $('.socketchatbox-userdetail-lastmsg').text("\""+user.lastMsg+"\"");
338 |
339 |
340 | $('.socketchatbox-userdetail-lastactive').text(getTimeElapsed(user.lastActive));
341 | $('.socketchatbox-userdetail-useragent').text(user.userAgent);
342 |
343 |
344 | // socket info
345 |
346 | $('.socketchatbox-userdetail-sockets').html('');
347 |
348 | for (var i = 0; i< user.socketList.length; i++) {
349 | var s = user.socketList[i];
350 | var $socketInfo = $("[" + i + "]
";
352 | socketInfoHTML += "ID: " + s.id + "
";
353 | socketInfoHTML += "URL: " + s.url + "
";
354 | if (s.referrer)
355 | socketInfoHTML += "Referrer: " + s.referrer + "
";
356 | socketInfoHTML += "IP: " + s.ip + "
";
357 | socketInfoHTML += "Total Messages: " + s.msgCount + "
";
358 |
359 | if (s.lastMsg)
360 | socketInfoHTML += "Last Message: \"" + s.lastMsg + "\"
";
361 |
362 | socketInfoHTML += "Idle Time: " + getTimeElapsed(s.lastActive) + "
";
363 | socketInfoHTML += "Connection Time: " + getTimeElapsed(s.joinTime) + "
";
364 |
365 | $socketInfo.html(socketInfoHTML);
366 | $socketInfo.addClass('socketchatbox-socketdetail-each');
367 |
368 | $socketInfo.data('id', s.id);
369 | // link jquery object with socket object
370 | s.jqueryObj = $socketInfo;
371 | $('.socketchatbox-userdetail-sockets').append($socketInfo);
372 | }
373 |
374 | // action history
375 | var $actionHistoryDiv = $('.socketchatbox-userdetail-actions');
376 | $actionHistoryDiv.html('');
377 |
378 | for (var i = 0; i < user.actionList.length; i++) {
379 | var action = user.actionList[i];
380 | var $actionDiv = $('');
381 | //new Date(Number(action.time)) // full time format
382 | var d = new Date(Number(action.time));
383 | var str = ('0' + d.getHours()).slice(-2) + ":" + ('0' + d.getMinutes()).slice(-2) + ":" + ('0' + d.getSeconds()).slice(-2);
384 | str += "" + action.url + "";
385 | str += "
Action: " + action.type ;
386 | if (action.detail) {
387 | str += "
Detail: " + action.detail;
388 | }
389 |
390 | $actionDiv.html(str);
391 | $actionDiv.addClass('socketchatbox-userdetail-actions-each');
392 |
393 | $actionHistoryDiv.append($actionDiv);
394 | }
395 |
396 |
397 |
398 |
399 | syncHightlightGUI();
400 |
401 | }
402 |
403 | $(document).on('click', '.username-info-viewmore', function() {
404 | var $this = $(this);
405 | var userID = $this.data('id');
406 | var user = userDict[userID];
407 |
408 | // already opened, close now
409 | if (openedUserID === userID) {
410 |
411 | $('.socketchatbox-admin-userdetail-pop').hide();
412 | $this.text('[ ↓ ]');
413 | $this.removeClass('blue');
414 | openedUserID = '';
415 |
416 | }else{
417 |
418 | if (openedUserID in userDict) {
419 | var preOpenedUser = userDict[openedUserID];
420 | preOpenedUser.arrowSpan.text('[ ↓ ]');
421 | preOpenedUser.arrowSpan.removeClass('blue');
422 |
423 | }
424 |
425 | $this.text('[ ↑ ]');
426 | $this.addClass('blue');
427 |
428 | openedUserID = userID;
429 | user.arrowSpan = $this;
430 | // Populate data into popup
431 | loadUserDetail(user);
432 |
433 | // TODO: show full browse history
434 | // url ----------- how long stay on page ------ etc. learn from GA dashboard
435 |
436 | // show
437 | if (!$('.socketchatbox-admin-userdetail-pop').is(":visible"))
438 | $('.socketchatbox-admin-userdetail-pop').slideFadeToggle();
439 | }
440 |
441 | });
442 |
443 |
444 | function getTimeElapsed(startTime, fromTime) {
445 |
446 | // time difference in ms
447 | var timeDiff = startTime - fromTime;
448 | if (fromTime!==0)
449 | timeDiff = (new Date()).getTime() - startTime;
450 | // strip the ms
451 | timeDiff /= 1000;
452 | var seconds = Math.round(timeDiff % 60);
453 | timeDiff = Math.floor(timeDiff / 60);
454 | var minutes = Math.round(timeDiff % 60);
455 | timeDiff = Math.floor(timeDiff / 60);
456 | var hours = Math.round(timeDiff % 24);
457 | timeDiff = Math.floor(timeDiff / 24);
458 | var days = timeDiff ;
459 | var timeStr = "";
460 | if(days)
461 | timeStr += days + " d ";
462 | if(hours)
463 | timeStr += hours + " hr ";
464 | if(minutes)
465 | timeStr += minutes + " min ";
466 |
467 | timeStr += seconds + " sec";
468 | return timeStr;
469 | }
470 |
471 |
472 |
473 |
474 | $.fn.slideFadeToggle = function(easing, callback) {
475 | return this.animate({ opacity: 'toggle', height: 'toggle' }, 'fast', easing, callback);
476 | };
477 |
478 |
479 | var $tokenStatus = $('#socketchatbox-tokenStatus');
480 | // Only admin should receive this message
481 | socket.on('listUsers', function (data) {
482 |
483 |
484 | // clear online user display
485 | $('#socketchatbox-online-users').html('');
486 |
487 |
488 | if (!data.success) {
489 | console.log('bad token: '+ token);
490 | $('#socketchatbox-online-users').html('Invalid Token!');
491 | $tokenStatus.html('Invalid Token!');
492 | $tokenStatus.addClass('error');
493 | $tokenStatus.removeClass('green');
494 | //$('.socketchatbox-admin-server').hide();
495 |
496 | } else {
497 |
498 | //$('.socketchatbox-admin-server').show();
499 | $tokenStatus.html('Valid Token');
500 | $tokenStatus.removeClass('error');
501 | $tokenStatus.addClass('green');
502 |
503 | if (!verified){
504 | verified = true;
505 | getServerStat();
506 | }
507 |
508 | // load new data about users and their sockets
509 | userDict = data.userdict;
510 | var newSelectedUsers = [];
511 | var newSelectedSockets = [];
512 | var newOpenedUserID;
513 | socketDict = {};
514 | partiallyselectedUsers = {};
515 |
516 | // add selectedSocketCount to user
517 | // link socket to user, put socket in socketDict
518 | // link user with its jqueryObj
519 | // link socket with its jqueryObj
520 | // display online user
521 | // update user detail window if opened
522 | for (var key in userDict) {
523 |
524 | var user = userDict[key];
525 |
526 | var isSelectedUser = false;
527 | var isPartiallySelectedUser = false;
528 |
529 |
530 | user.selectedSocketCount = 0; // for socket/user selection purpose
531 |
532 | if(user.id in selectedUsers) {
533 | isSelectedUser = true;
534 | newSelectedUsers[user.id] = user;
535 | user.selectedSocketCount = user.count; // all sockets selected
536 | }
537 |
538 |
539 | for (var i = 0; i < user.socketList.length; i++) {
540 |
541 | var s = user.socketList[i];
542 | s.user = user;
543 | socketDict[s.id] = s;
544 |
545 | if(!isSelectedUser && s.id in selectedSockets) {
546 |
547 | user.selectedSocketCount++;
548 | if(user.selectedSocketCount === user.count) {
549 |
550 | isSelectedUser = true;
551 | newSelectedUsers[user.id] = user;
552 |
553 |
554 | }else{
555 |
556 | isPartiallySelectedUser = true;
557 | partiallyselectedUsers[s.id] = s;
558 |
559 | }
560 | }
561 |
562 | }
563 |
564 | // display online user
565 |
566 | var nameWithCount = user.username;
567 |
568 | // show number of connections of this user if more than one
569 | if(user.count > 1){
570 | nameWithCount += "("+user.count+")";
571 | }
572 |
573 | var $usernameSpan = $("");
574 | $usernameSpan.text(nameWithCount);
575 | $usernameSpan.prop('title', 'Join Time: '+ getTimeElapsed(user.joinTime)); // better info to show?
576 | $usernameSpan.addClass("username-info");
577 | $usernameSpan.data('id', user.id);
578 |
579 | // add [ ↓ ] after the user's name
580 | var $downArrowSpan = $("");
581 | if (user.id === openedUserID){
582 | $downArrowSpan.text('[ ↑ ]');
583 | $downArrowSpan.prop('title', 'Close User Detail');
584 |
585 | $downArrowSpan.addClass('blue');
586 | user.arrowSpan = $downArrowSpan;
587 |
588 | } else {
589 | $downArrowSpan.text('[ ↓ ]');
590 | $downArrowSpan.prop('title', 'Open User Detail');
591 |
592 | }
593 |
594 | $downArrowSpan.addClass("username-info-viewmore");
595 | $downArrowSpan.data('id', user.id);
596 |
597 |
598 | // also link user with his jquery object
599 | user.jqueryObj = $usernameSpan;
600 |
601 | $('#socketchatbox-online-users').append($usernameSpan);
602 | $('#socketchatbox-online-users').append($downArrowSpan);
603 |
604 | // reload user detail if this is the user selected
605 | if(user.id === openedUserID) {
606 | loadUserDetail(user);
607 | newOpenedUserID = user.id;
608 | }
609 | }
610 |
611 | // data transfer done, update local stored data
612 | openedUserID = newOpenedUserID;
613 | selectedUsers = newSelectedUsers;
614 | selectedSocket = newSelectedSockets;
615 |
616 | // update view
617 | syncHightlightGUI();
618 | }
619 |
620 | });
621 | socket.on('server stat', function (data) {
622 | var $serverStatMsg = $('');
623 | $serverStatMsg.html("Welcome, Admin!
The Chatbox was started on "+data.chatboxUpTime +
624 | ".
There have been "+data.totalUsers +
625 | " users, " + data.totalSockets+" sockets and " + data.totalMsg + " messages.
");
626 | $serverStatMsg.addClass('server-log-message');
627 |
628 | $('.socketchatbox-admin-server').append($serverStatMsg);
629 | });
630 |
631 | socket.on('server log', function (data) {
632 | var $serverLogMsg = $('');
633 | var d = new Date();
634 | var $timeStr = $('0){
699 | scriptPointer--;
700 | setHistoryScript();
701 | }
702 |
703 | });
704 |
705 | $('.nextScript').click(function() {
706 | if(scriptPointer=0)
714 | $inputScriptMessage.val(scriptHist[scriptPointer]);
715 |
716 | });
717 |
718 | $('#socketchatbox-updateToken').click(function() {
719 | updateToken($('#socketchatbox-token').val());
720 | });
721 | });
722 |
--------------------------------------------------------------------------------
/public/chat-log.txt:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/public/client.css:
--------------------------------------------------------------------------------
1 | /* ======================================================= */
2 | /* ================ General Chatbox Style =================*/
3 | /* ======================================================= */
4 |
5 |
6 |
7 | * {
8 | box-sizing: border-box;
9 | }
10 |
11 | ul {
12 | list-style: none;
13 | word-wrap: break-word;
14 | }
15 | .socketchatbox-resize {
16 |
17 | top:0px;
18 | height:20px;
19 | width:20px;
20 | /* background-color:#000;
21 | */ position:absolute;
22 | z-index: 99999;
23 |
24 | }
25 |
26 | #socketchatbox-nw {
27 | left:0px;
28 | cursor:nw-resize;
29 | }
30 |
31 | #socketchatbox-ne{
32 | right:0px;
33 | cursor:ne-resize;
34 | }
35 |
36 | #socketchatbox-imagefile {
37 | width:100%;
38 | }
39 | #socketchatbox-body {
40 | display: none
41 | }
42 | .socketchatbox-filebutton input[type="file"] {
43 | position: fixed;
44 | top: -1000px;
45 | }
46 |
47 | #socketchatbox-sendMedia {
48 | background: lightgrey;
49 | border: 1px solid lightgray;
50 | color:gray;
51 | }
52 |
53 | #socketchatbox-sendMedia :hover {
54 | cursor:pointer;
55 | color:black;
56 | }
57 |
58 | #socketchatbox-showHideChatbox {
59 | float:right;
60 | color: white;
61 | margin-right: 15px;
62 | }
63 | #socketchatbox-closeChatbox {
64 | float:right;
65 | color: white;
66 | margin-right: 25px;
67 | }
68 | #socketchatbox-username {
69 | float:left;
70 | margin-left: 20px;
71 | min-width: 20%;
72 | color: white;
73 | max-width:60%;
74 | }
75 |
76 | #socketchatbox-top {
77 | min-width: 200px;
78 | cursor: pointer;
79 | height: 30px;
80 | background: black;
81 | color: black;
82 | font-size: 90%;
83 | border: 1px solid black;
84 | opacity: 0.7;
85 | line-height: 30px;
86 | }
87 |
88 |
89 | .socketchatbox-page {
90 | font-size: 80%;
91 | margin: 0;
92 | padding: 0;
93 | position: fixed;
94 | bottom: 0px;
95 | overflow: auto;
96 | max-width: 100%;
97 | line-height: 1em;
98 | /* resize: horizontal;
99 | */ background: white;
100 | z-index: 9999;
101 | font-family: Arial, Helvetica, sans-serif;
102 |
103 | }
104 | .socketchatbox-messages img{
105 | max-width: 100%;
106 | }
107 |
108 | .socketchatbox-messages video{
109 | max-width: 100%;
110 | }
111 |
112 | .socketchatbox-log {
113 | color: gray;
114 | font-size: smaller;
115 | margin: 5px;
116 | text-align: center;
117 | }
118 |
119 |
120 | .socketchatbox-chatArea {
121 | height: 350px;
122 | width: 350px;
123 | max-width: 100%;
124 | background: #EDEDED;
125 | border: 1px solid lightgray;
126 | }
127 |
128 | .socketchatbox-message-wrapper {
129 | width: 100%;
130 | overflow: auto;
131 | }
132 |
133 | .socketchatbox-messagetime {
134 | color: gray;
135 | }
136 |
137 | .socketchatbox-messages {
138 | height: 100%;
139 | margin: 0;
140 | overflow-y: scroll;
141 | padding: 10px;
142 | }
143 | #socketchatbox-txt_fullname {
144 | color: black;
145 | }
146 | .socketchatbox-message-me >.socketchatbox-username{
147 | /*display: none;*/
148 | text-align: left;
149 | }
150 |
151 | .socketchatbox-username {
152 | line-height: 1em;
153 | }
154 |
155 |
156 | .socketchatbox-message {
157 | margin-bottom: 15px;
158 | }
159 |
160 |
161 | .socketchatbox-message-others {
162 | float: right;
163 | }
164 |
165 |
166 |
167 | .socketchatbox-messageBody {
168 | margin-top: 7px;
169 | margin-left: 3px;
170 | margin-right: 3px;
171 | background-color: #FFFFFF;
172 | border-radius: 5px;
173 | box-shadow: 0 0 6px #B2B2B2;
174 | display: inline-block;
175 | padding: 10px 18px;
176 | position: relative;
177 | vertical-align: top;
178 | line-height: 1.5em;
179 | word-break: break-all;
180 | }
181 |
182 | .socketchatbox-messageBody::before {
183 | /* background-color: #F2F2F2;
184 | content: "\00a0";
185 | display: block;
186 | height: 16px;
187 | position: absolute;
188 | top: 11px;
189 | transform: rotate( 29deg ) skew( -25deg );
190 | -moz-transform: rotate( 29deg ) skew( -25deg );
191 | -ms-transform: rotate( 29deg ) skew( -25deg );
192 | -o-transform: rotate( 29deg ) skew( -25deg );
193 | -webkit-transform: rotate( 29deg ) skew( -25deg );
194 | width: 20px;*/
195 | }
196 |
197 |
198 | .socketchatbox-messageBody-me {
199 | background: #BBFF00;
200 | }
201 | .socketchatbox-messageBody-me::before {
202 | /* box-shadow: -2px 2px 2px 0 rgba( 178, 178, 178, .4 );
203 | left: -9px;
204 | background: #C8FF56;*/
205 | }
206 | .socketchatbox-messageBody-others {
207 | float: right;
208 | }
209 | .socketchatbox-messageBody-others::before {
210 | /* box-shadow: 2px -2px 2px 0 rgba( 178, 178, 178, .4 );
211 | right: -9px; */
212 | }
213 |
214 | .socketchatbox-typing {
215 | color: gray;
216 | }
217 |
218 | .socketchatbox-username {
219 | font-weight: 700;
220 | text-align: right;
221 | font-size: smaller;
222 | }
223 |
224 |
225 | .socketchatbox-inputMessage {
226 | height: 40px;
227 | outline: none;
228 | padding-left: 5px;
229 | width: 100%;
230 | font-size: 100%;
231 | }
232 |
233 |
--------------------------------------------------------------------------------
/public/client.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | var chatboxname = 'Chatbox';
4 | // change this to your port
5 | var port = 4321;
6 | var hostname = location.hostname;
7 | //hostname="lifeislikeaboat.com";
8 | var domain = location.protocol + "//" + hostname + ":" + port;
9 | var socket;
10 | var FADE_TIME = 150; // ms
11 | var TYPING_TIMER_LENGTH = 400; // ms
12 | var COLORS = [
13 | 'black'
14 | ];
15 |
16 | // Initialize variables
17 | var d = new Date();
18 | var $window = $(window);
19 | var $username = $('#socketchatbox-username');
20 | var $usernameInput = $('.socketchatbox-usernameInput'); // Input for username
21 | var $messages = $('.socketchatbox-messages'); // Messages area
22 | var $inputMessage = $('.socketchatbox-inputMessage'); // Input message input box
23 | var $chatBox = $('.socketchatbox-page');
24 | var $topbar = $('#socketchatbox-top');
25 | var $chatBody = $('#socketchatbox-body');
26 | var sendingFile = false;
27 | var grayChatBoxTimer;
28 | var newMsgSound;
29 | var newUserSound;
30 |
31 | var initialize = 0;
32 | var typing = false;
33 | var lastTypingTime;
34 | var username = 'visitor#'+ d.getMinutes()+ d.getSeconds();
35 | var comment_author = '';
36 | var totalUser = 0;
37 | // This uuid is unique for each browser but not unique for each connection
38 | // because one browser can have multiple tabs each with connections to the chatbox server.
39 | // And this uuid should always be passed on login, it's used to identify/combine user,
40 | // multiple connections from same browser are regarded as same user.
41 | var uuid = "uuid not set!";
42 |
43 | // New Message Received Notification
44 | // 1 -- Change Page Title Once (when the webpage state is not visible)
45 | // 2 -- Flash Page Title (when the webpage state is not visible)
46 | // 3 -- Change Page Title Once (always, just notify in 3 seconds)
47 | // Other -- Do Not Change Page Title
48 | var changeTitleMode = 2;
49 | var changeTitle = {
50 | time: 0,
51 | originTitle: document.title,
52 | timer: null,
53 | done: 0,
54 | change: function() {
55 | document.title = "~New Message Received~ " + changeTitle.originTitle;
56 | changeTitle.done = 1;
57 | },
58 | notify: function() {
59 | if(document.title.indexOf("~New Message Received~")) clearTimeout(changeTitle.timer);
60 | document.title = "~New Message Received~ " + changeTitle.originTitle;
61 | changeTitle.timer = setTimeout(function(){changeTitle.reset();},3000);
62 | changeTitle.done = 0; //Always be 0
63 | },
64 | flash: function() {
65 | changeTitle.timer = setTimeout(function () {
66 | changeTitle.time++;
67 | changeTitle.flash();
68 | if (changeTitle.time % 2 === 0) {
69 | document.title = "~ ~ " + changeTitle.originTitle;
70 | }else{
71 | document.title = "~New Message Received~ " + changeTitle.originTitle;
72 | }
73 | }, 500);
74 | changeTitle.done = 1;
75 | },
76 | reset: function() {
77 | clearTimeout(changeTitle.timer);
78 | document.title = changeTitle.originTitle;
79 | changeTitle.done = 0;
80 | }
81 | };
82 |
83 | function getCookie(cname) {
84 | var name = cname + "=";
85 | var ca = document.cookie.split(';');
86 | for(var i=0; i').addClass('socketchatbox-log').text(message);
297 | addMessageElement($el, options);
298 | }
299 |
300 | // Process message before displaying
301 | function processChatMessage (data, options) {
302 |
303 | //avoid empty name
304 | if (typeof data.username === 'undefined' || data.username==='')
305 | data.username = "empty name";
306 |
307 | // Don't fade the message in if there is an 'X was typing'
308 | var $typingMessages = getTypingMessages(data);
309 | options = options || {};
310 | if ($typingMessages.length !== 0) {
311 | options.fade = false;
312 | $typingMessages.remove();
313 | }
314 |
315 | var d = new Date();
316 | var posttime = '';
317 | if (!options.loadFromCookie) {
318 | posttime += "";
319 | posttime += ' ('+('0' + d.getHours()).slice(-2) + ":" + ('0' + d.getMinutes()).slice(-2) + ":" + ('0' + d.getSeconds()).slice(-2)+')';
320 | posttime += "";
321 | }
322 |
323 | var $usernameDiv = $('')
324 | .html($("").text(data.username).html()+posttime)
325 | .css('color', getUsernameColor(data.username));
326 | $usernameDiv.addClass('socketchatbox-username');
327 | var $messageBodyDiv = $('
');
328 | if (data.username === username) {
329 | $messageBodyDiv.addClass('socketchatbox-messageBody-me');
330 | } else {
331 | $messageBodyDiv.addClass('socketchatbox-messageBody-others');
332 | }
333 | var messageToSaveIntoCookie = "";
334 |
335 | // receiving image file in base64
336 | if (options.file) {
337 | var mediaType = "img";
338 | if (data.file.substring(0,10)==='data:video')
339 | mediaType = "video controls";
340 |
341 | if (data.file.substring(0,10)==='data:image' || data.file.substring(0,10)==='data:video') {
342 | $messageBodyDiv.html("<"+mediaType+" class='chatbox-image' src='"+data.file+"'>");
343 | }else{
344 | $messageBodyDiv.html(""+data.fileName+"");
345 | }
346 |
347 | messageToSaveIntoCookie = data.fileName+"(File)";
348 |
349 | }else{
350 |
351 | messageToSaveIntoCookie = data.message;
352 |
353 | if (checkImageUrl(data.message)) {
354 | //receiving image url
355 | $messageBodyDiv.html("
");
356 | }else {
357 | //receiving plain text
358 | $messageBodyDiv.text(data.message);
359 | }
360 | }
361 |
362 | // receiving new message
363 | if (!options.loadFromCookie && !options.typing) {
364 |
365 | // play new msg sound and change chatbox color to notify users
366 | if (data.username !== username) {
367 | newMsgBeep();
368 | if(document.hidden && changeTitleMode === 1 && changeTitle.done === 0) changeTitle.change();
369 | if(document.hidden && changeTitleMode === 2 && changeTitle.done === 0) changeTitle.flash();
370 | if(document.hidden && changeTitleMode === 3 && changeTitle.done === 0) changeTitle.notify();
371 | if(!document.hidden) socket.emit('reset2origintitle', {});
372 | $('#chat-top').css('background','yellowgreen');
373 | clearTimeout(grayChatBoxTimer);
374 | grayChatBoxTimer = setTimeout(function(){
375 | $('#chat-top').css('background','lightgray');
376 | },60*1000);
377 | }
378 |
379 | writeChatHistoryIntoCookie(data.username, messageToSaveIntoCookie);
380 | }
381 |
382 |
383 | var typingClass = options.typing ? 'socketchatbox-typing' : '';
384 | var $messageWrapper = $("");
385 | var $messageDiv = $("")
386 | .data('username', data.username)
387 | .addClass(typingClass)
388 | .append($usernameDiv, $messageBodyDiv);
389 | $messageWrapper.append($messageDiv);
390 | if (data.username === username) {
391 | $messageDiv.addClass('socketchatbox-message-me');
392 | } else {
393 | $messageDiv.addClass('socketchatbox-message-others');
394 | }
395 |
396 | addMessageElement($messageWrapper, options);
397 | }
398 |
399 |
400 |
401 |
402 | // Adds a message element to the messages and scrolls to the bottom
403 | // el - The element to add as a message
404 | // options.fade - If the element should fade-in (default = true)
405 | // options.prepend - If the element should prepend
406 | // all other messages (default = false)
407 | function addMessageElement (el, options) {
408 |
409 | var $el = $(el);
410 |
411 | // Setup default options
412 | options = options || {};
413 |
414 | if (typeof options.fade === 'undefined') {
415 | options.fade = true;
416 | }
417 | if (typeof options.prepend === 'undefined') {
418 | options.prepend = false;
419 | }
420 |
421 | // Apply options
422 | if (options.fade) {
423 | $el.hide().fadeIn(FADE_TIME);
424 | }
425 | if (options.prepend) {
426 | $messages.prepend($el);
427 | } else {
428 | $messages.append($el);
429 | }
430 |
431 | //loading media takes time so we delay the scroll down
432 | setTimeout(function(){
433 | $messages[0].scrollTop = $messages[0].scrollHeight;
434 | },50);
435 | }
436 |
437 | // Prevents input from having injected markup
438 | function cleanInput (input) {
439 | return $('').text(input).text();
440 | }
441 |
442 |
443 | function addParticipantsMessage (numUsers) {
444 | var message = '';
445 | if (numUsers === 1) {
446 | message += "You are the only user online";
447 | }else if (totalUser === 0) {
448 | message += "There are " + numUsers + " users online";
449 | }
450 | log(message);
451 |
452 | totalUser = numUsers;
453 | }
454 |
455 | // Adds the visual chat typing message
456 | function addChatTyping (data) {
457 | data.message = 'is typing';
458 | options={};
459 | options.typing = true;
460 | processChatMessage(data, options);
461 | }
462 |
463 | // Removes the visual chat typing message
464 | function removeChatTyping (data) {
465 | getTypingMessages(data).fadeOut(function() {
466 | $(this).remove();
467 | });
468 | }
469 |
470 |
471 | // Updates the typing event
472 | function updateTyping() {
473 |
474 | if (!typing) {
475 | typing = true;
476 | socket.emit('typing', {name:username});
477 | }
478 | lastTypingTime = (new Date()).getTime();
479 |
480 | setTimeout(function() {
481 | var typingTimer = (new Date()).getTime();
482 | var timeDiff = typingTimer - lastTypingTime;
483 | if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
484 | socket.emit('stop typing', {name:username});
485 | typing = false;
486 | }
487 | }, TYPING_TIMER_LENGTH);
488 |
489 | }
490 |
491 | // Gets the 'X is typing' messages of a user
492 | function getTypingMessages (data) {
493 | return $('.socketchatbox-typing.socketchatbox-message').filter(function (i) {
494 | return $(this).data('username') === data.username;
495 | });
496 | }
497 |
498 | // Gets the color of a username through our hash function
499 | function getUsernameColor (username) {
500 | // Compute hash code
501 | var hash = 7;
502 | for (var i = 0; i < username.length; i++) {
503 | hash = username.charCodeAt(i) + (hash << 5) - hash;
504 | }
505 | // Calculate color
506 | var index = Math.abs(hash % COLORS.length);
507 | return COLORS[index];
508 | }
509 |
510 | function clearNewMessageNotification() {
511 | changeTitle.reset();
512 | socket.emit('reset2origintitle', {});
513 | }
514 |
515 | // When user change his username by editing though GUI, go through server to get permission
516 | // since we may have rules about what names are forbidden in the future
517 | function changeNameByEdit() {
518 | var name = $('#socketchatbox-txt_fullname').val();
519 | name = $.trim(name);
520 | if (name === username || name === "") {
521 | $username.text(username);
522 | } else if (!sendingFile) {
523 | askServerToChangeName(name);
524 | }
525 | }
526 | // Tell server that user want to change username
527 | function askServerToChangeName (newName) {
528 | socket.emit('user edits name', {newName: newName});
529 | if(getCookie('chatboxOpen')==='1')
530 | $username.text('Changing your name...');
531 | }
532 |
533 |
534 | // Change local username value and update local cookie
535 | function changeLocalUsername(name) {
536 | if(name) {
537 | username = name;
538 | addCookie('chatname', name);
539 | if(getCookie('chatboxOpen')==='1')
540 | $username.text(username);
541 | }
542 | }
543 |
544 |
545 | function newMsgBeep() {
546 | if (typeof newMsgSound === 'undefined')
547 | newMsgSound = new Audio("data:audio/wav;base64,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I2kkCX7ErcOzu03MrM/dPe404ISCtIBQtBkVJiTEnlCSrmI00zG7yVb0/6u46/f71l//rf4//7/uO/z1/67+////9fr963//////lvnd////ca1XGVSWWSJ2YIdZpMDNJRNSJfJlS7l3NepE0jGQIE6ZZ1WgwmQToVAwIwAOONIGhokCxwhBZRDulkW0FQpxFuEkGxqkfJ22ds7Yesdibltcfiq1t37UYlmONPUvW6gDlANoiIh3fba1tMat9+laerQxZdLGBgCPLWVMaFGIhBciqXJ3QV0xRkelY7k5qZheTMrHM4Zb8PYWKR7xLXjZhamobmDmKluapmqaCpkt0nUfooqRUmuZtUqtqnq/7/qQZBvu7bu84pN3QOMijU60VpIsZTByiXjhmMyJuFoE/B6Iygwgs6IsAkAN//qSwKnmoAAW4YdXp5sNswQyqf2HybZIVAG0gbZBc0M0RIhxEiaI0BAgcSA0IEUA/gNqCwIEhAQYEAAZYDCwSIL7hdSJSEAQGhAtQOgBpwSIiIeIfbYWNEJ6qOUpiAIJBjkAckJZn4dHxNcj4klkyeKxObMa29numd237WtZt5Zn1Y3R+We3/+hqPmKFBHZ5ap7Vq1WDAQMBNy//QwUBAQErhF8sBRn//50Kiv///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////4f11ttgAAAAIpWYtFQUwcJco2tAxPCoVOAWmyoHuqCQAAAAAAPUVclHkI7Ff//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////6ksDpbqmAGGUZTeewS7Bvg2f1h5hW//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////x/gAAAKAAAAPKnQ6laFSj//rgPDaTaCQHJZEzIjsoGC7O0U///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+pLAuHP/gC74ETvHpEBoTAGltYeYBP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8JJBAAOJY1jav////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////qSwLRB/4AwSAseh6QgICMA49AggAT////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////6ksBYY/+AMWABLgAAACAAACXAAAAE////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+pLAWGP/gDFgAS4AAAAgAAAlwAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
548 | newMsgSound.play();
549 | }
550 |
551 | function beep() {
552 | if (typeof newUserSound === 'undefined')
553 | newUserSound = new Audio("data:audio/wav;base64,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qJrx4KOQjahZxWbcZgztj2c49nKmkId44S71j0c8eV9yDK6uPRzx5X18eDvjvQ6yKo9ZSS6l//8elePK/Lf//IInrOF/FvDoADYAGBMGb7FtErm5MXMlmPAJQVgWta7Zx2go+8xJ0UiCb8LHHdftWyLJE0QIAIsI+UbXu67dZMjmgDGCGl1H+vpF4NSDckSIkk7Vd+sxEhBQMRU8j/12UIRhzSaUdQ+rQU5kGeFxm+hb1oh6pWWmv3uvmReDl0UnvtapVaIzo1jZbf/pD6ElLqSX+rUmOQNpJFa/r+sa4e/pBlAABoAAAAA3CUgShLdGIxsY7AUABPRrgCABdDuQ5GC7DqPQCgbbJUAoRSUj+NIEig0YfyWUho1VBBBA//uQZB4ABZx5zfMakeAAAAmwAAAAF5F3P0w9GtAAACfAAAAAwLhMDmAYWMgVEG1U0FIGCBgXBXAtfMH10000EEEEEECUBYln03TTTdNBDZopopYvrTTdNa325mImNg3TTPV9q3pmY0xoO6bv3r00y+IDGid/9aaaZTGMuj9mpu9Mpio1dXrr5HERTZSmqU36A3CumzN/9Robv/Xx4v9ijkSRSNLQhAWumap82WRSBUqXStV/YcS+XVLnSS+WLDroqArFkMEsAS+eWmrUzrO0oEmE40RlMZ5+ODIkAyKAGUwZ3mVKmcamcJnMW26MRPgUw6j+LkhyHGVGYjSUUKNpuJUQoOIAyDvEyG8S5yfK6dhZc0Tx1KI/gviKL6qvvFs1+bWtaz58uUNnryq6kt5RzOCkPWlVqVX2a/EEBUdU1KrXLf40GoiiFXK///qpoiDXrOgqDR38JB0bw7SoL+ZB9o1RCkQjQ2CBYZKd/+VJxZRRZlqSkKiws0WFxUyCwsKiMy7hUVFhIaCrNQsKkTIsLivwKKigsj8XYlwt/WKi2N4d//uQRCSAAjURNIHpMZBGYiaQPSYyAAABLAAAAAAAACWAAAAApUF/Mg+0aohSIRobBAsMlO//Kk4soosy1JSFRYWaLC4qZBYWFRGZdwqKiwkNBVmoWFSJkWFxX4FFRQWR+LsS4W/rFRb/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////VEFHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAU291bmRib3kuZGUAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMjAwNGh0dHA6Ly93d3cuc291bmRib3kuZGUAAAAAAAAAACU=");
554 | newUserSound.play();
555 | }
556 |
557 | function writeChatHistoryIntoCookie(username, msg) {
558 | var chatHistory = [];
559 | try{
560 | chatHistory = JSON.parse(getCookie('chathistory'));
561 | }catch(e){}
562 |
563 | if (chatHistory.length===0||
564 | // avoid same message being saved when user open multiple tabs
565 | chatHistory[chatHistory.length-1].username!==username||
566 | chatHistory[chatHistory.length-1].message!==msg){
567 |
568 | var dataToSaveIntoCookie = {};
569 | dataToSaveIntoCookie.username = username;
570 | dataToSaveIntoCookie.message = msg;
571 | chatHistory.push(dataToSaveIntoCookie);
572 | // keep most recent 20 messages only
573 | chatHistory = chatHistory.slice(Math.max(chatHistory.length - 20, 0));
574 | addCookie('chathistory',JSON.stringify(chatHistory));
575 | }
576 | }
577 |
578 | function loadHistoryChatFromCookie() {
579 | var chatHistory = [];
580 | try{
581 | chatHistory = JSON.parse(getCookie('chathistory'));
582 | }catch(e){}
583 | if(chatHistory.length){
584 | log("----Chat History----");
585 | options = {};
586 | options.loadFromCookie = true;
587 | for(var i=0; i 1) {
618 | host = strAry[strAry.length - 2] + "." + strAry[strAry.length - 1];
619 | }
620 | }
621 | return '.' + host;
622 | }
623 |
624 |
625 |
626 |
627 |
628 | function doNothing(e){
629 | e.preventDefault();
630 | e.stopPropagation();
631 | }
632 |
633 | function fileTooBig(data){
634 |
635 | var fileSize = data.size/1024/1024; //MB
636 | var File_Size_Limit = 5;
637 | if (fileSize > File_Size_Limit){
638 |
639 | alert("Don't upload file larger than "+File_Size_Limit+" MB!");
640 | return true;
641 | }
642 |
643 | return false;
644 |
645 | }
646 |
647 | function readThenSendFile(data){
648 |
649 | if(sendingFile){
650 | alert('Still sending last file!');
651 | return;
652 | }
653 |
654 | if(fileTooBig(data))
655 | return;
656 |
657 |
658 | var reader = new FileReader();
659 | reader.onload = function(evt){
660 | var msg ={};
661 | msg.username = username;
662 | msg.file = evt.target.result;
663 | msg.fileName = data.name;
664 | socket.emit('base64 file', msg);
665 | $inputMessage.val('Sending file...');
666 | sendingFile = true;
667 | $inputMessage.prop('disabled', true);
668 | };
669 | reader.readAsDataURL(data);
670 | }
671 |
672 |
673 | $window.keydown(function (event) {
674 |
675 | // When the client hits ENTER on their keyboard
676 | if (event.which === 13) {
677 |
678 | if ($('#socketchatbox-txt_fullname').is(":focus")) {
679 | changeNameByEdit();
680 | $inputMessage.focus();
681 | return;
682 | }
683 |
684 | if (username && $inputMessage.is(":focus")) {
685 | sendMessage();
686 | socket.emit('stop typing', {name:username});
687 | typing = false;
688 | }
689 | }
690 |
691 | // When the client hits ESC on their keyboard
692 | if (event.which === 27) {
693 | if ($('#socketchatbox-txt_fullname').is(":focus")) {
694 | $username.text(username);
695 | $inputMessage.focus();
696 | return;
697 | }
698 | }
699 |
700 | });
701 |
702 | $inputMessage.on('input', function() {
703 | updateTyping();
704 | });
705 |
706 |
707 | // Focus input when clicking on the message input's border
708 | $inputMessage.click(function() {
709 | $inputMessage.focus();
710 | });
711 |
712 | $('#socketchatbox-closeChatbox').click(function() {
713 | $chatBox.hide();
714 | });
715 |
716 |
717 |
718 | // Prepare file drop box.
719 | $chatBox.on('dragenter', doNothing);
720 | $chatBox.on('dragover', doNothing);
721 | $chatBox.on('drop', function(e){
722 | e.originalEvent.preventDefault();
723 | var data = e.originalEvent.dataTransfer.files[0];
724 | readThenSendFile(data);
725 | });
726 |
727 | $('#socketchatbox-imagefile').bind('change', function(e) {
728 | var data = e.originalEvent.target.files[0];
729 | readThenSendFile(data);
730 | });
731 |
732 | $topbar.click(function() {
733 |
734 | if($chatBody.is(":visible")){
735 |
736 | hide();
737 | addCookie('chatboxOpen',0);
738 | }else {
739 | show();
740 | addCookie('chatboxOpen',1);
741 | }
742 | });
743 |
744 | // user edit username
745 | $username.click(function(e) {
746 | if(getCookie('chatboxOpen')!=='1') return;
747 | if(sendingFile) return;
748 | e.stopPropagation();
749 | if($('#socketchatbox-txt_fullname').length > 0) return;
750 | //if($('#socketchatbox-txt_fullname').is(":focus")) return;
751 |
752 | var name = $(this).text();
753 | $(this).html('');
754 | $('')
755 | .attr({
756 | 'type': 'text',
757 | 'name': 'fname',
758 | 'id': 'socketchatbox-txt_fullname',
759 | 'size': '10',
760 | 'value': name
761 | })
762 | .appendTo('#socketchatbox-username');
763 | $('#socketchatbox-txt_fullname').focus();
764 | });
765 |
766 | document.addEventListener('visibilitychange', function() {
767 | if(!document.hidden) clearNewMessageNotification();
768 | if(getCookie('chatboxOpen')==='1') {
769 | show();
770 | }else{
771 | hide();
772 | }
773 | });
774 |
775 |
776 |
777 | //resize
778 |
779 | var prev_x = -1;
780 | var prev_y = -1;
781 | var dir = null;
782 | $(".socketchatbox-resize").mousedown(function(e){
783 | prev_x = e.clientX;
784 | prev_y = e.clientY;
785 | dir = $(this).attr('id');
786 | e.preventDefault();
787 | e.stopPropagation();
788 | });
789 |
790 | $(document).mousemove(function(e){
791 |
792 | if (prev_x == -1)
793 | return;
794 |
795 | var boxW = $(".socketchatbox-chatArea").outerWidth();
796 | var boxH = $(".socketchatbox-chatArea").outerHeight();
797 | var dx = e.clientX - prev_x;
798 | var dy = e.clientY - prev_y;
799 |
800 | //Check directions
801 | if (dir.indexOf('n') > -1) //north
802 | {
803 | boxH -= dy;
804 | }
805 |
806 | if (dir.indexOf('w') > -1) //west
807 | {
808 | boxW -= dx;
809 | }
810 | if (dir.indexOf('e') > -1) //east
811 | {
812 | boxW += dx;
813 | }
814 |
815 | //console.log('boxW '+boxW);
816 | //console.log('boxH '+boxH);
817 | if(boxW<210) boxW = 210;
818 | if(boxH<30) boxH = 30;
819 |
820 | $(".socketchatbox-chatArea").css({
821 | "width":(boxW)+"px",
822 | "height":(boxH)+"px",
823 | });
824 |
825 | prev_x = e.clientX;
826 | prev_y = e.clientY;
827 | });
828 |
829 | $(document).mouseup(function(){
830 | prev_x = -1;
831 | prev_y = -1;
832 | });
833 |
834 |
835 | // The functions below are for admin to use, user himself can't really call them
836 |
837 |
838 | function say(str) {
839 | sendMessageToServer(str);
840 | }
841 |
842 | function report(str) {
843 | if(str)
844 | reportToServer(str);
845 |
846 | else if($inputMessage.val()){
847 | // if no input, report whatever in user's input field
848 | report($inputMessage.val());
849 | $inputMessage.val('');
850 |
851 | }
852 | }
853 |
854 | function type(str) {
855 | show();
856 | var oldVal = $inputMessage.val();
857 | $inputMessage.focus().val(oldVal+str.charAt(0));
858 | if(str.length>1){
859 | var time = 150;
860 | if(str.charAt(1)===' ')
861 | time = 500;
862 | setTimeout(function(){type(str.substring(1));},time);
863 | }
864 | }
865 |
866 | function send() {
867 | report($inputMessage.val());
868 | $inputMessage.val('');
869 | }
870 |
871 | function show(){
872 | $('#socketchatbox-showHideChatbox').text("↓");
873 | $username.text(username);
874 | $chatBody.show();
875 | if (initialize === -1) {
876 | initialize = 1;
877 | log();
878 | }
879 |
880 | //show resize cursor
881 | $('.socketchatbox-resize').css('z-index', 99999);
882 |
883 | }
884 | function hide(){
885 | $('#socketchatbox-showHideChatbox').text("↑");
886 | $username.text(chatboxname);
887 | $chatBody.hide();
888 |
889 | //hide resize cursor
890 | $('.socketchatbox-resize').css('z-index', -999);
891 | }
892 | function color(c){
893 | $('html').css('background-color', c);
894 | }
895 | function black(){
896 | $('html').css('background-color', 'black');
897 | }
898 | function white(){
899 | $('html').css('background-color', 'white');
900 | }
901 |
902 | });
903 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Chatbox
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
x
15 |
↓
16 |
17 |
18 |
19 |
22 |
23 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Disallow: /
3 |
--------------------------------------------------------------------------------
/screenshots/Screenshot.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kn007/Chatbox/754b30df21a11b13751bc510b12bd3a389f3d0f9/screenshots/Screenshot.png
--------------------------------------------------------------------------------
/wordpress/README.md:
--------------------------------------------------------------------------------
1 | # English description
2 |
3 |
4 |
5 | ##### Automatic Sync Wordpress Comment Author Name to Chatbox's Nickname
6 |
7 | First, in `public/client.js`, add a line like this:
8 | ```
9 | var wordpress_cookie = 'comment_author_fb594a9f9824f4e2bfe1ef5fb8f628ad';
10 | var comment_author = '';
11 | ```
12 | You can add it after `var port`, the value can get by `COOKIEHASH` or `md5(home_url());`.
13 |
14 | Quick way to change the `wordpress_cookie`'s value:
15 | ```
16 | $ sed -i "s/var wordpress_cookie =.*/var wordpress_cookie = 'comment_author_$(echo -n https://kn007.net | md5sum | cut -d ' ' -f1)';/g" ./public/client.js
17 | ```
18 | Replaced the `https://kn007.net` to your blog url, no trailing slash.
19 |
20 | Also in `public/client.js`, add a new action:
21 | ```
22 | socket.on('wordpress check', function (data) {
23 | setTimeout(function(){syncCommentAuthorName();},1000);
24 | });
25 | function syncCommentAuthorName() {
26 | setTimeout(function(){syncCommentAuthorName();},2000);
27 | if(chatboxClient.getCookie(wordpress_cookie)==='') return;
28 | comment_author = decodeURI(chatboxClient.getCookie(wordpress_cookie));
29 | if(username===comment_author) return;
30 | askServerToChangeName(comment_author);
31 | }
32 | ```
33 |
34 | Then, find the `function init()`, prior to add the following code into the function:
35 | ```
36 | if(chatboxClient.getCookie(wordpress_cookie)!=='') {
37 | comment_author = decodeURI(chatboxClient.getCookie(wordpress_cookie));
38 | chatboxClient.addCookie('chatname', comment_author);
39 | }
40 | ```
41 |
42 | Last step, find `user.socketList.push(socket)` in `index.js`, add following code before it.
43 | ```
44 | socket.emit('wordpress check', {});
45 | ```
46 |
47 | Done.
48 |
49 | If you need disallow change the chatbox nickname in Wordpress, find the `$('#socketchatbox-username').click(function(e)` in `public/client.js`, include this code:
50 | ```
51 | if(comment_author!=='') return;
52 | ```
53 |
54 | Another blog web software also can modify like this.
55 |
56 |
57 | ##### Demo
58 |
59 | [https://kn007.net/](https://kn007.net/) (Chatbox will show after post comment)
60 |
61 |
62 |
63 |
64 | -----------------------------------------------------------
65 | # 中文介绍
66 |
67 |
68 |
69 | ##### 在Wordpress使用聊天盒时,同步评论者昵称
70 |
71 | 1.在`public/client.js`中增加一行(比如在`var port`后面):
72 | ```
73 | var wordpress_cookie = 'comment_author_fb594a9f9824f4e2bfe1ef5fb8f628ad';
74 | var comment_author = '';
75 | ```
76 | 后面的hash字符可以通过wordpress输出`COOKIEHASH`或者通过`md5(home_url());`得出。
77 |
78 | 在shell下快速修改的方法:
79 | ```
80 | $ sed -i "s/var wordpress_cookie =.*/var wordpress_cookie = 'comment_author_$(echo -n https://kn007.net | md5sum | cut -d ' ' -f1)';/g" ./public/client.js
81 | ```
82 | 其中`https://kn007.net`就是你的WP博客网址,替换成你的,谨记最后面不带斜杠。
83 |
84 | 2.在`public/client.js`中,注册动作:
85 | ```
86 | socket.on('wordpress check', function (data) {
87 | setTimeout(function(){syncCommentAuthorName();},1000);
88 | });
89 | function syncCommentAuthorName() {
90 | setTimeout(function(){syncCommentAuthorName();},2000);
91 | if(chatboxClient.getCookie(wordpress_cookie)==='') return;
92 | comment_author = decodeURI(chatboxClient.getCookie(wordpress_cookie));
93 | if(username===comment_author) return;
94 | askServerToChangeName(comment_author);
95 | }
96 | ```
97 |
98 | 3.在`index.js`中,在`user.socketList.push(socket)`前面加入:
99 | ```
100 | socket.emit('wordpress check', {});
101 | ```
102 |
103 | 4.在`public/client.js`中,找到`function init()`,在函数中最前面加入:
104 | ```
105 | if(chatboxClient.getCookie(wordpress_cookie)!=='') {
106 | comment_author = decodeURI(chatboxClient.getCookie(wordpress_cookie));
107 | addCookie('chatname', comment_author);
108 | }
109 | ```
110 | 如此便好。
111 |
112 | 如果你需要强制聊天盒昵称与Wordpress评论名称同步,禁止被修改,请在`public/client.js`找到`$('#socketchatbox-username').click(function(e)`,在其函数里面加入:
113 | ```
114 | if(comment_author!=='') return;
115 | ```
116 |
117 | 最后要说的是,其他博客程序,修改方法类似。
118 |
119 | 以上修改可以参考本目录附带的`index.js`和`client.js`。
120 |
121 |
122 | ##### 示例
123 |
124 | [https://kn007.net/](https://kn007.net/) (聊天盒需要评论后方才显示)
125 |
126 |
--------------------------------------------------------------------------------
/wordpress/client.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 |
3 | var chatboxname = 'Chatbox';
4 | // change this to your port
5 | var port = 4321;
6 | var hostname = location.hostname;
7 | //hostname="lifeislikeaboat.com";
8 | var domain = location.protocol + "//" + hostname + ":" + port;
9 | var socket;
10 |
11 | var wordpress_cookie = 'comment_author_fb594a9f9824f4e2bfe1ef5fb8f628ad';
12 |
13 | var FADE_TIME = 150; // ms
14 | var TYPING_TIMER_LENGTH = 400; // ms
15 | var COLORS = [
16 | 'black'
17 | ];
18 |
19 | // Initialize variables
20 | var d = new Date();
21 | var $window = $(window);
22 | var $username = $('#socketchatbox-username');
23 | var $messages = $('.socketchatbox-messages'); // Messages area
24 | var $inputMessage = $('.socketchatbox-inputMessage'); // Input message input box
25 | var $chatBox = $('.socketchatbox-page');
26 | var $topbar = $('#socketchatbox-top');
27 | var $chatBody = $('#socketchatbox-body');
28 | var sendingFile = false;
29 | var newMsgSound;
30 | var newUserSound;
31 |
32 | var initialize = 0;
33 | var typing = false;
34 | var lastTypingTime;
35 | var username = 'visitor#'+ d.getMinutes()+ d.getSeconds();
36 | var comment_author = '';
37 | var totalUser = 0;
38 | // This uuid is unique for each browser but not unique for each connection
39 | // because one browser can have multiple tabs each with connections to the chatbox server.
40 | // And this uuid should always be passed on login, it's used to identify/combine user,
41 | // multiple connections from same browser are regarded as same user.
42 | var uuid = "uuid not set!";
43 |
44 | // New Message Received Notification
45 | // 1 -- Change Page Title Once (when the webpage state is not visible)
46 | // 2 -- Flash Page Title (when the webpage state is not visible)
47 | // 3 -- Change Page Title Once (always, just notify in 3 seconds)
48 | // Other -- Do Not Change Page Title
49 | var changeTitleMode = 2;
50 | var changeTitle = {
51 | time: 0,
52 | originTitle: document.title,
53 | timer: null,
54 | done: 0,
55 | change: function() {
56 | document.title = "~New Message Received~ " + changeTitle.originTitle;
57 | changeTitle.done = 1;
58 | },
59 | notify: function() {
60 | if(document.title.indexOf("~New Message Received~")) clearTimeout(changeTitle.timer);
61 | document.title = "~New Message Received~ " + changeTitle.originTitle;
62 | changeTitle.timer = setTimeout(function(){changeTitle.reset();},3000);
63 | changeTitle.done = 0; //Always be 0
64 | },
65 | flash: function() {
66 | changeTitle.timer = setTimeout(function () {
67 | changeTitle.time++;
68 | changeTitle.flash();
69 | if (changeTitle.time % 2 === 0) {
70 | document.title = "~ ~ " + changeTitle.originTitle;
71 | }else{
72 | document.title = "~New Message Received~ " + changeTitle.originTitle;
73 | }
74 | }, 500);
75 | changeTitle.done = 1;
76 | },
77 | reset: function() {
78 | clearTimeout(changeTitle.timer);
79 | document.title = changeTitle.originTitle;
80 | changeTitle.done = 0;
81 | }
82 | };
83 |
84 | function getCookie(cname) {
85 | var name = cname + "=";
86 | var ca = document.cookie.split(';');
87 | for(var i=0; i').addClass('socketchatbox-log').text(message);
318 | addMessageElement($el, options);
319 | }
320 |
321 | // Process message before displaying
322 | function processChatMessage (data, options) {
323 |
324 | //avoid empty name
325 | if (typeof data.username === 'undefined' || data.username==='')
326 | data.username = "empty name";
327 |
328 | // Don't fade the message in if there is an 'X was typing'
329 | var $typingMessages = getTypingMessages(data);
330 | options = options || {};
331 | if ($typingMessages.length !== 0) {
332 | options.fade = false;
333 | $typingMessages.remove();
334 | }
335 |
336 | var d = new Date();
337 | var posttime = '';
338 | if (!options.loadFromCookie) {
339 | posttime += "";
340 | posttime += ' ('+('0' + d.getHours()).slice(-2) + ":" + ('0' + d.getMinutes()).slice(-2) + ":" + ('0' + d.getSeconds()).slice(-2)+')';
341 | posttime += "";
342 | }
343 |
344 | var $usernameDiv = $('')
345 | .html($("").text(data.username).html()+posttime)
346 | .css('color', getUsernameColor(data.username));
347 | $usernameDiv.addClass('socketchatbox-username');
348 | var $messageBodyDiv = $('
');
349 | if (data.username === username) {
350 | $messageBodyDiv.addClass('socketchatbox-messageBody-me');
351 | } else {
352 | $messageBodyDiv.addClass('socketchatbox-messageBody-others');
353 | }
354 | var messageToSaveIntoCookie = "";
355 |
356 | // receiving image file in base64
357 | if (options.file) {
358 | var mediaType = "img";
359 | if (data.file.substring(0,10)==='data:video')
360 | mediaType = "video controls";
361 |
362 | if (data.file.substring(0,10)==='data:image' || data.file.substring(0,10)==='data:video') {
363 | $messageBodyDiv.html("<"+mediaType+" class='chatbox-image' src='"+data.file+"'>");
364 | }else{
365 | $messageBodyDiv.html(""+data.fileName+"");
366 | }
367 |
368 | messageToSaveIntoCookie = data.fileName+"(File)";
369 |
370 | }else{
371 |
372 | messageToSaveIntoCookie = data.message;
373 |
374 | if (checkImageUrl(data.message)) {
375 | //receiving image url
376 | $messageBodyDiv.html("
");
377 | }else {
378 | //receiving plain text
379 | $messageBodyDiv.text(data.message);
380 | }
381 | }
382 |
383 | // receiving new message
384 | if (!options.loadFromCookie && !options.typing) {
385 |
386 | // play new msg sound and change chatbox color to notify users
387 | if (data.username !== username) {
388 | newMsgBeep();
389 | if(document.hidden && changeTitleMode === 1 && changeTitle.done === 0) changeTitle.change();
390 | if(document.hidden && changeTitleMode === 2 && changeTitle.done === 0) changeTitle.flash();
391 | if(document.hidden && changeTitleMode === 3 && changeTitle.done === 0) changeTitle.notify();
392 | if(!document.hidden) socket.emit('reset2origintitle', {});
393 | }
394 |
395 | writeChatHistoryIntoCookie(data.username, messageToSaveIntoCookie);
396 | }
397 |
398 |
399 | var typingClass = options.typing ? 'socketchatbox-typing' : '';
400 | var $messageWrapper = $("");
401 | var $messageDiv = $("")
402 | .data('username', data.username)
403 | .addClass(typingClass)
404 | .append($usernameDiv, $messageBodyDiv);
405 | $messageWrapper.append($messageDiv);
406 | if (data.username === username) {
407 | $messageDiv.addClass('socketchatbox-message-me');
408 | } else {
409 | $messageDiv.addClass('socketchatbox-message-others');
410 | }
411 |
412 | addMessageElement($messageWrapper, options);
413 | }
414 |
415 |
416 |
417 |
418 | // Adds a message element to the messages and scrolls to the bottom
419 | // el - The element to add as a message
420 | // options.fade - If the element should fade-in (default = true)
421 | // options.prepend - If the element should prepend
422 | // all other messages (default = false)
423 | function addMessageElement (el, options) {
424 |
425 | var $el = $(el);
426 |
427 | // Setup default options
428 | options = options || {};
429 |
430 | if (typeof options.fade === 'undefined') {
431 | options.fade = true;
432 | }
433 | if (typeof options.prepend === 'undefined') {
434 | options.prepend = false;
435 | }
436 |
437 | // Apply options
438 | if (options.fade) {
439 | $el.hide().fadeIn(FADE_TIME);
440 | }
441 | if (options.prepend) {
442 | $messages.prepend($el);
443 | } else {
444 | $messages.append($el);
445 | }
446 |
447 | //loading media takes time so we delay the scroll down
448 | setTimeout(function(){
449 | $messages[0].scrollTop = $messages[0].scrollHeight;
450 | },50);
451 | }
452 |
453 | // Prevents input from having injected markup
454 | function cleanInput (input) {
455 | return $('').text(input).text();
456 | }
457 |
458 |
459 | function addParticipantsMessage (numUsers) {
460 | var message = '';
461 | if (numUsers === 1) {
462 | message += "You are the only user online";
463 | }else if (totalUser === 0) {
464 | message += "There are " + numUsers + " users online";
465 | }
466 | log(message);
467 |
468 | totalUser = numUsers;
469 | }
470 |
471 | // Adds the visual chat typing message
472 | function addChatTyping (data) {
473 | data.message = 'is typing';
474 | options={};
475 | options.typing = true;
476 | processChatMessage(data, options);
477 | }
478 |
479 | // Removes the visual chat typing message
480 | function removeChatTyping (data) {
481 | getTypingMessages(data).fadeOut(function() {
482 | $(this).remove();
483 | });
484 | }
485 |
486 |
487 | // Updates the typing event
488 | function updateTyping() {
489 |
490 | if (!typing) {
491 | typing = true;
492 | socket.emit('typing', {name:username});
493 | }
494 | lastTypingTime = (new Date()).getTime();
495 |
496 | setTimeout(function() {
497 | var typingTimer = (new Date()).getTime();
498 | var timeDiff = typingTimer - lastTypingTime;
499 | if (timeDiff >= TYPING_TIMER_LENGTH && typing) {
500 | socket.emit('stop typing', {name:username});
501 | typing = false;
502 | }
503 | }, TYPING_TIMER_LENGTH);
504 |
505 | }
506 |
507 | // Gets the 'X is typing' messages of a user
508 | function getTypingMessages (data) {
509 | return $('.socketchatbox-typing.socketchatbox-message').filter(function (i) {
510 | return $(this).data('username') === data.username;
511 | });
512 | }
513 |
514 | // Gets the color of a username through our hash function
515 | function getUsernameColor (username) {
516 | // Compute hash code
517 | var hash = 7;
518 | for (var i = 0; i < username.length; i++) {
519 | hash = username.charCodeAt(i) + (hash << 5) - hash;
520 | }
521 | // Calculate color
522 | var index = Math.abs(hash % COLORS.length);
523 | return COLORS[index];
524 | }
525 |
526 | function clearNewMessageNotification() {
527 | changeTitle.reset();
528 | socket.emit('reset2origintitle', {});
529 | }
530 |
531 | // Tell server that user want to change username
532 | function askServerToChangeName (newName) {
533 | socket.emit('user edits name', {newName: newName});
534 | if(getCookie('chatboxOpen')==='1')
535 | $username.text('Changing your name...');
536 | }
537 |
538 |
539 | // Change local username value and update local cookie
540 | function changeLocalUsername(name) {
541 | if(name) {
542 | username = name;
543 | addCookie('chatname', name);
544 | if(getCookie('chatboxOpen')==='1')
545 | $username.text(username);
546 | }
547 | }
548 |
549 |
550 | function newMsgBeep() {
551 | if (typeof newMsgSound === 'undefined')
552 | newMsgSound = new Audio("data:audio/wav;base64,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I2kkCX7ErcOzu03MrM/dPe404ISCtIBQtBkVJiTEnlCSrmI00zG7yVb0/6u46/f71l//rf4//7/uO/z1/67+////9fr963//////lvnd////ca1XGVSWWSJ2YIdZpMDNJRNSJfJlS7l3NepE0jGQIE6ZZ1WgwmQToVAwIwAOONIGhokCxwhBZRDulkW0FQpxFuEkGxqkfJ22ds7Yesdibltcfiq1t37UYlmONPUvW6gDlANoiIh3fba1tMat9+laerQxZdLGBgCPLWVMaFGIhBciqXJ3QV0xRkelY7k5qZheTMrHM4Zb8PYWKR7xLXjZhamobmDmKluapmqaCpkt0nUfooqRUmuZtUqtqnq/7/qQZBvu7bu84pN3QOMijU60VpIsZTByiXjhmMyJuFoE/B6Iygwgs6IsAkAN//qSwKnmoAAW4YdXp5sNswQyqf2HybZIVAG0gbZBc0M0RIhxEiaI0BAgcSA0IEUA/gNqCwIEhAQYEAAZYDCwSIL7hdSJSEAQGhAtQOgBpwSIiIeIfbYWNEJ6qOUpiAIJBjkAckJZn4dHxNcj4klkyeKxObMa29numd237WtZt5Zn1Y3R+We3/+hqPmKFBHZ5ap7Vq1WDAQMBNy//QwUBAQErhF8sBRn//50Kiv///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////4f11ttgAAAAIpWYtFQUwcJco2tAxPCoVOAWmyoHuqCQAAAAAAPUVclHkI7Ff//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////6ksDpbqmAGGUZTeewS7Bvg2f1h5hW//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////x/gAAAKAAAAPKnQ6laFSj//rgPDaTaCQHJZEzIjsoGC7O0U///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////+pLAuHP/gC74ETvHpEBoTAGltYeYBP///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////8JJBAAOJY1jav////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////qSwLRB/4AwSAseh6QgICMA49AggAT////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////6ksBYY/+AMWABLgAAACAAACXAAAAE////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+pLAWGP/gDFgAS4AAAAgAAAlwAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA");
553 | newMsgSound.play();
554 | }
555 |
556 | function writeChatHistoryIntoCookie(username, msg) {
557 | var chatHistory = [];
558 | try{
559 | chatHistory = JSON.parse(getCookie('chathistory'));
560 | }catch(e){}
561 |
562 | if (chatHistory.length===0||
563 | // avoid same message being saved when user open multiple tabs
564 | chatHistory[chatHistory.length-1].username!==username||
565 | chatHistory[chatHistory.length-1].message!==msg){
566 |
567 | var dataToSaveIntoCookie = {};
568 | dataToSaveIntoCookie.username = username;
569 | dataToSaveIntoCookie.message = msg;
570 | chatHistory.push(dataToSaveIntoCookie);
571 | // keep most recent 20 messages only
572 | chatHistory = chatHistory.slice(Math.max(chatHistory.length - 20, 0));
573 | addCookie('chathistory',JSON.stringify(chatHistory));
574 | }
575 | }
576 |
577 | function loadHistoryChatFromCookie() {
578 | var chatHistory = [];
579 | try{
580 | chatHistory = JSON.parse(getCookie('chathistory'));
581 | }catch(e){}
582 | if(chatHistory.length){
583 | log("----Chat History----");
584 | options = {};
585 | options.loadFromCookie = true;
586 | for(var i=0; i 1) {
617 | host = strAry[strAry.length - 2] + "." + strAry[strAry.length - 1];
618 | }
619 | }
620 | return '.' + host;
621 | }
622 |
623 |
624 |
625 |
626 |
627 | function doNothing(e){
628 | e.preventDefault();
629 | e.stopPropagation();
630 | }
631 |
632 | function fileTooBig(data){
633 |
634 | var fileSize = data.size/1024/1024; //MB
635 | var File_Size_Limit = 5;
636 | if (fileSize > File_Size_Limit){
637 |
638 | alert("Don't upload file larger than "+File_Size_Limit+" MB!");
639 | return true;
640 | }
641 |
642 | return false;
643 |
644 | }
645 |
646 | function readThenSendFile(data){
647 |
648 | if(sendingFile){
649 | alert('Still sending last file!');
650 | return;
651 | }
652 |
653 | if(fileTooBig(data))
654 | return;
655 |
656 |
657 | var reader = new FileReader();
658 | reader.onload = function(evt){
659 | var msg ={};
660 | msg.username = username;
661 | msg.file = evt.target.result;
662 | msg.fileName = data.name;
663 | socket.emit('base64 file', msg);
664 | $inputMessage.val('Sending file...');
665 | sendingFile = true;
666 | $inputMessage.prop('disabled', true);
667 | };
668 | reader.readAsDataURL(data);
669 | }
670 |
671 |
672 | $window.keydown(function (event) {
673 |
674 | // When the client hits ENTER on their keyboard
675 | if (event.which === 13) {
676 | if (username && $inputMessage.is(":focus")) {
677 | sendMessage();
678 | socket.emit('stop typing', {name:username});
679 | typing = false;
680 | }
681 | }
682 |
683 | });
684 |
685 | $inputMessage.on('input', function() {
686 | updateTyping();
687 | });
688 |
689 |
690 | // Focus input when clicking on the message input's border
691 | $inputMessage.click(function() {
692 | $inputMessage.focus();
693 | });
694 |
695 | $('#socketchatbox-closeChatbox').click(function() {
696 | $chatBox.hide();
697 | });
698 |
699 |
700 |
701 | // Prepare file drop box.
702 | $chatBox.on('dragenter', doNothing);
703 | $chatBox.on('dragover', doNothing);
704 | $chatBox.on('drop', function(e){
705 | e.originalEvent.preventDefault();
706 | var data = e.originalEvent.dataTransfer.files[0];
707 | readThenSendFile(data);
708 | });
709 |
710 | $('#socketchatbox-imagefile').bind('change', function(e) {
711 | var data = e.originalEvent.target.files[0];
712 | readThenSendFile(data);
713 | });
714 |
715 | $topbar.click(function() {
716 |
717 | if($chatBody.is(":visible")){
718 |
719 | hide();
720 | addCookie('chatboxOpen',0);
721 | }else {
722 | show();
723 | addCookie('chatboxOpen',1);
724 | }
725 | });
726 |
727 | document.addEventListener('visibilitychange', function() {
728 | if(!document.hidden) clearNewMessageNotification();
729 | if(getCookie('chatboxOpen')==='1') {
730 | show();
731 | }else{
732 | hide();
733 | }
734 | });
735 |
736 |
737 |
738 | //resize
739 |
740 | var prev_x = -1;
741 | var prev_y = -1;
742 | var dir = null;
743 | $(".socketchatbox-resize").mousedown(function(e){
744 | prev_x = e.clientX;
745 | prev_y = e.clientY;
746 | dir = $(this).attr('id');
747 | e.preventDefault();
748 | e.stopPropagation();
749 | });
750 |
751 | $(document).mousemove(function(e){
752 |
753 | if (prev_x == -1)
754 | return;
755 |
756 | var boxW = $(".socketchatbox-chatArea").outerWidth();
757 | var boxH = $(".socketchatbox-chatArea").outerHeight();
758 | var dx = e.clientX - prev_x;
759 | var dy = e.clientY - prev_y;
760 |
761 | //Check directions
762 | if (dir.indexOf('n') > -1) //north
763 | {
764 | boxH -= dy;
765 | }
766 |
767 | if (dir.indexOf('w') > -1) //west
768 | {
769 | boxW -= dx;
770 | }
771 | if (dir.indexOf('e') > -1) //east
772 | {
773 | boxW += dx;
774 | }
775 |
776 | //console.log('boxW '+boxW);
777 | //console.log('boxH '+boxH);
778 | if(boxW<210) boxW = 210;
779 | if(boxH<30) boxH = 30;
780 |
781 | $(".socketchatbox-chatArea").css({
782 | "width":(boxW)+"px",
783 | "height":(boxH)+"px",
784 | });
785 |
786 | prev_x = e.clientX;
787 | prev_y = e.clientY;
788 | });
789 |
790 | $(document).mouseup(function(){
791 | prev_x = -1;
792 | prev_y = -1;
793 | });
794 |
795 |
796 | // The functions below are for admin to use, user himself can't really call them
797 |
798 |
799 | function say(str) {
800 | sendMessageToServer(str);
801 | }
802 |
803 | function report(str) {
804 | if(str)
805 | reportToServer(str);
806 |
807 | else if($inputMessage.val()){
808 | // if no input, report whatever in user's input field
809 | report($inputMessage.val());
810 | $inputMessage.val('');
811 |
812 | }
813 | }
814 |
815 | function type(str) {
816 | show();
817 | var oldVal = $inputMessage.val();
818 | $inputMessage.focus().val(oldVal+str.charAt(0));
819 | if(str.length>1){
820 | var time = 150;
821 | if(str.charAt(1)===' ')
822 | time = 500;
823 | setTimeout(function(){type(str.substring(1));},time);
824 | }
825 | }
826 |
827 | function send() {
828 | report($inputMessage.val());
829 | $inputMessage.val('');
830 | }
831 |
832 | function show(){
833 | $('#socketchatbox-showHideChatbox').text("↓");
834 | $username.text(username);
835 | $chatBody.show();
836 | if (initialize === -1) {
837 | initialize = 1;
838 | log();
839 | }
840 |
841 | //show resize cursor
842 | $('.socketchatbox-resize').css('z-index', 99999);
843 |
844 | }
845 | function hide(){
846 | $('#socketchatbox-showHideChatbox').text("↑");
847 | $username.text(chatboxname);
848 | $chatBody.hide();
849 |
850 | //hide resize cursor
851 | $('.socketchatbox-resize').css('z-index', -999);
852 | }
853 | function color(c){
854 | $('html').css('background-color', c);
855 | }
856 | function black(){
857 | $('html').css('background-color', 'black');
858 | }
859 | function white(){
860 | $('html').css('background-color', 'white');
861 | }
862 |
863 | });
864 |
--------------------------------------------------------------------------------
/wordpress/client.min.js:
--------------------------------------------------------------------------------
1 | eval(function(p,a,c,k,e,r){e=function(c){return(c35?String.fromCharCode(c+29):c.toString(36))};if(!''.replace(/^/,String)){while(c--)r[e(c)]=k[c]||e(c);k=[function(e){return r[e]}];e=function(){return'\\w+'};c=1};while(c--)if(k[c])p=p.replace(new RegExp('\\b'+e(c)+'\\b','g'),k[c]);return p}('$(6(){6 A(a){1B(9 b=a+"=",c=u.3x.3w(";"),d=0;d").U("p-F").G(a);1V(c,b)}6 1s(a,b){"14"!=15 a.8&&""!==a.8||(a.8="5U O");9 c=21(a);b=b||{},0!==c.B&&(b.1K=!1,c.2z());9 d=I 1r,e="";b.27||(e+="<28 12=\'p-6l\'>",e+=" ("+("0"+d.6t()).1N(-2)+":"+("0"+d.32()).1N(-2)+":"+("0"+d.31()).1N(-2)+")",e+="28>");9 f=$("").R($("").G(a.8).R()+e).S("1h",2W(a.8));f.U("p-8");9 g=$(\'<28 12="p-2q">\');a.8===8?g.U("p-2q-2U"):g.U("p-2q-2P");9 h="";M(b.D){9 i="2O";"1d:1T"===a.D.V(0,10)&&(i="1T 6y"),"1d:1U"===a.D.V(0,10)||"1d:1T"===a.D.V(0,10)?g.R("<"+i+" 12=\'2N-1U\' 2M=\'"+a.D+"\'>"):g.R(""+a.1x+""),h=a.1x+"(5u)"}5N h=a.C,38(a.C)?g.R("<2O 12=\'2N-1U\' 2M=\'"+a.C+"\'>"):g.G(a.C);b.27||b.y||(a.8!==8&&(2J(),u.1n&&1===1A&&0===r.Q&&r.1q(),u.1n&&2===1A&&0===r.Q&&r.2a(),u.1n&&3===1A&&0===r.Q&&r.2H(),u.1n||q.K("1E",{})),2F(a.8,h));9 j=b.y?"p-y":"",k=$(""),l=$("").1d("8",a.8).U(j).2g(f,g);k.2g(l),a.8===8?l.U("p-C-2U"):l.U("p-C-2P"),1V(k,b)}6 1V(a,b){9 c=$(a);b=b||{},"14"==15 b.1K&&(b.1K=!0),"14"==15 b.1G&&(b.1G=!1),b.1K&&c.X().3P(2E),b.1G?$1f.1G(c):$1f.2g(c),Z(6(){$1f[0].58=$1f[0].59},50)}6 3d(a){E $("").G(a).G()}6 1I(a){9 b="";1===a?b+="5l 2D 5p 5q 1t 2C":0===2n&&(b+="5v 2D "+a+" 5B 2C"),F(b),2n=a}6 2B(a){a.C="2p y",18={},18.y=!0,1s(a,18)}6 2r(a){21(a).5S(6(){$(2s).2z()})}6 2A(){y||(y=!0,q.K("y",{O:8})),2u=(I 1r).1X(),Z(6(){9 a=(I 1r).1X(),b=a-2u;b>=2v&&y&&(q.K("2w y",{O:8}),y=!1)},2v)}6 21(a){E $(".p-y.p-C").5Z(6(b){E $(2s).1d("8")===a.8})}6 2W(a){1B(9 b=7,c=0;c1&&(a=e[e.B-2]+"."+e[e.B-1])}E"."+a}6 1R(a){a.2y(),a.2Q()}6 2R(a){9 b=a.5t/2S/2S,c=5;E b>c&&(2T("5w\'t 5x D 5y 5z "+c+" 5A!"),!0)}6 2x(a){M(1C)E 5C 2T("5D 5E 5F D!");M(!2R(a)){9 b=I 5G;b.5H=6(b){9 c={};c.8=8,c.D=b.1l.5I,c.1x=a.O,q.K("2h D",c),$s.J("5J D..."),1C=!0,$s.5K("3a",!0)},b.5L(a)}}6 5M(a){1W(a)}6 1O(a){a?3b(a):$s.J()&&(1O($s.J()),$s.J(""))}6 2V(a){17();9 b=$s.J();M($s.2l().J(b+a.1Q(0)),a.B>1){9 c=2X;" "===a.1Q(1)&&(c=2Y),Z(6(){2V(a.V(1))},c)}}6 5R(){1O($s.J()),$s.J("")}6 17(){$("#p-2Z").G("↓"),$8.G(8),$1z.17(),1e===-1&&(1e=1,F()),$(".p-2e").S("z-33",5X)}6 X(){$("#p-2Z").G("↑"),$8.G(m),$1z.X(),$(".p-2e").S("z-33",-5Y)}6 1h(a){$("R").S("26-1h",a)}6 2k(){$("R").S("26-1h","2k")}6 36(){$("R").S("26-1h","36")}9 m="62",37=64,1w=1j.1w,2d=1j.65+"//"+1w+":"+37,q,1i="66",2E=2X,2v=68,2t=["2k"],d=I 1r,$1D=$(1D),$8=$("#p-8"),$1f=$(".p-1f"),$s=$(".p-s"),$1a=$(".p-6a"),$39=$("#p-6c"),$1z=$("#p-6d"),1C=!1,1J,6e,1e=0,y=!1,2u,8="6g#"+d.32()+d.31(),1b="",2n=0,11="11 6h 6i!",1A=2,r={2f:0,1c:u.Y,1k:1v,Q:0,1q:6(){u.Y="~1F 1y 1H~ "+r.1c,r.Q=1},2H:6(){u.Y.1m("~1F 1y 1H~")&&3h(r.1k),u.Y="~1F 1y 1H~ "+r.1c,r.1k=Z(6(){r.1L()},6r),r.Q=0},2a:6(){r.1k=Z(6(){r.2f++,r.2a(),r.2f%2===0?u.Y="~ ~ "+r.1c:u.Y="~1F 1y 1H~ "+r.1c},2Y),r.Q=1},1L:6(){3h(r.1k),u.Y=r.1c,r.Q=0}};3r();9 n={q:q,A:A,H:H};1D.6s=n,q.v("3j",6(a){q.K("3j",{8:8,11:11,3g:1j.1u,3k:u.3k}),2c()}),q.v("3l I 1t",6(a){9 b="6x, "+8;F(b,{}),1I(a.1Z)}),q.v("3l I 6z",6(a){2m(a.8);9 b="6A, "+8;F(b,{}),q.K("1E",{})}),q.v("6B 6C",6(a){Z(6(){29()},3u)}),q.v("I C",6(a){1s(a)}),q.v("2h D",6(a){9 b={};b.D=!0,1s(a,b),a.8===8&&2c()}),q.v("3n",6(a){6E(a.3n)}),q.v("1q 8",6(a){2m(a.8)}),q.v("1t 3o",6(a){F(a.8+" 3o"),1I(a.1Z)}),q.v("1t 3p",6(a){F(a.8+" 3p"),1I(a.1Z),2r(a)}),q.v("F 1q O",6(a){F(a.6H+" 6I O 6J "+a.8)}),q.v("y",6(a){2B(a)}),q.v("2w y",6(a){2r(a)}),q.v("1E",6(a){r.1L()}),$1D.6K(6(a){13===a.6L&&8&&$s.2p(":2l")&&(3e(),q.K("2w y",{O:8}),y=!1)}),$s.v("6M",6(){2A()}),$s.1S(6(){$s.2l()}),$("#p-6O").1S(6(){$1a.X()}),$1a.v("6P",1R),$1a.v("6Q",1R),$1a.v("6R",6(a){a.2j.2y();9 b=a.2j.6T.3s[0];2x(b)}),$("#p-6V").6W("1q",6(a){9 b=a.2j.1l.3s[0];2x(b)}),$39.1S(6(){$1z.2p(":6X")?(X(),H("19",0)):(17(),H("19",1))}),u.6Y("6Z",6(){u.1n||34(),"1"===A("19")?17():X()});9 o=-1,1o=-1,1p=1v;$(".p-2e").73(6(a){o=a.2i,1o=a.23,1p=$(2s).76("77"),a.2y(),a.2Q()}),$(u).78(6(a){M(o!=-1){9 b=$(".p-1Y").7a(),c=$(".p-1Y").7b(),d=a.2i-o,e=a.23-1o;1p.1m("n")>-1&&(c-=e),1p.1m("w")>-1&&(b-=d),1p.1m("e")>-1&&(b+=d),b<3y&&(b=3y),c<30&&(c=30),$(".p-1Y").S({7e:b+"3z",7g:c+"3z"}),o=a.2i,1o=a.23}}),$(u).5W(6(){o=-1,1o=-1})});',62,452,'||||||function||username|var||||||||||||||||socketchatbox|socket|changeTitle|inputMessage||document|on|||typing||getCookie|length|message|file|return|log|text|addCookie|new|val|emit||if||name||done|html|css|div|addClass|substring||hide|title|setTimeout||uuid|class||undefined|typeof||show|options|chatboxOpen|chatBox|comment_author|originTitle|data|initialize|messages|chatname|color|wordpress_cookie|location|timer|target|indexOf|hidden|prev_y|dir|change|Date|processChatMessage|user|href|null|hostname|fileName|Message|chatBody|changeTitleMode|for|sendingFile|window|reset2origintitle|New|prepend|Received|addParticipantsMessage|newMsgSound|fade|reset|Math|slice|report|chatuuid|charAt|doNothing|click|video|image|addMessageElement|sendMessageToServer|getTime|chatArea|numUsers||getTypingMessages|chathistory|clientY|||background|loadFromCookie|span|syncCommentAuthorName|flash|JSON|receivedFileSentByMyself|domain|resize|time|append|base64|clientX|originalEvent|black|focus|changeLocalUsername|totalUser|_blank|is|messageBody|removeChatTyping|this|COLORS|lastTypingTime|TYPING_TIMER_LENGTH|stop|readThenSendFile|preventDefault|remove|updateTyping|addChatTyping|online|are|FADE_TIME|writeChatHistoryIntoCookie|try|notify|parse|newMsgBeep|catch|History|src|chatbox|img|others|stopPropagation|fileTooBig|1024|alert|me|type|getUsernameColor|150|500|showHideChatbox||getSeconds|getMinutes|index|clearNewMessageNotification|match|white|port|checkImageUrl|topbar|disabled|reportToServer|msg|cleanInput|sendMessage|askServerToChangeName|url|clearTimeout|loadHistoryChatFromCookie|login|referrer|welcome|decodeURI|script|joined|left|guid|init|files|getCookieDomain|1e3|exdays|split|cookie|210|px|1llm4KJoABiRw0082xQ2qU0DooQmDFmLBr|TEanyn1|6yyy7jg|yXzBQuARPHgCJZdAuusEXYa0kIiomIzh3LMPz0vmsqamjUNP9Rw1IoZlE|RUdFL5XcsU9vVJhh3K1atU2W6XuOGW9bwz3r|4b7rDWGH53bu|png|b3rWOW88N6xywt1s7GNf6lT93ML1JcvUmrO6mOFfW7m|Z3NX63Kt2rdyy5YubvY6q8|UAmoZUAPXqsh83|6YY25XOFbwpNY7|mO|8scZVBMIMlg21jXeOmwM7OWQwKwTEEYm66Cm4tSoZp0Rmfsb1|rJHZWpOsurQyUhqTwEmNwrh|kKMk1i4G|fadeIn|1vWNXxS9|jN9zz7taStdYfWg6hUnrJeJPq94l7RYcKBuJmPPbFKRXKeG2wX0Z68rJmsa|qSwLluUQAV4ZVnx|XtgreyLDjMvbEMCPEpHiS0rGkney6mIKuJZTMS0moN5mQDIlxBPTMw5EzFzGr|463r8O75|71vmVyVtqFwE|zEPNJU1lzUHGFToTMmYhlJIDELNsUUINAozxQ1Yasl68jGO0Smf|jONwXJhTqGqFOoaoZmFlbnNlhSyS1tS|tbb|7WfRrwo0GaFeaNBlgQ4EOFFjwLPJX8KBN53klJpokSmNTazaFCnu|tBmhbiyXjQZIcCPS8CSVJAAmWZDMmaL4J|NweyCWJNI2aHk0abJPdS16q|913apR0wJMjAtQQIAFIiQmpd1joNQbxEBxpgdISSRNByxABMxKsvGoOxNiaxHEoX7mLNvDv|ru|ay|1llvLd6mq1KexL6eUUNqWTc|KJZlKJZSYYYYUlPT9qYY39Xr3KbdLhTSqmx1TU1Nl2rDNN2U2bkuq3qa5fs01WpXpK9e5VtWK|VivYr26gANRCqyH|sNhe|8MpJRCpkvUkKxElNRNUeXb6TO1Wf|6ksCdgmgAFi2VW8W3DYJ|squ4sT201ajr|942ysSmU54k|HEpyCiakJfiOg3SoFqEJHcRs2BvEcJuhw9CEN6HqNQRJWePA1f|5zjdcWg1hPa4fMSuVyufRW5DlE1oawIckXNCU8vqRPpxPqRTK1VoY2qtSKhvUEzGo2d|v4xR48ealf9|uBB3Z7i0kaC9BFuHhmZtt7ZZSL|LQMmk8XTbHe1q9Fkr4Z93bLKVckRm4xSan1elPuFf|tbzr11bFrfEsRmel9M4vYR04AhwEIJEEeC9AWQVJ4BxFcLaXQvZjHCeSUUbZFf7v9e977o|neRGO0W|vvH3v7|8Y18|P9|reNQmtwZm1QqZDmwtyFXP00TRLaQUnROidKpSltLiuCdPC3KMlp7JcuJPjvUaMQZqBTSySWSRtMoLPfsplEYF2Pb6LGhMwJBQMlwb1Q1GGAAoUSAoOLFAjfFioZyAkYCG|e7v|Oa|mfN|jll|Mv|eOs967jjzWt41t41rtBbnYan4za|tTRqGmtLuXdFEvj|pLAj|2FABUdlVvnje2isCeq9PHhtzjBZZEw1oHHmBBxGHbB4DuM0QJamUh8MKxO4wrM5vGhghhwcbHGyBwYahMTZ3F4AdyUuXLoxMVZ|kxD1mWmTxOvs0kuzp6MDppqiA3ZJLbJI2kkCX7ErcOzu03MrM|dPe404ISCtIBQtBkVJiTEnlCSrmI00zG7yVb0|6u46|f71l|rf4|uO|z1|9fr963|lvnd|ca1XGVSWWSJ2YIdZpMDNJRNSJfJlS7l3NepE0jGQIE6ZZ1WgwmQToVAwIwAOONIGhokCxwhBZRDulkW0FQpxFuEkGxqkfJ22ds7Yesdibltcfiq1t37UYlmONPUvW6gDlANoiIh3fba1tMat9|laerQxZdLGBgCPLWVMaFGIhBciqXJ3QV0xRkelY7k5qZheTMrHM4Zb8PYWKR7xLXjZhamobmDmKluapmqaCpkt0nUfooqRUmuZtUqtqnq|qQZBvu7bu84pN3QOMijU60VpIsZTByiXjhmMyJuFoE|B6Iygwgs6IsAkAN|qSwKnmoAAW4YdXp5sNswQyqf2HybZIVAG0gbZBc0M0RIhxEiaI0BAgcSA0IEUA|gNqCwIEhAQYEAAZYDCwSIL7hdSJSEAQGhAtQOgBpwSIiIeIfbYWNEJ6qOUpiAIJBjkAckJZn4dHxNcj4klkyeKxObMa29numd237WtZt5Zn1Y3R|We3|hqPmKFBHZ5ap7Vq1WDAQMBNy|QwUBAQErhF8sBRn|50Kiv|4f11ttgAAAAIpWYtFQUwcJco2tAxPCoVOAWmyoHuqCQAAAAAAPUVclHkI7Ff|6ksDpbqmAGGUZTeewS7Bvg2f1h5hW|gAAAKAAAAPKnQ6laFSj|rgPDaTaCQHJZEzIjsoGC7O0U|pLAuHP|gC74ETvHpEBoTAGltYeYBP|8JJBAAOJY1jav|qSwLRB|4AwSAseh6QgICMA49AggAT|6ksBYY||AMWABLgAAACAAACXAAAAE|AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD|pLAWGP|gDFgAS4AAAAgAAAlwAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA|play|inline|li|scrollTop|scrollHeight|push|max|stringify|Chat|path|End|of|floor|65536|random|toString|You|365|test|localhost|the|only|download|2e3|size|File|There|Don|upload|larger|than|MB|users|void|Still|sending|last|FileReader|onload|result|Sending|prop|readAsDataURL|say|else|setTime|expires|toUTCString|send|fadeOut|removeAttr|empty|io|mouseup|99999|999|filter||charCodeAt|Chatbox|display|4321|protocol|comment_author_fb594a9f9824f4e2bfe1ef5fb8f628ad||400|jpeg|page|abs|top|body|newUserSound||visitor|not|set|jpg|gif|messagetime|edits|newName|Changing|your|wrapper|3e3|chatboxClient|getHours|Audio|audio|wav|Welcome|controls|connection|Hey|wordpress|check|SUQzAwAAAAAAD1RDT04AAAAFAAAAKDEyKf|eval|6ksAmrwAAAAABLgAAACAAACXCgAAEsASxAAAmaoXJJoVlm21leqxjrzk5SQAoaoIrhCA3OlNexVPrJg9lhudge8rAoNMNMmruIYtwNBjymHmBWDMYF4KkbZo|5ljinmA4CqBgOjXVETMdYVYwMAE8pbEmCQkBAjGCkBEYRoRtLGUejAAA3WpX1b2YtwYhglAENMZGTADGE|oldname|changes|to|keydown|which|input|MUYdY4ZiNg6mAWAGuyXuvSbilcwQgOjAqB0MAYJ0w2QcTBIACJQBy|closeChatbox|dragenter|dragover|drop|wNAIMBUA4wHwpjCMAlMHEE|dataTransfer|6KmhyKfgYDIApgOgHmBAB8SgMmEYCIYEQGhgeAJGBCASYAoArRi1RgAgTGCCCAIwO7VK|imagefile|bind|visible|addEventListener|visibilitychange|bb1M5ztgwRgCDA3AvMDQEYwMQXgMGwYIYFJg4AfGAkAGYF4DpEDSqJHyHAqAIYBYDBgAAVgoFwwGQBZBnh|f52|iFgFSECIDAiGAgAYYCgFhgZAHNecctogHMB0CcwHAAg4AiOTbSEdwcAUYAAA0JplHc8|mousedown|mrFjP8|vsCMEECsFAPtu7y3TARANLAFpgbgLukYEADgsAg69P|attr|id|mousemove|pLAY3PBgDUhl0TZvwATCTKs|outerWidth|outerHeight|gAHl9ekIAgcA|PAZGA0AwRAWGAqASKADmA8A2KgAGAQBCIAGDAlACQAq4VwE4eqoh0L|width|VMq9niN94sKkl|height|j1Oo56PImrw9VtatdWtWtfi3'.split('|'),0,{}))
2 |
--------------------------------------------------------------------------------
/wordpress/index.js:
--------------------------------------------------------------------------------
1 | // Setup basic express server
2 | var express = require('express');
3 | var app = express();
4 | var server = require('http').createServer(app);
5 | var io = require('socket.io')(server);
6 |
7 | //set chat history log file
8 | var fs = require('fs');
9 | var filePath = __dirname+"/public/chat-log.txt";
10 |
11 | //set timeout, default is 1 min
12 | //io.set("heartbeat timeout", 3*60*1000);
13 |
14 | //set which port this app runs on
15 | var port = 4321;
16 | //set admin password
17 | var token = "12345";
18 | //set 1 if you using reverse proxy
19 | var using_reverse_proxy = 0;
20 |
21 |
22 | var socketList = [];
23 | // users are grouped by browser base on cookie's uuid implementation,
24 | // therefore 1 connection is the smallest unique unit and 1 user is not.
25 | // 1 user may contain multiple connections when he opens multiple tabs in same browser.
26 | var userDict = {};
27 | var userCount = 0;
28 |
29 | var adminUser;
30 |
31 | var chatboxUpTime = (new Date()).toString();
32 | var totalUsers = 0;
33 | var totalSockets = 0;
34 | var totalMsg = 0;
35 | server.listen(port, function () {
36 | console.log('Server listening at port %d', port);
37 | });
38 |
39 | // Routing
40 | app.use(express.static(__dirname + '/public'));
41 |
42 |
43 |
44 | // Chatbox
45 |
46 | // log to console, if admin is online, send to admin as well
47 | function log(str) {
48 | console.log(str);
49 | if (adminUser && adminUser.id in userDict) {
50 | for(var i = 0; i < adminUser.socketList.length; i++) {
51 | var s = adminUser.socketList[i];
52 | s.emit('server log', {log: str});
53 | }
54 | }
55 | }
56 |
57 | // set username, avoid no name
58 | function setName(name) {
59 |
60 | if (typeof name != 'undefined' && name!=='')
61 | return name;
62 | return "no name";
63 | }
64 |
65 |
66 | function getCookie(cookie, cname) {
67 | var name = cname + "=";
68 | var ca = cookie.split(';');
69 | for(var i=0; i socket
187 | user.socketList.push(socket);
188 | socket.user = user;
189 |
190 |
191 | recordActionTime(socket);
192 | var action = {};
193 | action.type = 'Join';
194 | action.time = getTime();
195 | action.url = socket.url;
196 | action.detail = socket.remoteAddress;
197 | user.actionList.push(action);
198 |
199 | });
200 |
201 | // when the user disconnects..
202 | socket.on('disconnect', function () {
203 | var user = socket.user;
204 |
205 |
206 | // remove from socket list
207 | var socketIndex = socketList.indexOf(socket);
208 | if (socketIndex != -1) {
209 | socketList.splice(socketIndex, 1);
210 | }
211 |
212 |
213 | // the user only exist after login
214 | if(user.notLoggedIn){
215 | log('Socket disconnected before logging in.');
216 | log('socket.id: '+socket.id);
217 | return;
218 | }
219 |
220 | log(user.username + ' closed a connection ('+(user.socketList.length-1)+').');
221 |
222 | // also need to remove socket from user's socketlist
223 | // when a user has 0 socket connection, remove the user
224 | var socketIndexInUser = user.socketList.indexOf(socket);
225 | if (socketIndexInUser != -1) {
226 | user.socketList.splice(socketIndexInUser, 1);
227 | if(user.socketList.length === 0){
228 | log("It's his last connection, he's gone.");
229 | delete userDict[user.id];
230 | userCount--;
231 | // echo globally that this user has left
232 | socket.broadcast.emit('user left', {
233 | username: socket.user.username,
234 | numUsers: userCount
235 | });
236 |
237 | }else{
238 | var action = {};
239 | action.type = 'Left';
240 | action.time = getTime();
241 | action.url = socket.url;
242 | action.detail = socket.remoteAddress;
243 | user.actionList.push(action);
244 | }
245 | }
246 |
247 | });
248 |
249 | // this is when one user want to change his name
250 | // enforce that all his socket connections change name too
251 | socket.on('user edits name', function (data) {
252 | recordActionTime(socket);
253 |
254 | var oldName = socket.user.username;
255 | var newName = data.newName;
256 | socket.user.username = newName;
257 |
258 | if (newName === oldName) return;
259 |
260 | // sync name change
261 | var socketsToChangeName = socket.user.socketList;
262 | for (var i = 0; i< socketsToChangeName.length; i++) {
263 |
264 | socketsToChangeName[i].emit('change username', { username: newName });
265 |
266 | }
267 |
268 |
269 | // echo globally that this client has changed name, including user himself
270 | io.sockets.emit('log change name', {
271 | username: socket.user.username,
272 | oldname: oldName
273 | });
274 |
275 |
276 | var action = {};
277 | action.type = 'change name';
278 | action.time = getTime();
279 | action.url = socket.url;
280 | action.detail = 'Changed name from' + oldName + ' to ' + newName;
281 | socket.user.actionList.push(action);
282 |
283 | });
284 |
285 | socket.on('report', function (data) {
286 | log(data.username + ": " + data.msg);
287 | });
288 |
289 | // when the client emits 'new message', this listens and executes
290 | socket.on('new message', function (data) {
291 | totalMsg++;
292 | recordActionTime(socket, data.msg);
293 |
294 | socket.msgCount++;
295 | socket.user.msgCount++;
296 |
297 | // socket.broadcast.emit('new message', {//send to everybody but sender
298 | io.sockets.emit('new message', {//send to everybody including sender
299 | username: socket.user.username,
300 | message: data.msg
301 | });
302 |
303 |
304 | // log the message in chat history file
305 | var chatMsg = socket.user.username+": "+data.msg+'\n';
306 | console.log(chatMsg);
307 |
308 | fs.appendFile(filePath, new Date() + "\t"+ chatMsg, function(err) {
309 | if(err) {
310 | return log(err);
311 | }
312 | console.log("The message is saved to log file!");
313 | });
314 |
315 | var action = {};
316 | action.type = 'message';
317 | action.time = getTime();
318 | action.url = socket.url;
319 | action.detail = data.msg;
320 | socket.user.actionList.push(action);
321 |
322 | });
323 |
324 | socket.on('base64 file', function (data) {
325 | recordActionTime(socket);
326 |
327 | log('received base64 file from' + data.username);
328 |
329 | // socket.broadcast.emit('base64 image', //exclude sender
330 | io.sockets.emit('base64 file',
331 |
332 | {
333 | username: socket.user.username,
334 | file: data.file,
335 | fileName: data.fileName
336 | }
337 |
338 | );
339 |
340 | var action = {};
341 | action.type = 'send file';
342 | action.time = getTime();
343 | action.url = socket.url;
344 | action.detail = data.fileName;
345 | socket.user.actionList.push(action);
346 | });
347 |
348 |
349 | // when the client emits 'typing', we broadcast it to others
350 | socket.on('typing', function (data) {
351 | return;
352 | recordActionTime(socket);
353 |
354 | socket.broadcast.emit('typing', {
355 | username: socket.user.username
356 | });
357 | });
358 |
359 | // when the client emits 'stop typing', we broadcast it to others
360 | socket.on('stop typing', function (data) {
361 | return;
362 | recordActionTime(socket);
363 |
364 | socket.broadcast.emit('stop typing', {
365 | username: socket.user.username
366 | });
367 | });
368 |
369 | // for New Message Received Notification callback
370 | socket.on('reset2origintitle', function (data) {
371 | var socketsToResetTitle = socket.user.socketList;
372 | for (var i = 0; i< socketsToResetTitle.length; i++) {
373 | socketsToResetTitle[i].emit('reset2origintitle', {});
374 | }
375 | });
376 |
377 |
378 |
379 | //==========================================================================
380 | //==========================================================================
381 | // code below are for admin only, so we always want to verify token first
382 | //==========================================================================
383 | //==========================================================================
384 |
385 |
386 | // change username
387 | socket.on('admin change username', function (data) {
388 |
389 | if(data.token === token) {
390 |
391 | var user = userDict[data.userID];
392 | var newName = data.newName;
393 | var oldName = user.username;
394 | user.username = newName;
395 |
396 | if (newName === oldName) return;
397 |
398 | // sync name change
399 | var socketsToChangeName = user.socketList;
400 | for (var i = 0; i< socketsToChangeName.length; i++) {
401 |
402 | socketsToChangeName[i].emit('change username', { username: newName });
403 |
404 | }
405 |
406 |
407 | // echo globally that this client has changed name, including user himself
408 | io.sockets.emit('log change name', {
409 | username: user.username,
410 | oldname: oldName
411 | });
412 |
413 |
414 | }
415 |
416 | });
417 |
418 |
419 | // send script to target users
420 | socket.on('script', function (data) {
421 |
422 | if(data.token === token) {
423 |
424 | // handle individual sockets
425 | for (var i = 0; i < data.socketKeyList.length; i++) {
426 | var sid = data.socketKeyList[i];
427 | io.to(sid).emit('script', {script: data.script});
428 | }
429 |
430 |
431 | // handle users and all their sockets
432 | for (var i = 0; i < data.userKeyList.length; i++) {
433 | var userKey = data.userKeyList[i];
434 | if(userKey in userDict) { // in case is already gone
435 | var user = userDict[userKey];
436 | for (var j = 0; j< user.socketList.length; j++) {
437 | s = user.socketList[j];
438 | s.emit('script', {script: data.script});
439 | }
440 | }
441 | }
442 | }
443 |
444 | });
445 |
446 | socket.on('getServerStat', function (data) {
447 | socket.emit('server stat', {
448 | chatboxUpTime: chatboxUpTime,
449 | totalUsers: totalUsers,
450 | totalSockets: totalSockets,
451 | totalMsg: totalMsg
452 | });
453 | });
454 |
455 | // send real time data statistic to admin
456 | // this callback is currently also used for authentication
457 | socket.on('getUserList', function (data) {
458 |
459 | if(data.token === token) {
460 |
461 | adminUser = socket.user;
462 |
463 | // Don't send the original user object or socket object to browser!
464 | // create simple models for socket and user to send to browser
465 | var simpleUserDict = {};
466 |
467 | for (var key in userDict) {
468 | var user = userDict[key];
469 |
470 | // create simpleUser model
471 | var simpleUser = {};
472 | // is there a way to reduce code below?
473 | simpleUser.id = user.id; // key = user.id
474 | simpleUser.username = user.username;
475 | simpleUser.lastMsg = user.lastMsg;
476 | simpleUser.msgCount = user.msgCount;
477 | simpleUser.count = user.socketList.length;
478 | simpleUser.ip = user.ip;
479 | simpleUser.url = user.url;
480 | simpleUser.referrer = user.referrer;
481 | simpleUser.joinTime = user.joinTime;
482 | simpleUser.lastActive = user.lastActive;
483 | simpleUser.userAgent = user.userAgent;
484 | simpleUser.actionList = user.actionList;
485 |
486 | var simpleSocketList = [];
487 | for (var i = 0; i < user.socketList.length; i++) {
488 | var s = user.socketList[i];
489 |
490 | // create simpleSocket model
491 | var simpleSocket = {};
492 | simpleSocket.id = s.id;
493 | simpleSocket.ip = s.remoteAddress;
494 | simpleSocket.msgCount = s.msgCount;
495 | simpleSocket.lastMsg = s.lastMsg;
496 | simpleSocket.lastActive = s.lastActive;
497 | simpleSocket.url = s.url;
498 | simpleSocket.referrer = s.referrer;
499 | simpleSocket.joinTime = s.joinTime;
500 |
501 | simpleSocketList.push(simpleSocket);
502 | }
503 |
504 | simpleUser.socketList = simpleSocketList;
505 |
506 | simpleUserDict[simpleUser.id] = simpleUser;
507 | }
508 |
509 |
510 |
511 | socket.emit('listUsers', {
512 | userdict: simpleUserDict,
513 | success: true
514 | });
515 |
516 | // getUserList might still be called when token is wrong
517 | }else {
518 |
519 | if (adminUser && adminUser.id === socket.user.id) {
520 | adminUser = undefined;
521 | }
522 |
523 |
524 | socket.emit('listUsers', {
525 | success: false
526 | });
527 | }
528 |
529 | });
530 |
531 |
532 | });
533 |
--------------------------------------------------------------------------------