├── .gitignore
├── README.md
├── demos.html
├── fonts
├── .DS_Store
└── icomoon
│ ├── Read Me.txt
│ ├── demo-files
│ ├── demo.css
│ └── demo.js
│ ├── demo.html
│ ├── fonts
│ ├── icomoon.eot
│ ├── icomoon.svg
│ ├── icomoon.ttf
│ └── icomoon.woff
│ ├── selection.json
│ └── style.css
├── images
├── blank-white.jpg
├── file
│ ├── default-file-icon.jpg
│ ├── default-link-icon.jpg
│ └── default_file_icon.jpg
├── mesibo-bg-white.png
├── mesibo-logo.png
├── paper-plane.png
└── profile
│ ├── default-group-icon.jpg
│ └── default-profile-icon.jpg
├── index.html
├── login
├── login.css
└── login.js
├── mesibo
├── .files.js.swp
├── calls.js
├── config.js
├── files.js
├── login.js
├── recorder.js
└── utils.js
├── messenger.html
├── scripts
├── controller.js
└── ui.js
└── styles
├── messenger.css
├── popup.css
└── popupdesign.css
/.gitignore:
--------------------------------------------------------------------------------
1 | backup.sh
2 |
3 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## Messenger-Javascript
2 | This repository contains the source code for the Mesibo Sample Web apps built using Mesibo Javascript API.
3 |
4 | - **messenger** - A Whatsapp like messaging app that loads a list of users on the left and messages on the right
5 |
6 | > Please note that this is currently **under development** and will be continuously updated.
7 |
8 | ## Features:
9 | - One-to-One Messaging, Voice and Video Call
10 | - Group Messaging
11 | - Read receipts
12 | - Forward, Delete & Resend
13 | - Sending Files
14 | - Record and Send live audio, video & picture
15 | - Link Preview
16 | - Multi-Device Synchronization(*Supported if you are using mesibo On-Premise*)
17 | - Multi-Tab popup
18 |
19 | ### Features under implementation
20 | - Date header in the message area
21 |
22 | ## Instructions
23 | All these demos require a mesibo user token which you can configure in `config.js` or use login screen to generate it.
24 |
25 | If you do not know what is mesibo user token, refer to the [Get-Started Guide](https://mesibo.com/documentation/tutorials/get-started) to learn about the basics of mesibo before continuing further.
26 |
27 | Refer to the `config.js` for configuration and instructions.
28 |
29 | ### Login
30 | The login code is completely independent of demos which makes it easier for you to rebrand. Login code generates token and saves into local storage and launches messenger (you can change this). The messenger/popup code reads token from either `config.js` (if configured) or local storage.
31 |
32 | To login, in the login screen provide the phone number along with country code starting with `+` For Example, If your country code is `1 (United States)` and your ten-digit phone number is `XXXXXXXXXX`, enter your phone number as `+1XXXXXXXXXX` (without any spaces or special characters in between)
33 |
34 | You need to log in to your mesibo account to generate OTP.
35 |
36 | ## Support
37 | Refer to following links before raising any support requests
38 |
39 | - https://mesibo.com/documentation/faq/support/#what-information-should-i-provide-when-requesting-technical-support
40 |
41 | - https://mesibo.com/documentation/faq/support/#can-you-help-with-messenger-whatsapp-clone-demo-and-ui-modules
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/demos.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
14 |
15 |
Mesibo Javascript Sample Apps
16 |
17 |
18 | The following sample apps are built using Mesibo Javascript SDK .
19 |
20 |
These sample apps are meant to be a starting point to help you build applications using mesibo.
21 |
Before you go ahead, it is recommended that you read the Get Started tutorial and try Mesibo Javascript Basic Demo .
22 |
23 |
24 |
To use any of the sample apps below, you need an access token. You can get this token by creating a user in the console or using backend API . Once you get the the token you need to edit config.js , set the token and the app-id that you used to generate the token.
25 |
26 |
27 |
28 |
29 |
30 |
Messenger Demo
31 |
This is a fully featured Whatsapp like app which displays a list of users on the left and messages on the right.
32 |
Open Messenger
33 |
34 |
35 |
36 |
37 |
Popup demo
38 |
This is a simple chat popup app. You can send messages and make calls to a single user.
39 |
Open Popup
40 |
41 |
42 |
43 |
44 |
Multi-tab Popup
45 |
This is a multi-tab chat popup app. This chat popup app can be simultaneously opened across multiple tabs. All the tabs share a single connection to mesibo.
46 |
Open Multitab Popup
47 |
48 |
49 |
50 |
51 |
52 |
53 |
--------------------------------------------------------------------------------
/fonts/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/fonts/.DS_Store
--------------------------------------------------------------------------------
/fonts/icomoon/Read Me.txt:
--------------------------------------------------------------------------------
1 | Open *demo.html* to see a list of all the glyphs in your font along with their codes/ligatures.
2 |
3 | To use the generated font in desktop programs, you can install the TTF font. In order to copy the character associated with each icon, refer to the text box at the bottom right corner of each glyph in demo.html. The character inside this text box may be invisible; but it can still be copied. See this guide for more info: https://icomoon.io/#docs/local-fonts
4 |
5 | You won't need any of the files located under the *demo-files* directory when including the generated font in your own projects.
6 |
7 | You can import *selection.json* back to the IcoMoon app using the *Import Icons* button (or via Main Menu → Manage Projects) to retrieve your icon selection.
8 |
--------------------------------------------------------------------------------
/fonts/icomoon/demo-files/demo.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding: 0;
3 | margin: 0;
4 | font-family: sans-serif;
5 | font-size: 1em;
6 | line-height: 1.5;
7 | color: #555;
8 | background: #fff;
9 | }
10 | h1 {
11 | font-size: 1.5em;
12 | font-weight: normal;
13 | }
14 | small {
15 | font-size: .66666667em;
16 | }
17 | a {
18 | color: #e74c3c;
19 | text-decoration: none;
20 | }
21 | a:hover, a:focus {
22 | box-shadow: 0 1px #e74c3c;
23 | }
24 | .bshadow0, input {
25 | box-shadow: inset 0 -2px #e7e7e7;
26 | }
27 | input:hover {
28 | box-shadow: inset 0 -2px #ccc;
29 | }
30 | input, fieldset {
31 | font-family: sans-serif;
32 | font-size: 1em;
33 | margin: 0;
34 | padding: 0;
35 | border: 0;
36 | }
37 | input {
38 | color: inherit;
39 | line-height: 1.5;
40 | height: 1.5em;
41 | padding: .25em 0;
42 | }
43 | input:focus {
44 | outline: none;
45 | box-shadow: inset 0 -2px #449fdb;
46 | }
47 | .glyph {
48 | font-size: 16px;
49 | width: 15em;
50 | padding-bottom: 1em;
51 | margin-right: 4em;
52 | margin-bottom: 1em;
53 | float: left;
54 | overflow: hidden;
55 | }
56 | .liga {
57 | width: 80%;
58 | width: calc(100% - 2.5em);
59 | }
60 | .talign-right {
61 | text-align: right;
62 | }
63 | .talign-center {
64 | text-align: center;
65 | }
66 | .bgc1 {
67 | background: #f1f1f1;
68 | }
69 | .fgc1 {
70 | color: #999;
71 | }
72 | .fgc0 {
73 | color: #000;
74 | }
75 | p {
76 | margin-top: 1em;
77 | margin-bottom: 1em;
78 | }
79 | .mvm {
80 | margin-top: .75em;
81 | margin-bottom: .75em;
82 | }
83 | .mtn {
84 | margin-top: 0;
85 | }
86 | .mtl, .mal {
87 | margin-top: 1.5em;
88 | }
89 | .mbl, .mal {
90 | margin-bottom: 1.5em;
91 | }
92 | .mal, .mhl {
93 | margin-left: 1.5em;
94 | margin-right: 1.5em;
95 | }
96 | .mhmm {
97 | margin-left: 1em;
98 | margin-right: 1em;
99 | }
100 | .mls {
101 | margin-left: .25em;
102 | }
103 | .ptl {
104 | padding-top: 1.5em;
105 | }
106 | .pbs, .pvs {
107 | padding-bottom: .25em;
108 | }
109 | .pvs, .pts {
110 | padding-top: .25em;
111 | }
112 | .unit {
113 | float: left;
114 | }
115 | .unitRight {
116 | float: right;
117 | }
118 | .size1of2 {
119 | width: 50%;
120 | }
121 | .size1of1 {
122 | width: 100%;
123 | }
124 | .clearfix:before, .clearfix:after {
125 | content: " ";
126 | display: table;
127 | }
128 | .clearfix:after {
129 | clear: both;
130 | }
131 | .hidden-true {
132 | display: none;
133 | }
134 | .textbox0 {
135 | width: 3em;
136 | background: #f1f1f1;
137 | padding: .25em .5em;
138 | line-height: 1.5;
139 | height: 1.5em;
140 | }
141 | #testDrive {
142 | display: block;
143 | padding-top: 24px;
144 | line-height: 1.5;
145 | }
146 | .fs0 {
147 | font-size: 16px;
148 | }
149 | .fs1 {
150 | font-size: 28px;
151 | }
152 | .fs2 {
153 | font-size: 24px;
154 | }
155 |
156 |
--------------------------------------------------------------------------------
/fonts/icomoon/demo-files/demo.js:
--------------------------------------------------------------------------------
1 | if (!('boxShadow' in document.body.style)) {
2 | document.body.setAttribute('class', 'noBoxShadow');
3 | }
4 |
5 | document.body.addEventListener("click", function(e) {
6 | var target = e.target;
7 | if (target.tagName === "INPUT" &&
8 | target.getAttribute('class').indexOf('liga') === -1) {
9 | target.select();
10 | }
11 | });
12 |
13 | (function() {
14 | var fontSize = document.getElementById('fontSize'),
15 | testDrive = document.getElementById('testDrive'),
16 | testText = document.getElementById('testText');
17 | function updateTest() {
18 | testDrive.innerHTML = testText.value || String.fromCharCode(160);
19 | if (window.icomoonLiga) {
20 | window.icomoonLiga(testDrive);
21 | }
22 | }
23 | function updateSize() {
24 | testDrive.style.fontSize = fontSize.value + 'px';
25 | }
26 | fontSize.addEventListener('change', updateSize, false);
27 | testText.addEventListener('input', updateTest, false);
28 | testText.addEventListener('change', updateTest, false);
29 | updateSize();
30 | }());
31 |
--------------------------------------------------------------------------------
/fonts/icomoon/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/fonts/icomoon/fonts/icomoon.eot
--------------------------------------------------------------------------------
/fonts/icomoon/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/fonts/icomoon/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/fonts/icomoon/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/fonts/icomoon/fonts/icomoon.woff
--------------------------------------------------------------------------------
/images/blank-white.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/blank-white.jpg
--------------------------------------------------------------------------------
/images/file/default-file-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/file/default-file-icon.jpg
--------------------------------------------------------------------------------
/images/file/default-link-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/file/default-link-icon.jpg
--------------------------------------------------------------------------------
/images/file/default_file_icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/file/default_file_icon.jpg
--------------------------------------------------------------------------------
/images/mesibo-bg-white.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/mesibo-bg-white.png
--------------------------------------------------------------------------------
/images/mesibo-logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/mesibo-logo.png
--------------------------------------------------------------------------------
/images/paper-plane.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/paper-plane.png
--------------------------------------------------------------------------------
/images/profile/default-group-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/profile/default-group-icon.jpg
--------------------------------------------------------------------------------
/images/profile/default-profile-icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/images/profile/default-profile-icon.jpg
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
mesibo open-source messenger demo
24 |
25 |
26 |
27 |
28 |
29 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/login/login.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: "Roboto", sans-serif;
3 | background-color: #f8fafb; }
4 |
5 | p {
6 | color: #737373;
7 | font-weight: 300;
8 | }
9 |
10 | h1, h2, h3, h4, h5, h6,
11 | .h1, .h2, .h3, .h4, .h5, .h6 {
12 | font-family: "Roboto", sans-serif; }
13 |
14 | a {
15 | -webkit-transition: .3s all ease;
16 | -o-transition: .3s all ease;
17 | transition: .3s all ease;
18 | text-decoration: none !important;
19 | color: #00868b !important;
20 | }
21 | a:hover {
22 | text-decoration: none !important; }
23 |
24 | .content {
25 | padding: 7rem 0; }
26 |
27 | h2 {
28 | font-size: 20px; }
29 |
30 | .form-block {
31 | background: #fff;
32 | padding: 60px;
33 | -webkit-box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.1);
34 | box-shadow: 0 2px 3px 0 rgba(0, 0, 0, 0.1); }
35 | @media (max-width: 991.98px) {
36 | .form-block {
37 | padding: 30px; } }
38 |
39 | @media (max-width: 991.98px) {
40 | .content .bg {
41 | height: 500px; } }
42 |
43 | .content .contents, .content .bg {
44 | width: 50%; }
45 | @media (max-width: 1199.98px) {
46 | .content .contents, .content .bg {
47 | width: 100%; } }
48 | .content .contents .form-group, .content .bg .form-group {
49 | position: relative; }
50 | .content .contents .form-group label, .content .bg .form-group label {
51 | position: absolute;
52 | top: 50%;
53 | -webkit-transform: translateY(-50%);
54 | -ms-transform: translateY(-50%);
55 | transform: translateY(-50%);
56 | -webkit-transition: .3s all ease;
57 | -o-transition: .3s all ease;
58 | transition: .3s all ease; }
59 | .content .contents .form-group input, .content .bg .form-group input {
60 | background: transparent;
61 | border-bottom: 1px solid #ccc; }
62 | .content .contents .form-group.first, .content .bg .form-group.first {
63 | border-top-left-radius: 7px;
64 | border-top-right-radius: 7px; }
65 | .content .contents .form-group.last, .content .bg .form-group.last {
66 | border-bottom-left-radius: 7px;
67 | border-bottom-right-radius: 7px; }
68 | .content .contents .form-group label, .content .bg .form-group label {
69 | font-size: 12px;
70 | display: block;
71 | margin-bottom: 0;
72 | color: #b3b3b3; }
73 | .content .contents .form-group.focus, .content .bg .form-group.focus {
74 | background: #fff; }
75 | .content .contents .form-group.field--not-empty label, .content .bg .form-group.field--not-empty label {
76 | margin-top: -25px; }
77 | .content .contents .form-control, .content .bg .form-control {
78 | border: none;
79 | padding: 0;
80 | font-size: 20px;
81 | border-radius: 0; }
82 | .content .contents .form-control:active, .content .contents .form-control:focus, .content .bg .form-control:active, .content .bg .form-control:focus {
83 | outline: none;
84 | -webkit-box-shadow: none;
85 | box-shadow: none; }
86 |
87 | .content .bg {
88 | background-size: cover;
89 | background-position: center; }
90 |
91 | .content a {
92 | color: #888;
93 | text-decoration: underline; }
94 |
95 | .content .btn {
96 | height: 54px;
97 | padding-left: 30px;
98 | padding-right: 30px; }
99 |
100 | .content .forgot-pass {
101 | position: relative;
102 | top: 2px;
103 | font-size: 14px; }
104 |
105 | .content .btn-pill {
106 | border-radius: 30px; }
107 |
108 | .social-login a {
109 | text-decoration: none;
110 | position: relative;
111 | text-align: center;
112 | color: #fff;
113 | margin-bottom: 10px;
114 | width: 50px;
115 | height: 50px;
116 | border-radius: 50%;
117 | display: inline-block; }
118 | .social-login a span {
119 | position: absolute;
120 | top: 50%;
121 | left: 50%;
122 | -webkit-transform: translate(-50%, -50%);
123 | -ms-transform: translate(-50%, -50%);
124 | transform: translate(-50%, -50%); }
125 | .social-login a:hover {
126 | color: #fff; }
127 | .social-login a.facebook {
128 | background: #3b5998; }
129 | .social-login a.facebook:hover {
130 | background: #344e86; }
131 | .social-login a.twitter {
132 | background: #1da1f2; }
133 | .social-login a.twitter:hover {
134 | background: #0d95e8; }
135 | .social-login a.google {
136 | background: #ea4335; }
137 | .social-login a.google:hover {
138 | background: #e82e1e; }
139 |
140 | .control {
141 | display: block;
142 | position: relative;
143 | padding-left: 30px;
144 | margin-bottom: 15px;
145 | cursor: pointer;
146 | font-size: 14px; }
147 | .control .caption {
148 | position: relative;
149 | top: .2rem;
150 | color: #888; }
151 |
152 | .control input {
153 | position: absolute;
154 | z-index: -1;
155 | opacity: 0; }
156 |
157 | .control__indicator {
158 | position: absolute;
159 | top: 2px;
160 | left: 0;
161 | height: 20px;
162 | width: 20px;
163 | background: #e6e6e6;
164 | border-radius: 4px; }
165 |
166 | .control--radio .control__indicator {
167 | border-radius: 50%; }
168 |
169 | .control:hover input ~ .control__indicator,
170 | .control input:focus ~ .control__indicator {
171 | background: #ccc; }
172 |
173 | .control input:checked ~ .control__indicator {
174 | background: #38d39f; }
175 |
176 | .control:hover input:not([disabled]):checked ~ .control__indicator,
177 | .control input:checked:focus ~ .control__indicator {
178 | background: #4dd8a9; }
179 |
180 | .control input:disabled ~ .control__indicator {
181 | background: #e6e6e6;
182 | opacity: 0.9;
183 | pointer-events: none; }
184 |
185 | .control__indicator:after {
186 | font-family: 'icomoon';
187 | content: '\e5ca';
188 | position: absolute;
189 | display: none;
190 | font-size: 16px;
191 | -webkit-transition: .3s all ease;
192 | -o-transition: .3s all ease;
193 | transition: .3s all ease; }
194 |
195 | .control input:checked ~ .control__indicator:after {
196 | display: block;
197 | color: #fff; }
198 |
199 | .control--checkbox .control__indicator:after {
200 | top: 50%;
201 | left: 50%;
202 | margin-top: -1px;
203 | -webkit-transform: translate(-50%, -50%);
204 | -ms-transform: translate(-50%, -50%);
205 | transform: translate(-50%, -50%); }
206 |
207 | .control--checkbox input:disabled ~ .control__indicator:after {
208 | border-color: #7b7b7b; }
209 |
210 | .control--checkbox input:disabled:checked ~ .control__indicator {
211 | background-color: #7e0cf5;
212 | opacity: .2; }
213 |
214 | .my-primary{
215 | color: lightYellow ;
216 | background-color: #00868b;
217 | }
218 |
219 | .my-primary{
220 | background-color: #00868b;
221 | }
222 |
223 | .my-primary:focus, .my-primary:hover{
224 | background-color: #00869b;
225 | }
226 |
227 |
--------------------------------------------------------------------------------
/login/login.js:
--------------------------------------------------------------------------------
1 | $(function() {
2 | 'use strict';
3 |
4 |
5 | console.log('login init');
6 | $('.form-control').on('input', function() {
7 | var $field = $(this).closest('.form-group');
8 | if (this.value) {
9 | $field.addClass('field--not-empty');
10 | } else {
11 | $field.removeClass('field--not-empty');
12 | }
13 | });
14 |
15 | });
16 |
17 | function redirect_messenger() {
18 | window.location.replace("messenger.html");
19 | }
20 |
21 | function login_init() {
22 | console.log('start login');
23 | var token = getLoginToken();
24 | if(token && token.length > 16) {
25 | redirect_messenger();
26 | return;
27 | }
28 | var mesibo = Mesibo.getInstance();
29 |
30 | document.getElementById("otpdiv").style.display = "none";
31 | document.getElementById("otp").value = '';
32 | document.getElementById('phone').readOnly = false;
33 | document.getElementById("phone").value = '';
34 | _displayLoginError(null);
35 | }
36 |
37 | function login_start() {
38 | var phone = document.getElementById("phone").value;
39 | if(!phone || phone.length < 10) {
40 | _displayLoginError("Invalid Phone Number");
41 | return "";
42 | }
43 |
44 | _displayLoginError(null);
45 | if(phone[0] == '+'){
46 | phone = phone.substr(1);
47 | }
48 |
49 | var otp = document.getElementById("otp").value;
50 |
51 | var p = {};
52 | p['op'] = 'login';
53 | p['appid'] = MESIBO_APP_ID;
54 | p['phone'] = phone;
55 |
56 | if(otp && otp.length > 4){
57 | p['otp'] = otp;
58 | console.log("gen with otp");
59 | }
60 |
61 |
62 | var http = Mesibo.getInstance().createhttpRequest();
63 | http.setUrl(MESSENGER_API_URL);
64 | http.setPostData(p, true);
65 | http.send(null, function(cbdata, response) {
66 | console.log(response);
67 | var resp = JSON.parse(response);
68 | if(resp.result != "OK") {
69 | console.log(resp);
70 | _displayLoginError(resp.result);
71 | return;
72 | }
73 |
74 | document.getElementById('phone').readOnly = true;
75 | document.getElementById("otpdiv").style.display = "block";
76 | document.getElementById("otp").value = "";
77 |
78 | var token = resp.token;
79 | if(token && token.length > 16){
80 | console.log("Login Successfull");
81 |
82 | document.getElementById("phone").innerHTML = null;
83 | document.getElementById("otp").innerHTML = null;
84 |
85 | saveLoginToken(token);
86 | redirect_messenger();
87 | }
88 |
89 | });
90 | }
91 |
92 | function _displayLoginError(error) {
93 | document.getElementById("errmsg").style.display = error?"block":"none";
94 | if(error)
95 | document.getElementById("errmsg").value = error;
96 | }
97 |
98 |
99 |
--------------------------------------------------------------------------------
/mesibo/.files.js.swp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/mesibo/messenger-javascript/2f54fb0234f7c2935c506c0204519e93b6323b88/mesibo/.files.js.swp
--------------------------------------------------------------------------------
/mesibo/calls.js:
--------------------------------------------------------------------------------
1 | // calls.js
2 |
3 | /** Copyright (c) 2021 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | *
43 | */
44 |
45 |
46 | function MesiboCall(s) {
47 | this.scope = s;
48 | this.api = {};
49 | this.init()
50 |
51 | }
52 |
53 | MesiboCall.prototype.init = function(){
54 | this.api = this.scope.getMesibo();
55 | if(!isValid(this.api)){
56 | MesiboLog("Invalid Mesibo Instance");
57 | return -1;
58 | }
59 |
60 | return this;
61 | }
62 |
63 | MesiboCall.prototype.videoCall = function() {
64 |
65 | // Setup UI elements for video call
66 | this.scope.showVideoCall();
67 | this.api.setupVideoCall("localVideo", "remoteVideo", true);
68 |
69 | //Video Call API
70 | this.api.call(this.scope.selected_user.getAddress());
71 |
72 | }
73 |
74 | MesiboCall.prototype.voiceCall = function() {
75 | MesiboLog('voiceCall');
76 |
77 | // Setup UI elements for audio call
78 | this.scope.showVoiceCall();
79 | this.api.setupVoiceCall("audioPlayer");
80 |
81 | //Voice Call API
82 | this.api.call(this.scope.selected_user.getAddress());
83 | }
84 |
85 | MesiboCall.prototype.answer = function() {
86 |
87 | //Common modal popup notification for a call. Select the required modal to be displayed
88 | if (this.scope.is_video_call)
89 | this.video_answer();
90 | else
91 | this.voice_answer();
92 |
93 | }
94 |
95 | /** Control Modal View **/
96 | MesiboCall.prototype.hangup = function() {
97 | this.scope.hangupCall();
98 | this.api.hangup(0);
99 | }
100 |
101 | MesiboCall.prototype.video_answer = function() {
102 | this.scope.showVideoCall();
103 | this.api.answer(true);
104 | }
105 |
106 | MesiboCall.prototype.video_hangup = function() {
107 | this.scope.hangupVideoCall();
108 | this.api.hangup(0);
109 | }
110 |
111 | MesiboCall.prototype.voice_answer = function() {
112 | this.scope.showVoiceCall();
113 | this.api.answer(true);
114 | }
115 |
116 | MesiboCall.prototype.voice_hangup = function() {
117 | this.scope.hangupAudioCall();
118 | this.api.hangup(0);
119 | }
120 |
121 |
--------------------------------------------------------------------------------
/mesibo/config.js:
--------------------------------------------------------------------------------
1 | // config.js
2 |
3 | /** Copyright (c) 2021 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | *
43 | */
44 |
45 | /* Refer following tutorial and API documentation to know how to create a user token
46 | * https://mesibo.com/documentation/tutorials/first-app/
47 | *
48 | * Note, that if you are using logging in with your phone, Mesibo will generate the token.
49 | * In that case, there is no need to configure token here
50 | *
51 | */
52 | var MESIBO_ACCESS_TOKEN = "";
53 |
54 | /* App ID used to create a user token. */
55 | var MESIBO_APP_ID = "com.mesibo.jsdemo";
56 |
57 | /* If you are hosting Mesibo backend on your own server, change this accordingly.
58 | * Refer https://github.com/mesibo/messenger-app-backend
59 | */
60 | const MESSENGER_API_URL = "https://messenger.mesibo.com";
61 |
62 | /* Default images */
63 | const MESIBO_DEFAULT_PROFILE_IMAGE = "images/profile/default-profile-icon.jpg";
64 | const MESIBO_DEFAULT_GROUP_IMAGE = "images/profile/default-group-icon.jpg";
65 |
66 | /************************ Messenger Config Start *****************************/
67 |
68 | /* Toggle for synchronizing messages
69 | * See https://mesibo.com/documentation/tutorials/get-started/synchronization/
70 | */
71 | var isMessageSync = true;
72 |
73 | /*Optional link preview*/
74 | const isLinkPreview = false; //Set to false if link preview not required
75 |
76 | /************************ Messenger Config End *****************************/
77 |
78 | /************************ Popup Config Start *****************************/
79 |
80 | /* A destination where the popup demo app will send message or make calls */
81 | const POPUP_DESTINATION_USER = "
"
82 |
83 | /************************ Popup Config End *****************************/
84 |
85 |
86 | /* Debug Mode Configuration */
87 | isDebug = true ;// toggle this to turn on / off for global control
88 | if (isDebug) var MesiboLog = console.log.bind(window.console);
89 | else var MesiboLog = function() {}
90 |
91 | var ErrorLog = console.log.bind(window.console);
92 |
93 | function saveLoginToken(token){
94 | localStorage.setItem("MESIBO_MESSENGER_TOKEN", token);
95 | return 0;
96 | }
97 |
98 | function deleteTokenInStorage(){
99 | localStorage.removeItem("MESIBO_MESSENGER_TOKEN");
100 | }
101 |
102 | function hasHardCodedLoginToken(){
103 | return(MESIBO_ACCESS_TOKEN && MESIBO_ACCESS_TOKEN.length > 16);
104 | }
105 |
106 | function getLoginToken(){
107 | if(MESIBO_ACCESS_TOKEN && MESIBO_ACCESS_TOKEN.length > 16)
108 | return MESIBO_ACCESS_TOKEN;
109 |
110 | var token = localStorage.getItem("MESIBO_MESSENGER_TOKEN");
111 | if(token && token.length > 16) return token;
112 |
113 | return null;
114 | }
115 |
116 | function showAuthFailAlert() {
117 | alert("Invalid Token\n\nEnsure that the token was generated using appid: " + MESIBO_APP_ID);
118 | }
119 |
--------------------------------------------------------------------------------
/mesibo/files.js:
--------------------------------------------------------------------------------
1 | // files.js
2 |
3 | /** Copyright (c) 2021 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | *
43 | */
44 |
45 | function MessengerFile(s) {
46 | this.scope = s;
47 | this.api ={};
48 | this.init();
49 | }
50 |
51 | MessengerFile.prototype.init = function(){
52 | this.api = this.scope.getMesibo();
53 | }
54 |
55 |
56 | MessengerFile.prototype.dataURItoBlob = function(dataURI) {
57 | // convert base64 to raw binary data held in a string
58 | // doesn't handle URLEncoded DataURIs
59 | var byteString = atob(dataURI.split(',')[1]);
60 |
61 | // separate out the mime component
62 | var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
63 |
64 | // write the bytes of the string to an ArrayBuffer
65 | var ab = new ArrayBuffer(byteString.length);
66 |
67 | // create a view into the buffer
68 | var ia = new Uint8Array(ab);
69 |
70 | // set the bytes of the buffer to the correct values
71 | for (var i = 0; i < byteString.length; i++) {
72 | ia[i] = byteString.charCodeAt(i);
73 | }
74 |
75 | // write the ArrayBuffer to a blob, and you're done
76 | var blob = new Blob([ab], {
77 | type: mimeString
78 | });
79 | return blob;
80 |
81 | }
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/mesibo/login.js:
--------------------------------------------------------------------------------
1 | //login.js
2 |
3 | /** Copyright (c) 2021 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | *
43 | */
44 |
45 | function _getPhoneNumber(){
46 | var phone = document.getElementById("phone").value;
47 | if(!phone)
48 | return "";
49 |
50 | //xxx:Validate Phone Number
51 | if(phone[0] == '+'){
52 | phone = phone.substr(1); //Strip +
53 | }
54 |
55 | return phone;
56 | }
57 |
58 | function _getVerificationCode(){
59 | var code = document.getElementById("otp").value;
60 | if(!isValidString(code))
61 | return "";
62 |
63 | //xxx:Validate code
64 | return code;
65 | }
66 |
67 | function _getAppId(){
68 | if(!isValidString(MESIBO_APP_ID))
69 | return "";
70 |
71 | return MESIBO_APP_ID;
72 | }
73 |
74 | function getMesiboDemoAppToken(api) {
75 | var p = {};
76 | p['op'] = 'login';
77 | p['appid'] = _getAppId();
78 | p['phone'] = _getPhoneNumber();
79 | var otp = _getVerificationCode();
80 |
81 | if(isValidString(otp)){
82 | p['otp'] = otp;
83 | //Login with OTP
84 | console.log("gen with otp");
85 | }
86 | else if(isValidString(phone)){
87 | //Register Phone to get OTP
88 | document.getElementById('phone').readOnly = true;
89 | document.getElementById("otp-input").style.display = "block";
90 | document.getElementById("otp").innerHTML = "";
91 | }
92 |
93 | var http = api.createhttpRequest();
94 | http.setUrl(MESSENGER_API_URL);
95 | http.setPostData(p, true);
96 | http.send(null, function(cbdata, response) {
97 | console.log(response);
98 | var resp = JSON.parse(response);
99 | if(resp.result != "OK") {
100 | console.log(resp);
101 | _displayLoginError(resp.result);
102 | return;
103 | }
104 |
105 | var token = resp.token;
106 | if(isValidString(token)){
107 | console.log("Login Successfull");
108 |
109 | document.getElementById("phone").innerHTML = null;
110 | document.getElementById("otp").innerHTML = null;
111 |
112 | $('#ModalLoginForm').modal('hide');
113 | MESIBO_ACCESS_TOKEN = token;
114 | setTokenInStorage(token);
115 |
116 | //Launch Messenger Application
117 | launchMessenger();
118 | }
119 |
120 | });
121 | }
122 |
123 | function _displayLoginError(error) {
124 | alert("Login Error: " + error + "\n Please ensure you have entered a valid phone number & otp");
125 | }
126 |
127 | function loadLoginWindow(){
128 | $('#ModalLoginForm').modal({backdrop: 'static', keyboard: false});
129 | document.getElementById("otp-input").style.display = "none";
130 | document.getElementById("otp").innerHTML = null;
131 | }
132 |
133 |
--------------------------------------------------------------------------------
/mesibo/recorder.js:
--------------------------------------------------------------------------------
1 | // recorder.js
2 |
3 | /** Copyright (c) 2022 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | * Recorder can be used to capture and send live media(captured from WebCam etc)
43 | * Example: Audio Clip, Video Clip, Picture, etc
44 | *
45 | * Uses:
46 | * WebRTC https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API
47 | * Media Recorder https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
48 | *
49 | * Audio Recording uses code from the Web Dictaphone demo.
50 | * Refer to the source code at https://github.com/mdn/web-dictaphone/
51 | *
52 | * Taking still photos with WebRTC
53 | * https://developer.mozilla.org/en-US/docs/Web/API/WebRTC_API/Taking_still_photos
54 | *
55 | */
56 |
57 | function MesiboRecorder(s, type) {
58 | this.scope = s;
59 | this.type = type;
60 |
61 | this.record = document.querySelector('.record');
62 | this.stop = document.querySelector('.stop');
63 | this.mediaClips = document.querySelector('.sound-clips');
64 | this.canvas = document.querySelector('.visualizer');
65 | this.camera = document.querySelector('.camera');
66 | this.photo = document.getElementById('photo_button');
67 | this.stream = null;
68 | this.videoRecorder = false;
69 |
70 | this.canvasCtx = this.canvas.getContext("2d");
71 |
72 | window.rCtx = this;
73 |
74 | if(type == 'audio')
75 | this.audioRecorder();
76 | else if(type == 'picture')
77 | this.pictureRecorder();
78 | else if(type == 'video') {
79 | this.pictureRecorder();
80 | this.videoRecorder = true;
81 | }
82 | }
83 |
84 | MesiboRecorder.prototype.audioRecorder = function(){
85 |
86 | // disable stop button while not recording
87 | this.stop.disabled = true;
88 |
89 | // visualiser setup - create web audio api context and canvas
90 | this.audioCtx = null;
91 |
92 | //Initially hide clips area and show only visualizer, record & stop buttons
93 | this.canvas.style.display = "initial";
94 |
95 | this.record.style.display = "inline-block";
96 | this.stop.style.display = "inline-block";
97 |
98 | this.mediaClips.style.display = "none";
99 | this.record.style.background = "";
100 | this.record.style.color = "";
101 |
102 | this.camera.style.display = "none";
103 |
104 | }
105 |
106 | MesiboRecorder.prototype.pictureRecorder = function(){
107 |
108 | this.camera.style.display = "block";
109 |
110 | this.canvas.style.display = "none";
111 | this.photo.disabled = false;
112 | // disable stop button while not recording
113 | this.stop.disabled = true;
114 | this.record.style.display = "inline-block";
115 | this.stop.style.display = "inline-block";
116 |
117 | this.mediaClips.style.display = "none";
118 |
119 |
120 | this.video = document.getElementById("capture-video");
121 | this.photo = document.getElementById('captured-photo');
122 |
123 | this.video.style.display = "inline-block";
124 | this.photo.style.display = "none";
125 |
126 | document.getElementById("buttons").style.display = "block";
127 | document.getElementById("recording_area").style.display = "block";
128 |
129 | }
130 |
131 | //main block for doing the video and picture recording
132 | MesiboRecorder.prototype.initPictureRecording = function(){
133 | MesiboLog("initPictureRecording called", this);
134 |
135 | let rCtx = window.rCtx;
136 |
137 | if(!rCtx.stream){
138 | navigator.mediaDevices.getUserMedia({ video: true, audio: this.videoRecorder })
139 | .then(function(stream) {
140 | rCtx.stream = stream;
141 | rCtx.video.srcObject = stream;
142 | rCtx.video.play();
143 | rCtx.recordMedia(stream, true); //Recording Video
144 | })
145 | .catch(function(err) {
146 | console.log("An error occurred: " + err);
147 | });
148 |
149 | }
150 |
151 |
152 | // The width and height of the captured photo. We will set the
153 | // width to the value defined here, but the height will be
154 | // calculated based on the aspect ratio of the input stream.
155 |
156 | var width = 640; // We will scale the photo width to this
157 | var height = 0; // This will be computed based on the input stream
158 | height = this.video.videoHeight / (this.video.videoWidth/width);
159 |
160 | // |streaming| indicates whether or not we're currently streaming
161 | // video from the camera. Obviously, we start at false.
162 |
163 | var streaming = false;
164 |
165 | this.video.addEventListener('canplay', function(ev){
166 | if (!streaming) {
167 |
168 | // Firefox currently has a bug where the height can't be read from
169 | // the video, so we will make assumptions if this happens.
170 |
171 | if (isNaN(height)) {
172 | height = width / (4/3);
173 | }
174 |
175 |
176 | rCtx.video.setAttribute('width', width);
177 | rCtx.video.setAttribute('height', height);
178 | rCtx.canvas.setAttribute('width', width);
179 | rCtx.canvas.setAttribute('height', height);
180 | streaming = true;
181 | }
182 | }, false);
183 |
184 | var photo_button = document.getElementById('photo_button');
185 | var record_buttons = document.getElementById('buttons');
186 | if(this.videoRecorder) {
187 | photo_button.style.display = "none";
188 | record_buttons.style.display = "block";
189 | } else {
190 | photo_button.style.display = "inline-block";
191 | record_buttons.style.display = "none";
192 | }
193 | while (rCtx.camera.lastElementChild) {
194 | if(rCtx.camera.lastElementChild.classList.contains('clip'))
195 | rCtx.camera.removeChild(rCtx.camera.lastElementChild);
196 | else
197 | break;
198 | }
199 |
200 | photo_button.addEventListener('click', function(ev){
201 | takepicture();
202 | ev.preventDefault();
203 | document.getElementById("buttons").style.display = "none";
204 | }, false);
205 |
206 | clearphoto();
207 |
208 |
209 | // Fill the photo with an indication that none has been
210 | // captured.
211 |
212 | function clearphoto() {
213 | var context = rCtx.canvas.getContext('2d');
214 | context.fillStyle = "#AAA";
215 | context.fillRect(0, 0, rCtx.canvas.width, rCtx.canvas.height);
216 |
217 | var data = rCtx.canvas.toDataURL('image/png');
218 |
219 | rCtx.photo.setAttribute('src', data);
220 | }
221 |
222 | // Capture a photo by fetching the current contents of the video
223 | // and drawing it into a canvas, then converting that to a PNG
224 | // format data URL. By drawing it on an offscreen canvas and then
225 | // drawing that to the screen, we can change its size and/or apply
226 | // other changes before drawing it.
227 |
228 | function takepicture() {
229 | var context = rCtx.canvas.getContext('2d');
230 | if (width && height) {
231 | rCtx.canvas.width = width;
232 | rCtx.canvas.height = height;
233 | context.drawImage(rCtx.video, 0, 0, width, height);
234 |
235 | var data = rCtx.canvas.toDataURL('image/png');
236 |
237 | rCtx.photo.setAttribute('src', data);
238 | rCtx.photo.style.display = "inline-block";
239 |
240 | rCtx.video.style.display = "none";
241 |
242 | const buttonContainer = document.createElement('article');
243 | const cancelButton = document.createElement('button');
244 | const sendButton = document.createElement('button');
245 |
246 | buttonContainer.classList.add('clip');
247 | buttonContainer.style.textAlign = 'center';
248 | cancelButton.textContent = 'Cancel';
249 | cancelButton.className = 'cancel btn btn-danger';
250 | cancelButton.style.marginTop = '5px';
251 | cancelButton.style.marginLeft = '5px';
252 |
253 | sendButton.textContent = 'Send';
254 | sendButton.className = 'send btn btn-success';
255 | sendButton.style.marginTop = '5px';
256 |
257 | buttonContainer.appendChild(sendButton);
258 | buttonContainer.appendChild(cancelButton);
259 |
260 | while (rCtx.camera.lastElementChild) {
261 | if(rCtx.camera.lastElementChild.classList.contains('clip'))
262 | rCtx.camera.removeChild(rCtx.camera.lastElementChild);
263 | else
264 | break;
265 | }
266 |
267 | rCtx.camera.appendChild(buttonContainer);
268 | document.getElementById('photo_button').style.display = "none";
269 |
270 |
271 | function resetRecorder(e){
272 | let evtTgt = e.target;
273 | evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
274 | document.getElementById("buttons").style.display = "block";
275 | document.getElementById("recording_area").style.display = "block";
276 | }
277 |
278 | cancelButton.onclick = function(e) {
279 | resetRecorder(e);
280 | rCtx.pictureRecorder();
281 | rCtx.initPictureRecording();
282 | }
283 |
284 | sendButton.onclick = function(e) {
285 | var blob = rCtx._dataURItoBlob(data);
286 | var image = rCtx._blobToFile(blob, "capture-"+ Date.now()+".png", {type: "image/png"})
287 | rCtx.sendRecordedFile(image);
288 | MesiboLog("close stream", rCtx.stream, rCtx.stream.getTracks());
289 | rCtx.scope.closeRecorder(); //TBD: Should we close the recorder when the send button is clicked
290 | }
291 |
292 |
293 | } else {
294 | clearphoto();
295 | }
296 | }
297 |
298 | }
299 |
300 | MesiboRecorder.prototype.recordMedia = function(stream, video){
301 | MesiboLog("recordMedia", stream, video);
302 | if(!stream)
303 | return;
304 |
305 | let chunks = [];
306 | let rCtx = this;
307 |
308 | const mediaRecorder = new MediaRecorder(stream);
309 |
310 | rCtx.stream = stream;
311 | if(!video){
312 | MesiboLog("Activate audio visuals");
313 | rCtx.visualize(stream);
314 | }
315 |
316 | rCtx.record.onclick = function() {
317 | mediaRecorder.start();
318 | console.log(mediaRecorder.state);
319 | console.log("recorder started");
320 | rCtx.record.style.background = "red";
321 |
322 | rCtx.stop.disabled = false;
323 | rCtx.record.disabled = true;
324 |
325 | let pb = document.getElementById("photo_button");
326 | if(pb)
327 | pb.disabled = true;
328 | }
329 |
330 | rCtx.stop.onclick = function() {
331 | mediaRecorder.stop();
332 | console.log(mediaRecorder.state);
333 | console.log("recorder stopped");
334 | rCtx.record.style.background = "";
335 | rCtx.record.style.color = "";
336 |
337 | rCtx.stop.disabled = true;
338 | rCtx.record.disabled = false;
339 | }
340 |
341 | mediaRecorder.onstop = function(e) {
342 | console.log("data available after MediaRecorder.stop() called.");
343 |
344 | // const clipName = prompt('Enter a name for your sound clip?','My unnamed clip');
345 | const clipName = null; //Disabling clip name/caption for now
346 |
347 | const clipContainer = document.createElement('article');
348 | const clipLabel = document.createElement('p');
349 |
350 | var media = null;
351 | if(video){
352 | document.getElementById("recording_area").style.display = "none";
353 | media = document.createElement('video');
354 | media.width = "320";
355 | media.height = "240";
356 | }
357 | else{
358 | media = document.createElement('audio');
359 | }
360 | const cancelButton = document.createElement('button');
361 | const sendButton = document.createElement('button');
362 |
363 | clipContainer.classList.add('clip');
364 | clipContainer.style.textAlign = 'center';
365 | cancelButton.textContent = 'Cancel';
366 | cancelButton.className = 'cancel btn btn-danger';
367 | cancelButton.style.marginLeft = '5px';
368 | sendButton.textContent = 'Send';
369 | sendButton.className = 'send btn btn-success ml-1';
370 |
371 | if(clipName === null) {
372 | clipLabel.textContent = '';
373 | } else {
374 | clipLabel.textContent = clipName;
375 | }
376 |
377 | rCtx.canvas.style.display = "none";
378 | rCtx.record.style.display = "none";
379 | rCtx.stop.style.display = "none";
380 |
381 | clipContainer.appendChild(media);
382 | clipContainer.appendChild(clipLabel);
383 | clipContainer.appendChild(sendButton);
384 | clipContainer.appendChild(cancelButton);
385 |
386 | while (rCtx.mediaClips.lastElementChild) {
387 | rCtx.mediaClips.removeChild(rCtx.mediaClips.lastElementChild);
388 | }
389 | rCtx.mediaClips.appendChild(clipContainer);
390 | rCtx.mediaClips.style.display = "block";
391 |
392 | media.controls = true;
393 |
394 | var blob = null;
395 | if(video)
396 | blob = new Blob(chunks, { 'type' : 'video/mp4; codecs=opus' });
397 | else
398 | blob = new Blob(chunks, { 'type' : 'audio/webm; codecs=opus' });
399 | chunks = [];
400 | const mediaUrl = window.URL.createObjectURL(blob);
401 | media.src = mediaUrl;
402 |
403 | var mediaFile = null;
404 |
405 | if(video)
406 | mediaFile = rCtx._blobToFile(blob, clipLabel.textContent + '.mp4', {type: "video/mp4"});
407 | else
408 | mediaFile = rCtx._blobToFile(blob, clipLabel.textContent + '.wav', {type: "audio/wav"});
409 |
410 | function resetRecorder(e){
411 |
412 | let evtTgt = e.target;
413 | evtTgt.parentNode.parentNode.removeChild(evtTgt.parentNode);
414 |
415 | if(!video)
416 | rCtx.canvas.style.display = "initial";
417 | rCtx.record.style.display = "inline-block";
418 | rCtx.stop.style.display = "inline-block";
419 |
420 | rCtx.record.style.background = "";
421 | rCtx.record.style.color = "";
422 |
423 | if(video){
424 | this.photo_button.disabled = false;
425 | document.getElementById("buttons").style.display = "block";
426 | document.getElementById("recording_area").style.display = "block";
427 | }
428 |
429 | }
430 |
431 | cancelButton.onclick = function(e) {
432 | resetRecorder(e);
433 | }
434 |
435 | sendButton.onclick = function(e) {
436 | rCtx.sendRecordedFile(mediaFile);
437 | MesiboLog("close stream", rCtx.stream);
438 | rCtx.scope.closeRecorder(); //TBD: Should we close the recorder when the send button is clicked
439 | }
440 |
441 | }
442 |
443 | mediaRecorder.ondataavailable = function(e) {
444 | chunks.push(e.data);
445 | }
446 | }
447 |
448 |
449 |
450 | //main block for doing the audio recording
451 | MesiboRecorder.prototype.initAudioRecording = function(){
452 | MesiboLog("initAudioRecording called..");
453 | this.canvas.height = "60";
454 |
455 | if (!navigator.mediaDevices.getUserMedia){
456 | console.log('getUserMedia not supported on your browser!');
457 | return -1;
458 | }
459 |
460 | const constraints = { audio: true };
461 | let chunks = [];
462 |
463 | let rCtx = window.rCtx;
464 | let onSuccess = function(stream) {
465 | rCtx.recordMedia(stream, false); //Audio Recording
466 | }
467 |
468 | let onError = function(err) {
469 | console.log('The following error occured: ' + err);
470 | }
471 |
472 | navigator.mediaDevices.getUserMedia(constraints).then(onSuccess, onError);
473 |
474 | }
475 |
476 | MesiboRecorder.prototype._blobToFile = function(b, fileName, options){
477 | var file = new File([b], fileName, options);
478 | MesiboLog("_blobToFile, generated file of type", file.type, file.name)
479 | return file;
480 | }
481 |
482 | MesiboRecorder.prototype._dataURItoBlob = function(dataURI) {
483 | // convert base64 to raw binary data held in a string
484 | // doesn't handle URLEncoded DataURIs
485 | var byteString = atob(dataURI.split(',')[1]);
486 |
487 | // separate out the mime component
488 | var mimeString = dataURI.split(',')[0].split(':')[1].split(';')[0]
489 |
490 | // write the bytes of the string to an ArrayBuffer
491 | var ab = new ArrayBuffer(byteString.length);
492 |
493 | // create a view into the buffer
494 | var ia = new Uint8Array(ab);
495 |
496 | // set the bytes of the buffer to the correct values
497 | for (var i = 0; i < byteString.length; i++) {
498 | ia[i] = byteString.charCodeAt(i);
499 | }
500 |
501 | // write the ArrayBuffer to a blob, and you're done
502 | var blob = new Blob([ab], {
503 | type: mimeString
504 | });
505 | return blob;
506 |
507 | }
508 |
509 | MesiboRecorder.prototype.sendRecordedFile = function(f){
510 | MesiboLog("sendRecordedFile", f);
511 | this.scope.selected_file = f;
512 | this.scope.sendFile();
513 | }
514 |
515 | MesiboRecorder.prototype.visualize = function(stream) {
516 | MesiboLog("visualize", stream);
517 | if(!this.audioCtx) {
518 | this.audioCtx = new AudioContext();
519 | }
520 |
521 | const source = this.audioCtx.createMediaStreamSource(stream);
522 |
523 | const analyser = this.audioCtx.createAnalyser();
524 | analyser.fftSize = 2048;
525 | const bufferLength = analyser.frequencyBinCount;
526 | const dataArray = new Uint8Array(bufferLength);
527 |
528 | source.connect(analyser);
529 | //analyser.connect(audioCtx.destination);
530 |
531 | draw();
532 |
533 | function draw() {
534 | let rCtx = window.rCtx;
535 | const WIDTH = rCtx.canvas.width;
536 | const HEIGHT = rCtx.canvas.height;
537 |
538 | requestAnimationFrame(draw);
539 |
540 | analyser.getByteTimeDomainData(dataArray);
541 |
542 | rCtx.canvasCtx.fillStyle = 'rgb(200, 200, 200)';
543 | rCtx.canvasCtx.fillRect(0, 0, WIDTH, HEIGHT);
544 |
545 | rCtx.canvasCtx.lineWidth = 2;
546 | rCtx.canvasCtx.strokeStyle = 'rgb(0, 0, 0)';
547 |
548 | rCtx.canvasCtx.beginPath();
549 |
550 | let sliceWidth = WIDTH * 1.0 / bufferLength;
551 | let x = 0;
552 |
553 |
554 | for(let i = 0; i < bufferLength; i++) {
555 |
556 | let v = dataArray[i] / 128.0;
557 | let y = v * HEIGHT/2;
558 |
559 | if(i === 0) {
560 | rCtx.canvasCtx.moveTo(x, y);
561 | } else {
562 | rCtx.canvasCtx.lineTo(x, y);
563 | }
564 |
565 | x += sliceWidth;
566 | }
567 |
568 | rCtx.canvasCtx.lineTo(rCtx.canvas.width, rCtx.canvas.height/2);
569 | rCtx.canvasCtx.stroke();
570 |
571 | }
572 |
573 | }
574 |
575 | MesiboRecorder.prototype.close = function(){
576 | MesiboLog("MesiboRecorder.close called", this.stream.getTracks(), this.type);
577 |
578 | this.stream.getTracks().forEach(function(track) {
579 | MesiboLog('close Track', track);
580 | track.stop();
581 | });
582 |
583 | }
584 |
585 |
586 |
--------------------------------------------------------------------------------
/mesibo/utils.js:
--------------------------------------------------------------------------------
1 | //utils.js
2 |
3 | /** Copyright (c) 2021 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/samples/js-beta
41 | *
42 | *
43 | */
44 |
45 | let decodeString = (s) => {
46 | // console.log(s);
47 | if(!s)
48 | return "";
49 | return new TextDecoder("utf-8").decode(s);
50 | };
51 |
52 | let isValidString = (ele)=>{
53 | return isValid(ele) && ""!=ele;
54 | };
55 |
56 | // One validation function for all file types
57 | let isValidFileType = (fName, fType)=> {
58 | var extensionLists = {}; //Create an object for all extension lists
59 | extensionLists.video = ['m4v', 'avi', 'mpg', 'mp4', 'webm', 'wmv', 'mov', 'qt', 'mkv', 'flv', 'mpeg', 'm2v', '3gp'];
60 | extensionLists.image = ['jpg', 'jpeg', 'gif', 'bmp', 'png', 'webp', 'svg', 'ico', 'tif', 'tiff'];
61 | extensionLists.audio = ['mp3', 'mp4', 'aac', 'flac', 'm4a', 'wav','wva', 'ogg', 'pcm'];
62 | extensionLists.document = ['doc', 'txt', 'pdf', 'docx', 'xls', 'xlx'];
63 | return extensionLists[fType].indexOf(fName.split('.').pop()) > -1;
64 | }
65 |
66 | let isValid = (ele)=>{
67 | return null!=ele && undefined!=ele;
68 | };
69 |
70 | let isValidImage = (fName) =>{
71 | if(!fName)
72 | return false;
73 |
74 | return isValidFileType(fName, 'image');
75 | };
76 |
77 | let isValidVideo = (fName) =>{
78 | if(!fName)
79 | return false;
80 |
81 | return isValidFileType(fName, 'video');
82 | };
83 |
84 | let isGroup = (user) => {
85 | if(!isValid(user))
86 | return false;
87 |
88 | if(undefined == user.getGroupId())
89 | return false;
90 |
91 | return (user.getGroupId() > 0);
92 | }
93 |
94 | const getUrlInText = (text) => {
95 | //This function returns the first url it finds in text, empty string if no url is found
96 | if(!isValidString(text))
97 | return "";
98 | var matches = text.match(/\bhttps?:\/\/\S+/gi);
99 | if(!isValid(matches) || matches.length == 0)
100 | return "";
101 |
102 | return matches[0];
103 | }
104 |
105 |
106 |
107 |
--------------------------------------------------------------------------------
/messenger.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Mesibo Messenger
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
{{getUserName(getSelfProfile())}}
39 |
{{connection_status}}
40 |
41 |
42 |
43 |
44 |
45 |
46 |
56 |
57 |
58 |
59 |
60 |
You have no messages
61 |
62 |
63 |
64 |
65 |
●
66 |
67 |
{{getFirstLetter(u)}}
68 |
69 |
70 |
71 |
{{getNameFromMessage(u)}}
72 |
73 |
74 |
75 | {{getUserLastMessage(u)}}
76 |
77 |
78 |
79 |
{{getUserLastMessageTime(u)}}
80 |
{{getUserUnreadCount(u, $index)}}
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
Select a Contact
90 |
91 |
92 |
93 |
94 |
95 |
96 |
You have no existing contacts
97 |
98 |
99 |
100 |
{{getUserName(u)}}
101 |
{{getUserStatus(u)}}
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
Profile
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
{{getUserName(display_profile)}}
125 |
+{{display_profile.getAddress()}}
126 |
{{getLastSeen(display_profile)}}
127 |
{{display_profile.getStatus()}}
128 |
129 |
130 |
131 |
132 | {{isBlocked(display_profile)?"Unblock":"Block"}} User
133 |
134 |
135 |
136 |
{{membersList.length}} Members
137 |
138 |
139 |
140 |
141 |
142 |
143 |
{{getUserName(u.getProfile())}}
144 |
{{getMemberType(u)}}
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
Forward Message to..
158 |
159 |
160 |
161 |
You have no existing contacts
162 |
163 |
164 |
165 |
{{getUserName(u)}}
166 |
{{getUserStatus(u)}}
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
327 |
328 |
329 |
330 |
375 |
376 |
377 |
378 |
379 |
380 |
425 |
426 |
427 |
428 |
429 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
460 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
479 |
{{call_alert_message}}
480 |
484 |
485 |
486 |
487 |
488 |
489 |
490 |
491 |
492 |
493 |
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
532 |
533 |
534 |
535 |
536 |
537 |
543 |
544 |
545 |
547 |
548 |
549 |
550 |
554 |
555 |
556 |
557 |
558 |
559 |
560 |
561 |
562 |
563 |
564 |
565 |
571 |
572 |
573 |
574 |
575 |
Video stream not available.
576 |
577 |
578 |
579 | Take Photo
580 |
581 |
582 |
583 |
584 | Record
585 |
586 |
587 | Stop
588 |
589 |
590 |
591 |
593 |
594 |
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
620 |
621 |
622 |
--------------------------------------------------------------------------------
/scripts/controller.js:
--------------------------------------------------------------------------------
1 | //controller.js
2 |
3 | /** Copyright (c) 2023 Mesibo
4 | * https://mesibo.com
5 | * All rights reserved.
6 | *
7 | * Redistribution and use in source and binary forms, with or without
8 | * modification, are permitted provided that the terms and condition mentioned
9 | * on https://mesibo.com as well as following conditions are met:
10 | *
11 | * Redistributions of source code must retain the above copyright notice, this
12 | * list of conditions, the following disclaimer and links to documentation and
13 | * source code repository.
14 | *
15 | * Redistributions in binary form must reproduce the above copyright notice,
16 | * this list of conditions and the following disclaimer in the documentation
17 | * and/or other materials provided with the distribution.
18 | *
19 | * Neither the name of Mesibo nor the names of its contributors may be used to
20 | * endorse or promote products derived from this software without specific prior
21 | * written permission.
22 | *
23 | *
24 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
25 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
28 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
29 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
30 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
31 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
32 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
33 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 | * POSSIBILITY OF SUCH DAMAGE.
35 | *
36 | * Documentation
37 | * https://mesibo.com/documentation/
38 | *
39 | * Source Code Repository
40 | * https://github.com/mesibo/messenger-javascript
41 | *
42 | *
43 | */
44 |
45 |
46 |
47 | //The number of messages loaded into the message area in one read call
48 | const MAX_MESSAGES_READ = 100;
49 |
50 | //The number of users to be loaded (summary)
51 | const MAX_MESSAGES_READ_SUMMARY = 100;
52 |
53 | const MAX_FILE_SIZE_SUPPORTED = 10000000;
54 |
55 |
56 |
57 |
58 | var mesiboWeb = angular.module('MesiboWeb', ['ngSanitize']);
59 | mesiboWeb.filter('unsafe', ['$sce', function ($sce) {
60 | return function (input) {
61 | return $sce.trustAsHtml(input);
62 | }
63 | }]);
64 |
65 | mesiboWeb.directive('imageonload', function() {
66 | return {
67 | restrict: 'A',
68 | link: function(scope, element, attrs) {
69 | if(!scope.$last)
70 | return;
71 |
72 | element.bind('load', function() {
73 | MesiboLog("image load")
74 | scrollToEnd(true);
75 | });
76 |
77 | element.bind('error', function(){
78 | ErrorLog('Error loading image');
79 | });
80 | }
81 | };
82 | });
83 |
84 | mesiboWeb.directive('videoonload', function() {
85 | return {
86 | restrict: 'A',
87 | link: function(scope, element, attrs) {
88 | if(!scope.$last)
89 | return;
90 |
91 | element.bind('loadeddata', function() {
92 | MesiboLog("video loadeddata");
93 | scrollToEnd(true);
94 | });
95 |
96 | element.bind('error', function(){
97 | ErrorLog('Error loading video');
98 | });
99 | }
100 | };
101 | });
102 |
103 |
104 | mesiboWeb.directive('onFinishRender', function($timeout) {
105 | return {
106 | link: function(scope, element, attr) {
107 | if (scope.$last === true) {
108 | $timeout(function() {
109 | scope.$emit(attr.onFinishRender);
110 | });
111 | }
112 | }
113 | };
114 | });
115 |
116 | mesiboWeb.directive("getTarget", function() {
117 | return {
118 | link: function(scope, element, attrs) {
119 | console.log(element);
120 | }
121 | }
122 | });
123 |
124 | mesiboWeb.controller('AppController', ['$scope', '$window', '$anchorScroll', function ($scope, $window, $anchorScroll) {
125 |
126 | console.log("AppController loaded");
127 | var token = getLoginToken();
128 | if(!token || token.length < 16) {
129 | window.location.replace("index.html");
130 | return;
131 | }
132 |
133 | $scope.isConnected = false;
134 | $scope.isLoggedIn = false;
135 | $scope.connection_status = '';
136 | $scope.summarySession = null;
137 | $scope.msg_read_limit_reached = false;
138 | $scope.users_synced = false;
139 | $scope.scroll_messages = null;
140 | $scope.new_contact_name = '';
141 | $scope.new_contact_phone = '';
142 |
143 | $scope.messages = [];
144 | $scope.summary = [];
145 |
146 | $scope.selected_user = null;
147 | $scope.selected_user_count = 0;
148 |
149 | $scope.forward_message = null;
150 |
151 | $scope.mesibo = null;
152 |
153 | //Main UI
154 | $scope.display_profile = null;
155 | $scope.display_image = null;
156 | $scope.membersList = [];
157 | $scope.users_panel_show = false;
158 | $scope.message_area_show = false;
159 |
160 | //Input Area
161 | $scope.input_message_text = "";
162 | $scope.link_preview = null;
163 | $scope.self_profile_name = "";
164 |
165 | //Calls
166 | $scope.is_answer_call = false;
167 | $scope.is_video_call = false;
168 | $scope.is_voice_call = true;
169 | $scope.call_status = "Call Status: ";
170 | $scope.call_alert_message = "";
171 |
172 | //Files
173 | $scope.selected_file = {};
174 | $scope.input_file_caption = "";
175 |
176 | //Recorder
177 | $scope.recorder = null;
178 |
179 | $scope.MAX_MEDIA_WIDTH = '320px';
180 | $scope.MAX_MEDIA_HEIGHT = '240px';
181 |
182 | $scope.MIN_MEDIA_WIDTH = '160px';
183 | $scope.MIN_MEDIA_HEIGHT = '120px';
184 |
185 | $scope.refresh = function(){
186 | $scope.$applyAsync();
187 | }
188 |
189 | $scope.scrollToLastMsg = function() {
190 | $scope.$$postDigest(function () {
191 | //$anchorScroll("messages_end");
192 | scrollToEnd(false);
193 | });
194 | }
195 |
196 | $scope.updateMessagesScroll = function(){
197 |
198 | }
199 |
200 | $scope.$on('onMessagesRendered', function(e) {
201 | MesiboLog("onMessagesRendered");
202 | if($scope.scroll_messages &&
203 | $scope.scroll_messages.scrollTop == 0
204 | && $scope.messageSession
205 | && $scope.messages.length){
206 | MesiboLog('onMessagesRendered');
207 | }
208 |
209 | $scope.scrollToLastMsg();
210 |
211 | });
212 |
213 |
214 | angular.element(document.getElementById('messages')).bind('scroll', function(e){
215 | //MesiboLog("scrolling");
216 | $scope.checkScroll(e);
217 | })
218 |
219 |
220 | $scope.checkScroll = function(e) {
221 | if(!(e && e.target))
222 | return;
223 |
224 | $scope.scroll_messages = e.target;
225 |
226 | if($scope.scroll_messages.scrollTop == 0){
227 | if(!$scope.messageSession){
228 | return;
229 | }
230 |
231 | var m = $scope.messages.length;
232 | if(m == 0){
233 | return;
234 | }
235 |
236 | MesiboLog("checkScroll: Scrolled to top!");
237 | //Load more messages
238 | $scope.messageSession.read(MAX_MESSAGES_READ);
239 | }
240 |
241 | }
242 |
243 |
244 | $scope.getMesibo = function(){
245 | return $scope.mesibo;
246 | }
247 |
248 | $scope.showAvailableUsers = function() {
249 | MesiboLog('showAvailableUsers');
250 | $scope.users_panel_show = true;
251 |
252 | //prompt to add a contact if no contacts available
253 | if(!$scope.hasContacts())
254 | $scope.showContactForm();
255 |
256 | $scope.refresh();
257 | }
258 |
259 | $scope.hideAvailableUsers = function() {
260 | MesiboLog('hideAvailableUsers');
261 | $scope.users_panel_show = false;
262 | $scope.refresh();
263 | }
264 |
265 | $scope.getContacts = function(){
266 | var c = $scope.mesibo.getSortedProfiles();
267 | return c;
268 | }
269 |
270 | $scope.hasContacts = function(){
271 | var c = $scope.getContacts();
272 | if(c && c.length) return true;
273 | return false;
274 | }
275 |
276 | // [OPTIONAL] refer to the comment below
277 | $scope.Mesibo_onGroupMembers = function(p, members) {
278 | $scope.membersList = members;
279 | $scope.refresh();
280 | }
281 |
282 | $scope.showProfile = function(p) {
283 | if(!p)
284 | return;
285 | if(p.isSelfProfile()) {
286 | $scope.self_profile_name = p.getName();
287 | }
288 | $scope.display_profile = p;
289 | $scope.display_image = $scope.display_profile.getImage();
290 |
291 | // various profile information keys
292 | var values = p.getValues();
293 |
294 | $scope.membersList = [];
295 | if(p.getGroupId() > 0) {
296 | // you can either pass a function or listenr, this code list
297 | // both teh syntax for reference
298 | if(true) {
299 | p.getMembers(0, false, $scope);
300 | } else {
301 | p.getMembers(0, false, function(p, members) {
302 | $scope.membersList = members;
303 | $scope.refresh();
304 | });
305 | }
306 | }
307 | $scope.refresh();
308 | };
309 |
310 | $scope.editProfile = function() {
311 | $scope.showProfile($scope.getSelfProfile());
312 | }
313 |
314 | $scope.showProfileFromMessage = function(m) {
315 | var p = $scope.getProfileFromMessage(m);
316 | if(!p)
317 | return;
318 | $scope.showProfile(p);
319 |
320 | $scope.refresh();
321 | };
322 |
323 | $scope.hideProfileSettings = function() {
324 | $scope.display_profile = null;
325 | $scope.membersList = [];
326 | $scope.refresh();
327 | };
328 |
329 | $scope.hideForwardList = function() {
330 | $scope.forward_message = null;
331 | $scope.refresh();
332 | };
333 |
334 | //fm is the message to be forwarded
335 | $scope.showForwardList = function(fm){
336 | if(!fm)
337 | return;
338 |
339 | $scope.forward_message = fm;
340 | $scope.refresh();
341 | }
342 |
343 | $scope.showDeliveryTime = function(m) {
344 | if(!m || !m.isMessage() || !m.mid || !m.isOutgoing()) return false;
345 | if(!m.isDelivered() && !m.isReadByPeer()) return false;
346 | var o = document.getElementById('deltime-' + m.mid);
347 | if(!o) return false;
348 | m.getDeliveryTimestamp(null, function(d) {
349 | if(!d) o.style.display = 'none';
350 | else o.innerHTML = "Delivered on: " + d.getDate(true) + " " + d.getTime(true);
351 | });
352 | return true;
353 | }
354 |
355 | $scope.showReadTime = function(m) {
356 | if(!m || !m.isMessage() || !m.mid || !m.isOutgoing()) return false;
357 | if(!m.isReadByPeer()) return false;
358 | var o = document.getElementById('readtime-' + m.mid);
359 | if(!o) return false;
360 | m.getReadTimestamp(null, function(d) {
361 | if(!d) o.style.display = 'none';
362 | else o.innerHTML = "Read on: " + d.getDate(true) + " " + d.getTime(true);
363 | });
364 | return true;
365 | }
366 |
367 | $scope.showMessageHeader = function(m) {
368 | return (m.isForwarded() || m.isReply() || m.isModified());
369 | }
370 |
371 | $scope.getMessageHeader = function(m) {
372 | if(m.isForwarded()) return "Forwarded";
373 | if(m.isReply()) return "Reply";
374 | if(m.isModified()) return "Edited";
375 | return "";
376 | }
377 |
378 | $scope.isSent = function(msg){
379 | return isSentMessage(msg.status);
380 | }
381 |
382 | $scope.isReceived = function(msg){
383 | return !isSentMessage(msg.status);
384 | }
385 |
386 | $scope.isMessageVisible = function(m) {
387 | return true;
388 | if(m.message || m.isDeleted() || $scope.isFileMsg(m)) return true;
389 | return false;
390 | }
391 |
392 | $scope.getMessageText = function(m) {
393 | if(m.isDeleted()) return "This message was deleted";
394 | if(m.isDate())
395 | return m.getTimestamp().getDate(true, "Today", "Yesterday");
396 |
397 | if(!m.isCall())
398 | return m.message;
399 |
400 | var type = m.isVideoCall()?"Video":"Audio";
401 | var dir = "Missed";
402 | if(m.isIncomingCall()) dir = "Incoming";
403 | else if(m.isOutgoingCall()) dir = "Outgoing";
404 | return dir + " " + type + " call at " + m.getTimestamp().getTime();
405 | }
406 |
407 | $scope.getFormattedMessageText = function(m) {
408 | var text = $scope.getMessageText(m);
409 | if(!text) return "";
410 | var t = text.split(/(https?:\/\/\S*)\b/);
411 | if(!t) return text;
412 | text = '';
413 | for(var i=0; i < t.length; i++) {
414 | if(t[i].startsWith('https://') || t[i].startsWith('http://')) {
415 | text += '