├── .reuse └── dep5 ├── CREDITS ├── LICENSE ├── LICENSES └── Apache-2.0.txt ├── README.md ├── app.js ├── manifest.yml ├── package.json ├── public ├── css │ ├── style.css │ └── stylesheet.css ├── img │ ├── chat_logo.png │ └── unnamed.png └── js │ ├── chat.js │ └── moment.min.js ├── routes.js └── views ├── chat.html └── home.html /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: cloud-sample-node-chat 3 | Upstream-Contact: Indu Sankar 4 | Source: https://github.com/SAP-samples/cloud-sample-node-chat 5 | Disclaimer: The code in this project may include calls to APIs (“API Calls”) of 6 | SAP or third-party products or services developed outside of this project 7 | (“External Products”). 8 | “APIs” means application programming interfaces, as well as their respective 9 | specifications and implementing code that allows software to communicate with 10 | other software. 11 | API Calls to External Products are not licensed under the open source license 12 | that governs this project. The use of such API Calls and related External 13 | Products are subject to applicable additional agreements with the relevant 14 | provider of the External Products. In no event shall the open source license 15 | that governs this project grant any rights in or to any External Products,or 16 | alter, expand or supersede any terms of the applicable additional agreements. 17 | If you have a valid license agreement with SAP for the use of a particular SAP 18 | External Product, then you may make use of any API Calls included in this 19 | project’s code for that SAP External Product, subject to the terms of such 20 | license agreement. If you do not have a valid license agreement for the use of 21 | a particular SAP External Product, then you may only make use of any API Calls 22 | in this project for that SAP External Product for your internal, non-productive 23 | and non-commercial test and evaluation of such API Calls. Nothing herein grants 24 | you any rights to use or access any SAP External Product, or provide any third 25 | parties the right to use of access any SAP External Product, through API Calls. 26 | 27 | Files: * 28 | Copyright: 2020 SAP SE or an SAP affiliate company and cloud-sample-node-chat contributors 29 | License: Apache-2.0 30 | © 2020 GitHub, Inc. -------------------------------------------------------------------------------- /CREDITS: -------------------------------------------------------------------------------- 1 | This program references the following third party open source or other free download components. 2 | The third party licensors of these components may provide additional license rights, 3 | terms and conditions and/or require certain notices as described below. 4 | 5 | Nodejs (https://nodejs.org/) 6 | Licensed under MIT - https://github.com/nodejs/node/blob/master/LICENSE 7 | 8 | EJS (https://ejs.co/) 9 | Licensed under the Apache License, version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 10 | 11 | Express (https://expressjs.com/) 12 | Licensed under MIT - https://github.com/expressjs/express/blob/master/LICENSE 13 | 14 | Gravatar (https://en.gravatar.com/) 15 | Licensed under MIT - https://github.com/sinisterchipmunk/gravatar/blob/master/LICENSE 16 | 17 | Socket.io (https://socket.io) 18 | Licensed under MIT - https://github.com/socketio/socket.io/blob/master/LICENSE 19 | 20 | JQuery (https://jquery.com/) 21 | Licensed under MIT - https://github.com/jquery/jquery/blob/master/LICENSE.txt 22 | 23 | Moment.min.js (https://momentjs.com/) 24 | Licensed under MIT - https://github.com/moment/moment/blob/develop/LICENSE 25 | 26 | pg (https://node-postgres.com/) 27 | Licensed under MIT -https://github.com/brianc/node-postgres/blob/master/LICENSE 28 | 29 | cfenv (https://www.npmjs.com/package/cfenv) 30 | Licensed under the Apache License, version 2.0 - http://www.apache.org/licenses/LICENSE-2.0 31 | 32 | pg-pool (https://github.com/brianc/node-pg-pool) 33 | Licensed under MIT - https://github.com/brianc/node-pg-pool/blob/master/LICENSE 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, 6 | AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, and distribution 13 | as defined by Sections 1 through 9 of this document. 14 | 15 | 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 18 | owner that is granting the License. 19 | 20 | 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all other entities 23 | that control, are controlled by, or are under common control with that entity. 24 | For the purposes of this definition, "control" means (i) the power, direct 25 | or indirect, to cause the direction or management of such entity, whether 26 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more 27 | of the outstanding shares, or (iii) beneficial ownership of such entity. 28 | 29 | 30 | 31 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions 32 | granted by this License. 33 | 34 | 35 | 36 | "Source" form shall mean the preferred form for making modifications, including 37 | but not limited to software source code, documentation source, and configuration 38 | files. 39 | 40 | 41 | 42 | "Object" form shall mean any form resulting from mechanical transformation 43 | or translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, and conversions to other media types. 45 | 46 | 47 | 48 | "Work" shall mean the work of authorship, whether in Source or Object form, 49 | made available under the License, as indicated by a copyright notice that 50 | is included in or attached to the work (an example is provided in the Appendix 51 | below). 52 | 53 | 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object form, 56 | that is based on (or derived from) the Work and for which the editorial revisions, 57 | annotations, elaborations, or other modifications represent, as a whole, an 58 | original work of authorship. For the purposes of this License, Derivative 59 | Works shall not include works that remain separable from, or merely link (or 60 | bind by name) to the interfaces of, the Work and Derivative Works thereof. 61 | 62 | 63 | 64 | "Contribution" shall mean any work of authorship, including the original version 65 | of the Work and any modifications or additions to that Work or Derivative 66 | Works thereof, that is intentionally submitted to Licensor for inclusion in 67 | the Work by the copyright owner or by an individual or Legal Entity authorized 68 | to submit on behalf of the copyright owner. For the purposes of this definition, 69 | "submitted" means any form of electronic, verbal, or written communication 70 | sent to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, and 72 | issue tracking systems that are managed by, or on behalf of, the Licensor 73 | for the purpose of discussing and improving the Work, but excluding communication 74 | that is conspicuously marked or otherwise designated in writing by the copyright 75 | owner as "Not a Contribution." 76 | 77 | 78 | 79 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 80 | of whom a Contribution has been received by Licensor and subsequently incorporated 81 | within the Work. 82 | 83 | 2. Grant of Copyright License. Subject to the terms and conditions of this 84 | License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 85 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare 86 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute 87 | the Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 90 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 91 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 92 | license to make, have made, use, offer to sell, sell, import, and otherwise 93 | transfer the Work, where such license applies only to those patent claims 94 | licensable by such Contributor that are necessarily infringed by their Contribution(s) 95 | alone or by combination of their Contribution(s) with the Work to which such 96 | Contribution(s) was submitted. If You institute patent litigation against 97 | any entity (including a cross-claim or counterclaim in a lawsuit) alleging 98 | that the Work or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses granted to You 100 | under this License for that Work shall terminate as of the date such litigation 101 | is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the Work or 104 | Derivative Works thereof in any medium, with or without modifications, and 105 | in Source or Object form, provided that You meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or Derivative Works a copy 108 | of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices stating that 111 | You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works that You distribute, 114 | all copyright, patent, trademark, and attribution notices from the Source 115 | form of the Work, excluding those notices that do not pertain to any part 116 | of the Derivative Works; and 117 | 118 | (d) If the Work includes a "NOTICE" text file as part of its distribution, 119 | then any Derivative Works that You distribute must include a readable copy 120 | of the attribution notices contained within such NOTICE file, excluding those 121 | notices that do not pertain to any part of the Derivative Works, in at least 122 | one of the following places: within a NOTICE text file distributed as part 123 | of the Derivative Works; within the Source form or documentation, if provided 124 | along with the Derivative Works; or, within a display generated by the Derivative 125 | Works, if and wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and do not modify the 127 | License. You may add Your own attribution notices within Derivative Works 128 | that You distribute, alongside or as an addendum to the NOTICE text from the 129 | Work, provided that such additional attribution notices cannot be construed 130 | as modifying the License. 131 | 132 | You may add Your own copyright statement to Your modifications and may provide 133 | additional or different license terms and conditions for use, reproduction, 134 | or distribution of Your modifications, or for any such Derivative Works as 135 | a whole, provided Your use, reproduction, and distribution of the Work otherwise 136 | complies with the conditions stated in this License. 137 | 138 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 139 | Contribution intentionally submitted for inclusion in the Work by You to the 140 | Licensor shall be under the terms and conditions of this License, without 141 | any additional terms or conditions. Notwithstanding the above, nothing herein 142 | shall supersede or modify the terms of any separate license agreement you 143 | may have executed with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade names, 146 | trademarks, service marks, or product names of the Licensor, except as required 147 | for reasonable and customary use in describing the origin of the Work and 148 | reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to 151 | in writing, Licensor provides the Work (and each Contributor provides its 152 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 153 | KIND, either express or implied, including, without limitation, any warranties 154 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 155 | A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness 156 | of using or redistributing the Work and assume any risks associated with Your 157 | exercise of permissions under this License. 158 | 159 | 8. Limitation of Liability. In no event and under no legal theory, whether 160 | in tort (including negligence), contract, or otherwise, unless required by 161 | applicable law (such as deliberate and grossly negligent acts) or agreed to 162 | in writing, shall any Contributor be liable to You for damages, including 163 | any direct, indirect, special, incidental, or consequential damages of any 164 | character arising as a result of this License or out of the use or inability 165 | to use the Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all other commercial 167 | damages or losses), even if such Contributor has been advised of the possibility 168 | of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing the Work 171 | or Derivative Works thereof, You may choose to offer, and charge a fee for, 172 | acceptance of support, warranty, indemnity, or other liability obligations 173 | and/or rights consistent with this License. However, in accepting such obligations, 174 | You may act only on Your own behalf and on Your sole responsibility, not on 175 | behalf of any other Contributor, and only if You agree to indemnify, defend, 176 | and hold each Contributor harmless for any liability incurred by, or claims 177 | asserted against, such Contributor by reason of your accepting any such warranty 178 | or additional liability. END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following boilerplate 183 | notice, with the fields enclosed by brackets "[]" replaced with your own identifying 184 | information. (Don't include the brackets!) The text should be enclosed in 185 | the appropriate comment syntax for the file format. We also recommend that 186 | a file or class name and description of purpose be included on the same "printed 187 | page" as the copyright notice for easier identification within third-party 188 | archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | 194 | you may not use this file except in compliance with the License. 195 | 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | 204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 205 | 206 | See the License for the specific language governing permissions and 207 | 208 | limitations under the License. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # NodeJS Chat Application 2 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-sample-node-chat)](https://api.reuse.software/info/github.com/SAP-samples/cloud-sample-node-chat) 3 | 4 | ## Description: 5 | This is a simple chat application that is built on NodeJS. It uses socket.io library which enables real-time, event-based communication. It shows how socket.io rooms can be used to create different chat rooms. 6 | This application can be run locally as well as on Cloud Foundry landscape. 7 | 8 | Features: 9 | * Login with name, email Id. 10 | * Chat with a user who has logged into the same chat room. 11 | * Only 2 people are allowed per room. If more people enter, they will be directed to a different room. 12 | 13 | ## Requirements 14 | - [Node js](https://nodejs.org/en/download/) 15 | - [Cloud Foundry Command Line Interface (CLI)](https://github.com/cloudfoundry/cli#downloads) 16 | - Cloud Foundry trial or enterprise account, [sign up for a Cloud Foundry environment trial account on SAP Business technology Platform(https://developers.sap.com/tutorials/hcp-create-trial-account.html) 17 | 18 | ## Download and Installation 19 | Running the application 20 | 21 | 1. [Clone](https://help.github.com/articles/cloning-a-repository/) this repository 22 | 2. Login to Cloud Foundry by typing the below commands on command prompt 23 | ``` 24 | cf api 25 | cf login -u -p 26 | ``` 27 | `api` - [URL of the Cloud Foundry landscape](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/350356d1dc314d3199dca15bd2ab9b0e.html) that you are trying to connect to. 28 | 29 | `username` - Email address of your sap.com account. 30 | `password` - Your sap.com password 31 | 32 | Select the org and space when prompted to. For more information on the same refer [link](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/75125ef1e60e490e91eb58fe48c0f9e7.html#loio4ef907afb1254e8286882a2bdef0edf4). 33 | 34 | 3. Push the application 35 | 36 | ```cf push --random-route``` 37 | 5. Once the application has been pushed successfully, open the URl in a web browser. 38 | You can test by opening the same chat room in different browser tabs. 39 | 40 | ## Known Issues 41 | No known issues. 42 | 43 | ## How to Obtain Support 44 | 45 | In case you find a bug, or you need additional support, please open an issue here in GitHub. 46 | 47 | ## License 48 | 49 | Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt)file. 50 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | 3 | app = express(); 4 | 5 | var port = process.env.PORT || 8089; 6 | 7 | var io = require('socket.io').listen(app.listen(port)); 8 | 9 | app.set('view engine', 'html'); 10 | app.engine('html', require('ejs').renderFile); 11 | app.set('views', __dirname + '/views'); 12 | app.use(express.static(__dirname + '/public')); 13 | 14 | require('./routes')(app, io); 15 | 16 | console.log('Your application is running on http://localhost:' + port); -------------------------------------------------------------------------------- /manifest.yml: -------------------------------------------------------------------------------- 1 | --- 2 | applications: 3 | - name: chat 4 | path: . 5 | memory: 128M 6 | buildpack: nodejs_buildpack 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "NodeChatApp", 3 | "version": "0.0.1", 4 | "scripts": { 5 | "start": "node app.js" 6 | }, 7 | "dependencies": { 8 | "ejs": "2.6.1", 9 | "express": "4.16.4", 10 | "gravatar": "1.8.0", 11 | "socket.io": "2.2.0" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /public/css/style.css: -------------------------------------------------------------------------------- 1 | body{ 2 | 3 | background: -webkit-gradient(linear, left top, left bottom, from(#648880), to(#293f50)) fixed; 4 | } 5 | 6 | .box{ 7 | min-height: 500px; 8 | width: 500px; 9 | background-color: #fff; 10 | position: fixed; 11 | top: 50%; 12 | left: 50%; 13 | transform: translate(-50%, -50%); 14 | border-radius: 15px; 15 | } 16 | 17 | .logo{ 18 | text-align: center; 19 | margin-top: 75px; 20 | } 21 | 22 | .logo img{ 23 | padding: 10px; 24 | width: 150px; 25 | } 26 | 27 | .chat-btn-holder{ 28 | text-align: center; 29 | padding: 10px; 30 | } 31 | 32 | .chat-btn { 33 | color: #fff !important; 34 | text-transform: uppercase; 35 | background: #1E8DD6; 36 | padding: 20px; 37 | border-radius: 5px; 38 | display: inline-block; 39 | border: none; 40 | cursor: pointer; 41 | } 42 | 43 | .chat-btn:hover { 44 | background: #434343; 45 | letter-spacing: 1px; 46 | -webkit-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57); 47 | -moz-box-shadow: 0px 5px 40px -10px rgba(0,0,0,0.57); 48 | box-shadow: 5px 40px -10px rgba(0,0,0,0.57); 49 | transition: all 0.4s ease 0s; 50 | } -------------------------------------------------------------------------------- /public/css/stylesheet.css: -------------------------------------------------------------------------------- 1 | /*------------- Reset browser differences ------------- */ 2 | 3 | 4 | 5 | * { 6 | padding: 0; 7 | margin: 0; 8 | } 9 | 10 | 11 | /* ------------ Body styles ------------ */ 12 | 13 | 14 | body { 15 | /*background-color: #f0f1f3;*/ 16 | /*color:#667073;*/ 17 | font: 16px/1.3 'Arial',sans-serif; 18 | background: -webkit-gradient(linear, left top, left bottom, from(#648880), to(#293f50)) fixed; 19 | } 20 | 21 | 22 | 23 | 24 | /* ------------ Header styles ------------ */ 25 | 26 | 27 | 28 | /* ------ Home ------ */ 29 | 30 | 31 | .homebanner { 32 | height: 120px; 33 | background-color: #3c4446; 34 | } 35 | 36 | .homebannertext { 37 | text-align:center; 38 | padding-top: 35px; 39 | font-family: 'Open Sans Condensed',serif; 40 | font-weight: bold; 41 | color:white; 42 | } 43 | .homebannertext span { 44 | color: #d6441b; 45 | } 46 | 47 | #logo { 48 | text-decoration: none; 49 | color:white; 50 | } 51 | #logo:visited { 52 | color: white; 53 | } 54 | 55 | 56 | 57 | /* ------ Invite ------ */ 58 | 59 | 60 | .banner { 61 | height: 70px; 62 | background-color: #3c4446; 63 | margin-bottom: 200px; 64 | } 65 | 66 | .bannertext { 67 | text-align:center; 68 | padding-top: 15px; 69 | font-family: 'Open Sans Condensed',serif; 70 | font-weight: bold; 71 | color:white; 72 | font-size:25px; 73 | } 74 | .bannertext span { 75 | color: #d6441b; 76 | } 77 | 78 | 79 | 80 | 81 | /* ------------ Section styles ------------ */ 82 | 83 | 84 | 85 | /* ------ Home ------ */ 86 | 87 | 88 | .homesection { 89 | text-align:center; 90 | margin-top:140px; 91 | } 92 | 93 | .node { 94 | padding-top: 20px; 95 | text-align:center; 96 | margin-bottom:120px; 97 | } 98 | .node #chat { 99 | font-family: 'Open Sans Condensed'; 100 | font-weight: normal; 101 | color: #707368; 102 | } 103 | 104 | #nodelogo { 105 | width: 410px; 106 | height: 110px; 107 | margin-bottom: 35px; 108 | } 109 | 110 | #create { 111 | width: 300px; 112 | height: 100px; 113 | background-color: #70b1d1; 114 | text-align:center; 115 | display: block; 116 | margin:0px auto; 117 | border-radius: 3px; 118 | box-shadow: 0px 5px 5px #e4e8e7; 119 | text-decoration: none; 120 | margin-bottom: 70px; 121 | color: #eeffff; 122 | } 123 | #create:visited { 124 | color: #eeffff; 125 | text-decoration: none; 126 | } 127 | #createbutton { 128 | padding-top: 28px; 129 | } 130 | #little { 131 | font-weight: normal; 132 | font-size: 13px; 133 | } 134 | #big { 135 | font-weight: bold; 136 | font-size: 23px; 137 | } 138 | .tutorial{ 139 | margin-bottom:90px; 140 | } 141 | #tutorial { 142 | color: #88bfd4; 143 | } 144 | #tutorial:visited { 145 | color: #88bfd4; 146 | } 147 | 148 | 149 | 150 | /* ------ Connected ------*/ 151 | 152 | .connected { 153 | text-align:center; 154 | display: block; 155 | /* margin:0px auto; 156 | width: 700px; 157 | height: 400px; 158 | background-color: #ffffff; 159 | border-radius: 3px; 160 | box-shadow:0px 3px 3px #e3e4e6;*/ 161 | 162 | width: 700px; 163 | height: 400px; 164 | background-color: #fff; 165 | position: fixed; 166 | top: 50%; 167 | left: 50%; 168 | transform: translate(-50%, -50%); 169 | border-radius: 15px; 170 | 171 | color: #637277; 172 | } 173 | .connected h2 { 174 | padding-top: 20px; 175 | } 176 | .infoConnected { 177 | text-align: left; 178 | float: right; 179 | margin: 65px 165px 0 0; 180 | } 181 | #creatorImage { 182 | float: left; 183 | margin: 90px 0 0 140px; 184 | height: 120px; 185 | border-radius: 50%; 186 | } 187 | 188 | #name1 { 189 | float:left; 190 | padding-left:10px; 191 | width: 200px; 192 | height: 30px; 193 | border-radius: 3px; 194 | box-shadow:0px 3px 3px #e3e4e6; 195 | background-color: #f0f1f3; 196 | border: 1px solid #d1d1d1; 197 | border-radius: 3px; 198 | box-shadow:0px 3px 3px #e3e4e6; 199 | margin-bottom:20px; 200 | overflow: hidden; 201 | } 202 | 203 | #email1 { 204 | float:left; 205 | padding-left: 10px; 206 | width: 200px; 207 | height: 30px; 208 | border-radius: 3px; 209 | box-shadow:0px 3px 3px #e3e4e6; 210 | background-color: #f0f1f3; 211 | border: 1px solid #d1d1d1; 212 | border-radius: 3px; 213 | box-shadow:0px 3px 3px #e3e4e6; 214 | margin-bottom:40px; 215 | } 216 | 217 | #chat1Enter { 218 | float:left; 219 | width: 110px; 220 | height: 40px; 221 | text-align:center; 222 | background-color: #31AF90; 223 | border: none; 224 | box-shadow: 3px 3px 3px #efedee; 225 | border-radius: 2px; 226 | color:white; 227 | font-weight: bold; 228 | font-size: 15px; 229 | cursor: pointer; 230 | } 231 | 232 | #email1:focus, #name1:focus{ 233 | border-color: #31AF90; 234 | } 235 | /*input:focus{ 236 | border-color: #31AF90; 237 | }*/ 238 | 239 | 240 | 241 | /* ------ Person Inside ------*/ 242 | 243 | .personinside { 244 | /*text-align:center; 245 | display: block; 246 | margin:0px auto; 247 | width: 700px; 248 | height: 400px; 249 | background-color: #ffffff; 250 | border-radius: 3px; 251 | box-shadow:0px 3px 3px #e3e4e6; 252 | color: #637277;*/ 253 | 254 | background-color: #fff; 255 | position: fixed; 256 | top: 50%; 257 | left: 50%; 258 | transform: translate(-50%, -50%); 259 | border-radius: 15px; 260 | width: 700px; 261 | height: 400px; 262 | } 263 | .personinside h2 { 264 | padding-top: 20px; 265 | width: 210px; 266 | overflow: hidden; 267 | } 268 | 269 | .infoInside { 270 | text-align: left; 271 | float: right; 272 | margin: 65px 165px 0 0; 273 | } 274 | 275 | #ownerImage { 276 | float: left; 277 | margin: 90px 0 0 140px; 278 | width: 100px; 279 | height: 100px; 280 | border-radius: 50%; 281 | } 282 | 283 | #name2 { 284 | float:left; 285 | padding-left:10px; 286 | width: 200px; 287 | height: 30px; 288 | border-radius: 3px; 289 | box-shadow:0px 3px 3px #e3e4e6; 290 | background-color: #f0f1f3; 291 | border: 1px solid #d1d1d1; 292 | border-radius: 3px; 293 | box-shadow:0px 3px 3px #e3e4e6; 294 | margin-bottom:20px; 295 | } 296 | 297 | #email2 { 298 | float:left; 299 | padding-left: 10px; 300 | width: 200px; 301 | height: 30px; 302 | border-radius: 3px; 303 | box-shadow:0px 3px 3px #e3e4e6; 304 | background-color: #f0f1f3; 305 | border: 1px solid #d1d1d1; 306 | border-radius: 3px; 307 | box-shadow:0px 3px 3px #e3e4e6; 308 | margin-bottom:40px; 309 | } 310 | 311 | #chat2Enter { 312 | float:left; 313 | width: 110px; 314 | height: 40px; 315 | text-align:center; 316 | background-color: #31AF90; 317 | border: none; 318 | box-shadow: 3px 3px 3px #efedee; 319 | border-radius: 2px; 320 | color:white; 321 | font-weight: bold; 322 | font-size: 15px; 323 | cursor: pointer; 324 | } 325 | 326 | 327 | /* ------ Invite ------ */ 328 | 329 | 330 | .invite-textfield { 331 | /*text-align:center; 332 | display: block; 333 | margin:0px auto; 334 | width: 750px; 335 | height: 300px; 336 | background-color: #ffffff; 337 | border-radius: 3px; 338 | box-shadow:0px 3px 3px #e3e4e6; 339 | color: #637277;*/ 340 | 341 | text-align: center; 342 | background-color: #fff; 343 | position: fixed; 344 | top: 50%; 345 | left: 50%; 346 | transform: translate(-50%, -50%); 347 | border-radius: 15px; 348 | width: 750px; 349 | height: 300px; 350 | } 351 | .invite-textfield h2 { 352 | padding-top: 80px; 353 | } 354 | .invite-textfield h5 { 355 | font-weight: normal; 356 | margin: 10px 0 35px; 357 | } 358 | .invite-textfield .link { 359 | width: 600px; 360 | height: 35px; 361 | text-align:center; 362 | display: block; 363 | margin:0px auto; 364 | padding-top: 15px; 365 | margin-bottom:120px; 366 | border-radius: 4px; 367 | background-color: #f0f1f3; 368 | } 369 | .invite-textfield #link{ 370 | text-decoration: none; 371 | color: #637277; 372 | } 373 | 374 | 375 | /* ------ Left ------*/ 376 | 377 | .left { 378 | text-align:center; 379 | display: block; 380 | margin:0px auto; 381 | width: 750px; 382 | height: 300px; 383 | background-color: #ffffff; 384 | border-radius: 3px; 385 | box-shadow:0px 3px 3px #e3e4e6; 386 | color: #637277; 387 | margin-bottom: 400px; 388 | } 389 | .left h2 { 390 | padding-top: 50px; 391 | padding-bottom: 6px; 392 | } 393 | .left h5 { 394 | font-weight: normal; 395 | margin-bottom: 100px; 396 | } 397 | 398 | #leftImage { 399 | float: left; 400 | margin: 90px 0 0 140px; 401 | width: 100px; 402 | height: 100px; 403 | border-radius: 50%; 404 | } 405 | .nickname-left { 406 | width: 40px; 407 | overflow: hidden; 408 | } 409 | 410 | /* ------ Too many People ------*/ 411 | 412 | 413 | .toomanypeople { 414 | text-align:center; 415 | display: block; 416 | margin:0px auto; 417 | width: 750px; 418 | height: 300px; 419 | background-color: #ffffff; 420 | border-radius: 3px; 421 | box-shadow:0px 3px 3px #e3e4e6; 422 | color: #637277; 423 | } 424 | .toomanypeople h2 { 425 | padding-top: 100px; 426 | padding-bottom: 8px; 427 | } 428 | .toomanypeople h5 { 429 | font-weight: normal; 430 | margin-bottom: 100px; 431 | } 432 | 433 | #room:visited { 434 | color: #88bfd4; 435 | } 436 | 437 | 438 | 439 | /* ------ No Messages ------ */ 440 | 441 | 442 | .nomessages { 443 | /*text-align:center; 444 | display: block; 445 | margin:0px auto; 446 | width: 750px; 447 | height: 300px; 448 | background-color: #ffffff; 449 | border-radius: 3px; 450 | box-shadow:0px 3px 3px #e3e4e6; 451 | color: #637277; 452 | margin-bottom: 400px;*/ 453 | 454 | background-color: #fff; 455 | position: fixed; 456 | top: 50%; 457 | left: 50%; 458 | transform: translate(-50%, -50%); 459 | border-radius: 15px; 460 | width: 710px; 461 | text-align: center; 462 | padding: 10px; 463 | } 464 | .nomessages h2 { 465 | padding-top: 50px; 466 | overflow: hidden; 467 | padding-bottom: 6px; 468 | } 469 | .nomessages h5 { 470 | font-weight: normal; 471 | margin-bottom: 100px; 472 | } 473 | 474 | .nomessages img{ 475 | width: 100px; 476 | } 477 | 478 | #img { 479 | float: left; 480 | margin: 90px 0 0 140px; 481 | width: 100px; 482 | height: 100px; 483 | border-radius: 50%; 484 | } 485 | 486 | .nickname-chat { 487 | overflow: hidden; 488 | } 489 | 490 | 491 | /* ------ Chat Screen ------ */ 492 | 493 | 494 | 495 | .image { 496 | clear:both; 497 | float:left; 498 | height: 160px; 499 | width: 80px; 500 | text-align:center; 501 | overflow: hidden; 502 | } 503 | 504 | .image img { 505 | float: left; 506 | width: 80px; 507 | height: 80px; 508 | border-radius: 50%; 509 | } 510 | 511 | .body { 512 | text-align:center; 513 | width: 40%; 514 | display: block; 515 | margin:0px auto 650px; 516 | background-color: #ffffff; 517 | color: #637277; 518 | } 519 | 520 | .chats { 521 | list-style-type: none; 522 | margin-top:10px; 523 | color: #fff; 524 | } 525 | 526 | .me { 527 | clear:both; 528 | float:left; 529 | margin-bottom:10px; 530 | } 531 | 532 | .you { 533 | clear:both; 534 | float:right; 535 | margin-bottom:10px; 536 | } 537 | 538 | .you .image{ 539 | float:right; 540 | } 541 | 542 | .chats .me p { 543 | text-align:left; 544 | float: left; 545 | display: inline-block; 546 | margin-left: 20px; 547 | margin-bottom:30px; 548 | padding: 25px 34px; 549 | min-width: 160px; 550 | min-height: 10px; 551 | max-width: 510px; 552 | background-color: #FFF; 553 | border-radius: 3px; 554 | color: #637277; 555 | line-height: 1.4; 556 | word-wrap: break-word; 557 | } 558 | 559 | .chats .you p { 560 | text-align:left; 561 | float: left; 562 | display: inline-block; 563 | margin-right:20px; 564 | margin-bottom:30px; 565 | padding: 25px 34px; 566 | min-width: 160px; 567 | min-height: 10px; 568 | max-width: 510px; 569 | background-color: #FFF; 570 | border-radius: 3px; 571 | box-shadow: 0px 3px 3px #E3E4E6; 572 | color: #637277; 573 | line-height: 1.4; 574 | word-wrap: break-word; 575 | } 576 | 577 | .image b { 578 | overflow: hidden; 579 | display: block; 580 | clear: both; 581 | padding-top: 7px; 582 | } 583 | 584 | .image i{ 585 | font-size: 12px; 586 | line-height: 1.2; 587 | display: block; 588 | opacity: 0.8; 589 | padding-top: 4px; 590 | } 591 | 592 | 593 | /* ------------ Footer styles ------------ */ 594 | 595 | footer { 596 | position:fixed; 597 | bottom:0; 598 | left:0; 599 | width:100%; 600 | padding-top: 30px; 601 | height: 130px; 602 | } 603 | #chatform { 604 | width:700px; 605 | margin: 0 auto; 606 | background-color: white; 607 | } 608 | 609 | #message { 610 | width: 550px; 611 | height: 80px; 612 | display: block; 613 | float:left; 614 | margin: 0 auto; 615 | background-color: #f9f9f9; 616 | border: 1px solid #cccccc; 617 | border-radius: 15px; 618 | padding-left: 20px; 619 | padding-top: 20px; 620 | resize: none; 621 | font:inherit; 622 | color:inherit; 623 | } 624 | 625 | #submit { 626 | width: 110px; 627 | height: 60px; 628 | float:right; 629 | text-align:center; 630 | background-color: #31AF90; 631 | display: block; 632 | margin: 0 auto; 633 | border: none; 634 | border-radius: 2px; 635 | color:white; 636 | font-weight: bold; 637 | font-size: 17px; 638 | outline: none; 639 | cursor:pointer; 640 | } 641 | 642 | 643 | 644 | 645 | /* ---------------------- HIDE -------------------------- */ 646 | 647 | 648 | .connected { 649 | display: none; 650 | } 651 | 652 | .personinside { 653 | display: none; 654 | } 655 | 656 | .invite-textfield { 657 | display:none; 658 | } 659 | 660 | .left { 661 | display:none; 662 | } 663 | 664 | .roomFull { 665 | display:none; 666 | } 667 | 668 | .nomessages{ 669 | display:none; 670 | } 671 | 672 | footer { 673 | display:none; 674 | } 675 | 676 | .chatscreen{ 677 | display:none; 678 | width:700px; 679 | margin: 0 auto 200px; 680 | overflow:hidden; 681 | } 682 | -------------------------------------------------------------------------------- /public/img/chat_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cloud-sample-node-chat/8268dfefa2a35218cc27badd51e0cc070e9c8b2d/public/img/chat_logo.png -------------------------------------------------------------------------------- /public/img/unnamed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-samples/cloud-sample-node-chat/8268dfefa2a35218cc27badd51e0cc070e9c8b2d/public/img/unnamed.png -------------------------------------------------------------------------------- /public/js/chat.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | 3 | var socket = io(); 4 | 5 | var name = "", 6 | email = "", 7 | img = "", 8 | friend = ""; 9 | 10 | // getting room id from url 11 | var id = Number(window.location.pathname.match(/\/chat\/(\d+)$/)[1]); 12 | 13 | 14 | // cache some jQuery objects 15 | var section = $(".section"), 16 | footer = $("footer"), 17 | onConnect = $(".connected"), 18 | inviteSomebody = $(".invite-textfield"), 19 | personInside = $(".personinside"), 20 | chatScreen = $(".chatscreen"), 21 | left = $(".left"), 22 | noMessages = $(".nomessages"), 23 | roomFull = $(".roomFull"); 24 | chatNickname = $(".nickname-chat"), 25 | leftNickname = $(".nickname-left"), 26 | loginForm = $(".loginForm"), 27 | name1 = $("#name1"), 28 | email1 = $("#email1"), 29 | name2 = $("#name2"), 30 | email2 = $("#email2"), 31 | chatForm = $("#chatform"), 32 | textarea = $("#message"), 33 | messageTimeSent = $(".timesent"), 34 | chats = $(".chats"); 35 | 36 | // these variables hold images 37 | var ownerImage = $("#ownerImage"), 38 | leftImage = $("#leftImage"), 39 | noMessagesImage = $("#noMessagesImage"); 40 | 41 | 42 | // on connection to server get the id of person's room 43 | socket.on('connect', function(){ 44 | 45 | socket.emit('load', id); 46 | }); 47 | 48 | // save the gravatar url 49 | socket.on('img', function(data){ 50 | img = data; 51 | }); 52 | 53 | // receive the names and avatars of all people in the chat room 54 | socket.on('peopleinchat', function(data){ 55 | 56 | if(data.number === 0){ 57 | 58 | showMessage("connected"); 59 | 60 | loginForm.on('submit', function(e){ 61 | 62 | e.preventDefault(); 63 | 64 | name = $.trim(name1.val()); 65 | 66 | if(name.length < 1){ 67 | alert("Please enter a nick name longer than 1 character!"); 68 | return; 69 | } 70 | 71 | email = email1.val(); 72 | 73 | if(!isValid(email)) { 74 | alert("Please enter a valid email!"); 75 | } 76 | else { 77 | 78 | showMessage("inviteSomebody"); 79 | 80 | // call the server-side function 'login' and send user's parameters 81 | socket.emit('login', {user: name, avatar: email, id: id}); 82 | } 83 | 84 | }); 85 | } 86 | 87 | else { 88 | 89 | showMessage("personinchat",data); 90 | 91 | loginForm.on('submit', function(e){ 92 | 93 | e.preventDefault(); 94 | 95 | name = $.trim(name2.val()); 96 | 97 | if(name.length < 1){ 98 | alert("Please enter a nick name longer than 1 character!"); 99 | return; 100 | } 101 | 102 | if(name == data.user){ 103 | alert("There already is a \"" + name + "\" in this room!"); 104 | return; 105 | } 106 | email = email2.val(); 107 | 108 | if(!isValid(email)){ 109 | alert("Wrong e-mail format!"); 110 | } 111 | else { 112 | socket.emit('login', {user: name, avatar: email, id: id}); 113 | } 114 | 115 | }); 116 | } 117 | 118 | }); 119 | 120 | // Other useful 121 | 122 | socket.on('startChat', function(data){ 123 | console.log(data); 124 | if(data.boolean && data.id == id) { 125 | 126 | chats.empty(); 127 | 128 | if(name === data.users[0]) { 129 | 130 | showMessage("user1StartChat",data); 131 | } 132 | else { 133 | 134 | showMessage("user2StartChat",data); 135 | } 136 | 137 | chatNickname.text(friend); 138 | } 139 | }); 140 | 141 | socket.on('leave',function(data){ 142 | 143 | if(data.boolean && id==data.room){ 144 | 145 | showMessage("somebodyLeft", data); 146 | //chats.empty(); 147 | } 148 | 149 | }); 150 | 151 | socket.on('tooMany', function(data){ 152 | 153 | if(data.boolean && name.length === 0) { 154 | 155 | showMessage('roomFull'); 156 | } 157 | }); 158 | 159 | socket.on('receive', function(data){ 160 | 161 | showMessage('chatStarted'); 162 | 163 | if(data.msg.trim().length) { 164 | createChatMessage(data.msg, data.user, data.img, moment()); 165 | scrollToBottom(); 166 | } 167 | }); 168 | 169 | textarea.keypress(function(e){ 170 | 171 | // Submit the form on enter 172 | 173 | if(e.which == 13) { 174 | e.preventDefault(); 175 | chatForm.trigger('submit'); 176 | } 177 | 178 | }); 179 | 180 | chatForm.on('submit', function(e){ 181 | 182 | e.preventDefault(); 183 | 184 | // Create a new chat message and display it directly 185 | 186 | showMessage("chatStarted"); 187 | 188 | if(textarea.val().trim().length) { 189 | createChatMessage(textarea.val(), name, img, moment()); 190 | scrollToBottom(); 191 | 192 | // Send the message to the other person in the chat 193 | socket.emit('msg', {msg: textarea.val(), user: name, img: img}); 194 | 195 | } 196 | // Empty the textarea 197 | textarea.val(""); 198 | }); 199 | 200 | // Update the relative time stamps on the chat messages every minute 201 | 202 | setInterval(function(){ 203 | 204 | messageTimeSent.each(function(){ 205 | var each = moment($(this).data('time')); 206 | $(this).text(each.fromNow()); 207 | }); 208 | 209 | },60000); 210 | 211 | // Function that creates a new chat message 212 | 213 | function createChatMessage(msg,user,imgg,now){ 214 | 215 | var who = ''; 216 | 217 | if(user===name) { 218 | who = 'me'; 219 | } 220 | else { 221 | who = 'you'; 222 | } 223 | 224 | var li = $( 225 | '
  • '+ 226 | '
    ' + 227 | '' + 228 | '' + 229 | ' ' + 230 | '
    ' + 231 | '

    ' + 232 | '
  • '); 233 | 234 | // use the 'text' method to escape malicious user input 235 | li.find('p').text(msg); 236 | li.find('b').text(user); 237 | 238 | chats.append(li); 239 | 240 | messageTimeSent = $(".timesent"); 241 | messageTimeSent.last().text(now.fromNow()); 242 | } 243 | 244 | function scrollToBottom(){ 245 | $("html, body").animate({ scrollTop: $(document).height()-$(window).height() },1000); 246 | } 247 | 248 | function isValid(thatemail) { 249 | 250 | var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/; 251 | return re.test(thatemail); 252 | } 253 | 254 | function showMessage(status,data){ 255 | 256 | if(status === "connected"){ 257 | 258 | section.children().css('display', 'none'); 259 | onConnect.fadeIn(1200); 260 | } 261 | 262 | else if(status === "inviteSomebody"){ 263 | 264 | // Set the invite link content 265 | $("#link").text(window.location.href); 266 | 267 | onConnect.fadeOut(1200, function(){ 268 | inviteSomebody.fadeIn(1200); 269 | }); 270 | } 271 | 272 | else if(status === "personinchat"){ 273 | 274 | onConnect.css("display", "none"); 275 | personInside.fadeIn(1200); 276 | 277 | chatNickname.text(data.user); 278 | } 279 | 280 | else if(status === "user1StartChat") { 281 | 282 | left.fadeOut(1200, function() { 283 | inviteSomebody.fadeOut(1200,function(){ 284 | noMessages.fadeIn(1200); 285 | footer.fadeIn(1200); 286 | }); 287 | }); 288 | 289 | friend = data.users[1]; 290 | } 291 | 292 | else if(status === "user2StartChat") { 293 | 294 | personInside.fadeOut(1200,function(){ 295 | noMessages.fadeIn(1200); 296 | footer.fadeIn(1200); 297 | }); 298 | 299 | friend = data.users[0]; 300 | } 301 | 302 | else if(status === "chatStarted"){ 303 | 304 | section.children().css('display','none'); 305 | chatScreen.css('display','block'); 306 | } 307 | 308 | else if(status === "somebodyLeft"){ 309 | 310 | leftImage.attr("src",data.avatar); 311 | leftNickname.text(data.user); 312 | 313 | section.children().css('display','none'); 314 | footer.css('display', 'none'); 315 | left.fadeIn(1200); 316 | } 317 | 318 | else if(status === "roomFull") { 319 | 320 | section.children().css('display', 'none'); 321 | roomFull.fadeIn(1200); 322 | } 323 | } 324 | 325 | }); 326 | -------------------------------------------------------------------------------- /public/js/moment.min.js: -------------------------------------------------------------------------------- 1 | //! moment.js 2 | //! version : 2.5.1 3 | //! authors : Tim Wood, Iskren Chernev, Moment.js contributors 4 | //! license : MIT 5 | //! momentjs.com 6 | (function(a){function b(){return{empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function c(a,b){return function(c){return k(a.call(this,c),b)}}function d(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function e(){}function f(a){w(a),h(this,a)}function g(a){var b=q(a),c=b.year||0,d=b.month||0,e=b.week||0,f=b.day||0,g=b.hour||0,h=b.minute||0,i=b.second||0,j=b.millisecond||0;this._milliseconds=+j+1e3*i+6e4*h+36e5*g,this._days=+f+7*e,this._months=+d+12*c,this._data={},this._bubble()}function h(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function i(a){var b,c={};for(b in a)a.hasOwnProperty(b)&&qb.hasOwnProperty(b)&&(c[b]=a[b]);return c}function j(a){return 0>a?Math.ceil(a):Math.floor(a)}function k(a,b,c){for(var d=""+Math.abs(a),e=a>=0;d.lengthd;d++)(c&&a[d]!==b[d]||!c&&s(a[d])!==s(b[d]))&&g++;return g+f}function p(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=Tb[a]||Ub[b]||b}return a}function q(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=p(c),b&&(d[b]=a[c]));return d}function r(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}db[b]=function(e,f){var g,h,i=db.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=db().utc().set(d,a);return i.call(db.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function s(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function t(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function u(a){return v(a)?366:365}function v(a){return a%4===0&&a%100!==0||a%400===0}function w(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[jb]<0||a._a[jb]>11?jb:a._a[kb]<1||a._a[kb]>t(a._a[ib],a._a[jb])?kb:a._a[lb]<0||a._a[lb]>23?lb:a._a[mb]<0||a._a[mb]>59?mb:a._a[nb]<0||a._a[nb]>59?nb:a._a[ob]<0||a._a[ob]>999?ob:-1,a._pf._overflowDayOfYear&&(ib>b||b>kb)&&(b=kb),a._pf.overflow=b)}function x(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function y(a){return a?a.toLowerCase().replace("_","-"):a}function z(a,b){return b._isUTC?db(a).zone(b._offset||0):db(a).local()}function A(a,b){return b.abbr=a,pb[a]||(pb[a]=new e),pb[a].set(b),pb[a]}function B(a){delete pb[a]}function C(a){var b,c,d,e,f=0,g=function(a){if(!pb[a]&&rb)try{require("./lang/"+a)}catch(b){}return pb[a]};if(!a)return db.fn._lang;if(!m(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&o(e,d,!0)>=b-1)break;b--}f++}return db.fn._lang}function D(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function E(a){var b,c,d=a.match(vb);for(b=0,c=d.length;c>b;b++)d[b]=Yb[d[b]]?Yb[d[b]]:D(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function F(a,b){return a.isValid()?(b=G(b,a.lang()),Vb[b]||(Vb[b]=E(b)),Vb[b](a)):a.lang().invalidDate()}function G(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(wb.lastIndex=0;d>=0&&wb.test(a);)a=a.replace(wb,c),wb.lastIndex=0,d-=1;return a}function H(a,b){var c,d=b._strict;switch(a){case"DDDD":return Ib;case"YYYY":case"GGGG":case"gggg":return d?Jb:zb;case"Y":case"G":case"g":return Lb;case"YYYYYY":case"YYYYY":case"GGGGG":case"ggggg":return d?Kb:Ab;case"S":if(d)return Gb;case"SS":if(d)return Hb;case"SSS":if(d)return Ib;case"DDD":return yb;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return Cb;case"a":case"A":return C(b._l)._meridiemParse;case"X":return Fb;case"Z":case"ZZ":return Db;case"T":return Eb;case"SSSS":return Bb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"ww":case"WW":return d?Hb:xb;case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"W":case"e":case"E":return xb;default:return c=new RegExp(P(O(a.replace("\\","")),"i"))}}function I(a){a=a||"";var b=a.match(Db)||[],c=b[b.length-1]||[],d=(c+"").match(Qb)||["-",0,0],e=+(60*d[1])+s(d[2]);return"+"===d[0]?-e:e}function J(a,b,c){var d,e=c._a;switch(a){case"M":case"MM":null!=b&&(e[jb]=s(b)-1);break;case"MMM":case"MMMM":d=C(c._l).monthsParse(b),null!=d?e[jb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[kb]=s(b));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=s(b));break;case"YY":e[ib]=s(b)+(s(b)>68?1900:2e3);break;case"YYYY":case"YYYYY":case"YYYYYY":e[ib]=s(b);break;case"a":case"A":c._isPm=C(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[lb]=s(b);break;case"m":case"mm":e[mb]=s(b);break;case"s":case"ss":e[nb]=s(b);break;case"S":case"SS":case"SSS":case"SSSS":e[ob]=s(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=I(b);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":a=a.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=b)}}function K(a){var b,c,d,e,f,g,h,i,j,k,l=[];if(!a._d){for(d=M(a),a._w&&null==a._a[kb]&&null==a._a[jb]&&(f=function(b){var c=parseInt(b,10);return b?b.length<3?c>68?1900+c:2e3+c:c:null==a._a[ib]?db().weekYear():a._a[ib]},g=a._w,null!=g.GG||null!=g.W||null!=g.E?h=Z(f(g.GG),g.W||1,g.E,4,1):(i=C(a._l),j=null!=g.d?V(g.d,i):null!=g.e?parseInt(g.e,10)+i._week.dow:0,k=parseInt(g.w,10)||1,null!=g.d&&ju(e)&&(a._pf._overflowDayOfYear=!0),c=U(e,0,a._dayOfYear),a._a[jb]=c.getUTCMonth(),a._a[kb]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=l[b]=d[b];for(;7>b;b++)a._a[b]=l[b]=null==a._a[b]?2===b?1:0:a._a[b];l[lb]+=s((a._tzm||0)/60),l[mb]+=s((a._tzm||0)%60),a._d=(a._useUTC?U:T).apply(null,l)}}function L(a){var b;a._d||(b=q(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],K(a))}function M(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function N(a){a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=C(a._l),h=""+a._i,i=h.length,j=0;for(d=G(a._f,g).match(vb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),Yb[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),J(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[lb]<12&&(a._a[lb]+=12),a._isPm===!1&&12===a._a[lb]&&(a._a[lb]=0),K(a),w(a)}function O(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function P(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function Q(a){var c,d,e,f,g;if(0===a._f.length)return a._pf.invalidFormat=!0,a._d=new Date(0/0),void 0;for(f=0;fg)&&(e=g,d=c));h(a,d||c)}function R(a){var b,c,d=a._i,e=Mb.exec(d);if(e){for(a._pf.iso=!0,b=0,c=Ob.length;c>b;b++)if(Ob[b][1].exec(d)){a._f=Ob[b][0]+(e[6]||" ");break}for(b=0,c=Pb.length;c>b;b++)if(Pb[b][1].exec(d)){a._f+=Pb[b][0];break}d.match(Db)&&(a._f+="Z"),N(a)}else a._d=new Date(d)}function S(b){var c=b._i,d=sb.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?R(b):m(c)?(b._a=c.slice(0),K(b)):n(c)?b._d=new Date(+c):"object"==typeof c?L(b):b._d=new Date(c)}function T(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function U(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function V(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function W(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function X(a,b,c){var d=hb(Math.abs(a)/1e3),e=hb(d/60),f=hb(e/60),g=hb(f/24),h=hb(g/365),i=45>d&&["s",d]||1===e&&["m"]||45>e&&["mm",e]||1===f&&["h"]||22>f&&["hh",f]||1===g&&["d"]||25>=g&&["dd",g]||45>=g&&["M"]||345>g&&["MM",hb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,W.apply({},i)}function Y(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=db(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function Z(a,b,c,d,e){var f,g,h=U(a,0,1).getUTCDay();return c=null!=c?c:e,f=e-h+(h>d?7:0)-(e>h?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:u(a-1)+g}}function $(a){var b=a._i,c=a._f;return null===b?db.invalid({nullInput:!0}):("string"==typeof b&&(a._i=b=C().preparse(b)),db.isMoment(b)?(a=i(b),a._d=new Date(+b._d)):c?m(c)?Q(a):N(a):S(a),new f(a))}function _(a,b){db.fn[a]=db.fn[a+"s"]=function(a){var c=this._isUTC?"UTC":"";return null!=a?(this._d["set"+c+b](a),db.updateOffset(this),this):this._d["get"+c+b]()}}function ab(a){db.duration.fn[a]=function(){return this._data[a]}}function bb(a,b){db.duration.fn["as"+a]=function(){return+this/b}}function cb(a){var b=!1,c=db;"undefined"==typeof ender&&(a?(gb.moment=function(){return!b&&console&&console.warn&&(b=!0,console.warn("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.")),c.apply(null,arguments)},h(gb.moment,c)):gb.moment=db)}for(var db,eb,fb="2.5.1",gb=this,hb=Math.round,ib=0,jb=1,kb=2,lb=3,mb=4,nb=5,ob=6,pb={},qb={_isAMomentObject:null,_i:null,_f:null,_l:null,_strict:null,_isUTC:null,_offset:null,_pf:null,_lang:null},rb="undefined"!=typeof module&&module.exports&&"undefined"!=typeof require,sb=/^\/?Date\((\-?\d+)/i,tb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,ub=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,vb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYYY|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,wb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,xb=/\d\d?/,yb=/\d{1,3}/,zb=/\d{1,4}/,Ab=/[+\-]?\d{1,6}/,Bb=/\d+/,Cb=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Db=/Z|[\+\-]\d\d:?\d\d/gi,Eb=/T/i,Fb=/[\+\-]?\d+(\.\d{1,3})?/,Gb=/\d/,Hb=/\d\d/,Ib=/\d{3}/,Jb=/\d{4}/,Kb=/[+-]?\d{6}/,Lb=/[+-]?\d+/,Mb=/^\s*(?:[+-]\d{6}|\d{4})-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d(?::?\d\d)?|\s*Z)?)?$/,Nb="YYYY-MM-DDTHH:mm:ssZ",Ob=[["YYYYYY-MM-DD",/[+-]\d{6}-\d{2}-\d{2}/],["YYYY-MM-DD",/\d{4}-\d{2}-\d{2}/],["GGGG-[W]WW-E",/\d{4}-W\d{2}-\d/],["GGGG-[W]WW",/\d{4}-W\d{2}/],["YYYY-DDD",/\d{4}-\d{3}/]],Pb=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Qb=/([\+\-]|\d\d)/gi,Rb="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),Sb={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},Tb={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},Ub={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},Vb={},Wb="DDD w W M D d".split(" "),Xb="M D H h m s w W".split(" "),Yb={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return k(this.year()%100,2)},YYYY:function(){return k(this.year(),4)},YYYYY:function(){return k(this.year(),5)},YYYYYY:function(){var a=this.year(),b=a>=0?"+":"-";return b+k(Math.abs(a),6)},gg:function(){return k(this.weekYear()%100,2)},gggg:function(){return k(this.weekYear(),4)},ggggg:function(){return k(this.weekYear(),5)},GG:function(){return k(this.isoWeekYear()%100,2)},GGGG:function(){return k(this.isoWeekYear(),4)},GGGGG:function(){return k(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return s(this.milliseconds()/100)},SS:function(){return k(s(this.milliseconds()/10),2)},SSS:function(){return k(this.milliseconds(),3)},SSSS:function(){return k(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+k(s(a/60),2)+":"+k(s(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+k(s(a/60),2)+k(s(a)%60,2)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()},Q:function(){return this.quarter()}},Zb=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];Wb.length;)eb=Wb.pop(),Yb[eb+"o"]=d(Yb[eb],eb);for(;Xb.length;)eb=Xb.pop(),Yb[eb+eb]=c(Yb[eb],2);for(Yb.DDDD=c(Yb.DDD,3),h(e.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=db.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=db([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return Y(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),db=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._i=c,g._f=d,g._l=e,g._strict=f,g._isUTC=!1,g._pf=b(),$(g)},db.utc=function(c,d,e,f){var g;return"boolean"==typeof e&&(f=e,e=a),g={},g._isAMomentObject=!0,g._useUTC=!0,g._isUTC=!0,g._l=e,g._i=c,g._f=d,g._strict=f,g._pf=b(),$(g).utc()},db.unix=function(a){return db(1e3*a)},db.duration=function(a,b){var c,d,e,f=a,h=null;return db.isDuration(a)?f={ms:a._milliseconds,d:a._days,M:a._months}:"number"==typeof a?(f={},b?f[b]=a:f.milliseconds=a):(h=tb.exec(a))?(c="-"===h[1]?-1:1,f={y:0,d:s(h[kb])*c,h:s(h[lb])*c,m:s(h[mb])*c,s:s(h[nb])*c,ms:s(h[ob])*c}):(h=ub.exec(a))&&(c="-"===h[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},f={y:e(h[2]),M:e(h[3]),d:e(h[4]),h:e(h[5]),m:e(h[6]),s:e(h[7]),w:e(h[8])}),d=new g(f),db.isDuration(a)&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},db.version=fb,db.defaultFormat=Nb,db.updateOffset=function(){},db.lang=function(a,b){var c;return a?(b?A(y(a),b):null===b?(B(a),a="en"):pb[a]||C(a),c=db.duration.fn._lang=db.fn._lang=C(a),c._abbr):db.fn._lang._abbr},db.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),C(a)},db.isMoment=function(a){return a instanceof f||null!=a&&a.hasOwnProperty("_isAMomentObject")},db.isDuration=function(a){return a instanceof g},eb=Zb.length-1;eb>=0;--eb)r(Zb[eb]);for(db.normalizeUnits=function(a){return p(a)},db.invalid=function(a){var b=db.utc(0/0);return null!=a?h(b._pf,a):b._pf.userInvalidated=!0,b},db.parseZone=function(a){return db(a).parseZone()},h(db.fn=f.prototype,{clone:function(){return db(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){var a=db(this).utc();return 00:!1},parsingFlags:function(){return h({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=F(this,a||db.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a?db.duration(+b,a):db.duration(a,b),l(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a?db.duration(+b,a):db.duration(a,b),l(this,c,-1),this},diff:function(a,b,c){var d,e,f=z(a,this),g=6e4*(this.zone()-f.zone());return b=p(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-db(this).startOf("month")-(f-db(f).startOf("month")))/d,e-=6e4*(this.zone()-db(this).startOf("month").zone()-(f.zone()-db(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:j(e)},from:function(a,b){return db.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(db(),a)},calendar:function(){var a=z(db(),this).startOf("day"),b=this.diff(a,"days",!0),c=-6>b?"sameElse":-1>b?"lastWeek":0>b?"lastDay":1>b?"sameDay":2>b?"nextDay":7>b?"nextWeek":"sameElse";return this.format(this.lang().calendar(c,this))},isLeapYear:function(){return v(this.year())},isDST:function(){return this.zone()+db(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+db(a).startOf(b)},isSame:function(a,b){return b=b||"ms",+this.clone().startOf(b)===+z(a,this).startOf(b)},min:function(a){return a=db.apply(null,arguments),this>a?this:a},max:function(a){return a=db.apply(null,arguments),a>this?this:a},zone:function(a){var b=this._offset||0;return null==a?this._isUTC?b:this._d.getTimezoneOffset():("string"==typeof a&&(a=I(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,b!==a&&l(this,db.duration(b-a,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return this._tzm?this.zone(this._tzm):"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?db(a).zone():0,(this.zone()-a)%60===0},daysInMonth:function(){return t(this.year(),this.month())},dayOfYear:function(a){var b=hb((db(this).startOf("day")-db(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},quarter:function(){return Math.ceil((this.month()+1)/3)},weekYear:function(a){var b=Y(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=Y(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=Y(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},get:function(a){return a=p(a),this[a]()},set:function(a,b){return a=p(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=C(b),this)}}),eb=0;eb { 7 | 8 | res.render('home'); 9 | }); 10 | 11 | app.get('/create', (req, res) => { 12 | 13 | roomGenerator+=1; 14 | var id = Math.round((roomGenerator)); 15 | 16 | res.redirect('/chat/' + id); 17 | }); 18 | 19 | app.get('/chat/:id', (req, res) => { 20 | 21 | res.render('chat'); 22 | }); 23 | 24 | // Initialize a new socket.io application, named 'chat' 25 | var chat = io.on('connection', function (socket) { 26 | 27 | socket.on('load', (data) => { 28 | 29 | var room = findClients(io, data); 30 | if (room.length === 0) { 31 | 32 | socket.emit('peopleinchat', { 33 | number: 0 34 | }); 35 | } else if (room.length === 1) { 36 | 37 | socket.emit('peopleinchat', { 38 | number: 1, 39 | user: room[0].username, 40 | avatar: room[0].avatar, 41 | id: data 42 | }); 43 | } else if (room.length > 1) { 44 | 45 | chat.emit('tooMany', { 46 | boolean: true 47 | }); 48 | } 49 | }); 50 | 51 | // add clients to the room 52 | socket.on('login', function (data) { 53 | 54 | var id = data.id; 55 | var email = data.avatar; 56 | var user = data.user; 57 | 58 | var room = findClients(io, data.id); 59 | 60 | if (room.length < 2) { 61 | 62 | socket.username = data.user; 63 | socket.room = data.id; 64 | socket.avatar = gravatar.url(data.avatar, { 65 | s: '140', 66 | r: 'x', 67 | d: 'mm' 68 | }); 69 | 70 | socket.emit('img', socket.avatar); 71 | 72 | 73 | socket.join(data.id); 74 | 75 | if (room.length == 1) { 76 | 77 | var usernames = [], 78 | avatars = []; 79 | 80 | usernames.push(room[0].username); 81 | usernames.push(socket.username); 82 | 83 | avatars.push(room[0].avatar); 84 | avatars.push(socket.avatar); 85 | 86 | chat.in(data.id).emit('startChat', { 87 | boolean: true, 88 | id: data.id, 89 | users: usernames, 90 | avatars: avatars 91 | }); 92 | } 93 | } else { 94 | socket.emit('tooMany', { 95 | boolean: true 96 | }); 97 | } 98 | }); 99 | 100 | // Notify others When somebody has left the chat 101 | socket.on('disconnect', () => { 102 | 103 | socket.broadcast.to(this.room).emit('leave', { 104 | boolean: true, 105 | room: this.room, 106 | user: this.username, 107 | avatar: this.avatar 108 | }); 109 | 110 | socket.leave(socket.room); 111 | }); 112 | 113 | 114 | // Send message to people in a room 115 | socket.on('msg', function (data) { 116 | var message = data.msg; 117 | var user = data.user; 118 | 119 | socket.broadcast.to(socket.room).emit('receive', { 120 | msg: data.msg, 121 | user: data.user, 122 | img: data.img 123 | }); 124 | 125 | }); 126 | }); 127 | }; 128 | 129 | function findClients(io, roomId, namespace) { 130 | var res = [], 131 | ns = io.of(namespace || "/"); 132 | 133 | if (ns) { 134 | for (var id in ns.connected) { 135 | if (roomId) { 136 | var index = ns.connected[id].rooms; 137 | if (index.hasOwnProperty(roomId)) { 138 | res.push(ns.connected[id]); 139 | } 140 | } else { 141 | res.push(ns.connected[id]); 142 | } 143 | } 144 | } 145 | return res; 146 | } -------------------------------------------------------------------------------- /views/chat.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | chat room 5 | 6 | 7 | 8 | 9 |
    10 | 11 |
    12 | 13 |
    14 |

    Login:

    15 |
    16 |
    17 |
    18 |
    19 | 20 |
    21 |
    22 |
    23 |
    24 | 25 |
    26 |

    Login:

    27 |
    28 |
    29 |
    30 |
    31 | 32 |
    33 |
    34 |
    35 |
    36 |

    Chat room is empty

    37 |
    Invite a friend by sending them this URL
    38 | 41 |
    42 |
    43 | 44 |
    45 |

    has left this chat.

    46 |
    Invite somebody else by sending them this page.
    47 |
    48 |
    49 |
    50 |
    There are already two people in the room. Would you like to create a new room?
    51 |
    52 |
    53 | 54 |
    55 |

    is online

    56 |
    57 |
    58 |
    59 |
      60 | 61 |
    62 |
    63 |
    64 |
    65 |
    66 | 67 | 68 |
    69 |
    70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /views/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Chat room 5 | 6 | 7 | 8 | 9 |
    10 |
    11 | 15 | 20 |
    21 |
    22 | 23 | --------------------------------------------------------------------------------