├── CHANGELOG.md
├── README.md
├── package.json
├── plugin.xml
├── src
└── ios
│ ├── UtilsPlugin.h
│ └── UtilsPlugin.m
└── www
├── bit6.js
├── index-bit6.js
├── phonegap-websocket.js
├── push-wrappers.js
└── utils.js
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.11.0 [2017-05-05]
2 |
3 | ### Release Notes
4 | Changed plugin name, added package.json, updated README
5 |
6 | ### Bugfixes
7 | - Fixed the issue with sending video attachments for which the thumbnail generation fails.
8 |
9 | ## 0.10.0 [2016-12-23]
10 |
11 | ### Release Notes
12 | Phonertc module is removed. For Webrtc support on iOS and on Adnroid<5 iosrtc and Crosswalk webview plugins are used.
13 |
14 |
15 | ### Breaking Changes
16 | - For voice/video calls (iOS, Android<5) the plugins mentioned above are required.
17 | - For real time notifications support on Android<4.4 Crosswalk Webview is required.
18 |
19 | ### Bugfixes
20 | - Fixed the issue with missing remote video during iOS calls
21 | - iOS 10 support
22 |
23 | ## 0.9.9 [2016-09-02]
24 |
25 | ### Release Notes
26 |
27 | This release adds support for camera selection for video calls and audio recording during the calls.
28 |
29 | ### Features
30 |
31 | - Added support for camera selection on initiating a video call
32 | - Added support for call recording
33 |
34 | ### Bugfixes
35 |
36 | - Fixed the issue with push key cleanup. Now after logout the user will not get push notifications.
37 |
38 |
39 | ## 0.9.8 [2016-05-25]
40 |
41 | ### Release Notes
42 |
43 | This release adds support for custom push handling, switchCamera api and a mechanism to keep the user logged in.
44 |
45 | ### Features
46 |
47 | - Added support for custom push handling/implementation.
48 | - Added switchCamera method to Bit6 api: supported only on Android < 5.0
49 | - Added a mechanism to keep the user logged in after app restart.
50 |
51 | ### Bugfixes
52 |
53 | - Fixed the issue with opening the app by tapping on a notification when the app is closed.
54 |
55 |
56 | ## 0.9.7 [2016-04-28]
57 |
58 | ### Release Notes
59 |
60 | This release adds support for new push plugin (phonegap-plugin-push) while keeping the compatibility with the previously supported push plugins.
61 |
62 | ### Features
63 |
64 | - Added support for phonegap-plugin-push
65 | - Added push alerts localizatoin support on iOS. Works with new phonegap-plugin-push only
66 |
67 | ### Bugfixes
68 |
69 | - Fixed the call issue appearing on some Android devices caused by a missing permission
70 | - Fixed issues with Intel XDK Emulator (iOS, Android) and debugging in AppPreview (Android only)
71 | - Fixed camera selection on Android to use the front camera for video calls
72 |
73 | ## 0.9.6.1 [2016-03-15]
74 |
75 | ### Release Notes
76 |
77 | This release adds support for cordova-ios 4.x, reduces plugin size, and fixes an issue with iOS push notifications for Ad Hoc builds.
78 |
79 | ### Breaking Changes
80 |
81 | - iOS simulator build is not supported - i386 slice is removed from iOS WebRTC library
82 | - Push plugin dependency must be specified manually in the application - see 'Push notifications' section in README
83 |
84 | ### Features
85 |
86 | - Added support for cordova-ios 4.x
87 | - Significantly reduced plugin size
88 | - Updated WebRTC lib: Android - m49
89 |
90 | ### Bugfixes
91 |
92 | - Fixed iOS push issue for Ad Hoc builds
93 |
94 |
95 | ## 0.9.6 [2016-02-11]
96 |
97 | ### Release Notes
98 |
99 | This release has improved API for video elements, updated native WebRTC libs and some important bug fixes.
100 |
101 | ### Features
102 |
103 | - New video element layout mechanism
104 | - External authentication support
105 | - Automatic selection of iOS APNS environment
106 | - Updated WebRTC libs : iOS - m49, Andoid - m48
107 |
108 | ### Bugfixes
109 |
110 | - Fixed the call setup issue between devices (caused by incorrect buffering of ICE candidates)
111 | - Fixed the whitelist plugin warning about security policy tag
112 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Bit6 Cordova Plugin
2 | -------------------
3 | [](https://github.com/bit6/bit6-cordova)
4 |
5 | Add voice/video calling, texting, and rich media messaging into any mobile or web application.
6 |
7 | Get Bit6 sample app running in 10 minutes - follow our Quick Start guides for:
8 | * [Cordova CLI](http://docs.bit6.com/start/cordova-cli/)
9 | * [Intel XDK](http://docs.bit6.com/start/cordova-xdk/)
10 | * [Telerik App Builder](http://docs.bit6.com/start/cordova-telerik/)
11 |
12 | ### Prerequisites
13 | * Get the API Key at [Bit6 Dashboard](https://dashboard.bit6.com).
14 |
15 | ### Installation
16 | ```sh
17 | $ cordova plugin add https://github.com/bit6/bit6-cordova
18 | ```
19 |
20 | ### Configuration
21 | Initialize Bit6 Plugin with your API key.
22 | ```js
23 | document.addEventListener("deviceready", onDeviceReady, false);
24 | function onDeviceReady() {
25 | var opts = {'apikey': 'yourApiKey'};
26 | var b6 = Bit6.init(opts);
27 | // Bit6 SDK has been initialized
28 | }
29 | ```
30 |
31 | ### Video/Voice calls on iOS
32 |
33 | To enable call support on iOS add [iosrtc](https://github.com/eface2face/cordova-plugin-iosrtc) plugin
34 |
35 | ```
36 | cordova plugin add cordova-plugin-iosrtc
37 | ```
38 |
39 | Note: Check the plugin requirements on [this page](https://github.com/eface2face/cordova-plugin-iosrtc) and [building page](https://github.com/eface2face/cordova-plugin-iosrtc/blob/master/docs/Building.md)
40 |
41 | Note: no need to add iosrtc if your app does not use audio/video calls.
42 |
43 |
44 | ### Video/Voice calls on Android < 5
45 |
46 | To enable call support on Android < 5 add [Crosswalk Webview](https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview) plugin
47 |
48 | ```
49 | cordova plugin add cordova-plugin-crosswalk-webview
50 | ```
51 |
52 | ### Real time notifications on Android<4.4
53 |
54 | For real time notifications (websocket) on Android<4.4 Crosswalk Webview is required.
55 |
56 | ### Permissions on Android >= 6
57 |
58 | For call support on Android >= 6 grant `Camera` and `Microphone` permissions from `Settings->Apps->YourApp->Permissions`.
59 |
60 | `Storage` permission is required to support message attachments.
61 |
62 | ### Quickstart
63 | Create a user with a password authentication:
64 | ```js
65 | b6.session.signup({identity: 'usr:john', password: 'secret'}, function(err) {
66 | if (err) {
67 | console.log('login error', err);
68 | } else {
69 | console.log('login successful');
70 | }
71 | });
72 | ```
73 | Send a message to another user:
74 | ```js
75 | b6.compose('usr:tom').text('Hello!').send(function(err) {
76 | if (err) {
77 | console.log('error', err);
78 | } else {
79 | console.log('message sent');
80 | }
81 | });
82 | ```
83 | Make a video call:
84 | ```js
85 | // Start an outgoing call and get a call controller (Dialog)
86 | var d = b6.startCall('usr:tom', {audio: true, video: true});
87 | ```
88 |
89 | ### Documentation
90 | Bit6 Cordova Plugin exposes the same API as [Bit6 JS SDK](https://github.com/bit6/bit6-js-sdk). Check Bit6 [JS documentation](http://docs.bit6.com/guides/js/).
91 |
92 | ### Demo app
93 | The complete source code is available in the [demo repo](https://github.com/bit6/bit6-cordova-demo). Check out the same demo app running with JS SDK at http://demo.bit6.com.
94 |
95 | ### Supported platforms
96 | * iOS
97 | * Android
98 | * Browser
99 |
100 | ### Push notifications
101 |
102 | Push Notification support is required for receiving incoming calls and messages.
103 |
104 | To enable this functionality please add one of these push plugins to your project:
105 |
106 | * [phonegap-plugin-push](https://github.com/phonegap/phonegap-plugin-push) (recommended)
107 | ```sh
108 | # Set any value for SENDER_ID. Bit6 plugin will override it with the correct one.
109 | $ cordova plugin add phonegap-plugin-push --variable SENDER_ID="XXXXXXX"
110 | ```
111 |
112 | * [Telerik Push Plugin](https://github.com/Telerik-Verified-Plugins/PushNotification)
113 | ```sh
114 | $ cordova plugin add https://github.com/Telerik-Verified-Plugins/PushNotification
115 | ```
116 |
117 | * [Legacy PhoneGap Push Plugin](https://github.com/phonegap-build/PushPlugin) If you use cordova-ios 3.x
118 |
119 |
120 | Then complete platform-specific configuration:
121 |
122 | * __iOS APNs__ ([detailed guide](http://docs.bit6.com/guides/push-apns/))
123 | 1. Generate APNS certificate in iTunes Connect.
124 | 2. Export it into a p12 file.
125 | 3. Add the file to your app in [Bit6 Dashboard](https://dashboard.bit6.com).
126 |
127 | * __Android GCM__ ([detailed guide](http://docs.bit6.com/guides/push-gcm/))
128 | 1. Get the project number and server key from [Google Dev Console](http://developer.android.com/google/gcm/gs.html).
129 | 2. Add project number and server key for your app in [Bit6 Dashboard](https://dashboard.bit6.com).
130 |
131 | ### Building with Xcode 7
132 | Please disable Bitcode support when building your Cordova app with Xcode.
133 | Go to `Build Settings`, set `Enable Bitcode` to `No`.
134 |
135 | ### Third-party libraries
136 | Bit6 plugin leverages code from the excellent [WebRTC](http://www.webrtc.org/), [PhoneRTC](https://github.com/alongubkin/phonertc) and [phonegap-websocket](https://github.com/mkuklis/phonegap-websocket/) projects.
137 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "cordova-plugin-bit6",
3 | "version": "0.11.0",
4 | "description": "Add voice and video calling, text and media messaging to your app",
5 | "cordova": {
6 | "id": "cordova-plugin-bit6",
7 | "platforms": [
8 | "ios",
9 | "android",
10 | "browser"
11 | ]
12 | },
13 | "repository": {
14 | "type": "git",
15 | "url": "git+https://github.com/bit6/bit6-cordova.git"
16 | },
17 | "keywords": [
18 | "webrtc",
19 | "messaging",
20 | "communication",
21 | "text",
22 | "ecosystem:cordova",
23 | "cordova-ios",
24 | "cordova-android"
25 | ],
26 | "engines": [
27 | {
28 | "name": "cordova-ios",
29 | "version": ">=3.0.0"
30 | }
31 | ],
32 | "author": "bit6",
33 | "license": "Apache 2.0",
34 | "bugs": {
35 | "url": "https://github.com/bit6/bit6-cordova/issues"
36 | },
37 | "homepage": "https://github.com/bit6/bit6-cordova#readme"
38 | }
39 |
--------------------------------------------------------------------------------
/plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 | Bit6
8 | Add voice and video calling, text and media messaging to your app
9 | Apache 2.0
10 | webrtc,messaging,communication,text
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 |
39 | Requires camera for video calls
40 |
41 |
42 | Requires microphone for calls
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
--------------------------------------------------------------------------------
/src/ios/UtilsPlugin.h:
--------------------------------------------------------------------------------
1 | #import
2 |
3 | @interface UtilsPlugin : CDVPlugin
4 |
5 | - (void) isApnsProduction: (CDVInvokedUrlCommand*)command;
6 |
7 | @end
8 |
--------------------------------------------------------------------------------
/src/ios/UtilsPlugin.m:
--------------------------------------------------------------------------------
1 | #import "UtilsPlugin.h"
2 |
3 | @implementation UtilsPlugin
4 |
5 | - (void) pluginInitialize
6 | {
7 | [super pluginInitialize];
8 | }
9 |
10 |
11 | -(void) isApnsProduction: (CDVInvokedUrlCommand*)command
12 | {
13 | static BOOL isDevelopment = NO;
14 | #if TARGET_IPHONE_SIMULATOR
15 | isDevelopment = YES;
16 | #else
17 | NSData *data = [NSData dataWithContentsOfFile:[NSBundle.mainBundle pathForResource:@"embedded" ofType:@"mobileprovision"]];
18 | if (data) {
19 | const char *bytes = [data bytes];
20 | NSMutableString *profile = [[NSMutableString alloc] initWithCapacity:data.length];
21 | for (NSUInteger i = 0; i < data.length; i++) {
22 | [profile appendFormat:@"%c", bytes[i]];
23 | }
24 | NSString *cleared = [[profile componentsSeparatedByCharactersInSet:NSCharacterSet.whitespaceAndNewlineCharacterSet] componentsJoinedByString:@""];
25 | isDevelopment = [cleared rangeOfString:@"get-task-allow"].length > 0;
26 | }
27 | #endif
28 | CDVPluginResult* pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsBool: !isDevelopment];
29 | [self.commandDelegate sendPluginResult:pluginResult callbackId:command.callbackId];
30 | }
31 |
32 |
33 | @end
34 |
--------------------------------------------------------------------------------
/www/bit6.js:
--------------------------------------------------------------------------------
1 | // bit6 - v0.11.0
2 |
3 | (function() {
4 | var slice = [].slice;
5 |
6 | window.bit6 = {};
7 |
8 | bit6.EventEmitter = (function() {
9 | function EventEmitter() {
10 | this.events = {};
11 | }
12 |
13 | EventEmitter.prototype.emit = function() {
14 | var args, event, i, len, listener, ref;
15 | event = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : [];
16 | if (!this.events[event]) {
17 | return false;
18 | }
19 | ref = this.events[event];
20 | for (i = 0, len = ref.length; i < len; i++) {
21 | listener = ref[i];
22 | listener.apply(null, args);
23 | }
24 | return true;
25 | };
26 |
27 | EventEmitter.prototype.addListener = function(event, listener) {
28 | var base;
29 | this.emit('newListener', event, listener);
30 | ((base = this.events)[event] != null ? base[event] : base[event] = []).push(listener);
31 | return this;
32 | };
33 |
34 | EventEmitter.prototype.on = EventEmitter.prototype.addListener;
35 |
36 | EventEmitter.prototype.once = function(event, listener) {
37 | var fn;
38 | fn = (function(_this) {
39 | return function() {
40 | _this.removeListener(event, fn);
41 | return listener.apply(null, arguments);
42 | };
43 | })(this);
44 | this.on(event, fn);
45 | return this;
46 | };
47 |
48 | EventEmitter.prototype.removeListener = function(event, listener) {
49 | var l;
50 | if (!this.events[event]) {
51 | return this;
52 | }
53 | this.events[event] = (function() {
54 | var i, len, ref, results;
55 | ref = this.events[event];
56 | results = [];
57 | for (i = 0, len = ref.length; i < len; i++) {
58 | l = ref[i];
59 | if (l !== listener) {
60 | results.push(l);
61 | }
62 | }
63 | return results;
64 | }).call(this);
65 | return this;
66 | };
67 |
68 | EventEmitter.prototype.off = EventEmitter.prototype.removeListener;
69 |
70 | EventEmitter.prototype.removeAllListeners = function(event) {
71 | delete this.events[event];
72 | return this;
73 | };
74 |
75 | return EventEmitter;
76 |
77 | })();
78 |
79 | }).call(this);
80 |
81 | (function() {
82 | bit6.Conversation = (function() {
83 | function Conversation(id) {
84 | this.id = id;
85 | this.unread = 0;
86 | this.updated = 0;
87 | this.messages = [];
88 | this.modified = true;
89 | this.uri = this.id;
90 | }
91 |
92 | Conversation.prototype.isGroup = function() {
93 | return this.id.indexOf('grp:') === 0;
94 | };
95 |
96 | Conversation.prototype.getUnreadCount = function() {
97 | return this.unread;
98 | };
99 |
100 | Conversation.prototype.getLastMessage = function() {
101 | var n;
102 | n = this.messages.length;
103 | if (n > 0) {
104 | return this.messages[n - 1];
105 | } else {
106 | return null;
107 | }
108 | };
109 |
110 | Conversation.prototype.appendMessage = function(m) {
111 | this.messages.push(m);
112 | if (this.updated < m.created) {
113 | this.updated = m.created;
114 | }
115 | this._updateUnreadCount();
116 | this.modified = true;
117 | return this.modified;
118 | };
119 |
120 | Conversation.prototype.updateMessage = function(m) {
121 | this._updateUnreadCount();
122 | return this.modified;
123 | };
124 |
125 | Conversation.prototype.removeMessage = function(m) {
126 | var i, idx, j, len, n, o, ref, removed;
127 | n = this.messages.length;
128 | if (n > 0 && this.messages[n - 1].id === m.id) {
129 | this.modified = true;
130 | }
131 | idx = -1;
132 | ref = this.messages;
133 | for (i = j = 0, len = ref.length; j < len; i = ++j) {
134 | o = ref[i];
135 | if (o.id === m.id) {
136 | idx = i;
137 | break;
138 | }
139 | }
140 | if (idx >= 0) {
141 | removed = this.messages.splice(idx, 1);
142 | }
143 | this._updateUnreadCount();
144 | return this.modified;
145 | };
146 |
147 | Conversation.prototype._updateUnreadCount = function() {
148 | var j, len, m, num, ref;
149 | num = 0;
150 | ref = this.messages;
151 | for (j = 0, len = ref.length; j < len; j++) {
152 | m = ref[j];
153 | if (m.canMarkRead()) {
154 | num++;
155 | }
156 | }
157 | this.modified || (this.modified = num !== this.unread);
158 | return this.unread = num;
159 | };
160 |
161 | Conversation.prototype.domId = function() {
162 | if (!this.id) {
163 | console.log('ConvId is null', this);
164 | return null;
165 | }
166 | return bit6.Session.base64urlEncode(this.id);
167 | };
168 |
169 | Conversation.fromDomId = function(t) {
170 | if (!t) {
171 | return t;
172 | }
173 | return bit6.Session.base64urlDecode(t);
174 | };
175 |
176 | return Conversation;
177 |
178 | })();
179 |
180 | }).call(this);
181 |
182 | (function() {
183 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
184 | hasProp = {}.hasOwnProperty;
185 |
186 | bit6.Dialog = (function(superClass) {
187 | extend(Dialog, superClass);
188 |
189 | function Dialog(client, outgoing, other, invite, options) {
190 | var base, base1, base2, i, j, len, len1, myaddr, myname, ref, ref1, ref2, t;
191 | this.client = client;
192 | this.outgoing = outgoing;
193 | this.other = other;
194 | this.invite = invite;
195 | this.options = options;
196 | Dialog.__super__.constructor.apply(this, arguments);
197 | this.me = this.client.session.identity;
198 | if (this.options == null) {
199 | this.options = {};
200 | }
201 | ref = ['audio', 'video', 'screen', 'data'];
202 | for (i = 0, len = ref.length; i < len; i++) {
203 | t = ref[i];
204 | if ((base = this.options)[t] == null) {
205 | base[t] = false;
206 | }
207 | }
208 | if ((base1 = this.options).offnet == null) {
209 | base1.offnet = false;
210 | }
211 | if (this.options.offnet || this.other.indexOf('grp:') === 0) {
212 | this.options.mode = 'mix';
213 | }
214 | if ((base2 = this.options).mode == null) {
215 | base2.mode = 'p2p';
216 | }
217 | this.options.data = this.options.mode === 'p2p';
218 | this.remoteOptions = {};
219 | ref1 = ['audio', 'video', 'offnet'];
220 | for (j = 0, len1 = ref1.length; j < len1; j++) {
221 | t = ref1[j];
222 | this.remoteOptions[t] = this.options[t] && !this.outgoing;
223 | }
224 | this.params = {
225 | callID: null,
226 | userVariables: {
227 | mode: this.options.mode,
228 | offnet: this.options.offnet
229 | }
230 | };
231 | myaddr = this.me + '@' + this.client.apikey;
232 | myname = (ref2 = this.client.session.displayName) != null ? ref2 : this.me;
233 | if (this.outgoing) {
234 | this.state = 'req';
235 | this.params.destination_number = this.other + '@' + this.client.apikey;
236 | this.params.caller_id_name = myname;
237 | this.params.caller_id_number = myaddr;
238 | this.params.callID = bit6.JsonRpc.generateGUID();
239 | } else {
240 | this.state = 'pre-answer';
241 | this.params.callee_id_name = myname;
242 | this.params.callee_id_number = myaddr;
243 | }
244 | this._recording = false;
245 | this.transfers = [];
246 | this.renegotiating = false;
247 | }
248 |
249 | Dialog.prototype.supports = function(c) {
250 | switch (c) {
251 | case 'recording':
252 | return this.options.mode === 'mix';
253 | }
254 | return false;
255 | };
256 |
257 | Dialog.prototype.connect = function(opts) {
258 | var i, len, newv, oldv, ref, t;
259 | if (opts == null) {
260 | opts = {};
261 | }
262 | ref = ['audio', 'video', 'screen'];
263 | for (i = 0, len = ref.length; i < len; i++) {
264 | t = ref[i];
265 | if (opts[t] == null) {
266 | continue;
267 | }
268 | newv = opts[t];
269 | oldv = this.options[t];
270 | if (oldv === newv) {
271 | continue;
272 | }
273 | this.options[t] = newv;
274 | }
275 | this.client._ensureRtcCapture(this.options, (function(_this) {
276 | return function(err) {
277 | if (err != null) {
278 | _this.emit('error', 'Unable to start media');
279 | return _this.hangup();
280 | }
281 | return _this._onMediaReady();
282 | };
283 | })(this));
284 | return this;
285 | };
286 |
287 | Dialog.prototype.hasVideoStreams = function() {
288 | return this.options.video || this.remoteOptions.video;
289 | };
290 |
291 | Dialog.prototype.recording = function(flag) {
292 | var args, m;
293 | if ((flag == null) || this._recording === flag) {
294 | return this._recording;
295 | }
296 | this._recording = flag;
297 | args = {
298 | callID: this.params.callID,
299 | me: this.me,
300 | other: this.other,
301 | outgoing: this.outgoing
302 | };
303 | m = flag ? 'startRecording' : 'stopRecording';
304 | return this.client._sendNotification('control', m, args);
305 | };
306 |
307 | Dialog.prototype._onMediaReady = function() {
308 | var msg;
309 | if (!this.renegotiating) {
310 | if (!this.outgoing) {
311 | this._sendAcceptRejectIncomingCall(true);
312 | }
313 | this.emit('progress');
314 | }
315 | if (this.rtc == null) {
316 | this._createRtc();
317 | }
318 | this.rtc.update(this.client.capture, this.options, this.remoteOptions);
319 | if (this.renegotiating && !this.outgoing) {
320 | msg = {
321 | audio: this.options.audio,
322 | video: this.options.video
323 | };
324 | return this.sendJson('reneg2', msg);
325 | }
326 | };
327 |
328 | Dialog.prototype._createRtc = function() {
329 | var iceServers;
330 | this.rtc = this.client._createRtc();
331 | this.rtc.on('offerAnswer', (function(_this) {
332 | return function(offerAnswer) {
333 | return _this._sendOfferAnswer(offerAnswer, function(err, result) {
334 | if (offerAnswer.type === 'answer') {
335 | return _this.emit('answer');
336 | }
337 | });
338 | };
339 | })(this));
340 | this.rtc.on('video', (function(_this) {
341 | return function(v, op) {
342 | return _this.client._emitVideoEvent(v, _this, op);
343 | };
344 | })(this));
345 | this.rtc.on('dcOpen', (function(_this) {
346 | return function() {
347 | return _this._startNextPendingTransfer();
348 | };
349 | })(this));
350 | this.rtc.on('transfer', (function(_this) {
351 | return function(tr) {
352 | var n, o;
353 | if (!tr.outgoing) {
354 | n = tr.info.name;
355 | }
356 | if ('offer2' === n || 'answer2' === n) {
357 | if (tr.completed()) {
358 | o = tr.json();
359 | _this._gotRemoteOfferAnswer(o.type, o);
360 | }
361 | } else if ('reneg2' === n) {
362 | if (tr.completed()) {
363 | o = tr.json();
364 | if (o.audio != null) {
365 | _this.remoteOptions.audio = o.audio;
366 | }
367 | if (o.video != null) {
368 | _this.remoteOptions.video = o.video;
369 | }
370 | _this.connect();
371 | }
372 | } else {
373 | _this.emit('transfer', tr);
374 | }
375 | if (tr.err) {
376 |
377 | } else if (tr.completed() && tr.outgoing) {
378 | return _this._startNextPendingTransfer();
379 | }
380 | };
381 | })(this));
382 | iceServers = this.client.session.config.rtc.iceServers;
383 | return this.rtc.init(this.outgoing, iceServers);
384 | };
385 |
386 | Dialog.prototype._startNextPendingTransfer = function() {
387 | var i, len, ref, results, t;
388 | ref = this.transfers;
389 | results = [];
390 | for (i = 0, len = ref.length; i < len; i++) {
391 | t = ref[i];
392 | if (t.pending()) {
393 | this.rtc.startOutgoingTransfer(t);
394 | break;
395 | } else {
396 | results.push(void 0);
397 | }
398 | }
399 | return results;
400 | };
401 |
402 | Dialog.prototype.sendFile = function(info, data) {
403 | var tr;
404 | tr = new bit6.Transfer(true, info, data);
405 | this.transfers.push(tr);
406 | return tr._ensureSourceData((function(_this) {
407 | return function() {
408 | if (tr.err != null) {
409 | _this.emit('transfer', tr);
410 | }
411 | if (tr.data != null) {
412 | return _this.rtc.startOutgoingTransfer(tr);
413 | }
414 | };
415 | })(this));
416 | };
417 |
418 | Dialog.prototype.sendJson = function(name, o) {
419 | var tr;
420 | tr = new bit6.Transfer(true, {
421 | name: name
422 | });
423 | tr.json(o);
424 | this.transfers.push(tr);
425 | if (tr.data != null) {
426 | return this.rtc.startOutgoingTransfer(tr);
427 | }
428 | };
429 |
430 | Dialog.prototype.hangup = function() {
431 | if (this.rtc) {
432 | this.rtc.stop();
433 | this.rtc = null;
434 | this._sendHangupCall();
435 | } else if (!this.outgoing) {
436 | this._sendAcceptRejectIncomingCall(false);
437 | }
438 | return this.emit('end');
439 | };
440 |
441 | Dialog.prototype._sendOfferAnswer = function(offerAnswer, cb) {
442 | var msg, ref, ref1;
443 | msg = offerAnswer;
444 | if (msg.type === 'offer') {
445 | if (this.renegotiating) {
446 | return this.sendJson('offer2', msg);
447 | } else {
448 | this.state = 'sent-offer';
449 | msg.dialogParams = this.params;
450 | return (ref = this.client.rpc) != null ? ref.call('verto.invite', msg, cb) : void 0;
451 | }
452 | } else if (msg.type === 'answer') {
453 | if (this.renegotiating) {
454 | return this.sendJson('answer2', msg);
455 | } else {
456 | this.state = 'sent-answer';
457 | msg.dialogParams = this.params;
458 | this.renegotiating = true;
459 | return (ref1 = this.client.rpc) != null ? ref1.call('verto.answer', msg, cb) : void 0;
460 | }
461 | }
462 | };
463 |
464 | Dialog.prototype._sendAcceptRejectIncomingCall = function(accept) {
465 | var ref, type;
466 | type = accept ? 'accept' : 'reject';
467 | return this.client._sendNotification((ref = this.invite) != null ? ref.rdest : void 0, type);
468 | };
469 |
470 | Dialog.prototype._sendHangupCall = function() {
471 | var msg, ref;
472 | this.state = 'sent-bye';
473 | msg = {
474 | dialogParams: this.params
475 | };
476 | return (ref = this.client.rpc) != null ? ref.call('verto.bye', msg, (function(_this) {
477 | return function(err, result) {};
478 | })(this)) : void 0;
479 | };
480 |
481 | Dialog.prototype.handleRpcCall = function(method, params) {
482 | switch (method) {
483 | case 'verto.bye':
484 | return this._gotHangup(params);
485 | case 'verto.invite':
486 | this.params.callID = params.callID;
487 | this.params.caller_id_name = params.caller_id_name;
488 | this.params.caller_id_number = params.caller_id_number;
489 | return this._gotRemoteOfferAnswer('offer', params);
490 | case 'verto.answer':
491 | this._gotRemoteOfferAnswer('answer', params);
492 | return this.emit('answer');
493 | }
494 | };
495 |
496 | Dialog.prototype._gotRemoteOfferAnswer = function(type, offerAnswer) {
497 | this.state = 'got-' + type;
498 | offerAnswer.type = type;
499 | if (type === 'answer') {
500 | this.renegotiating = true;
501 | }
502 | if (this.rtc != null) {
503 | return this.rtc.gotRemoteOfferAnswer(offerAnswer, this.client.capture);
504 | } else {
505 | return console.log('Error: RTC not inited');
506 | }
507 | };
508 |
509 | Dialog.prototype._gotHangup = function(d) {
510 | this.state = 'got-bye';
511 | if (this.rtc != null) {
512 | this.rtc.stop();
513 | this.rtc = null;
514 | return this.emit('end');
515 | }
516 | };
517 |
518 | return Dialog;
519 |
520 | })(bit6.EventEmitter);
521 |
522 | }).call(this);
523 |
524 | (function() {
525 | bit6.Group = (function() {
526 | function Group(id) {
527 | this.id = id;
528 | this.meta = null;
529 | this.permissions = null;
530 | this.members = [];
531 | this.updated = 0;
532 | }
533 |
534 | Group.prototype.update = function(o, forceUpdate) {
535 | var k, v;
536 | if (!forceUpdate) {
537 | if (o.updated == null) {
538 | return false;
539 | }
540 | if (this.updated === o.updated) {
541 | return false;
542 | }
543 | }
544 | for (k in o) {
545 | v = o[k];
546 | this[k] = v;
547 | }
548 | return true;
549 | };
550 |
551 | Group.prototype._updateMemberProfile = function(ident, profile) {
552 | var i, len, m, ref;
553 | ref = this.members;
554 | for (i = 0, len = ref.length; i < len; i++) {
555 | m = ref[i];
556 | if (m.id === ident) {
557 | m.profile = profile;
558 | return true;
559 | }
560 | }
561 | return false;
562 | };
563 |
564 | Group.prototype.getMember = function(ident) {
565 | var i, len, m, ref;
566 | ref = this.members;
567 | for (i = 0, len = ref.length; i < len; i++) {
568 | m = ref[i];
569 | if (m.id === ident) {
570 | return m;
571 | }
572 | }
573 | return null;
574 | };
575 |
576 | Group.prototype.getConversationId = function() {
577 | return 'grp:' + this.id;
578 | };
579 |
580 | return Group;
581 |
582 | })();
583 |
584 | }).call(this);
585 |
586 | (function() {
587 | bit6.JsonRpc = (function() {
588 | JsonRpc.prototype.reconnectDelay = 4000;
589 |
590 | function JsonRpc(options) {
591 | var base;
592 | this.options = options;
593 | if ((base = this.options).sessid == null) {
594 | base.sessid = bit6.JsonRpc.generateGUID();
595 | }
596 | this.currentId = 1;
597 | this.callbacks = {};
598 | this.queue = [];
599 | this.closed = false;
600 | }
601 |
602 | JsonRpc.prototype.connect = function() {
603 | this.closed = false;
604 | if (this.ws != null) {
605 | return;
606 | }
607 | this.ws = new WebSocket(this.options.wsUrl);
608 | this.ws.onopen = (function(_this) {
609 | return function() {
610 | var i, len, m, ref;
611 | if (_this.queue.length > 0) {
612 | ref = _this.queue;
613 | for (i = 0, len = ref.length; i < len; i++) {
614 | m = ref[i];
615 | _this.ws.send(m);
616 | }
617 | return _this.queue = [];
618 | } else {
619 | return _this.call('login', {}, function(err, result) {
620 | if (err) {
621 | return console.log('rpc login err=', err, 'result=', result);
622 | }
623 | });
624 | }
625 | };
626 | })(this);
627 | this.ws.onmessage = (function(_this) {
628 | return function(e) {
629 | var cb, error, ex, m;
630 | try {
631 | m = JSON.parse(e.data);
632 | if ((m.result != null) || (m.error != null)) {
633 | if (m.id != null) {
634 | cb = _this.callbacks[m.id];
635 | delete _this.callbacks[m.id];
636 | cb(m.error, m.result);
637 | }
638 | }
639 | if ((m.method != null) && (m.params != null)) {
640 | if (m.id != null) {
641 | return _this.options.onRpcCall(m.method, m.params, function(err, result) {
642 | var t;
643 | t = {
644 | jsonrpc: '2.0',
645 | id: m.id
646 | };
647 | if (err != null) {
648 | t.error = err;
649 | }
650 | if (result != null) {
651 | t.result = result;
652 | }
653 | return _this._send(t);
654 | });
655 | } else {
656 | return _this.options.onRpcCall(m.method, m.params);
657 | }
658 | }
659 | } catch (error) {
660 | ex = error;
661 | console.log('Exception parsing JSON response ' + ex);
662 | return console.log(' -- RAW {{{' + e.data + '}}}');
663 | }
664 | };
665 | })(this);
666 | this.ws.onclose = (function(_this) {
667 | return function() {
668 | if (_this.closed) {
669 | return;
670 | }
671 | _this.ws = null;
672 | _this.queue = [];
673 | return setTimeout(function() {
674 | return _this.connect();
675 | }, _this.reconnectDelay);
676 | };
677 | })(this);
678 | return this.ws.onerror = (function(_this) {
679 | return function() {
680 | return console.log('Rpc ws got error. Socket state=' + _this.ws.readyState);
681 | };
682 | })(this);
683 | };
684 |
685 | JsonRpc.prototype.notify = function(m, params) {
686 | return this.call(m, params, false);
687 | };
688 |
689 | JsonRpc.prototype.call = function(m, params, cb) {
690 | var req;
691 | if (this.ws == null) {
692 | this.connect();
693 | }
694 | req = {
695 | jsonrpc: '2.0',
696 | method: m,
697 | params: params
698 | };
699 | req.params.login = this.options.login;
700 | req.params.passwd = this.options.password;
701 | if (this.options.sessid != null) {
702 | req.params.sessid = this.options.sessid;
703 | }
704 | if (cb !== false) {
705 | req.id = this.currentId++;
706 | this.callbacks[req.id] = cb;
707 | }
708 | return this._send(req);
709 | };
710 |
711 | JsonRpc.prototype._send = function(req) {
712 | var data;
713 | data = JSON.stringify(req);
714 | if ((this.ws != null) && this.ws.readyState === 1) {
715 | return this.ws.send(data);
716 | } else {
717 | return this.queue.push(data);
718 | }
719 | };
720 |
721 | JsonRpc.prototype.close = function() {
722 | this.closed = true;
723 | if (this.ws != null) {
724 | this.ws.close();
725 | }
726 | return this.ws = null;
727 | };
728 |
729 | JsonRpc.generateGUID = function() {
730 | return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
731 | var r, v;
732 | r = Math.random() * 16 | 0;
733 | v = c === 'x' ? r : r & 0x3 | 0x8;
734 | return v.toString(16);
735 | });
736 | };
737 |
738 | return JsonRpc;
739 |
740 | })();
741 |
742 | }).call(this);
743 |
744 | (function() {
745 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
746 | hasProp = {}.hasOwnProperty,
747 | slice = [].slice;
748 |
749 | bit6.Client = (function(superClass) {
750 | var endpoints;
751 |
752 | extend(Client, superClass);
753 |
754 | Client.version = '0.11.0';
755 |
756 | endpoints = {
757 | prod: 'https://api.bit6.com',
758 | dev: 'https://api.b6dev.net',
759 | local: 'http://127.0.0.1:3000'
760 | };
761 |
762 | function Client(opts) {
763 | var hasWebRTC, ref, ref1, ref2, url;
764 | Client.__super__.constructor.apply(this, arguments);
765 | if ((opts != null ? opts.apikey : void 0) == null) {
766 | throw 'Missing required "apikey" option';
767 | }
768 | this.apikey = opts.apikey;
769 | url = (ref = (ref1 = opts.url) != null ? ref1 : opts.env) != null ? ref : 'prod';
770 | this.apiurl = (ref2 = endpoints[url]) != null ? ref2 : url;
771 | hasWebRTC = (window.RTCPeerConnection != null) || (window.mozRTCPeerConnection != null) || (window.webkitRTCPeerConnection != null);
772 | this.caps = {
773 | audio: hasWebRTC,
774 | video: hasWebRTC,
775 | websocket: typeof WebSocket !== "undefined" && WebSocket !== null,
776 | attachment: (typeof Blob !== "undefined" && Blob !== null) && (typeof FormData !== "undefined" && FormData !== null) && (typeof FileReader !== "undefined" && FileReader !== null)
777 | };
778 | this._clear();
779 | this.session = new bit6.Session(this);
780 | }
781 |
782 | Client.prototype._clear = function() {
783 | this.lastSince = 0;
784 | this.me = {};
785 | this.messages = {};
786 | this.conversations = {};
787 | this.groups = {};
788 | this.presence = {};
789 | this.lastTypingSent = 0;
790 | this.capture = null;
791 | return this.dialogs = [];
792 | };
793 |
794 | Client.prototype._onLogin = function(cb) {
795 | this._connectRt();
796 | return this._loadMe((function(_this) {
797 | return function(err) {
798 | return cb(null);
799 | };
800 | })(this));
801 | };
802 |
803 | Client.prototype._onBeforeLogout = function(cb) {
804 | this._disconnectRt();
805 | this._clear();
806 | return cb(null);
807 | };
808 |
809 | Client.prototype._loadMe = function(cb) {
810 | var data;
811 | data = {
812 | embed: 'devices,identities,groups,messages',
813 | since: this.lastSince
814 | };
815 | return this.api('/me', data, (function(_this) {
816 | return function(err, result, headers) {
817 | var i, k, len, ref, ref1;
818 | if (err) {
819 | return cb(err);
820 | }
821 | _this.lastSince = (ref = headers != null ? headers.etag : void 0) != null ? ref : 0;
822 | ref1 = ['devices', 'identities', 'data', 'profile'];
823 | for (i = 0, len = ref1.length; i < len; i++) {
824 | k = ref1[i];
825 | if (result[k] != null) {
826 | _this.me[k] = result[k];
827 | }
828 | }
829 | if (result.groups != null) {
830 | _this._processGroupMemberships(result.groups);
831 | }
832 | _this._processMessages(result.messages);
833 | return cb();
834 | };
835 | })(this));
836 | };
837 |
838 | Client.prototype.setPrivateData = function(o, cb) {
839 | return this._setDataOrProfile('data', o, cb);
840 | };
841 |
842 | Client.prototype.setPublicProfile = function(o, cb) {
843 | return this._setDataOrProfile('profile', o, cb);
844 | };
845 |
846 | Client.prototype._setDataOrProfile = function(name, o, cb) {
847 | var old, ref;
848 | old = (ref = this.me[name]) != null ? ref : null;
849 | this.me[name] = o;
850 | return this.api('/me/' + name, 'POST', o, (function(_this) {
851 | return function(err, x) {
852 | if (err) {
853 | delete _this.me[name];
854 | if (old) {
855 | _this.me[name] = old;
856 | }
857 | } else {
858 | _this.me[name] = x;
859 | }
860 | return typeof cb === "function" ? cb(err, x) : void 0;
861 | };
862 | })(this));
863 | };
864 |
865 | Client.prototype.getConversation = function(uri) {
866 | var ref;
867 | return (ref = this.conversations[uri]) != null ? ref : null;
868 | };
869 |
870 | Client.prototype.getConversationByUri = function(uri) {
871 | var ref;
872 | return (ref = this.conversations[uri]) != null ? ref : null;
873 | };
874 |
875 | Client.prototype.getSortedConversations = function() {
876 | var cc;
877 | cc = this.conversations;
878 | return Object.keys(cc).sort(function(a, b) {
879 | return cc[b].updated - cc[a].updated;
880 | }).map(function(sortedKey) {
881 | return cc[sortedKey];
882 | });
883 | };
884 |
885 | Client.prototype.addEmptyConversation = function(uri) {
886 | var conv, convId, ref;
887 | convId = uri;
888 | conv = (ref = this.conversations[convId]) != null ? ref : null;
889 | if (!conv) {
890 | conv = new bit6.Conversation(convId);
891 | this.conversations[convId] = conv;
892 | conv.updated = Date.now();
893 | this.emit('conversation', conv, 1);
894 | }
895 | return conv;
896 | };
897 |
898 | Client.prototype.deleteConversation = function(conv, cb) {
899 | var other;
900 | conv = (conv != null ? conv.id : void 0) ? conv : this.getConversation(conv);
901 | other = encodeURIComponent(conv.id);
902 | return this.api('/me/messages?other=' + other, 'DELETE', (function(_this) {
903 | return function(err, result) {
904 | var i, len, m, msgs, op;
905 | if (err) {
906 | return typeof cb === "function" ? cb(err) : void 0;
907 | }
908 | msgs = conv.messages.slice();
909 | for (i = 0, len = msgs.length; i < len; i++) {
910 | m = msgs[i];
911 | m.deleted = Date.now();
912 | _this._processMessage(m, true);
913 | }
914 | op = 0;
915 | if (!conv.isGroup()) {
916 | op = -1;
917 | delete _this.conversations[conv.id];
918 | conv.deleted = Date.now();
919 | }
920 | _this.emit('conversation', conv, op);
921 | return typeof cb === "function" ? cb(null) : void 0;
922 | };
923 | })(this));
924 | };
925 |
926 | Client.prototype.markConversationAsRead = function(conv) {
927 | var i, len, m, num, other, ref;
928 | num = 0;
929 | if (!(conv != null ? conv.messages : void 0)) {
930 | return num;
931 | }
932 | ref = conv.messages;
933 | for (i = 0, len = ref.length; i < len; i++) {
934 | m = ref[i];
935 | if (!m.canMarkRead()) {
936 | continue;
937 | }
938 | m.status(bit6.Message.READ);
939 | this._processMessage(m);
940 | num++;
941 | }
942 | if (num === 0) {
943 | return;
944 | }
945 | other = encodeURIComponent(conv.uri);
946 | this.api('/me/messages?other=' + other, 'PUT', {
947 | status: 'read'
948 | }, function(err, result) {
949 | if (err) {
950 | return console.log('markAsRead result=', result, 'err=', err);
951 | }
952 | });
953 | return num;
954 | };
955 |
956 | Client.prototype.compose = function(dest) {
957 | var m;
958 | m = new bit6.Outgoing(this);
959 | if (dest != null) {
960 | m.to(dest);
961 | }
962 | return m;
963 | };
964 |
965 | Client.prototype._failOutgoingMessage = function(m) {
966 | m.status(bit6.Message.FAILED);
967 | return this._processMessage(m);
968 | };
969 |
970 | Client.prototype._sendMessagePost = function(m, cb) {
971 | var tmpId;
972 | tmpId = m.id;
973 | return this.api('/me/messages', 'POST', m._export(), (function(_this) {
974 | return function(err, o) {
975 | var tmp;
976 | if (err) {
977 | _this._failOutgoingMessage(m);
978 | return typeof cb === "function" ? cb(err) : void 0;
979 | } else {
980 | _this._processMessage(o);
981 | tmp = {
982 | id: tmpId,
983 | deleted: Date.now()
984 | };
985 | _this._processMessage(tmp);
986 | return typeof cb === "function" ? cb(null, _this.messages[o.id]) : void 0;
987 | }
988 | };
989 | })(this));
990 | };
991 |
992 | Client.prototype.markMessageAsRead = function(m) {
993 | if (!m.canMarkRead()) {
994 | return false;
995 | }
996 | m.status(bit6.Message.READ);
997 | this._processMessage(m);
998 | this.api('/me/messages/' + m.id, 'PUT', {
999 | status: 'read'
1000 | }, function(err, result) {});
1001 | return true;
1002 | };
1003 |
1004 | Client.prototype.deleteMessage = function(m, cb) {
1005 | m = (m != null ? m.id : void 0) ? m : this.messages[m];
1006 | return this.api('/me/messages/' + m.id, 'DELETE', (function(_this) {
1007 | return function(err, result) {
1008 | if (err) {
1009 | return typeof cb === "function" ? cb(err) : void 0;
1010 | }
1011 | m.deleted = Date.now();
1012 | _this._processMessage(m);
1013 | return typeof cb === "function" ? cb(null) : void 0;
1014 | };
1015 | })(this));
1016 | };
1017 |
1018 | Client.prototype._processMessages = function(messages) {
1019 | var c, i, id, len, o, ref, results;
1020 | if (!messages) {
1021 | return;
1022 | }
1023 | for (i = 0, len = messages.length; i < len; i++) {
1024 | o = messages[i];
1025 | this._processMessage(o, true);
1026 | }
1027 | ref = this.conversations;
1028 | results = [];
1029 | for (id in ref) {
1030 | c = ref[id];
1031 | if (c.modified) {
1032 | c.modified = false;
1033 | results.push(this.emit('conversation', c, 0));
1034 | } else {
1035 | results.push(void 0);
1036 | }
1037 | }
1038 | return results;
1039 | };
1040 |
1041 | Client.prototype._processMessage = function(o, noConvUpdateEvents) {
1042 | var conv, convId, m, oldConv, op, ref, ref1;
1043 | m = (ref = this.messages[o.id]) != null ? ref : null;
1044 | op = 0;
1045 | if (!m) {
1046 | if ((o != null ? o.deleted : void 0) > 0) {
1047 | return null;
1048 | }
1049 | if (o instanceof bit6.Message) {
1050 | m = o;
1051 | } else {
1052 | m = new bit6.Message(o);
1053 | }
1054 | this.messages[m.id] = m;
1055 | op = 1;
1056 | }
1057 | convId = m.getConversationId();
1058 | conv = oldConv = (ref1 = this.conversations[convId]) != null ? ref1 : null;
1059 | if (!conv) {
1060 | conv = new bit6.Conversation(convId);
1061 | this.conversations[convId] = conv;
1062 | }
1063 | if (op > 0) {
1064 | conv.appendMessage(m);
1065 | } else {
1066 | m.updateMessage(o);
1067 | if ((o != null ? o.deleted : void 0) > 0) {
1068 | delete this.messages[m.id];
1069 | conv.removeMessage(m);
1070 | op = -1;
1071 | } else {
1072 | conv.updateMessage(m);
1073 | }
1074 | }
1075 | if (!oldConv) {
1076 | conv.modified = false;
1077 | this.emit('conversation', conv, 1);
1078 | }
1079 | this.emit('message', m, op);
1080 | if (conv.modified && !noConvUpdateEvents) {
1081 | conv.modified = false;
1082 | this.emit('conversation', conv, 0);
1083 | }
1084 | return m;
1085 | };
1086 |
1087 | Client.prototype.getGroup = function(id) {
1088 | var ref;
1089 | if ((id != null ? id.indexOf('grp:') : void 0) === 0) {
1090 | id = id.substring(4);
1091 | }
1092 | return (ref = this.groups[id]) != null ? ref : null;
1093 | };
1094 |
1095 | Client.prototype.getGroupById = function(id) {
1096 | return this.getGroup(id);
1097 | };
1098 |
1099 | Client.prototype.createGroup = function(info, cb) {
1100 | if (info == null) {
1101 | info = {};
1102 | }
1103 | info.identity = this.session.identity;
1104 | return this.api('/groups', 'POST', info, (function(_this) {
1105 | return function(err, o) {
1106 | if (err) {
1107 | return cb(err);
1108 | }
1109 | return _this._loadMe(function(err) {
1110 | if (err) {
1111 | return cb(err);
1112 | }
1113 | return cb(null, _this.getGroup(o.id));
1114 | });
1115 | };
1116 | })(this));
1117 | };
1118 |
1119 | Client.prototype.joinGroup = function(g, role, cb) {
1120 | return this.inviteGroupMember(g, 'me', role, cb);
1121 | };
1122 |
1123 | Client.prototype.leaveGroup = function(g, cb) {
1124 | return this.kickGroupMember(g, 'me', cb);
1125 | };
1126 |
1127 | Client.prototype.inviteGroupMember = function(g, ident, role, cb) {
1128 | var gid, memberInfo;
1129 | gid = g.id != null ? g.id : g;
1130 | if (ident === 'me') {
1131 | ident = this.session.identity;
1132 | }
1133 | memberInfo = {
1134 | id: ident,
1135 | role: role
1136 | };
1137 | return this.api('/groups/' + gid + '/members', 'POST', memberInfo, (function(_this) {
1138 | return function(err, member) {
1139 | if (err) {
1140 | return cb(err);
1141 | }
1142 | return _this._loadMe(function(err) {
1143 | if (err) {
1144 | return cb(err);
1145 | }
1146 | return cb(null);
1147 | });
1148 | };
1149 | })(this));
1150 | };
1151 |
1152 | Client.prototype.kickGroupMember = function(g, m, cb) {
1153 | if (g.id == null) {
1154 | g = this.getGroup(g);
1155 | }
1156 | if (g == null) {
1157 | return cb(null);
1158 | }
1159 | if (m === 'me') {
1160 | m = g.me.identity;
1161 | }
1162 | if (!m.id) {
1163 | m = g.getMember(m);
1164 | }
1165 | if (m == null) {
1166 | return cb(null);
1167 | }
1168 | return this.api('/groups/' + g.id + '/members/' + m.id, 'DELETE', (function(_this) {
1169 | return function(err) {
1170 | if (err) {
1171 | return cb(err);
1172 | }
1173 | return _this._loadMe(function(err) {
1174 | if (err) {
1175 | return cb(err);
1176 | }
1177 | return cb(null);
1178 | });
1179 | };
1180 | })(this));
1181 | };
1182 |
1183 | Client.prototype._processGroupMemberships = function(infos) {
1184 | var g, groupsToSync, i, id, info, isUpdated, j, len, len1, me, o, op, ref, ref1, results, tmp;
1185 | tmp = this.groups;
1186 | this.groups = {};
1187 | groupsToSync = [];
1188 | for (i = 0, len = infos.length; i < len; i++) {
1189 | info = infos[i];
1190 | me = {
1191 | identity: info.identity,
1192 | role: info.role
1193 | };
1194 | o = (ref = info != null ? info.group : void 0) != null ? ref : {
1195 | id: info.id
1196 | };
1197 | o.me = me;
1198 | op = 0;
1199 | g = (ref1 = tmp[o.id]) != null ? ref1 : null;
1200 | if (g == null) {
1201 | op = 1;
1202 | g = new bit6.Group(o.id);
1203 | } else {
1204 | delete tmp[o.id];
1205 | }
1206 | this.groups[g.id] = g;
1207 | isUpdated = g.update(o);
1208 | if (isUpdated) {
1209 | this.emit('group', g, op);
1210 | }
1211 | this._ensureConversationForGroup(g, isUpdated);
1212 | if (isUpdated && me.role !== 'left') {
1213 | groupsToSync.push(g.id);
1214 | }
1215 | }
1216 | for (id in tmp) {
1217 | g = tmp[id];
1218 | this.emit('group', g, -1);
1219 | }
1220 | results = [];
1221 | for (j = 0, len1 = groupsToSync.length; j < len1; j++) {
1222 | id = groupsToSync[j];
1223 | results.push(this._loadGroupWithMembers(id, false));
1224 | }
1225 | return results;
1226 | };
1227 |
1228 | Client.prototype._loadGroupWithMembers = function(id, reloadMembshipsOnFail, cb) {
1229 | return this.api('/groups/' + id, {
1230 | embed: 'members'
1231 | }, (function(_this) {
1232 | return function(err, result) {
1233 | _this._processGroup(id, result);
1234 | if (err && reloadMembshipsOnFail) {
1235 | return _this._loadMe(function(err) {
1236 | return typeof cb === "function" ? cb(err) : void 0;
1237 | });
1238 | } else {
1239 | return typeof cb === "function" ? cb(err, result) : void 0;
1240 | }
1241 | };
1242 | })(this));
1243 | };
1244 |
1245 | Client.prototype._processGroup = function(id, o) {
1246 | var g, i, len, m, op, ref, ref1, ref2;
1247 | g = this.groups[id];
1248 | if (g == null) {
1249 | console.log('syncGroup - could not find Group in local DB', id, 'data=', o);
1250 | return;
1251 | }
1252 | op = 0;
1253 | if (o == null) {
1254 | if ((ref = g.me) != null) {
1255 | ref.role = 'left';
1256 | }
1257 | } else {
1258 | g.update(o, true);
1259 | }
1260 | if ((ref1 = g.me) != null ? ref1.identity : void 0) {
1261 | ref2 = g.members;
1262 | for (i = 0, len = ref2.length; i < len; i++) {
1263 | m = ref2[i];
1264 | if (m.id === g.me.identity) {
1265 | g.me.role = m.role;
1266 | break;
1267 | }
1268 | }
1269 | }
1270 | this._ensureConversationForGroup(g, op >= 0);
1271 | return this.emit('group', g, op);
1272 | };
1273 |
1274 | Client.prototype._ensureConversationForGroup = function(g, isGroupUpdated) {
1275 | var conv, convId, op;
1276 | op = 0;
1277 | convId = g.getConversationId();
1278 | conv = this.conversations[convId];
1279 | if (!conv) {
1280 | this.conversations[convId] = conv = new bit6.Conversation(convId);
1281 | conv.updated = g.updated;
1282 | op = 1;
1283 | }
1284 | if (op || isGroupUpdated) {
1285 | return this.emit('conversation', conv, op);
1286 | }
1287 | };
1288 |
1289 | Client.prototype.sendTypingNotification = function(to) {
1290 | var now;
1291 | now = Date.now();
1292 | if (now - this.lastTypingSent > 7000) {
1293 | this.lastTypingSent = now;
1294 | return this._sendNotification(to, 'typing');
1295 | }
1296 | };
1297 |
1298 | Client.prototype.sendNotification = function(to, type, data) {
1299 | if ((type != null ? type.length : void 0) < 1 || type.charAt(0) === '_') {
1300 | return false;
1301 | }
1302 | return this._sendNotification(to, '_' + type, data);
1303 | };
1304 |
1305 | Client.prototype._sendNotification = function(to, type, data) {
1306 | var m, msg, ref;
1307 | msg = {
1308 | type: type,
1309 | from: this.session.identity
1310 | };
1311 | if (data != null) {
1312 | msg.data = data;
1313 | }
1314 | m = {
1315 | to: to,
1316 | body: JSON.stringify(msg)
1317 | };
1318 | if ((ref = this.rpc) != null) {
1319 | ref.call('verto.info', {
1320 | msg: m
1321 | }, (function(_this) {
1322 | return function(err, result) {};
1323 | })(this));
1324 | }
1325 | return true;
1326 | };
1327 |
1328 | Client.prototype.startCall = function(to, opts) {
1329 | return this._createDialog(true, to, null, opts);
1330 | };
1331 |
1332 | Client.prototype.startPhoneCall = function(phone) {
1333 | var to;
1334 | to = 'tel:' + phone;
1335 | return this.startCall(to, {
1336 | audio: true,
1337 | offnet: true
1338 | });
1339 | };
1340 |
1341 | Client.prototype.findDialogByCallID = function(callID) {
1342 | var c, i, len, ref, ref1;
1343 | ref = this.dialogs;
1344 | for (i = 0, len = ref.length; i < len; i++) {
1345 | c = ref[i];
1346 | if (((ref1 = c.params) != null ? ref1.callID : void 0) === callID) {
1347 | return c;
1348 | }
1349 | }
1350 | return null;
1351 | };
1352 |
1353 | Client.prototype.findDialogByOther = function(other) {
1354 | var c, i, len, ref;
1355 | ref = this.dialogs;
1356 | for (i = 0, len = ref.length; i < len; i++) {
1357 | c = ref[i];
1358 | if (c.other === other) {
1359 | return c;
1360 | }
1361 | }
1362 | return null;
1363 | };
1364 |
1365 | Client.prototype.findDialogByRdest = function(rdest) {
1366 | var c, i, len, ref, ref1;
1367 | ref = this.dialogs;
1368 | for (i = 0, len = ref.length; i < len; i++) {
1369 | c = ref[i];
1370 | if (((ref1 = c.invite) != null ? ref1.rdest : void 0) === rdest) {
1371 | return c;
1372 | }
1373 | }
1374 | return null;
1375 | };
1376 |
1377 | Client.prototype._deleteDialog = function(d) {
1378 | var c, i, idx, len, ref, ref1;
1379 | ref = this.dialogs;
1380 | for (idx = i = 0, len = ref.length; i < len; idx = ++i) {
1381 | c = ref[idx];
1382 | if (c === d) {
1383 | this.dialogs.splice(idx, 1);
1384 | break;
1385 | }
1386 | }
1387 | if (this.dialogs.length === 0) {
1388 | if ((ref1 = this.capture) != null) {
1389 | ref1.stop();
1390 | }
1391 | return this.capture = null;
1392 | }
1393 | };
1394 |
1395 | Client.prototype._createDialog = function(outgoing, other, invite, opts) {
1396 | var c;
1397 | c = this.findDialogByOther(other);
1398 | if (c) {
1399 | return c;
1400 | }
1401 | c = new bit6.Dialog(this, outgoing, other, invite, opts);
1402 | this.dialogs.push(c);
1403 | c.on('error', (function(_this) {
1404 | return function(msg) {
1405 | return console.log('Dialog error: ', c, msg);
1406 | };
1407 | })(this));
1408 | c.on('end', (function(_this) {
1409 | return function() {
1410 | return _this._deleteDialog(c);
1411 | };
1412 | })(this));
1413 | return c;
1414 | };
1415 |
1416 | Client.prototype._createDeviceId = function() {
1417 | return 'web-' + bit6.JsonRpc.generateGUID();
1418 | };
1419 |
1420 | Client.prototype._createRtc = function() {
1421 | return new bit6.Rtc();
1422 | };
1423 |
1424 | Client.prototype._createRtcCapture = function() {
1425 | return new bit6.RtcCapture();
1426 | };
1427 |
1428 | Client.prototype._ensureRtcCapture = function(opts, cb) {
1429 | if (!this.capture) {
1430 | this.capture = this._createRtcCapture();
1431 | this.capture.on('video', (function(_this) {
1432 | return function(v, op) {
1433 | return _this._emitVideoEvent(v, null, op);
1434 | };
1435 | })(this));
1436 | }
1437 | return this.capture.request(opts, cb);
1438 | };
1439 |
1440 | Client.prototype._emitVideoEvent = function(v, d, op) {
1441 | return this.emit('video', v, d, op);
1442 | };
1443 |
1444 | Client.prototype.getNameFromIdentity = function(ident) {
1445 | var g, m, r, ref, ref1, t;
1446 | t = ident;
1447 | if (ident == null) {
1448 | console.log('getNameFromId null', ident);
1449 | return null;
1450 | }
1451 | r = ident.split(':');
1452 | if (r.length !== 2) {
1453 | return t;
1454 | }
1455 | switch (r[0]) {
1456 | case 'usr':
1457 | case 'pstn':
1458 | case 'mailto':
1459 | case 'tel':
1460 | t = r[1];
1461 | break;
1462 | case 'grp':
1463 | g = this.getGroup(ident);
1464 | if (!g) {
1465 | console.log('Group not found: ' + ident);
1466 | t = 'Group not found';
1467 | } else {
1468 | t = (ref = (ref1 = g.meta) != null ? ref1.title : void 0) != null ? ref : null;
1469 | if (!t && g.members.length) {
1470 | r = (function() {
1471 | var i, len, ref2, results;
1472 | ref2 = g.members;
1473 | results = [];
1474 | for (i = 0, len = ref2.length; i < len; i++) {
1475 | m = ref2[i];
1476 | if (m.role === 'user' || m.role === 'admin') {
1477 | results.push(this.getNameFromIdentity(m.id));
1478 | }
1479 | }
1480 | return results;
1481 | }).call(this);
1482 | if (r.length < 4) {
1483 | t = r.join(', ');
1484 | } else {
1485 | t = r[0] + ', ' + r[1] + ' + ' + (r.length - 2) + ' more';
1486 | }
1487 | }
1488 | if (t == null) {
1489 | t = 'Untitled Group';
1490 | }
1491 | }
1492 | }
1493 | return t;
1494 | };
1495 |
1496 | Client.prototype._handleRtMessage = function(m) {
1497 | var g, gid, i, j, len, len1, needToReloadMemberships, old, p, ref, ref1, ref2, ref3, ref4, ref5, ref6, ref7, ref8, ref9, results;
1498 | switch (m.type) {
1499 | case 'push':
1500 | return this._handlePushRtMessage(m.data);
1501 | case 'update':
1502 | if (((ref = m.data) != null ? ref.messages : void 0) != null) {
1503 | this._processMessages(m.data.messages);
1504 | }
1505 | if (((ref1 = m.data) != null ? (ref2 = ref1.groups) != null ? ref2.length : void 0 : void 0) > 0) {
1506 | needToReloadMemberships = false;
1507 | ref3 = m.data.groups;
1508 | for (i = 0, len = ref3.length; i < len; i++) {
1509 | g = ref3[i];
1510 | if (!this.groups[g.id]) {
1511 | needToReloadMemberships = true;
1512 | break;
1513 | }
1514 | }
1515 | if (needToReloadMemberships) {
1516 | return this._loadMe(function(err) {});
1517 | } else {
1518 | ref4 = m.data.groups;
1519 | results = [];
1520 | for (j = 0, len1 = ref4.length; j < len1; j++) {
1521 | g = ref4[j];
1522 | results.push(this._loadGroupWithMembers(g.id, true));
1523 | }
1524 | return results;
1525 | }
1526 | }
1527 | break;
1528 | case 'presence':
1529 | if ((m != null ? m.from : void 0) && ((m != null ? m.data : void 0) != null)) {
1530 | old = this.presence[m.from];
1531 | this.presence[m.from] = m.data;
1532 | if (m.data.status == null) {
1533 | this.presence[m.from].status = (ref5 = old != null ? old.status : void 0) != null ? ref5 : 'offline';
1534 | }
1535 | p = (ref6 = (ref7 = m.data) != null ? ref7.profile : void 0) != null ? ref6 : null;
1536 | if (p && (this.groups != null)) {
1537 | ref8 = this.groups;
1538 | for (gid in ref8) {
1539 | g = ref8[gid];
1540 | if (g._updateMemberProfile(m.from, p)) {
1541 | this.emit('group', g, 0);
1542 | }
1543 | }
1544 | }
1545 | return this.emit('notification', m);
1546 | }
1547 | break;
1548 | case 'typing':
1549 | return this.emit('notification', m);
1550 | default:
1551 | if ((m != null ? (ref9 = m.type) != null ? ref9.length : void 0 : void 0) > 1 && m.type.charAt(0) === '_') {
1552 | m.type = m.type.substring(1);
1553 | return this.emit('notification', m);
1554 | }
1555 | }
1556 | };
1557 |
1558 | Client.prototype._handlePushRtMessage = function(d) {
1559 | var c, ch, mtype, opts;
1560 | mtype = bit6.Message.typeFromFlags(d.flags);
1561 | switch (mtype) {
1562 | case bit6.Message.INC_CALL:
1563 | ch = bit6.Message.channelFromFlags(d.flags);
1564 | opts = {
1565 | audio: (ch & bit6.Message.AUDIO) === bit6.Message.AUDIO,
1566 | video: (ch & bit6.Message.VIDEO) === bit6.Message.VIDEO,
1567 | data: (ch & bit6.Message.DATA) === bit6.Message.DATA
1568 | };
1569 | c = this._createDialog(false, d.sender, d, opts);
1570 | return this.emit('incomingCall', c);
1571 | case bit6.Message.MISSED_CALL:
1572 | console.log('missed call from', d.sender, 'rdest=', d.rdest);
1573 | c = this.findDialogByRdest(d.rdest);
1574 | if (c != null) {
1575 | return c.hangup();
1576 | }
1577 | break;
1578 | case bit6.Message.ACCEPTED_CALL:
1579 | return console.log('accepted call from', d.sender, 'rdest=', d.rdest);
1580 | case bit6.Message.REJECTED_CALL:
1581 | return console.log('rejected call from', d.sender, 'rdest=', d.rdest);
1582 | case 0x0100:
1583 | case 0x0200:
1584 | case 0x0300:
1585 | case 0x0400:
1586 | return this._loadMe(function(err) {
1587 | if (err) {
1588 | return console.log('LoadMsgDeltas on push done', err);
1589 | }
1590 | });
1591 | default:
1592 | return console.log('Unknown push: ', d);
1593 | }
1594 | };
1595 |
1596 | Client.prototype._handleRpcNotify = function(method, params) {};
1597 |
1598 | Client.prototype._handleRpcCall = function(method, params, done) {
1599 | var c, error, ex, ref, t, x;
1600 | switch (method) {
1601 | case 'verto.info':
1602 | if ((params != null ? (ref = params.msg) != null ? ref.body : void 0 : void 0) != null) {
1603 | try {
1604 | x = JSON.parse(params.msg.body);
1605 | this._handleRtMessage(x);
1606 | } catch (error) {
1607 | ex = error;
1608 | console.log('Exception parsing JSON response verto.info ' + ex);
1609 | console.log(' -- RAW {{{' + params.msg.body + '}}}');
1610 | }
1611 | }
1612 | break;
1613 | case 'verto.bye':
1614 | c = this.findDialogByCallID(params.callID);
1615 | if (c) {
1616 | c.handleRpcCall(method, params);
1617 | }
1618 | break;
1619 | case 'verto.invite':
1620 | t = params.caller_id_number;
1621 | x = t != null ? t.split('@') : void 0;
1622 | c = this.findDialogByOther(x != null ? x[0] : void 0);
1623 | if (c) {
1624 | c.handleRpcCall(method, params);
1625 | }
1626 | break;
1627 | case 'verto.answer':
1628 | c = this.findDialogByCallID(params.callID);
1629 | if (c) {
1630 | c.handleRpcCall(method, params);
1631 | }
1632 | }
1633 | return done(null, {
1634 | 'method': method
1635 | });
1636 | };
1637 |
1638 | Client.prototype._connectRt = function() {
1639 | var opts, ref, ref1, s, servers;
1640 | servers = (ref = this.session.config) != null ? (ref1 = ref.rtc) != null ? ref1.vertoServers : void 0 : void 0;
1641 | if (servers.length < 1) {
1642 | console.log('Error: no Verto servers');
1643 | return;
1644 | }
1645 | s = servers[0];
1646 | opts = {
1647 | wsUrl: s.url,
1648 | login: s.username,
1649 | password: s.credential,
1650 | sessid: this.session.device,
1651 | onRpcCall: (function(_this) {
1652 | return function(m, params, done) {
1653 | return _this._handleRpcCall(m, params, done);
1654 | };
1655 | })(this),
1656 | onRpcNotify: (function(_this) {
1657 | return function(m, params) {
1658 | return _this._handleRpcNotify(m, params);
1659 | };
1660 | })(this)
1661 | };
1662 | this.rpc = new bit6.JsonRpc(opts);
1663 | return this.rpc.connect();
1664 | };
1665 |
1666 | Client.prototype._disconnectRt = function() {
1667 | var ref;
1668 | if ((ref = this.rpc) != null) {
1669 | ref.close();
1670 | }
1671 | return this.rpc = null;
1672 | };
1673 |
1674 | Client.prototype.api = function() {
1675 | var cb, i, params, path;
1676 | path = arguments[0], params = 3 <= arguments.length ? slice.call(arguments, 1, i = arguments.length - 1) : (i = 1, []), cb = arguments[i++];
1677 | return this._api.apply(this, ['/app/1' + path].concat(slice.call(params), [cb]));
1678 | };
1679 |
1680 | Client.prototype._api = function() {
1681 | var cb, data, i, m, params, path, url, xhr;
1682 | path = arguments[0], params = 3 <= arguments.length ? slice.call(arguments, 1, i = arguments.length - 1) : (i = 1, []), cb = arguments[i++];
1683 | if (params.length === 1) {
1684 | if (typeof params[0] === 'string') {
1685 | m = params[0];
1686 | }
1687 | if (typeof params[0] === 'object') {
1688 | data = params[0];
1689 | }
1690 | } else if (params.length >= 2) {
1691 | m = params[0], data = params[1];
1692 | }
1693 | if (m == null) {
1694 | m = 'get';
1695 | }
1696 | if (data == null) {
1697 | data = {};
1698 | }
1699 | m = m.toLowerCase();
1700 | url = this.apiurl + path;
1701 | url += path.indexOf('?') < 0 ? '?' : '&';
1702 | url += 'apikey=' + this.apikey;
1703 | if (m !== 'post') {
1704 | url += '&_method=' + m;
1705 | }
1706 | url += '&envelope=1';
1707 | if (this.session.token) {
1708 | data._auth = 'bearer ' + this.session.token;
1709 | }
1710 | xhr = new XMLHttpRequest();
1711 | xhr.open('POST', url, true);
1712 | xhr.setRequestHeader('Content-Type', 'application/json');
1713 | xhr.onreadystatechange = function() {
1714 | var error, ex, isXhrOk, r;
1715 | if (xhr.readyState === 4) {
1716 | r = null;
1717 | try {
1718 | r = JSON.parse(xhr.response);
1719 | } catch (error) {
1720 | ex = error;
1721 | if (typeof cb === "function") {
1722 | cb({
1723 | 'status': 501,
1724 | 'message': 'Cannot parse response',
1725 | 'data': xhr.response
1726 | });
1727 | }
1728 | return;
1729 | }
1730 | if ((r != null ? r.status : void 0) == null) {
1731 | if (typeof cb === "function") {
1732 | cb({
1733 | 'status': 501,
1734 | 'message': 'Incorrect envelope',
1735 | 'data': xhr.response
1736 | });
1737 | }
1738 | return;
1739 | }
1740 | isXhrOk = xhr.status === 200 || xhr.status === 0;
1741 | if (isXhrOk && r.status >= 200 && r.status < 300) {
1742 | return typeof cb === "function" ? cb(null, r.response, r.headers) : void 0;
1743 | } else {
1744 | return typeof cb === "function" ? cb(r.response, null, r.headers) : void 0;
1745 | }
1746 | }
1747 | };
1748 | return xhr.send(JSON.stringify(data));
1749 | };
1750 |
1751 | Client.prototype.urlencodeJsObject = function(obj, prefix) {
1752 | var k, p, str, v;
1753 | str = [];
1754 | str = (function() {
1755 | var results;
1756 | results = [];
1757 | for (p in obj) {
1758 | v = obj[p];
1759 | k = prefix ? prefix + '[' + p + ']' : p;
1760 | if (typeof v === 'object') {
1761 | results.push(this.urlencodeJsObject(v, k));
1762 | } else {
1763 | results.push(encodeURIComponent(k) + "=" + encodeURIComponent(v));
1764 | }
1765 | }
1766 | return results;
1767 | }).call(this);
1768 | return str.join('&');
1769 | };
1770 |
1771 | Client.normalizeIdentityUri = function(u) {
1772 | var filter, k, matcher, pos, v;
1773 | pos = u.indexOf(':');
1774 | if (pos < 0) {
1775 | return null;
1776 | }
1777 | k = u.substr(0, pos);
1778 | k = k.trim().toLowerCase();
1779 | v = u.substr(pos + 1);
1780 | filter = matcher = null;
1781 | switch (k) {
1782 | case 'mailto':
1783 | v = v.toLowerCase();
1784 | filter = /[^a-z0-9._%+-@]/;
1785 | matcher = /^[a-z0-9._%+-]+@[a-z0-9.-]+\.[a-z]{2,8}$/;
1786 | break;
1787 | case 'grp':
1788 | filter = /[\s]/;
1789 | matcher = /[0-9a-zA-Z._]{22}/;
1790 | break;
1791 | case 'tel':
1792 | filter = /[^\\d+]/;
1793 | matcher = /^\+[1-9]{1}[0-9]{8,15}$/;
1794 | break;
1795 | case 'uid':
1796 | v = v.toLowerCase();
1797 | filter = /[^0-9a-f-]/;
1798 | matcher = /[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/;
1799 | break;
1800 | case 'usr':
1801 | v = v.toLowerCase();
1802 | filter = /[^a-z0-9.]/;
1803 | matcher = /^[a-z0-9.]+$/;
1804 | break;
1805 | default:
1806 | filter = /[^a-zA-Z0-9._%+-@]/;
1807 | }
1808 | if (filter) {
1809 | v = v.replace(filter, '');
1810 | }
1811 | if (v.search(matcher) < 0) {
1812 | return null;
1813 | }
1814 | return k + ':' + v;
1815 | };
1816 |
1817 | return Client;
1818 |
1819 | })(bit6.EventEmitter);
1820 |
1821 | }).call(this);
1822 |
1823 | (function() {
1824 | bit6.Message = (function() {
1825 | var CHANNEL_MASK, INCOMING, STATUS_MASK, TYPE_MASK, callStatusAsString, messageStatusAsString;
1826 |
1827 | INCOMING = 0x1000;
1828 |
1829 | STATUS_MASK = 0x000f;
1830 |
1831 | CHANNEL_MASK = 0x00f0;
1832 |
1833 | TYPE_MASK = 0x0f00;
1834 |
1835 | Message.DELETED = 0x0000;
1836 |
1837 | Message.SENDING = 0x0001;
1838 |
1839 | Message.SENT = 0x0002;
1840 |
1841 | Message.FAILED = 0x0003;
1842 |
1843 | Message.DELIVERED = 0x0004;
1844 |
1845 | Message.READ = 0x0005;
1846 |
1847 | Message.ANSWER = 0x0001;
1848 |
1849 | Message.MISSED = 0x0002;
1850 |
1851 | Message.NOANSWER = 0x0004;
1852 |
1853 | Message.SMS = 0x0010;
1854 |
1855 | Message.AUDIO = 0x0010;
1856 |
1857 | Message.VIDEO = 0x0080;
1858 |
1859 | Message.DATA = 0x0020;
1860 |
1861 | Message.TEXT = 0x0100;
1862 |
1863 | Message.ATTACH = 0x0200;
1864 |
1865 | Message.GEOLOC = 0x0300;
1866 |
1867 | Message.CUSTOM = 0x0400;
1868 |
1869 | Message.CALL = 0x0500;
1870 |
1871 | Message.INC_CALL = 0x0500;
1872 |
1873 | Message.MISSED_CALL = 0x0600;
1874 |
1875 | messageStatusAsString = {
1876 | 0x0001: 'Sending',
1877 | 0x0002: 'Sent',
1878 | 0x0003: 'Failed',
1879 | 0x0004: 'Delivered',
1880 | 0x0005: 'Read'
1881 | };
1882 |
1883 | callStatusAsString = {
1884 | 0x0001: 'Answered',
1885 | 0x0002: 'Missed',
1886 | 0x0003: 'Failed',
1887 | 0x0004: 'No answer'
1888 | };
1889 |
1890 | function Message(o) {
1891 | this.id = null;
1892 | this.me = null;
1893 | this.other = null;
1894 | this.flags = 0;
1895 | if (o != null) {
1896 | this.populate(o);
1897 | }
1898 | }
1899 |
1900 | Message.prototype.populate = function(o) {
1901 | var j, k, len, ref, results;
1902 | ref = ['id', 'flags', 'me', 'other', 'content', 'data', 'created', 'updated', 'deleted'];
1903 | results = [];
1904 | for (j = 0, len = ref.length; j < len; j++) {
1905 | k = ref[j];
1906 | if ((o != null ? o[k] : void 0) != null) {
1907 | results.push(this[k] = o[k]);
1908 | } else {
1909 | results.push(void 0);
1910 | }
1911 | }
1912 | return results;
1913 | };
1914 |
1915 | Message.prototype.updateMessage = function(t) {
1916 | if (t.deleted != null) {
1917 | this.deleted = t.deleted;
1918 | }
1919 | if (t.flags) {
1920 | this.flags = t.flags;
1921 | }
1922 | if (t.others == null) {
1923 | return;
1924 | }
1925 | if (this.others != null) {
1926 | return this._updateMessageOthers(this.others, t.others);
1927 | } else {
1928 | return this.others = t.others;
1929 | }
1930 | };
1931 |
1932 | Message.prototype._updateMessageOthers = function(old, others) {
1933 | var i, idx, j, l, len, len1, o, ref, results, s, t;
1934 | results = [];
1935 | for (j = 0, len = others.length; j < len; j++) {
1936 | t = others[j];
1937 | idx = -1;
1938 | for (i = l = 0, len1 = old.length; l < len1; i = ++l) {
1939 | o = old[i];
1940 | if (o.uri === t.uri) {
1941 | idx = i;
1942 | break;
1943 | }
1944 | }
1945 | if (idx < 0) {
1946 | results.push(old.push(t));
1947 | } else {
1948 | s = (ref = old[idx]) != null ? ref.status : void 0;
1949 | if (!s || s < t.status) {
1950 | results.push(old[idx].status = t.status);
1951 | } else {
1952 | results.push(void 0);
1953 | }
1954 | }
1955 | }
1956 | return results;
1957 | };
1958 |
1959 | Message.prototype.incoming = function() {
1960 | return (this.flags & INCOMING) !== 0;
1961 | };
1962 |
1963 | Message.prototype.status = function(s) {
1964 | if (s != null) {
1965 | this.flags = (this.flags & (~STATUS_MASK)) | (s & STATUS_MASK);
1966 | }
1967 | return this.flags & STATUS_MASK;
1968 | };
1969 |
1970 | Message.prototype.channel = function(s) {
1971 | if (s != null) {
1972 | this.flags = (this.flags & (~CHANNEL_MASK)) | (s & CHANNEL_MASK);
1973 | }
1974 | return this.flags & CHANNEL_MASK;
1975 | };
1976 |
1977 | Message.prototype.type = function(s) {
1978 | if (s != null) {
1979 | this.flags = (this.flags & (~TYPE_MASK)) | (s & TYPE_MASK);
1980 | }
1981 | return this.flags & TYPE_MASK;
1982 | };
1983 |
1984 | Message.prototype.isCall = function() {
1985 | return (this.flags & TYPE_MASK) === Message.CALL;
1986 | };
1987 |
1988 | Message.prototype.canMarkRead = function() {
1989 | return this.incoming() && !this.isCall() && this.status() < bit6.Message.READ;
1990 | };
1991 |
1992 | Message.prototype.getConversationId = function() {
1993 | var convId;
1994 | convId = (this.me != null) && this.me.indexOf('grp:') === 0 ? this.me : this.other;
1995 | if (!convId) {
1996 | console.log('msgConvId is null', convId);
1997 | }
1998 | return convId;
1999 | };
2000 |
2001 | Message.prototype.getStatusString = function() {
2002 | var r, s, t;
2003 | t = this.flags & TYPE_MASK;
2004 | s = this.flags & STATUS_MASK;
2005 | r = t === Message.CALL ? callStatusAsString : messageStatusAsString;
2006 | return r[s];
2007 | };
2008 |
2009 | Message.prototype.domId = function() {
2010 | return 'm' + this.id;
2011 | };
2012 |
2013 | Message.fromDomId = function(t) {
2014 | if (t.length > 0 && t.charAt(0) === 'm') {
2015 | return t.substring(1);
2016 | } else {
2017 | return t;
2018 | }
2019 | };
2020 |
2021 | Message.typeFromFlags = function(s) {
2022 | return s & TYPE_MASK;
2023 | };
2024 |
2025 | Message.channelFromFlags = function(s) {
2026 | return s & CHANNEL_MASK;
2027 | };
2028 |
2029 | Message.statusFromFlags = function(s) {
2030 | return s & STATUS_MASK;
2031 | };
2032 |
2033 | return Message;
2034 |
2035 | })();
2036 |
2037 | }).call(this);
2038 |
2039 | (function() {
2040 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
2041 | hasProp = {}.hasOwnProperty;
2042 |
2043 | bit6.Outgoing = (function(superClass) {
2044 | extend(Outgoing, superClass);
2045 |
2046 | function Outgoing(client) {
2047 | this.client = client;
2048 | Outgoing.__super__.constructor.apply(this, arguments);
2049 | this.id = bit6.JsonRpc.generateGUID();
2050 | this.created = this.updated = Date.now();
2051 | this.flags = bit6.Message.TEXT | bit6.Message.SENDING;
2052 | this.from(this.client.session.identity, this.client.session.displayName);
2053 | }
2054 |
2055 | Outgoing.prototype._export = function() {
2056 | var i, k, len, n, x;
2057 | n = ['id', 'flags', 'me', 'from_name', 'other', 'content', 'data'];
2058 | x = {};
2059 | for (i = 0, len = n.length; i < len; i++) {
2060 | k = n[i];
2061 | if (this[k]) {
2062 | x[k] = this[k];
2063 | }
2064 | }
2065 | return x;
2066 | };
2067 |
2068 | Outgoing.prototype.hasAttachment = function() {
2069 | return this.attachFile != null;
2070 | };
2071 |
2072 | Outgoing.prototype.to = function(other) {
2073 | this.other = other;
2074 | return this;
2075 | };
2076 |
2077 | Outgoing.prototype.from = function(from, name) {
2078 | this.me = from;
2079 | if (name) {
2080 | this.from_name = name;
2081 | }
2082 | return this;
2083 | };
2084 |
2085 | Outgoing.prototype.text = function(text) {
2086 | this.content = text;
2087 | return this;
2088 | };
2089 |
2090 | Outgoing.prototype.attach = function(f) {
2091 | this.attachFile = f;
2092 | this.flags = bit6.Message.ATTACH | this.status();
2093 | this.progress = 0;
2094 | this.total = -1;
2095 | return this;
2096 | };
2097 |
2098 | Outgoing.prototype.send = function(cb) {
2099 | if (!this.hasAttachment()) {
2100 | this.client._processMessage(this);
2101 | return this.client._sendMessagePost(this, cb);
2102 | }
2103 | this._loadAttachmentAndThumbnail((function(_this) {
2104 | return function(err) {
2105 | _this.client._processMessage(_this);
2106 | if (err != null) {
2107 | return typeof cb === "function" ? cb(err) : void 0;
2108 | }
2109 | return _this._getUploadParams(function(err, params) {
2110 | if (err != null) {
2111 | return typeof cb === "function" ? cb(err) : void 0;
2112 | }
2113 | return _this._uploadAttachmentAndThumbnail(params, function(err) {
2114 | if (err != null) {
2115 | return typeof cb === "function" ? cb(err) : void 0;
2116 | }
2117 | return _this.client._sendMessagePost(_this, cb);
2118 | });
2119 | });
2120 | };
2121 | })(this));
2122 | return this;
2123 | };
2124 |
2125 | Outgoing.prototype._getUploadParams = function(cb) {
2126 | var opts, ref, ref1;
2127 | if (((ref = this.data) != null ? ref.type : void 0) == null) {
2128 | return cb('Attachment type is not specified', null);
2129 | }
2130 | opts = {
2131 | type: this.data.type
2132 | };
2133 | opts.thumb = (ref1 = this.thumbBlob) != null ? ref1.type : void 0;
2134 | return this.client.api('/me/messages/attach', 'post', opts, cb);
2135 | };
2136 |
2137 | Outgoing.prototype._loadAttachmentAndThumbnail = function(cb) {
2138 | return bit6.Transfer.readFileAsArrayBuffer(this.attachFile, (function(_this) {
2139 | return function(err, info, data) {
2140 | if (err) {
2141 | console.log('Read file ', info, 'err=', err);
2142 | }
2143 | if (err != null) {
2144 | return cb(err);
2145 | }
2146 | return _this._handleAttachmentFileLoaded(info, data, cb);
2147 | };
2148 | })(this));
2149 | };
2150 |
2151 | Outgoing.prototype._uploadAttachmentAndThumbnail = function(params, cb) {
2152 | var f, fileUploadedSize, x;
2153 | f = this.attachFile;
2154 | fileUploadedSize = 0;
2155 | x = params.uploads.attach;
2156 | return bit6.Outgoing.uploadFile(x.endpoint, x.params, f, (function(_this) {
2157 | return function(err) {
2158 | if (err) {
2159 | console.log('Main attach uploaded err=', err);
2160 | }
2161 | if (err != null) {
2162 | return cb(err);
2163 | }
2164 | _this.data.attach = params.keys.attach;
2165 | if ((_this.data.thumb == null) || (_this.thumbBlob == null)) {
2166 | return cb(null);
2167 | }
2168 | x = params.uploads.thumb;
2169 | return bit6.Outgoing.uploadFile(x.endpoint, x.params, _this.thumbBlob, function(err) {
2170 | if (err) {
2171 | console.log('Thumb uploaded err=', err);
2172 | }
2173 | if (err != null) {
2174 | return cb(err);
2175 | }
2176 | _this.data.thumb = params.keys.thumb;
2177 | return cb(null);
2178 | }, function(val, total) {
2179 | _this.progress = val + fileUploadedSize;
2180 | _this.total = total + fileUploadedSize;
2181 | return _this.client.emit('message', _this, 0);
2182 | });
2183 | };
2184 | })(this), (function(_this) {
2185 | return function(val, total) {
2186 | fileUploadedSize = val;
2187 | _this.progress = val;
2188 | _this.total = total;
2189 | return _this.client.emit('message', _this, 0);
2190 | };
2191 | })(this));
2192 | };
2193 |
2194 | Outgoing.prototype._handleAttachmentFileLoaded = function(info, data, cb) {
2195 | var blob, isAudio, isImage, isVideo, media, ref, srcUrl, t;
2196 | t = (ref = info != null ? info.type : void 0) != null ? ref : '';
2197 | isImage = t.indexOf('image/') === 0;
2198 | isVideo = t.indexOf('video/') === 0;
2199 | isAudio = t.indexOf('audio/') === 0;
2200 | if (isImage || isVideo) {
2201 | blob = new Blob([data], info);
2202 | srcUrl = (window.URL || window.webkitURL).createObjectURL(blob);
2203 | media = null;
2204 | if (isImage) {
2205 | media = new Image();
2206 | media.onload = (function(_this) {
2207 | return function() {
2208 | return _this._attachmentMediaLoaded(media, info, cb);
2209 | };
2210 | })(this);
2211 | } else {
2212 | media = document.createElement('video');
2213 | media.setAttribute('preload', '');
2214 | media.setAttribute('muted', '');
2215 | media.onloadedmetadata = (function(_this) {
2216 | return function() {
2217 | return console.log('Video meta loaded', media.videoWidth, media.videoHeight, media.duration);
2218 | };
2219 | })(this);
2220 | media.onloadeddata = (function(_this) {
2221 | return function() {
2222 | console.log('Video loaded', media);
2223 | return _this._attachmentMediaLoaded(media, info, cb);
2224 | };
2225 | })(this);
2226 | media.onerror = (function(_this) {
2227 | return function() {
2228 | console.log('Error on video loading. Still can send the file');
2229 | return _this._attachmentMediaLoaded(media, info, cb);
2230 | };
2231 | })(this);
2232 | }
2233 | return media.src = srcUrl;
2234 | } else if (isAudio) {
2235 | this.data = {
2236 | type: info.type
2237 | };
2238 | return cb(null);
2239 | } else {
2240 | return cb('Cannot handle files with type' + t);
2241 | }
2242 | };
2243 |
2244 | Outgoing.prototype._attachmentMediaLoaded = function(media, info, cb) {
2245 | (window.URL || window.webkitURL).revokeObjectURL(media.src);
2246 | return bit6.Outgoing.createThumbnail(media, (function(_this) {
2247 | return function(err, thumbDataUrl) {
2248 | if (err) {
2249 | console.log('Thumb created err=', err);
2250 | }
2251 | if (err != null) {
2252 | return cb(err);
2253 | }
2254 | if (thumbDataUrl != null) {
2255 | _this.thumbBlob = bit6.Transfer.dataUrlToBlob(thumbDataUrl);
2256 | }
2257 | _this.data = {
2258 | type: info.type,
2259 | attach: thumbDataUrl,
2260 | thumb: thumbDataUrl
2261 | };
2262 | return cb(null);
2263 | };
2264 | })(this));
2265 | };
2266 |
2267 | Outgoing.createThumbnail = function(media, cb) {
2268 | var canvas, ctx, dataUrl, maxHeight, maxWidth, ref, ref1, ref2, th, tw;
2269 | maxWidth = 320;
2270 | maxHeight = 320;
2271 | tw = (ref = media != null ? media.videoWidth : void 0) != null ? ref : media.width;
2272 | th = (ref1 = media != null ? media.videoHeight : void 0) != null ? ref1 : media.height;
2273 | if (tw > th) {
2274 | if (tw > maxWidth) {
2275 | th *= maxWidth / tw;
2276 | tw = maxWidth;
2277 | }
2278 | } else {
2279 | if (th > maxHeight) {
2280 | tw *= maxHeight / th;
2281 | th = maxHeight;
2282 | }
2283 | }
2284 | canvas = document.createElement('canvas');
2285 | canvas.width = tw;
2286 | canvas.height = th;
2287 | ctx = canvas.getContext('2d');
2288 | ctx.drawImage(media, 0, 0, tw, th);
2289 | dataUrl = canvas.toDataURL('image/jpeg');
2290 | if (((ref2 = dataUrl.split(":")[1]) != null ? ref2.length : void 0) < 2) {
2291 | dataUrl = null;
2292 | }
2293 | return cb(null, dataUrl);
2294 | };
2295 |
2296 | Outgoing.uploadFile = function(endpoint, params, f, cb, progressCb) {
2297 | var b;
2298 | b = new bit6.Outgoing.UploadDataBuilder(params, f);
2299 | return b.build(function(err, uploadData, contentType) {
2300 | if (err != null) {
2301 | return cb(err);
2302 | }
2303 | return bit6.Outgoing._upload(endpoint, uploadData, contentType, cb, progressCb);
2304 | });
2305 | };
2306 |
2307 | Outgoing._upload = function(endpoint, uploadData, contentType, cb, progressCb) {
2308 | var ref, up, xhr;
2309 | xhr = new XMLHttpRequest();
2310 | xhr.open('POST', endpoint, true);
2311 | xhr.onload = function(e) {
2312 | if (xhr.status >= 200 && xhr.status < 300) {
2313 | return cb(null);
2314 | } else {
2315 | return cb({
2316 | status: xhr.status,
2317 | text: xhr.statusText,
2318 | info: xhr.responseText
2319 | });
2320 | }
2321 | };
2322 | xhr.onerror = function(e) {
2323 | console.log('xhr error', e);
2324 | return cb(e);
2325 | };
2326 | xhr.onabort = function(e) {
2327 | console.log('xhr cancel');
2328 | return cb(e);
2329 | };
2330 | xhr.onprogress = function(e) {
2331 | return console.log('xhr progress', e);
2332 | };
2333 | up = (ref = xhr != null ? xhr.upload : void 0) != null ? ref : null;
2334 | if (up) {
2335 | up.onload = function(e) {
2336 | return console.log('xhr upload complete', e);
2337 | };
2338 | up.onerror = function(e) {
2339 | return console.log('xhr upload error', e);
2340 | };
2341 | up.onabort = function(e) {
2342 | return console.log('xhr upload cancel', e);
2343 | };
2344 | up.onprogress = function(e) {
2345 | var total;
2346 | total = e.lengthComputable ? e.total : -1;
2347 | return typeof progressCb === "function" ? progressCb(e.loaded, total) : void 0;
2348 | };
2349 | }
2350 | if (contentType != null) {
2351 | xhr.setRequestHeader('Content-Type', contentType);
2352 | }
2353 | return xhr.send(uploadData);
2354 | };
2355 |
2356 | return Outgoing;
2357 |
2358 | })(bit6.Message);
2359 |
2360 | bit6.Outgoing.UploadDataBuilder = (function() {
2361 | function UploadDataBuilder(params, f) {
2362 | if (!this._needsFormDataShim()) {
2363 | return this._createFormData(params, f);
2364 | }
2365 | this._initMultiPart(params, f);
2366 | }
2367 |
2368 | UploadDataBuilder.prototype._createFormData = function(params, f) {
2369 | var k, v;
2370 | this.fd = new FormData();
2371 | for (k in params) {
2372 | v = params[k];
2373 | this.fd.append(k, v);
2374 | }
2375 | return this.fd.append('file', f);
2376 | };
2377 |
2378 | UploadDataBuilder.prototype._initMultiPart = function(params, f) {
2379 | var k, v;
2380 | this.boundary = Array(21).join('-') + (+new Date() * (1e16 * Math.random())).toString(36);
2381 | this.parts = [];
2382 | for (k in params) {
2383 | v = params[k];
2384 | this._append(k, v);
2385 | }
2386 | if (f instanceof File) {
2387 | return this.fileToLoad = f;
2388 | } else {
2389 | return this._append('file', f);
2390 | }
2391 | };
2392 |
2393 | UploadDataBuilder.prototype._append = function(name, value, filename) {
2394 | this.parts.push('--' + this.boundary + '\r\nContent-Disposition: form-data; name="' + name + '"');
2395 | if (value instanceof Blob) {
2396 | if (filename == null) {
2397 | filename = 'blob';
2398 | }
2399 | this.parts.push('; filename="' + filename + '"\r\nContent-Type: ' + value.type);
2400 | }
2401 | this.parts.push('\r\n\r\n');
2402 | this.parts.push(value);
2403 | return this.parts.push('\r\n');
2404 | };
2405 |
2406 | UploadDataBuilder.prototype.build = function(cb) {
2407 | if (this.fd != null) {
2408 | return cb(null, this.fd);
2409 | }
2410 | return this._ensureFileLoaded('file', this.fileToLoad, (function(_this) {
2411 | return function(err) {
2412 | if (err) {
2413 | return cb(err);
2414 | }
2415 | return _this._completeMultiPart(cb);
2416 | };
2417 | })(this));
2418 | };
2419 |
2420 | UploadDataBuilder.prototype._ensureFileLoaded = function(name, f, cb) {
2421 | if (f == null) {
2422 | return cb(null);
2423 | }
2424 | return bit6.Transfer.readFileAsArrayBuffer(f, (function(_this) {
2425 | return function(err, info, data) {
2426 | if (err) {
2427 | return cb(err);
2428 | }
2429 | _this._append(name, new Blob([data], {
2430 | type: info.type
2431 | }, info.name));
2432 | return cb(null);
2433 | };
2434 | })(this));
2435 | };
2436 |
2437 | UploadDataBuilder.prototype._completeMultiPart = function(cb) {
2438 | var data, fr;
2439 | this.parts.push('--' + this.boundary + '--');
2440 | data = new Blob(this.parts);
2441 | fr = new FileReader();
2442 | fr.onload = (function(_this) {
2443 | return function() {
2444 | return cb(null, fr.result, 'multipart/form-data; boundary=' + _this.boundary);
2445 | };
2446 | })(this);
2447 | fr.onerror = function(err) {
2448 | return cb(err);
2449 | };
2450 | return fr.readAsArrayBuffer(data);
2451 | };
2452 |
2453 | UploadDataBuilder.prototype._needsFormDataShim = function() {
2454 | var flag, ua, v;
2455 | ua = navigator.userAgent;
2456 | v = navigator.vendor;
2457 | flag = ~ua.indexOf('Android') && ~v.indexOf('Google') && !~ua.indexOf('Chrome');
2458 | return flag && ua.match(/AppleWebKit\/(\d+)/).pop() <= 534;
2459 | };
2460 |
2461 | return UploadDataBuilder;
2462 |
2463 | })();
2464 |
2465 | }).call(this);
2466 |
2467 | (function() {
2468 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
2469 | hasProp = {}.hasOwnProperty;
2470 |
2471 | bit6.RtcCapture = (function(superClass) {
2472 | extend(RtcCapture, superClass);
2473 |
2474 | function RtcCapture() {
2475 | RtcCapture.__super__.constructor.apply(this, arguments);
2476 | this.options = {
2477 | audio: false,
2478 | video: false,
2479 | screen: false
2480 | };
2481 | this.preparingScreen = false;
2482 | this.preparingMedia = false;
2483 | this.errorScreen = null;
2484 | this.errorMedia = null;
2485 | this.cbs = [];
2486 | this.localStream = null;
2487 | this.localEl = null;
2488 | this.localScreenStream = null;
2489 | }
2490 |
2491 | RtcCapture.prototype.getStreams = function(opts) {
2492 | var arr;
2493 | arr = [];
2494 | if (opts.audio || opts.video) {
2495 | if (this.localStream) {
2496 | arr.push(this.localStream);
2497 | }
2498 | }
2499 | if (opts.screen) {
2500 | if (this.localScreenStream) {
2501 | arr.push(this.localScreenStream);
2502 | }
2503 | }
2504 | return arr;
2505 | };
2506 |
2507 | RtcCapture.prototype.request = function(opts, cb) {
2508 | return this._prepareScreenSharing(opts != null ? opts.screen : void 0, (function(_this) {
2509 | return function(err) {
2510 | var newAudio, newVideo, ref, ref1;
2511 | if (err) {
2512 | console.log('RtcCapture.request: Could not get ScreenSharing', err);
2513 | }
2514 | newAudio = (ref = opts.audio) != null ? ref : _this.options.audio;
2515 | newVideo = (ref1 = opts.video) != null ? ref1 : _this.options.video;
2516 | return _this._prepareCameraMic(newAudio, newVideo, function(err2) {
2517 | if (err2) {
2518 | console.log('RtcCapture.request: Could not get audio/video', err2);
2519 | }
2520 | return _this._check(cb);
2521 | });
2522 | };
2523 | })(this));
2524 | };
2525 |
2526 | RtcCapture.prototype._check = function(cb) {
2527 | var cb2, err, i, len, results, x;
2528 | if (this.preparingScreen || this.preparingMedia) {
2529 | this.cbs.push(cb);
2530 | return;
2531 | }
2532 | err = this.errorMedia;
2533 | if (!err && !this.localStream) {
2534 | err = this.errorScreen;
2535 | }
2536 | cb(err);
2537 | if (this.cbs.length > 0) {
2538 | x = this.cbs;
2539 | this.cbs = [];
2540 | results = [];
2541 | for (i = 0, len = x.length; i < len; i++) {
2542 | cb2 = x[i];
2543 | results.push(cb2(err));
2544 | }
2545 | return results;
2546 | }
2547 | };
2548 |
2549 | RtcCapture.prototype.stop = function() {
2550 | var e;
2551 | if (this.localScreenStream) {
2552 | bit6.Rtc.stopMediaStream(this.localScreenStream);
2553 | this.localScreenStream = null;
2554 | }
2555 | if (this.localStream) {
2556 | bit6.Rtc.stopMediaStream(this.localStream);
2557 | this.localStream = null;
2558 | }
2559 | if (this.localEl) {
2560 | e = this.localEl;
2561 | this.localEl = null;
2562 | if (e.src != null) {
2563 | e.src = '';
2564 | }
2565 | this.emit('video', e, -1);
2566 | }
2567 | return this.removeAllListeners();
2568 | };
2569 |
2570 | RtcCapture.prototype._getScreenSharingOpts = function(cb) {
2571 | var onmsg, opts;
2572 | if ((typeof navigator !== "undefined" && navigator !== null ? navigator.mozGetUserMedia : void 0) != null) {
2573 | opts = {
2574 | video: {
2575 | mozMediaSource: 'window',
2576 | mediaSource: 'window'
2577 | }
2578 | };
2579 | return cb(null, opts);
2580 | }
2581 | onmsg = function(msg) {
2582 | var d;
2583 | d = msg != null ? msg.data : void 0;
2584 | console.log('WebApp.gotMessage', d);
2585 | if ((d != null ? d.state : void 0) !== 'completed') {
2586 | return;
2587 | }
2588 | console.log('WebRTC onmsg done');
2589 | window.removeEventListener('message', onmsg, false);
2590 | opts = {
2591 | audio: false,
2592 | video: {
2593 | mandatory: {
2594 | chromeMediaSource: 'desktop',
2595 | chromeMediaSourceId: msg.data.streamId,
2596 | maxWidth: window.screen.width,
2597 | maxHeight: window.screen.height
2598 | }
2599 | }
2600 | };
2601 | return cb(null, opts);
2602 | };
2603 | window.addEventListener('message', onmsg, false);
2604 | return window.postMessage({
2605 | requestId: 100,
2606 | data: ['screen', 'window']
2607 | }, '*');
2608 | };
2609 |
2610 | RtcCapture.prototype._prepareScreenSharing = function(flag, cb) {
2611 | if (flag === false && this.localScreenStream) {
2612 | this.localScreenStream = null;
2613 | return cb(null);
2614 | }
2615 | if (!flag === true) {
2616 | return cb(null);
2617 | }
2618 | this.options.screen = true;
2619 | if (this.localScreenStream) {
2620 | return cb(null);
2621 | }
2622 | if (this.preparingScreen) {
2623 | return cb(null);
2624 | }
2625 | this.preparingScreen = true;
2626 | return this._getScreenSharingOpts((function(_this) {
2627 | return function(err, opts) {
2628 | if (err != null) {
2629 | _this.preparingScreen = false;
2630 | _this.errorScreen = err;
2631 | return cb(err);
2632 | }
2633 | return RtcCapture.getUserMedia(opts, function(err, stream) {
2634 | _this.preparingScreen = false;
2635 | if (err != null) {
2636 | _this.errorScreen = err;
2637 | }
2638 | if (err == null) {
2639 | _this.localScreenStream = stream;
2640 | }
2641 | return cb(err);
2642 | });
2643 | };
2644 | })(this));
2645 | };
2646 |
2647 | RtcCapture.prototype._prepareCameraMic = function(audio, video, cb) {
2648 | var opts;
2649 | if (this.options.audio === audio && this.options.video === video) {
2650 | return cb(null);
2651 | }
2652 | if (this.preparingMedia) {
2653 | return cb(null);
2654 | }
2655 | if (!audio && !video) {
2656 | this.options.audio = this.options.video = false;
2657 | this._handleLocalCameraMicStream(null);
2658 | return cb(null);
2659 | }
2660 | this.options.audio = audio;
2661 | this.options.video = video;
2662 | opts = {
2663 | audio: this.options.audio,
2664 | video: this.options.video
2665 | };
2666 | this.preparingMedia = true;
2667 | return RtcCapture.getUserMedia(opts, (function(_this) {
2668 | return function(err, stream) {
2669 | _this.preparingMedia = false;
2670 | if (err != null) {
2671 | _this.errorMedia = err;
2672 | }
2673 | _this._handleLocalCameraMicStream(stream);
2674 | return cb(err);
2675 | };
2676 | })(this));
2677 | };
2678 |
2679 | RtcCapture.prototype._handleLocalCameraMicStream = function(s) {
2680 | var e, olds, ref;
2681 | olds = this.localStream;
2682 | this.localStream = s;
2683 | if (olds && olds !== s) {
2684 | bit6.Rtc.stopMediaStream(olds);
2685 | }
2686 | e = (ref = this.localEl) != null ? ref : null;
2687 | if (this.options.video && s !== olds) {
2688 | if (!e) {
2689 | e = document.createElement('video');
2690 | e.setAttribute('autoplay', 'true');
2691 | e.setAttribute('muted', 'true');
2692 | e.muted = true;
2693 | this.emit('video', e, 1);
2694 | }
2695 | return this.localEl = bit6.Rtc.attachMediaStream(e, s);
2696 | } else if (!this.options.video && (e != null)) {
2697 | this.localEl = null;
2698 | e.src = '';
2699 | return this.emit('video', e, -1);
2700 | }
2701 | };
2702 |
2703 | RtcCapture.getUserMedia = function(opts, cb) {
2704 | var fn, n, ref, ref1, w;
2705 | w = window;
2706 | n = navigator;
2707 | fn = null;
2708 | if (!fn && (w != null ? w.getUserMedia : void 0)) {
2709 | fn = w.getUserMedia.bind(w);
2710 | }
2711 | if (!fn && n) {
2712 | fn = (ref = (ref1 = n.getUserMedia) != null ? ref1 : n.mozGetUserMedia) != null ? ref : n.webkitGetUserMedia;
2713 | if (fn) {
2714 | fn = fn.bind(n);
2715 | }
2716 | }
2717 | if (fn == null) {
2718 | return cb('WebRTC not supported. Could not find getUserMedia()');
2719 | }
2720 | return fn(opts, function(stream) {
2721 | return cb(null, stream);
2722 | }, cb);
2723 | };
2724 |
2725 | return RtcCapture;
2726 |
2727 | })(bit6.EventEmitter);
2728 |
2729 | }).call(this);
2730 |
2731 | (function() {
2732 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
2733 | hasProp = {}.hasOwnProperty;
2734 |
2735 | bit6.Rtc = (function(superClass) {
2736 | extend(Rtc, superClass);
2737 |
2738 | function Rtc() {
2739 | return Rtc.__super__.constructor.apply(this, arguments);
2740 | }
2741 |
2742 | Rtc.prototype.init = function(outgoing, iceServers) {
2743 | this.outgoing = outgoing;
2744 | this.pcConstraints = {
2745 | optional: [
2746 | {
2747 | DtlsSrtpKeyAgreement: true
2748 | }
2749 | ]
2750 | };
2751 | this.pcConfig = {
2752 | iceServers: iceServers
2753 | };
2754 | this.pc = null;
2755 | this.remoteEls = {};
2756 | this.outgoingTransfer = null;
2757 | this.incomingTransfer = null;
2758 | this.bufferedIceCandidates = [];
2759 | this.bufferedIceCandidatesDone = false;
2760 | this.bufferedOfferAnswer = null;
2761 | this.hadIceForAudio = false;
2762 | return this.hadIceForVideo = false;
2763 | };
2764 |
2765 | Rtc.prototype.update = function(capture, opts, remoteOpts) {
2766 | var m, sdpOpts;
2767 | this.options = opts;
2768 | if (this.outgoing && this._preparePeerConnection(capture)) {
2769 | if (this.options.data && (this.dc == null)) {
2770 | this._createDataChannel();
2771 | }
2772 | sdpOpts = {};
2773 | if (remoteOpts.audio || remoteOpts.video) {
2774 | m = {};
2775 | if (remoteOpts.audio) {
2776 | m.OfferToReceiveAudio = true;
2777 | }
2778 | if (remoteOpts.video) {
2779 | m.OfferToReceiveVideo = true;
2780 | }
2781 | sdpOpts.mandatory = m;
2782 | }
2783 | return this.pc.createOffer((function(_this) {
2784 | return function(offer) {
2785 | return _this._setLocalAndSendOfferAnswer(offer);
2786 | };
2787 | })(this), (function(_this) {
2788 | return function(err) {
2789 | return console.log('CreateOffer error', err);
2790 | };
2791 | })(this), sdpOpts);
2792 | }
2793 | };
2794 |
2795 | Rtc.prototype.stop = function() {
2796 | var e, id, ref, ref1, ref2;
2797 | ref = this.remoteEls;
2798 | for (id in ref) {
2799 | e = ref[id];
2800 | if (e.src != null) {
2801 | e.src = '';
2802 | }
2803 | this._removeDomElement(e);
2804 | }
2805 | this.remoteEls = {};
2806 | if ((ref1 = this.dc) != null) {
2807 | ref1.close();
2808 | }
2809 | this.dc = null;
2810 | if ((ref2 = this.pc) != null) {
2811 | ref2.close();
2812 | }
2813 | return this.pc = null;
2814 | };
2815 |
2816 | Rtc.prototype.getRemoteTrackKinds = function() {
2817 | var hasAudio, hasVideo, i, j, len, len1, ref, ref1, ref2, s, ss, t;
2818 | hasVideo = false;
2819 | hasAudio = false;
2820 | ss = (ref = (ref1 = this.pc) != null ? typeof ref1.getRemoteStreams === "function" ? ref1.getRemoteStreams() : void 0 : void 0) != null ? ref : [];
2821 | for (i = 0, len = ss.length; i < len; i++) {
2822 | s = ss[i];
2823 | ref2 = s.getTracks();
2824 | for (j = 0, len1 = ref2.length; j < len1; j++) {
2825 | t = ref2[j];
2826 | if (t.kind === 'video' && !t.muted) {
2827 | hasVideo = true;
2828 | }
2829 | if (t.kind === 'audio') {
2830 | hasAudio = true;
2831 | }
2832 | }
2833 | }
2834 | return {
2835 | audio: hasAudio,
2836 | video: hasVideo
2837 | };
2838 | };
2839 |
2840 | Rtc.prototype._preparePeerConnection = function(capture) {
2841 | var localStreams;
2842 | if (this.pc == null) {
2843 | this.pc = this._createPeerConnection();
2844 | }
2845 | if (this.pc == null) {
2846 | return false;
2847 | }
2848 | localStreams = capture.getStreams(this.options);
2849 | if ((typeof window !== "undefined" && window !== null ? window.mozRTCPeerConnection : void 0) != null) {
2850 | this._mozSyncLocalStreams(this.pc, localStreams);
2851 | } else {
2852 | this._syncLocalStreams(this.pc, localStreams);
2853 | }
2854 | return true;
2855 | };
2856 |
2857 | Rtc.prototype._syncLocalStreams = function(pc, localStreams) {
2858 | var i, j, k, len, len1, ref, results, s, toRemove;
2859 | toRemove = {};
2860 | ref = pc.getLocalStreams();
2861 | for (i = 0, len = ref.length; i < len; i++) {
2862 | s = ref[i];
2863 | toRemove[s.id] = s;
2864 | }
2865 | for (j = 0, len1 = localStreams.length; j < len1; j++) {
2866 | s = localStreams[j];
2867 | if (toRemove[s.id]) {
2868 | delete toRemove[s.id];
2869 | } else {
2870 | pc.addStream(s);
2871 | }
2872 | }
2873 | results = [];
2874 | for (k in toRemove) {
2875 | s = toRemove[k];
2876 | results.push(pc.removeStream(s));
2877 | }
2878 | return results;
2879 | };
2880 |
2881 | Rtc.prototype._mozSyncLocalStreams = function(pc, localStreams) {
2882 | var i, j, k, l, len, len1, len2, ref, ref1, results, s, t, toRemove;
2883 | toRemove = {};
2884 | ref = pc.getSenders();
2885 | for (i = 0, len = ref.length; i < len; i++) {
2886 | s = ref[i];
2887 | toRemove[s.track.id] = s;
2888 | }
2889 | for (j = 0, len1 = localStreams.length; j < len1; j++) {
2890 | s = localStreams[j];
2891 | ref1 = s.getTracks();
2892 | for (l = 0, len2 = ref1.length; l < len2; l++) {
2893 | t = ref1[l];
2894 | if (toRemove[t.id]) {
2895 | delete toRemove[t.id];
2896 | } else {
2897 | pc.addTrack(t, s);
2898 | }
2899 | }
2900 | }
2901 | results = [];
2902 | for (k in toRemove) {
2903 | s = toRemove[k];
2904 | results.push(pc.removeTrack(s));
2905 | }
2906 | return results;
2907 | };
2908 |
2909 | Rtc.prototype._createPeerConnection = function() {
2910 | var PeerConnection, error, ex, pc, ref, ref1;
2911 | try {
2912 | PeerConnection = (ref = (ref1 = window.RTCPeerConnection) != null ? ref1 : window.mozRTCPeerConnection) != null ? ref : window.webkitRTCPeerConnection;
2913 | pc = new PeerConnection(this.pcConfig, this.pcConstraints);
2914 | pc.onicecandidate = (function(_this) {
2915 | return function(evt) {
2916 | return _this._handleIceCandidate(evt.candidate);
2917 | };
2918 | })(this);
2919 | pc.ondatachannel = (function(_this) {
2920 | return function(evt) {
2921 | return _this._createDataChannel(evt.channel);
2922 | };
2923 | })(this);
2924 | pc.onaddstream = (function(_this) {
2925 | return function(evt) {
2926 | return _this._handleRemoteStreamAdded(evt.stream);
2927 | };
2928 | })(this);
2929 | pc.onremovestream = (function(_this) {
2930 | return function(evt) {
2931 | return _this._handleRemoteStreamRemoved(evt.stream);
2932 | };
2933 | })(this);
2934 | pc.onaddtrack = (function(_this) {
2935 | return function(evt) {
2936 | return console.log('onaddtrack', evt);
2937 | };
2938 | })(this);
2939 | pc.onremovetrack = (function(_this) {
2940 | return function(evt) {
2941 | return console.log('onremovetrack', evt);
2942 | };
2943 | })(this);
2944 | return pc;
2945 | } catch (error) {
2946 | ex = error;
2947 | console.log('Failed to create PeerConnection, exception: ', ex);
2948 | return null;
2949 | }
2950 | };
2951 |
2952 | Rtc.prototype._createDataChannel = function(dc) {
2953 | var opts;
2954 | if (!dc) {
2955 | opts = {
2956 | ordered: true
2957 | };
2958 | dc = this.pc.createDataChannel('mydc', opts);
2959 | }
2960 | this.dc = dc;
2961 | dc.binaryType = 'arraybuffer';
2962 | dc.onopen = (function(_this) {
2963 | return function() {
2964 | return _this._handleDcOpen();
2965 | };
2966 | })(this);
2967 | dc.onclose = (function(_this) {
2968 | return function() {
2969 | return _this._handleDcClose();
2970 | };
2971 | })(this);
2972 | dc.onerror = (function(_this) {
2973 | return function(err) {
2974 | return _this._handleDcError(err);
2975 | };
2976 | })(this);
2977 | return dc.onmessage = (function(_this) {
2978 | return function(evt) {
2979 | return _this._handleDcMessage(evt);
2980 | };
2981 | })(this);
2982 | };
2983 |
2984 | Rtc.prototype._handleDcOpen = function() {
2985 | return this.emit('dcOpen');
2986 | };
2987 |
2988 | Rtc.prototype._handleDcClose = function() {
2989 | if (this.outgoingTransfer || this.incomingTransfer) {
2990 | return this._handleDcError('DataChannel closed');
2991 | }
2992 | };
2993 |
2994 | Rtc.prototype._handleDcError = function(err) {
2995 | var i, len, ref, tr;
2996 | console.log("Data Channel Error:", err);
2997 | ref = [this.outgoingTransfer, this.incomingTransfer];
2998 | for (i = 0, len = ref.length; i < len; i++) {
2999 | tr = ref[i];
3000 | if (tr) {
3001 | tr.err = err;
3002 | }
3003 | if (tr) {
3004 | this.emit('transfer', tr);
3005 | }
3006 | }
3007 | return this.outgoingTransfer = this.incomingTransfer = null;
3008 | };
3009 |
3010 | Rtc.prototype._handleDcMessage = function(evt) {
3011 | var d, done, info;
3012 | d = evt.data;
3013 | if (this.incomingTransfer) {
3014 | if (d.byteLength != null) {
3015 | done = this.incomingTransfer._gotChunk(d);
3016 | this.emit('transfer', this.incomingTransfer);
3017 | if (done) {
3018 | return this.incomingTransfer = null;
3019 | }
3020 | } else {
3021 | return console.log('Error: incoming transfer data', d);
3022 | }
3023 | } else {
3024 | if (d.byteLength != null) {
3025 | return console.log('Error: incoming transfer not created for:', d);
3026 | } else {
3027 | info = JSON.parse(d);
3028 | if (info) {
3029 | this.incomingTransfer = new bit6.Transfer(false, info);
3030 | return this.emit('transfer', this.incomingTransfer);
3031 | } else {
3032 | return console.log('Error: could not parse incoming transfer info:', d);
3033 | }
3034 | }
3035 | }
3036 | };
3037 |
3038 | Rtc.prototype._handleIceCandidate = function(c) {
3039 | var base, idx;
3040 | if ((c != null ? c.candidate : void 0) != null) {
3041 | idx = c.sdpMLineIndex;
3042 | if ((base = this.bufferedIceCandidates)[idx] == null) {
3043 | base[idx] = [];
3044 | }
3045 | this.bufferedIceCandidates[idx].push(c);
3046 | return this.bufferedIceCandidatesDone = false;
3047 | } else {
3048 | this.bufferedIceCandidatesDone = true;
3049 | return this._maybeSendOfferAnswer();
3050 | }
3051 | };
3052 |
3053 | Rtc.prototype._maybeSendOfferAnswer = function() {
3054 | var offerAnswer;
3055 | if (this.bufferedOfferAnswer && this.bufferedIceCandidatesDone) {
3056 | offerAnswer = this._mergeSdp(this.bufferedOfferAnswer, this.bufferedIceCandidates);
3057 | this.bufferedOfferAnswer = null;
3058 | this.bufferedIceCandidates = [];
3059 | if (this.options.audio) {
3060 | this.hadIceForAudio = true;
3061 | }
3062 | if (this.options.video) {
3063 | this.hadIceForVideo = true;
3064 | }
3065 | if ((this.options.audio && !this.hadIceForAudio) || (this.options.video && !this.hadIceForVideo)) {
3066 | this.bufferedIceCandidatesDone = false;
3067 | }
3068 | if (offerAnswer) {
3069 | return this.emit('offerAnswer', offerAnswer);
3070 | }
3071 | }
3072 | };
3073 |
3074 | Rtc.prototype._mergeSdp = function(offerAnswer, candidatesByMlineIndex) {
3075 | var chunk, chunks, end, i, idx, len, sdp, start;
3076 | if (candidatesByMlineIndex.length === 0) {
3077 | return offerAnswer;
3078 | }
3079 | sdp = offerAnswer.sdp;
3080 | chunks = [];
3081 | start = 0;
3082 | while ((end = sdp.indexOf('m=', start + 1)) >= 0) {
3083 | chunks.push(sdp.substring(start, end));
3084 | start = end;
3085 | }
3086 | chunks.push(sdp.substring(start));
3087 | sdp = '';
3088 | for (idx = i = 0, len = chunks.length; i < len; idx = ++i) {
3089 | chunk = chunks[idx];
3090 | if (chunk.indexOf('m=audio') === 0) {
3091 | this.hadIceForAudio = true;
3092 | }
3093 | if (chunk.indexOf('m=video') === 0) {
3094 | this.hadIceForVideo = true;
3095 | }
3096 | sdp += chunk;
3097 | if (idx > 0 && (candidatesByMlineIndex[idx - 1] != null)) {
3098 | sdp += this._iceCandidatesToSdp(candidatesByMlineIndex[idx - 1]);
3099 | }
3100 | }
3101 | offerAnswer.sdp = sdp;
3102 | return offerAnswer;
3103 | };
3104 |
3105 | Rtc.prototype._iceCandidatesToSdp = function(arr) {
3106 | var c, i, len, t;
3107 | t = '';
3108 | for (i = 0, len = arr.length; i < len; i++) {
3109 | c = arr[i];
3110 | t += 'a=' + c.candidate + '\r\n';
3111 | }
3112 | return t;
3113 | };
3114 |
3115 | Rtc.prototype._handleRemoteStreamAdded = function(s) {
3116 | var e, hasAudio, hasVideo, i, len, ref, ref1, t;
3117 | s.onaddtrack = (function(_this) {
3118 | return function(ee) {
3119 | return _this._handleRemoteTrackAdded(ee.target, ee.track);
3120 | };
3121 | })(this);
3122 | s.onremovetrack = (function(_this) {
3123 | return function(ee) {
3124 | return _this._handleRemoteTrackRemoved(ee.target, ee.track);
3125 | };
3126 | })(this);
3127 | hasVideo = false;
3128 | hasAudio = false;
3129 | ref = s.getTracks();
3130 | for (i = 0, len = ref.length; i < len; i++) {
3131 | t = ref[i];
3132 | if (t.kind === 'video' && (!t.muted)) {
3133 | hasVideo = true;
3134 | }
3135 | if (t.kind === 'audio') {
3136 | hasAudio = true;
3137 | }
3138 | }
3139 | e = null;
3140 | if (hasVideo) {
3141 | e = document.createElement('video');
3142 | this.emit('video', e, 1);
3143 | } else if (hasAudio) {
3144 | e = document.createElement('audio');
3145 | if ((typeof window !== "undefined" && window !== null ? window.webrtcDetectedType : void 0) === 'plugin') {
3146 | if (typeof document !== "undefined" && document !== null) {
3147 | if ((ref1 = document.body) != null) {
3148 | ref1.appendChild(e);
3149 | }
3150 | }
3151 | }
3152 | }
3153 | if (e) {
3154 | e.setAttribute('autoplay', 'true');
3155 | e = bit6.Rtc.attachMediaStream(e, s);
3156 | }
3157 | return this.remoteEls[s.id] = e;
3158 | };
3159 |
3160 | Rtc.prototype._handleRemoteStreamRemoved = function(s) {
3161 | var e;
3162 | s.onaddtrack = null;
3163 | s.onremovetrack = null;
3164 | e = this.remoteEls[s.id];
3165 | delete this.remoteEls[s.id];
3166 | if (e != null) {
3167 | if (e.src != null) {
3168 | e.src = '';
3169 | }
3170 | return this._removeDomElement(e);
3171 | }
3172 | };
3173 |
3174 | Rtc.prototype._handleRemoteTrackAdded = function(s, t) {
3175 | if (t.kind === 'video') {
3176 | this._handleRemoteStreamRemoved(s);
3177 | return this._handleRemoteStreamAdded(s);
3178 | }
3179 | };
3180 |
3181 | Rtc.prototype._handleRemoteTrackRemoved = function(s, t) {
3182 | if (t.kind === 'video') {
3183 | this._handleRemoteStreamRemoved(s);
3184 | return this._handleRemoteStreamAdded(s);
3185 | }
3186 | };
3187 |
3188 | Rtc.prototype._removeDomElement = function(e) {
3189 | var i, isAudio, len, nodeName, p, ref, ref1, ref2;
3190 | nodeName = e != null ? (ref = e.nodeName) != null ? typeof ref.toLowerCase === "function" ? ref.toLowerCase() : void 0 : void 0 : void 0;
3191 | isAudio = false;
3192 | if ('object' === nodeName) {
3193 | if (e.children) {
3194 | ref1 = e.children;
3195 | for (i = 0, len = ref1.length; i < len; i++) {
3196 | p = ref1[i];
3197 | if ('tag' === p.name && 'audio' === p.value) {
3198 | isAudio = true;
3199 | }
3200 | }
3201 | }
3202 | } else if ('audio' === nodeName) {
3203 | isAudio = true;
3204 | }
3205 | if (isAudio) {
3206 | return (ref2 = e.parentNode) != null ? typeof ref2.removeChild === "function" ? ref2.removeChild(e) : void 0 : void 0;
3207 | } else {
3208 | return this.emit('video', e, -1);
3209 | }
3210 | };
3211 |
3212 | Rtc.prototype._setLocalAndSendOfferAnswer = function(offerAnswer) {
3213 | return this.pc.setLocalDescription(offerAnswer, (function(_this) {
3214 | return function() {
3215 | _this.bufferedOfferAnswer = {
3216 | type: offerAnswer.type,
3217 | sdp: offerAnswer.sdp
3218 | };
3219 | return _this._maybeSendOfferAnswer();
3220 | };
3221 | })(this), function(err) {
3222 | return console.log('Error setting PeerConnection localDescription', err, offerAnswer);
3223 | });
3224 | };
3225 |
3226 | Rtc.prototype.gotRemoteOfferAnswer = function(msg, capture) {
3227 | var SessionDescription, offerAnswer, ref;
3228 | SessionDescription = window.RTCSessionDescription || window.mozRTCSessionDescription || window.webkitRTCSessionDescription;
3229 | offerAnswer = new SessionDescription(msg);
3230 | switch (msg.type) {
3231 | case 'offer':
3232 | this._preparePeerConnection(capture);
3233 | this.pc.setRemoteDescription(offerAnswer, function() {
3234 | return true;
3235 | }, function(err) {
3236 | return console.log('Error setting PeerConnection remoteDescription for offer', err, offerAnswer);
3237 | });
3238 | return this.pc.createAnswer((function(_this) {
3239 | return function(answer) {
3240 | return _this._setLocalAndSendOfferAnswer(answer);
3241 | };
3242 | })(this), (function(_this) {
3243 | return function(err) {
3244 | return true;
3245 | };
3246 | })(this));
3247 | case 'answer':
3248 | return (ref = this.pc) != null ? ref.setRemoteDescription(offerAnswer, function() {
3249 | return true;
3250 | }, function(err) {
3251 | return console.log('Error setting PeerConnection remoteDescription for answer', err, offerAnswer);
3252 | }) : void 0;
3253 | }
3254 | };
3255 |
3256 | Rtc.prototype.gotHangup = function(msg) {
3257 | return this.stop();
3258 | };
3259 |
3260 | Rtc.prototype.startOutgoingTransfer = function(tr) {
3261 | var chunk, data, delay, intervalId, sent, total;
3262 | if (this.outgoingTransfer) {
3263 | return false;
3264 | }
3265 | if (!this.dc) {
3266 | return false;
3267 | }
3268 | if (this.dc.readyState !== 'open') {
3269 | return false;
3270 | }
3271 | this.outgoingTransfer = tr;
3272 | this.dc.send(JSON.stringify(tr.info));
3273 | data = tr.data;
3274 | delay = 10;
3275 | chunk = 16384;
3276 | sent = 0;
3277 | total = data.byteLength;
3278 | intervalId = 0;
3279 | return intervalId = setInterval((function(_this) {
3280 | return function() {
3281 | var clear, end, ref;
3282 | clear = false;
3283 | tr = _this.outgoingTransfer;
3284 | if (!tr || tr.err) {
3285 | clear = true;
3286 | }
3287 | if (!clear) {
3288 | if (((ref = _this.dc) != null ? ref.bufferedAmount : void 0) > chunk * 50) {
3289 | return;
3290 | }
3291 | end = sent + chunk;
3292 | if (end > total) {
3293 | end = total;
3294 | }
3295 | _this.dc.send(data.slice(sent, end));
3296 | sent = end;
3297 | tr.progress = sent;
3298 | clear = sent >= total;
3299 | }
3300 | if (clear) {
3301 | _this.outgoingTransfer = null;
3302 | clearInterval(intervalId);
3303 | }
3304 | if (tr) {
3305 | return _this.emit('transfer', tr);
3306 | }
3307 | };
3308 | })(this), delay);
3309 | };
3310 |
3311 | Rtc.attachMediaStream = function(elem, stream) {
3312 | if ((typeof window !== "undefined" && window !== null ? window.attachMediaStream : void 0) != null) {
3313 | return window.attachMediaStream(elem, stream);
3314 | }
3315 | if ((elem != null ? elem.srcObject : void 0) != null) {
3316 | elem.srcObject = stream;
3317 | } else if ((elem != null ? elem.mozSrcObject : void 0) != null) {
3318 | elem.mozSrcObject = stream;
3319 | } else if ((elem != null ? elem.src : void 0) != null) {
3320 | elem.src = window.URL.createObjectURL(stream);
3321 | } else {
3322 | console.log('Error attaching stream to element', elem);
3323 | }
3324 | return elem;
3325 | };
3326 |
3327 | Rtc.stopMediaStream = function(s) {
3328 | var i, len, ref, results, t;
3329 | if (s.stop) {
3330 | return s.stop();
3331 | } else if (s.getTracks) {
3332 | ref = s.getTracks();
3333 | results = [];
3334 | for (i = 0, len = ref.length; i < len; i++) {
3335 | t = ref[i];
3336 | results.push(t != null ? t.stop() : void 0);
3337 | }
3338 | return results;
3339 | }
3340 | };
3341 |
3342 | return Rtc;
3343 |
3344 | })(bit6.EventEmitter);
3345 |
3346 | }).call(this);
3347 |
3348 | (function() {
3349 | var extend = function(child, parent) { for (var key in parent) { if (hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; },
3350 | hasProp = {}.hasOwnProperty,
3351 | slice = [].slice;
3352 |
3353 | bit6.Session = (function(superClass) {
3354 | extend(Session, superClass);
3355 |
3356 | Session.prototype._sprops = ['authenticated', 'authInfo', 'config', 'device', 'identity', 'displayName', 'token', 'userid'];
3357 |
3358 | function Session(client) {
3359 | this.client = client;
3360 | Session.__super__.constructor.apply(this, arguments);
3361 | this._clear();
3362 | }
3363 |
3364 | Session.prototype._clear = function() {
3365 | var i, len, n, ref;
3366 | ref = this._sprops;
3367 | for (i = 0, len = ref.length; i < len; i++) {
3368 | n = ref[i];
3369 | this[n] = null;
3370 | }
3371 | return this.authenticated = false;
3372 | };
3373 |
3374 | Session.prototype.logout = function(cb) {
3375 | return this.client._onBeforeLogout((function(_this) {
3376 | return function(err) {
3377 | if (err) {
3378 | return typeof cb === "function" ? cb(err) : void 0;
3379 | }
3380 | _this._clear();
3381 | _this.emit('deauth');
3382 | return typeof cb === "function" ? cb(null) : void 0;
3383 | };
3384 | })(this));
3385 | };
3386 |
3387 | Session.prototype.getAuthInfo = function(cb) {
3388 | if (this.authInfo) {
3389 | return cb(null, this.authInfo);
3390 | }
3391 | return this.api('/info', (function(_this) {
3392 | return function(err, info) {
3393 | if (err) {
3394 | return cb(err);
3395 | }
3396 | _this.authInfo = info;
3397 | return cb(err, info);
3398 | };
3399 | })(this));
3400 | };
3401 |
3402 | Session.prototype.login = function(data, cb) {
3403 | data.identity = bit6.Client.normalizeIdentityUri(data.identity);
3404 | return this._auth('login', data, cb);
3405 | };
3406 |
3407 | Session.prototype.signup = function(data, cb) {
3408 | data.identity = bit6.Client.normalizeIdentityUri(data.identity);
3409 | return this._auth('signup', data, cb);
3410 | };
3411 |
3412 | Session.prototype.anonymous = function(cb) {
3413 | return this._auth('anonymous', {}, cb);
3414 | };
3415 |
3416 | Session.prototype.external = function(token, cb) {
3417 | return this._auth('external', {
3418 | token: token
3419 | }, cb);
3420 | };
3421 |
3422 | Session.prototype.oauth = function(provider, opts, cb) {
3423 | this._ensureOauthRedirectUri(opts);
3424 | return this.getAuthInfo((function(_this) {
3425 | return function(err, info) {
3426 | if (err) {
3427 | return cb(err);
3428 | }
3429 | return _this._auth(provider, opts, cb);
3430 | };
3431 | })(this));
3432 | };
3433 |
3434 | Session.prototype.oauth1_redirect = function(provider, opts, cb) {
3435 | this._ensureOauthRedirectUri(opts);
3436 | return this.api('/' + provider, 'POST', opts, cb);
3437 | };
3438 |
3439 | Session.prototype._ensureOauthRedirectUri = function(opts) {
3440 | var redirectUrl, ref, ref1, ref2;
3441 | redirectUrl = (ref = opts.redirect_uri) != null ? ref : opts.redirectUri;
3442 | if (redirectUrl == null) {
3443 | return opts.redirect_uri = typeof window !== "undefined" && window !== null ? (ref1 = window.location) != null ? (ref2 = ref1.href) != null ? ref2.split('?')[0] : void 0 : void 0 : void 0;
3444 | }
3445 | };
3446 |
3447 | Session.prototype.refresh = function(cb) {
3448 | return this._auth('refresh_token', {}, cb);
3449 | };
3450 |
3451 | Session.prototype.save = function() {
3452 | var i, len, n, ref, x;
3453 | x = {};
3454 | ref = this._sprops;
3455 | for (i = 0, len = ref.length; i < len; i++) {
3456 | n = ref[i];
3457 | x[n] = this[n];
3458 | }
3459 | return x;
3460 | };
3461 |
3462 | Session.prototype.resume = function(x, cb) {
3463 | var i, len, n, ref, ref1;
3464 | ref = this._sprops;
3465 | for (i = 0, len = ref.length; i < len; i++) {
3466 | n = ref[i];
3467 | this[n] = (ref1 = x != null ? x[n] : void 0) != null ? ref1 : null;
3468 | }
3469 | return this.refresh(cb);
3470 | };
3471 |
3472 | Session.prototype._auth = function(type, data, cb) {
3473 | if (data == null) {
3474 | data = {};
3475 | }
3476 | if (this.device == null) {
3477 | this.device = this.client._createDeviceId();
3478 | }
3479 | data.device = this.device;
3480 | data.embed = 'config';
3481 | return this.api('/' + type, 'POST', data, (function(_this) {
3482 | return function(err, result) {
3483 | var i, k, len, ref;
3484 | if (err) {
3485 | ref = ['config', 'identity', 'token', 'userid'];
3486 | for (i = 0, len = ref.length; i < len; i++) {
3487 | k = ref[i];
3488 | delete _this[k];
3489 | }
3490 | return cb(err);
3491 | }
3492 | return _this._authDone(result, cb);
3493 | };
3494 | })(this));
3495 | };
3496 |
3497 | Session.prototype._authDone = function(data, cb) {
3498 | var apikey2, claims, claimsStr, error, ex, i, k, len, r, ref, ref1, ref2, uid, x;
3499 | if (data.token == null) {
3500 | return cb('Jwt token is missing');
3501 | }
3502 | try {
3503 | r = data.token.split('.');
3504 | claimsStr = r != null ? r[1] : void 0;
3505 | if (claimsStr != null) {
3506 | claims = JSON.parse(bit6.Session.base64urlDecode(claimsStr));
3507 | }
3508 | } catch (error) {
3509 | ex = error;
3510 | console.log('Error parsing Jwt claims', r[1]);
3511 | }
3512 | x = claims != null ? (ref = claims.sub) != null ? ref.split('@') : void 0 : void 0;
3513 | if ((x != null) && x.length === 2) {
3514 | uid = x[0], apikey2 = x[1];
3515 | }
3516 | if (!((uid != null) && (apikey2 != null))) {
3517 | return cb('Jwt token has invalid format');
3518 | }
3519 | this.authenticated = true;
3520 | ref1 = ['config', 'identity', 'token', 'userid'];
3521 | for (i = 0, len = ref1.length; i < len; i++) {
3522 | k = ref1[i];
3523 | this[k] = (ref2 = data[k]) != null ? ref2 : this[k];
3524 | }
3525 | this.client._onLogin(cb);
3526 | return this.emit('auth');
3527 | };
3528 |
3529 | Session.prototype.api = function() {
3530 | var cb, i, params, path, ref;
3531 | path = arguments[0], params = 3 <= arguments.length ? slice.call(arguments, 1, i = arguments.length - 1) : (i = 1, []), cb = arguments[i++];
3532 | return (ref = this.client)._api.apply(ref, ['/auth/1' + path].concat(slice.call(params), [cb]));
3533 | };
3534 |
3535 | Session.base64urlEncode = function(s) {
3536 | s = btoa(s);
3537 | s = s.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
3538 | return s;
3539 | };
3540 |
3541 | Session.base64urlDecode = function(s) {
3542 | s += new Array(4 - ((s.length + 3) & 3)).join('=');
3543 | s = s.replace(/\-/g, '+').replace(/_/g, '/');
3544 | s = atob(s);
3545 | return s;
3546 | };
3547 |
3548 | return Session;
3549 |
3550 | })(bit6.EventEmitter);
3551 |
3552 | }).call(this);
3553 |
3554 | (function() {
3555 | bit6.Transfer = (function() {
3556 | function Transfer(outgoing, info1, data) {
3557 | var ref;
3558 | this.outgoing = outgoing;
3559 | this.info = info1;
3560 | this.progress = 0;
3561 | this.total = (ref = this.info.size) != null ? ref : 0;
3562 | this.err = null;
3563 | this.data = data != null ? data : null;
3564 | if (!this.outgoing) {
3565 | this.buf = [];
3566 | }
3567 | }
3568 |
3569 | Transfer.prototype.pending = function() {
3570 | return this.outgoing && this.data && !this.err && this.progress === 0;
3571 | };
3572 |
3573 | Transfer.prototype.completed = function() {
3574 | return this.progress === this.total && this.data && !this.err;
3575 | };
3576 |
3577 | Transfer.prototype.percentage = function() {
3578 | if (!this.total) {
3579 | return 0;
3580 | }
3581 | return (this.progress * 100 / this.total).toFixed(2);
3582 | };
3583 |
3584 | Transfer.prototype.json = function(o) {
3585 | var arr, i, j, k, ref, ref1, t;
3586 | if (o) {
3587 | this.info.type = 'application/json';
3588 | t = JSON.stringify(o);
3589 | arr = new Uint8Array(t.length);
3590 | for (i = j = 0, ref = t.length; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
3591 | arr[i] = t.charCodeAt(i);
3592 | }
3593 | this.info.size = arr.byteLength;
3594 | this.data = arr.buffer;
3595 | } else {
3596 | if (this.info.type === !'application/json') {
3597 | return null;
3598 | }
3599 | arr = new Uint8Array(this.data);
3600 | t = '';
3601 | for (i = k = 0, ref1 = arr.length; 0 <= ref1 ? k < ref1 : k > ref1; i = 0 <= ref1 ? ++k : --k) {
3602 | t += String.fromCharCode(arr[i]);
3603 | }
3604 | o = JSON.parse(t);
3605 | }
3606 | return o;
3607 | };
3608 |
3609 | Transfer.prototype._ensureSourceData = function(cb) {
3610 | if (this.data != null) {
3611 | return cb(null);
3612 | }
3613 | return bit6.Transfer.readFileAsArrayBuffer(this.info, (function(_this) {
3614 | return function(err, info2, data) {
3615 | if (err != null) {
3616 | _this.err = err;
3617 | }
3618 | if ((data != null) && (info2 != null)) {
3619 | _this.info = info2;
3620 | }
3621 | if (data != null) {
3622 | _this.data = data;
3623 | }
3624 | return cb(err);
3625 | };
3626 | })(this));
3627 | };
3628 |
3629 | Transfer.prototype._gotChunk = function(chunk) {
3630 | this.buf.push(chunk);
3631 | this.progress += chunk.byteLength;
3632 | if (this.progress < this.total) {
3633 | return false;
3634 | }
3635 | this._prepareReceivedData();
3636 | return true;
3637 | };
3638 |
3639 | Transfer.prototype._prepareReceivedData = function() {
3640 | var b, j, len, offset, ref, tmp;
3641 | tmp = new Uint8Array(this.progress);
3642 | offset = 0;
3643 | ref = this.buf;
3644 | for (j = 0, len = ref.length; j < len; j++) {
3645 | b = ref[j];
3646 | tmp.set(new Uint8Array(b), offset);
3647 | offset += b.byteLength;
3648 | }
3649 | this.buf = null;
3650 | return this.data = tmp.buffer;
3651 | };
3652 |
3653 | Transfer.readFileAsArrayBuffer = function(f, cb) {
3654 | var reader;
3655 | reader = new FileReader();
3656 | reader.onload = function(e) {
3657 | var data, info;
3658 | if (reader.readyState === FileReader.DONE) {
3659 | data = e.target.result;
3660 | console.log(f.name + ' - read ' + data.byteLength + 'b');
3661 | info = {
3662 | name: f.name,
3663 | type: f.type,
3664 | size: f.size
3665 | };
3666 | return cb(null, info, data);
3667 | }
3668 | };
3669 | reader.onerror = function(e) {
3670 | return cb(e);
3671 | };
3672 | reader.onabort = function(e) {
3673 | return cb(e);
3674 | };
3675 | return reader.readAsArrayBuffer(f);
3676 | };
3677 |
3678 | Transfer.dataUrlToBlob = function(url) {
3679 | var ab, arr, contentType, i, j, marker, parts, raw, rawLength, ref;
3680 | marker = ';base64,';
3681 | raw = null;
3682 | contentType = null;
3683 | if (url.indexOf(marker) < 0) {
3684 | parts = url.split(',');
3685 | contentType = parts[0].split(':')[1];
3686 | raw = decodeURIComponent(parts[1]);
3687 | } else {
3688 | parts = url.split(marker);
3689 | contentType = parts[0].split(':')[1];
3690 | raw = atob(parts[1]);
3691 | rawLength = raw.length;
3692 | ab = new ArrayBuffer(rawLength);
3693 | arr = new Uint8Array(ab);
3694 | for (i = j = 0, ref = rawLength; 0 <= ref ? j < ref : j > ref; i = 0 <= ref ? ++j : --j) {
3695 | arr[i] = raw.charCodeAt(i);
3696 | }
3697 | raw = ab;
3698 | }
3699 | return new Blob([raw], {
3700 | type: contentType
3701 | });
3702 | };
3703 |
3704 | return Transfer;
3705 |
3706 | })();
3707 |
3708 | }).call(this);
3709 |
--------------------------------------------------------------------------------
/www/index-bit6.js:
--------------------------------------------------------------------------------
1 | cordova.require("cordova-plugin-bit6.Bit6SDK");
2 |
3 | var pushWrappers = cordova.require("cordova-plugin-bit6.PushWrappers");
4 | var utils = cordova.require("cordova-plugin-bit6.Utils");
5 |
6 | exports.init = function(opts) {
7 |
8 | var b6 = new bit6.Client(opts);
9 | // Init DeviceId and Push support
10 | initPushService(b6, opts.pushSupport);
11 |
12 | // Just for iOS devices, making WebRTC methods available using iosrtc plugin
13 | if (window.device.platform === 'iOS' && cordova.plugins.iosrtc) {
14 | cordova.plugins.iosrtc.registerGlobals();
15 | }
16 |
17 | return b6;
18 | };
19 |
20 | function initPushService(b6, customPushSupport) {
21 | // Short name for the platform for Bit6
22 | var plat = device.platform.toLowerCase();
23 |
24 | // Is this a web-based emulator?
25 | var isEmu = false;
26 | if (navigator.simulator) {
27 | isEmu = true;
28 | }
29 | if (!isEmu && device.isVirtual) {
30 | isEmu = true;
31 | }
32 |
33 | // TODO: Are we sure that simulator is always web-based?
34 | // and not for example device emulator running on a PC?
35 | if (isEmu) {
36 | plat = 'web';
37 | } else if (plat == 'ios') {
38 | plat = 'ios';
39 | } else if (plat == 'android') {
40 | plat = 'and';
41 | } else {
42 | plat = 'web';
43 | }
44 |
45 | // DeviceId support
46 | if (device && device.platform && device.uuid) {
47 | b6._createDeviceId = function() {
48 | return plat + '-' + device.uuid;
49 | };
50 | }
51 |
52 | // Push support
53 | pushSupport = null;
54 | if (customPushSupport) {
55 | pushSupport = customPushSupport;
56 | }
57 | // TODO: Move this logic to pushWrappers since it contains both wrapper implementations?
58 | // Add a factory method in the wrappers file?
59 | else if (typeof PushNotification !== 'undefined' && PushNotification.init) { //PluginPush
60 | pushSupport = pushWrappers.pluginPushWrapper;
61 | } else if (window.plugins && window.plugins.pushNotification && window.plugins.pushNotification.register) { //legacy PushPlugin
62 | pushSupport = pushWrappers.legacyPushPluginWrapper;
63 | }
64 |
65 | // Helper function for submitting the push key to the server
66 | var sendPushkeyToServer = function(pushkey, cb) {
67 | var data = {
68 | id: b6.session.device,
69 | pushkey: pushkey,
70 | platform: plat,
71 | sdkv: bit6.Client.version
72 | };
73 | console.log('Send pushkey: ' + data.pushkey);
74 | console.log('Send device id: ' + data.id);
75 | b6.api('/me/devices', 'POST', data, function(err, res) {
76 | console.log('Dev update err=' + err);
77 | console.log('Dev update res=' + res);
78 |
79 | if (err) {
80 | alert('Dev update err=' + err);
81 | }
82 |
83 | if (cb) {
84 | cb();
85 | }
86 | });
87 | };
88 |
89 | var sendApnsPushkeyToServer = function(key) {
90 | //For iOS adding prefix p_/d_ to the push token for Bit6 server to use correct APNS
91 | // TODO: Do we want to call isApnsProduction() just once during init?
92 | utils.isApnsProduction(function(isProd) {
93 | var prefix = isProd ? 'p_' : 'd_';
94 | sendPushkeyToServer(prefix + key);
95 | });
96 | };
97 |
98 | // Listen to the completion of the auth procedure.
99 | // At that time we will have more info about the push config
100 | b6.session.on('auth', function() {
101 | if (!pushSupport) {
102 | alert("Missing push plugin. \nPlease install one of the push plugins known to Bit6, or provide your push implementation");
103 | return;
104 | }
105 |
106 | var pushOpts = {
107 | senderId: ''
108 | };
109 |
110 | if (plat === 'and') {
111 | if (b6.session.config.gcm === undefined) {
112 | alert("There will be errors as long as you don't enable GCM push notifications for this app.");
113 | return;
114 | }
115 | pushOpts.senderId = b6.session.config.gcm.senderId;
116 | }
117 |
118 | pushSupport.register(pushOpts, onPushRegistration, onPushNotification);
119 | });
120 |
121 |
122 | function onPushNotification(data) {
123 | if (!data) {
124 | alert("Bit6: Push data is missing!")
125 | return;
126 | }
127 | b6._handlePushRtMessage(data);
128 | }
129 |
130 | function onPushRegistration(pushToken) {
131 | if (plat == 'ios') {
132 | sendApnsPushkeyToServer(pushToken);
133 | } else {
134 | sendPushkeyToServer(pushToken);
135 | }
136 | }
137 |
138 | //Override _onBeforeLogout to remove the push key on the server
139 | var sdkBeforeLogout = b6._onBeforeLogout;
140 | b6._onBeforeLogout = function () {
141 | sendPushkeyToServer('', function() {
142 | sdkBeforeLogout.call(b6, function() {});
143 | });
144 | };
145 | }
146 |
--------------------------------------------------------------------------------
/www/phonegap-websocket.js:
--------------------------------------------------------------------------------
1 | var exec = require('cordova/exec');
2 |
3 | function hasWebSocket() {
4 | var m = /Android ([0-9]+)\.([0-9]+)/i.exec(navigator.userAgent);
5 | var hasConstructor = typeof WebSocket === "function";
6 | // WebRTC will be available either in Crosswalk or Web emulator
7 | // We assume if it is available, WebSocket will also be present
8 | // TODO: Check for PeerConnection instead?
9 | var hasWebRTC = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia || window.getUserMedia;
10 |
11 | console.log('WebSocket hasConstructor: ' + hasConstructor + ', hasWebRTC: ' + (hasWebRTC ? true : false) );
12 | console.log('WebSocket m: ' + m);
13 |
14 | if (hasWebRTC) { return true }
15 |
16 | // This is not an Android Cordova
17 | if (!m) { return hasConstructor; }
18 |
19 | var x = parseInt(m[1], 10);
20 | var y = parseInt(m[2], 10);
21 |
22 | return hasConstructor && (x > 4 || (x === 4 && y >= 4));
23 | }
24 |
25 | hasWebSocket() || (function() {
26 |
27 | console.log('WebSocket - building native');
28 |
29 | var websocketId = 0;
30 |
31 | // Websocket constructor
32 | var WebSocket = window.WebSocket = function(url, protocols, options) {
33 |
34 | var socket = this;
35 | options || (options = {});
36 | options.headers || (options.headers = {});
37 |
38 | if (Array.isArray(protocols)) {
39 | protocols = protocols.join(',');
40 | }
41 |
42 | if (protocols) {
43 | options.headers["Sec-WebSocket-Protocol"] = protocols;
44 | }
45 |
46 | this.events = [];
47 | this.options = options;
48 | this.url = url;
49 | this.readyState = WebSocket.CONNECTING;
50 | this.socketId = "_cordova_websocket_" + websocketId;
51 | websocketId += 1;
52 |
53 | cordova.exec(
54 | function (event) {
55 | socket._handleEvent(event);
56 | },
57 | function (event) {
58 | socket._handleEvent(event);
59 | }, "WebSocket", "connect", [ this.socketId, url, options ]);
60 | };
61 |
62 | WebSocket.prototype = {
63 | send: function (data) {
64 | if (this.readyState == WebSocket.CLOSED ||
65 | this.readyState == WebSocket.CLOSING) return;
66 |
67 | if (data instanceof ArrayBuffer) {
68 | data = arrayBufferToArray(data);
69 | }
70 | else if (data instanceof Blob) {
71 | var reader = new FileReader();
72 | reader.onloadend = function() {
73 | this.send(reader.result);
74 | }.bind(this);
75 |
76 | reader.readAsArrayBuffer(data);
77 | return;
78 | }
79 |
80 | cordova.exec(noob, noob, "WebSocket", "send", [ this.socketId, data ]);
81 | },
82 |
83 | close: function () {
84 | if (this.readyState == WebSocket.CLOSED ||
85 | this.readyState == WebSocket.CLOSING) return;
86 |
87 | this.readyState = WebSocket.CLOSING;
88 | cordova.exec(noob, noob, "WebSocket", "close", [ this.socketId ]);
89 | },
90 |
91 | addEventListener: function (type, listener, useCapture) {
92 | this.events[type] || (this.events[type] = []);
93 | this.events[type].push(listener);
94 | },
95 |
96 | removeEventListener: function (type, listener, useCapture) {
97 | var events;
98 |
99 | if (!this.events[type]) return;
100 |
101 | events = this.events[type];
102 |
103 | for (var i = events.length - 1; i >= 0; --i) {
104 | if (events[i] === listener) {
105 | events.splice(i, 1);
106 | return;
107 | }
108 | }
109 | },
110 |
111 | dispatchEvent: function (event) {
112 | var handler;
113 | var events = this.events[event.type] || [];
114 |
115 | for (var i = 0, l = events.length; i < l; i++) {
116 | //FIX (russaa): call event handler in context of the WebSocket instance
117 | events[i].call(this, event);
118 | }
119 |
120 | handler = this["on" + event.type];
121 | //FIX (russaa): call event handler in context of the WebSocket instance
122 | if (handler) handler.call(this, event);
123 | },
124 |
125 | _handleEvent: function (event) {
126 | this.readyState = event.readyState;
127 |
128 | if (event.type == "message") {
129 | event = createMessageEvent("message", event.data);
130 | }
131 | else if (event.type == "messageBinary") {
132 | var result = arrayToBinaryType(event.data, this.binaryType);
133 | event = createBinaryMessageEvent("message", result);
134 | }
135 | else {
136 | event = createSimpleEvent(event.type);
137 | }
138 |
139 | this.dispatchEvent(event);
140 |
141 | if (event.readyState == WebSocket.CLOSING ||
142 | event.readyState == WebSocket.CLOSED) {
143 | // cleanup socket from internal map
144 | cordova.exec(noob, noob, "WebSocket", "close", [ this.socketId ]);
145 | }
146 | }
147 | };
148 |
149 | WebSocket.prototype.CONNECTING = WebSocket.CONNECTING = 0;
150 | WebSocket.prototype.OPEN = WebSocket.OPEN = 1;
151 | WebSocket.prototype.CLOSING = WebSocket.CLOSING = 2;
152 | WebSocket.prototype.CLOSED = WebSocket.CLOSED = 3;
153 |
154 |
155 | // helpers
156 |
157 | function noob () {}
158 |
159 | function createSimpleEvent(type) {
160 | var event = document.createEvent("Event");
161 | event.initEvent(type, false, false);
162 | return event;
163 | }
164 |
165 | function createMessageEvent(type, data) {
166 | var event = document.createEvent("MessageEvent");
167 | event.initMessageEvent("message", false, false, data, null, null, window, null);
168 | return event;
169 | }
170 |
171 | function createBinaryMessageEvent(type, data) {
172 | // This does not match the WebSocket spec. The Event is suppose to be a
173 | // MessageEvent. But in Android WebView, MessageEvent.initMessageEvent()
174 | // makes a mess of ArrayBuffers. This should work with most clients, as
175 | // long as they don't do something odd with the event. The type is
176 | // correctly set to "message", so client event routing logic should work.
177 | var event = document.createEvent("Event");
178 |
179 | event.initEvent("message", false, false);
180 | event.data = data;
181 | return event;
182 | }
183 |
184 | function arrayBufferToArray(arrayBuffer) {
185 | var output = [];
186 | var utf8arr = new Uint8Array(arrayBuffer);
187 |
188 | for ( var i = 0, l = utf8arr.length; i < l; i++) {
189 | output.push(utf8arr[i]);
190 | }
191 |
192 | return output;
193 | }
194 |
195 | function arrayToBinaryType(array, binaryType) {
196 | var result = null;
197 |
198 | if (!array || !array.length) return result;
199 |
200 | var typedArr = new Uint8Array(array.length);
201 |
202 | typedArr.set(array);
203 |
204 | if (binaryType === "arraybuffer") {
205 | result = typedArr.buffer;
206 | }
207 | else if (binaryType === "blob") {
208 | if (window.WebKitBlobBuilder) {
209 | var builder = new WebKitBlobBuilder();
210 | builder.append(typedArr.buffer);
211 | result = builder.getBlob("application/octet-stream");
212 | }
213 | else {
214 | result = new Blob([ bytearray ], {
215 | type: 'application/octet-stream'
216 | });
217 | }
218 | }
219 |
220 | return result;
221 | }
222 |
223 | Array.isArray = Array.isArray || function (args) {
224 | return Object.prototype.toString.call(args) === "[object Array]";
225 | };
226 |
227 | window.ArrayBuffer = window.ArrayBuffer || function () {
228 | throw "ArrayBuffer not supported on this platform";
229 | };
230 |
231 | window.Blob = window.Blob || function () {
232 | throw "Blob not supported on this platform";
233 | };
234 |
235 | }());
236 |
237 | if (typeof module != 'undefined' && module.exports) {
238 | module.exports = window.WebSocket;
239 | }
240 |
241 |
--------------------------------------------------------------------------------
/www/push-wrappers.js:
--------------------------------------------------------------------------------
1 |
2 | // Helper functions for success / error responses
3 | var errh = function(r) {
4 | console.log('Push Error: ' + r);
5 | };
6 | var okh = function(r) {
7 | console.log('Push Success: ' + r);
8 | };
9 |
10 |
11 | var pluginPushWrapper = {
12 | _push: null,
13 | register: function(opts, registrationCallback, notificationCallback) {
14 | //var params = opts;
15 | var params = {
16 | android: {
17 | senderID: opts.senderId,
18 | clearNotifications: 'true'
19 | },
20 | ios: {
21 | alert: 'true',
22 | badge: 'true',
23 | sound: 'true',
24 | clearBadge: 'true'
25 | }
26 | };
27 |
28 | _push = PushNotification.init(params);
29 | _push.on('registration', function(data) {
30 | registrationCallback(data.registrationId);
31 | });
32 |
33 | _push.on('notification', function(data) {
34 | if (!(data && data.additionalData)) {
35 | alert("Missing Push Data!");
36 | return;
37 | }
38 |
39 | var bit6Data = data.additionalData.data ? data.additionalData.data : data.additionalData;
40 |
41 | if (data.count) {
42 | _push.setApplicationIconBadgeNumber(okh, errh, data.count);
43 | }
44 |
45 | notificationCallback(bit6Data);
46 | });
47 | }
48 | };
49 |
50 | var legacyPushPluginWrapper = {
51 | _push: null,
52 | register: function(opts, registrationCallback, notificationCallback) {
53 |
54 | var params = '';
55 | if (opts && opts.senderId) { //Android
56 | params = {
57 | senderID: opts.senderId,
58 | ecb: 'onPushGCM'
59 | };
60 | window.plugins.pushNotification.register(okh, errh, params);
61 | } else {
62 | params = {
63 | badge: 'true',
64 | sound: 'true',
65 | alert: 'true',
66 | ecb: 'onPushAPN'
67 | };
68 | //Hack to keep the old format of push payload, otherwise telerik/legacy plugin will fail
69 | bit6.Client.version = '0.9.6'
70 |
71 | window.plugins.pushNotification.register(registrationCallback, errh, params);
72 | }
73 |
74 | // Note that the function has to be in global scope!
75 | window.onPushGCM = function(e) {
76 | console.log('Got GCM: ' + e.event);
77 | switch (e.event) {
78 | // Registered with GCM
79 | case 'registered':
80 | if (e.regid && e.regid.length > 0) {
81 | // Notify the server about this GCM regId
82 | registrationCallback(e.regid);
83 | }
84 | break;
85 | // Got a push message
86 | case 'message':
87 | notificationCallback(e.payload);
88 | break;
89 | case 'error':
90 | alert('GCM err: ' + e.msg)
91 | break;
92 | default:
93 | console.log('GCM unknown event: ' + e.event)
94 | break;
95 | }
96 | }
97 |
98 | // Got notification from APN
99 | // Note that the function has to be in global scope!
100 | window.onPushAPN = function(e) {
101 | //FIXME: Getting error when gets push while app is running
102 | if (e.badge) {
103 | window.plugins.pushNotification.setApplicationIconBadgeNumber(okh, e.badge);
104 | }
105 | notificationCallback(e);
106 | }
107 | }
108 | };
109 |
110 |
111 | exports.pluginPushWrapper = pluginPushWrapper;
112 | exports.legacyPushPluginWrapper = legacyPushPluginWrapper;
113 |
114 |
--------------------------------------------------------------------------------
/www/utils.js:
--------------------------------------------------------------------------------
1 | var exec = require('cordova/exec');
2 |
3 | function isApnsProduction(onSuccess) {
4 | if (cordova.platformId === 'ios') { //This is needed only for ios.
5 | exec(onSuccess, null, 'UtilsPlugin', 'isApnsProduction', []);
6 | }
7 | else {
8 | onSuccess(false);
9 | }
10 | }
11 |
12 | exports.isApnsProduction = isApnsProduction;
13 |
--------------------------------------------------------------------------------