@roeehrl/tinode-sdk 0.25.1-sqlite.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +201 -0
- package/README.md +47 -0
- package/package.json +76 -0
- package/src/access-mode.js +567 -0
- package/src/cbuffer.js +244 -0
- package/src/cbuffer.test.js +107 -0
- package/src/comm-error.js +14 -0
- package/src/config.js +71 -0
- package/src/connection.js +537 -0
- package/src/db.js +1021 -0
- package/src/drafty.js +2758 -0
- package/src/drafty.test.js +1600 -0
- package/src/fnd-topic.js +123 -0
- package/src/index.js +29 -0
- package/src/index.native.js +35 -0
- package/src/large-file.js +325 -0
- package/src/me-topic.js +480 -0
- package/src/meta-builder.js +283 -0
- package/src/storage-sqlite.js +1081 -0
- package/src/tinode.js +2382 -0
- package/src/topic.js +2160 -0
- package/src/utils.js +309 -0
- package/src/utils.test.js +456 -0
- package/types/index.d.ts +1227 -0
- package/umd/tinode.dev.js +6856 -0
- package/umd/tinode.dev.js.map +1 -0
- package/umd/tinode.prod.js +2 -0
- package/umd/tinode.prod.js.map +1 -0
package/src/fnd-topic.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Definition of 'fnd' topic.
|
|
3
|
+
*
|
|
4
|
+
* @copyright 2015-2023 Tinode LLC.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
import * as Const from './config.js';
|
|
9
|
+
import Topic from './topic.js';
|
|
10
|
+
import {
|
|
11
|
+
mergeToCache
|
|
12
|
+
} from './utils.js';
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Special case of {@link Tinode.Topic} for searching for contacts and group topics
|
|
17
|
+
* @extends Tinode.Topic
|
|
18
|
+
*
|
|
19
|
+
*/
|
|
20
|
+
export default class TopicFnd extends Topic {
|
|
21
|
+
// List of users and topics uid or topic_name -> Contact object)
|
|
22
|
+
_contacts = {};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Create TopicFnd.
|
|
26
|
+
*
|
|
27
|
+
* @param {TopicFnd.Callbacks} callbacks - Callbacks to receive various events.
|
|
28
|
+
*/
|
|
29
|
+
constructor(callbacks) {
|
|
30
|
+
super(Const.TOPIC_FND, callbacks);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Override the original Topic._processMetaSubs
|
|
34
|
+
_processMetaSubs(subs) {
|
|
35
|
+
let updateCount = Object.getOwnPropertyNames(this._contacts).length;
|
|
36
|
+
// Reset contact list.
|
|
37
|
+
this._contacts = {};
|
|
38
|
+
for (let idx in subs) {
|
|
39
|
+
let sub = subs[idx];
|
|
40
|
+
const indexBy = sub.topic ? sub.topic : sub.user;
|
|
41
|
+
|
|
42
|
+
sub = mergeToCache(this._contacts, indexBy, sub);
|
|
43
|
+
updateCount++;
|
|
44
|
+
|
|
45
|
+
if (this.onMetaSub) {
|
|
46
|
+
this.onMetaSub(sub);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (updateCount > 0 && this.onSubsUpdated) {
|
|
51
|
+
this.onSubsUpdated(Object.keys(this._contacts));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Publishing to TopicFnd is not supported. {@link Topic#publish} is overriden and thows an {Error} if called.
|
|
57
|
+
* @memberof Tinode.TopicFnd#
|
|
58
|
+
* @throws {Error} Always throws an error.
|
|
59
|
+
*/
|
|
60
|
+
publish() {
|
|
61
|
+
return Promise.reject(new Error("Publishing to 'fnd' is not supported"));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* setMeta to TopicFnd resets contact list in addition to sending the message.
|
|
66
|
+
* @memberof Tinode.TopicFnd#
|
|
67
|
+
* @param {Tinode.SetParams} params parameters to update.
|
|
68
|
+
* @returns {Promise} Promise to be resolved/rejected when the server responds to request.
|
|
69
|
+
*/
|
|
70
|
+
setMeta(params) {
|
|
71
|
+
return Object.getPrototypeOf(TopicFnd.prototype).setMeta.call(this, params).then(_ => {
|
|
72
|
+
if (Object.keys(this._contacts).length > 0) {
|
|
73
|
+
this._contacts = {};
|
|
74
|
+
if (this.onSubsUpdated) {
|
|
75
|
+
this.onSubsUpdated([]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Check if the given tag is unique by asking the server.
|
|
83
|
+
* @param tag tag to check.
|
|
84
|
+
* @return promise to be resolved with true if the tag is unique, false otherwise.
|
|
85
|
+
*/
|
|
86
|
+
checkTagUniqueness(tag, caller) {
|
|
87
|
+
return new Promise((resolve, reject) => {
|
|
88
|
+
this.subscribe()
|
|
89
|
+
.then(_ => this.setMeta({
|
|
90
|
+
desc: {
|
|
91
|
+
public: tag
|
|
92
|
+
}
|
|
93
|
+
}))
|
|
94
|
+
.then(_ => this.getMeta(this.startMetaQuery().withTags().build()))
|
|
95
|
+
.then(meta => {
|
|
96
|
+
if (!meta || !Array.isArray(meta.tags) || meta.tags.length == 0) {
|
|
97
|
+
resolve(true);
|
|
98
|
+
}
|
|
99
|
+
const tags = meta.tags.filter(t => t !== caller);
|
|
100
|
+
resolve(tags.length == 0);
|
|
101
|
+
})
|
|
102
|
+
.catch(err => {
|
|
103
|
+
reject(err);
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Iterate over found contacts. If callback is undefined, use {@link this.onMetaSub}.
|
|
110
|
+
* @function
|
|
111
|
+
* @memberof Tinode.TopicFnd#
|
|
112
|
+
* @param {TopicFnd.ContactCallback} callback - Callback to call for each contact.
|
|
113
|
+
* @param {Object} context - Context to use for calling the `callback`, i.e. the value of `this` inside the callback.
|
|
114
|
+
*/
|
|
115
|
+
contacts(callback, context) {
|
|
116
|
+
const cb = (callback || this.onMetaSub);
|
|
117
|
+
if (cb) {
|
|
118
|
+
for (let idx in this._contacts) {
|
|
119
|
+
cb.call(context, this._contacts[idx], idx, this._contacts);
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tinode SDK - Web Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This is the main entry point for web/browser environments.
|
|
5
|
+
* Uses IndexedDB for storage via the DB class.
|
|
6
|
+
*
|
|
7
|
+
* For React Native, Metro bundler will use index.native.js instead.
|
|
8
|
+
*
|
|
9
|
+
* @module tinode-sdk
|
|
10
|
+
* @copyright 2015-2025 Tinode LLC, Activon
|
|
11
|
+
* @license Apache 2.0
|
|
12
|
+
*/
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
// Re-export everything from tinode.js
|
|
16
|
+
export {
|
|
17
|
+
Tinode,
|
|
18
|
+
AccessMode,
|
|
19
|
+
DB,
|
|
20
|
+
Drafty
|
|
21
|
+
}
|
|
22
|
+
from './tinode.js';
|
|
23
|
+
|
|
24
|
+
// Default export is Tinode class
|
|
25
|
+
export {
|
|
26
|
+
Tinode as
|
|
27
|
+
default
|
|
28
|
+
}
|
|
29
|
+
from './tinode.js';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Tinode SDK - React Native Entry Point
|
|
3
|
+
*
|
|
4
|
+
* This is the entry point for React Native (iOS/Android) environments.
|
|
5
|
+
* Metro bundler automatically picks this file for native platforms.
|
|
6
|
+
*
|
|
7
|
+
* Exports SQLiteStorage for persistent storage using expo-sqlite.
|
|
8
|
+
*
|
|
9
|
+
* @module tinode-sdk
|
|
10
|
+
* @copyright 2015-2025 Tinode LLC, Activon
|
|
11
|
+
* @license Apache 2.0
|
|
12
|
+
*/
|
|
13
|
+
'use strict';
|
|
14
|
+
|
|
15
|
+
// Re-export everything from tinode.js
|
|
16
|
+
export {
|
|
17
|
+
Tinode,
|
|
18
|
+
AccessMode,
|
|
19
|
+
DB,
|
|
20
|
+
Drafty
|
|
21
|
+
}
|
|
22
|
+
from './tinode.js';
|
|
23
|
+
|
|
24
|
+
// Default export is Tinode class
|
|
25
|
+
export {
|
|
26
|
+
Tinode as
|
|
27
|
+
default
|
|
28
|
+
}
|
|
29
|
+
from './tinode.js';
|
|
30
|
+
|
|
31
|
+
// Export SQLiteStorage for React Native persistence
|
|
32
|
+
export {
|
|
33
|
+
default as SQLiteStorage
|
|
34
|
+
}
|
|
35
|
+
from './storage-sqlite.js';
|
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Utilities for uploading and downloading files.
|
|
3
|
+
*
|
|
4
|
+
* @copyright 2015-2023 Tinode LLC.
|
|
5
|
+
*/
|
|
6
|
+
'use strict';
|
|
7
|
+
|
|
8
|
+
import CommError from './comm-error.js';
|
|
9
|
+
import {
|
|
10
|
+
isUrlRelative,
|
|
11
|
+
jsonParseHelper
|
|
12
|
+
} from './utils.js';
|
|
13
|
+
|
|
14
|
+
let XHRProvider;
|
|
15
|
+
|
|
16
|
+
function addURLParam(relUrl, key, value) {
|
|
17
|
+
const url = new URL(relUrl, window.location.origin);
|
|
18
|
+
url.searchParams.append(key, value);
|
|
19
|
+
return url.toString().substring(window.location.origin.length);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* @class LargeFileHelper - utilities for uploading and downloading files out of band.
|
|
24
|
+
* Don't instantiate this class directly. Use {Tinode.getLargeFileHelper} instead.
|
|
25
|
+
* @memberof Tinode
|
|
26
|
+
*
|
|
27
|
+
* @param {Tinode} tinode - the main Tinode object.
|
|
28
|
+
* @param {string} version - protocol version, i.e. '0'.
|
|
29
|
+
*/
|
|
30
|
+
export default class LargeFileHelper {
|
|
31
|
+
constructor(tinode, version) {
|
|
32
|
+
this._tinode = tinode;
|
|
33
|
+
this._version = version;
|
|
34
|
+
|
|
35
|
+
this._apiKey = tinode._apiKey;
|
|
36
|
+
this._authToken = tinode.getAuthToken();
|
|
37
|
+
|
|
38
|
+
// Ongoing requests.
|
|
39
|
+
this.xhr = [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Start uploading the file to an endpoint at baseUrl.
|
|
44
|
+
*
|
|
45
|
+
* @memberof Tinode.LargeFileHelper#
|
|
46
|
+
*
|
|
47
|
+
* @param {string} baseUrl base URL of upload server.
|
|
48
|
+
* @param {File|Blob} data data to upload.
|
|
49
|
+
* @param {string} avatarFor topic name if the upload represents an avatar.
|
|
50
|
+
* @param {Callback} onProgress callback. Takes one {float} parameter 0..1
|
|
51
|
+
* @param {Callback} onSuccess callback. Called when the file is successfully uploaded.
|
|
52
|
+
* @param {Callback} onFailure callback. Called in case of a failure.
|
|
53
|
+
*
|
|
54
|
+
* @returns {Promise} resolved/rejected when the upload is completed/failed.
|
|
55
|
+
*/
|
|
56
|
+
uploadWithBaseUrl(baseUrl, data, avatarFor, onProgress, onSuccess, onFailure) {
|
|
57
|
+
let url = `/v${this._version}/file/u/`;
|
|
58
|
+
if (baseUrl) {
|
|
59
|
+
let base = baseUrl;
|
|
60
|
+
if (base.endsWith('/')) {
|
|
61
|
+
// Removing trailing slash.
|
|
62
|
+
base = base.slice(0, -1);
|
|
63
|
+
}
|
|
64
|
+
if (base.startsWith('http://') || base.startsWith('https://')) {
|
|
65
|
+
url = base + url;
|
|
66
|
+
} else {
|
|
67
|
+
throw new Error(`Invalid base URL '${baseUrl}'`);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const instance = this;
|
|
72
|
+
const xhr = new XHRProvider();
|
|
73
|
+
this.xhr.push(xhr);
|
|
74
|
+
|
|
75
|
+
xhr.open('POST', url, true);
|
|
76
|
+
xhr.setRequestHeader('X-Tinode-APIKey', this._apiKey);
|
|
77
|
+
if (this._authToken) {
|
|
78
|
+
xhr.setRequestHeader('X-Tinode-Auth', `Token ${this._authToken.token}`);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let toResolve = null;
|
|
82
|
+
let toReject = null;
|
|
83
|
+
|
|
84
|
+
const result = new Promise((resolve, reject) => {
|
|
85
|
+
toResolve = resolve;
|
|
86
|
+
toReject = reject;
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
xhr.upload.onprogress = e => {
|
|
90
|
+
if (e.lengthComputable) {
|
|
91
|
+
if (onProgress) {
|
|
92
|
+
onProgress(e.loaded / e.total);
|
|
93
|
+
}
|
|
94
|
+
if (this.onProgress) {
|
|
95
|
+
this.onProgress(e.loaded / e.total);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
xhr.onload = function() {
|
|
101
|
+
let pkt;
|
|
102
|
+
try {
|
|
103
|
+
pkt = JSON.parse(this.response, jsonParseHelper);
|
|
104
|
+
} catch (err) {
|
|
105
|
+
instance._tinode.logger("ERROR: Invalid server response in LargeFileHelper", this.response);
|
|
106
|
+
pkt = {
|
|
107
|
+
ctrl: {
|
|
108
|
+
code: this.status,
|
|
109
|
+
text: this.statusText
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (this.status >= 200 && this.status < 300) {
|
|
115
|
+
if (toResolve) {
|
|
116
|
+
toResolve(pkt.ctrl.params.url);
|
|
117
|
+
}
|
|
118
|
+
if (onSuccess) {
|
|
119
|
+
onSuccess(pkt.ctrl);
|
|
120
|
+
}
|
|
121
|
+
} else if (this.status >= 400) {
|
|
122
|
+
if (toReject) {
|
|
123
|
+
toReject(new CommError(pkt.ctrl.text, pkt.ctrl.code));
|
|
124
|
+
}
|
|
125
|
+
if (onFailure) {
|
|
126
|
+
onFailure(pkt.ctrl);
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
instance._tinode.logger("ERROR: Unexpected server response status", this.status, this.response);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
|
|
133
|
+
xhr.onerror = function(e) {
|
|
134
|
+
if (toReject) {
|
|
135
|
+
toReject(e || new Error("failed"));
|
|
136
|
+
}
|
|
137
|
+
if (onFailure) {
|
|
138
|
+
onFailure(null);
|
|
139
|
+
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
xhr.onabort = function(e) {
|
|
143
|
+
if (toReject) {
|
|
144
|
+
toReject(new Error("upload cancelled by user"));
|
|
145
|
+
}
|
|
146
|
+
if (onFailure) {
|
|
147
|
+
onFailure(null);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const form = new FormData();
|
|
153
|
+
form.append('file', data);
|
|
154
|
+
form.set('id', this._tinode.getNextUniqueId());
|
|
155
|
+
if (avatarFor) {
|
|
156
|
+
form.set('topic', avatarFor);
|
|
157
|
+
}
|
|
158
|
+
xhr.send(form);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
if (toReject) {
|
|
161
|
+
toReject(err);
|
|
162
|
+
}
|
|
163
|
+
if (onFailure) {
|
|
164
|
+
onFailure(null);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Start uploading the file to default endpoint.
|
|
172
|
+
*
|
|
173
|
+
* @memberof Tinode.LargeFileHelper#
|
|
174
|
+
*
|
|
175
|
+
* @param {File|Blob} data to upload
|
|
176
|
+
* @param {string} avatarFor topic name if the upload represents an avatar.
|
|
177
|
+
* @param {Callback} onProgress callback. Takes one {float} parameter 0..1
|
|
178
|
+
* @param {Callback} onSuccess callback. Called when the file is successfully uploaded.
|
|
179
|
+
* @param {Callback} onFailure callback. Called in case of a failure.
|
|
180
|
+
*
|
|
181
|
+
* @returns {Promise} resolved/rejected when the upload is completed/failed.
|
|
182
|
+
*/
|
|
183
|
+
upload(data, avatarFor, onProgress, onSuccess, onFailure) {
|
|
184
|
+
const baseUrl = (this._tinode._secure ? 'https://' : 'http://') + this._tinode._host;
|
|
185
|
+
return this.uploadWithBaseUrl(baseUrl, data, avatarFor, onProgress, onSuccess, onFailure);
|
|
186
|
+
}
|
|
187
|
+
/**
|
|
188
|
+
* Download the file from a given URL using GET request. This method works with the Tinode server only.
|
|
189
|
+
*
|
|
190
|
+
* @memberof Tinode.LargeFileHelper#
|
|
191
|
+
*
|
|
192
|
+
* @param {string} relativeUrl - URL to download the file from. Must be relative url, i.e. must not contain the host.
|
|
193
|
+
* @param {string=} filename - file name to use for the downloaded file.
|
|
194
|
+
*
|
|
195
|
+
* @returns {Promise} resolved/rejected when the download is completed/failed.
|
|
196
|
+
*/
|
|
197
|
+
download(relativeUrl, filename, mimetype, onProgress, onError) {
|
|
198
|
+
if (!isUrlRelative(relativeUrl)) {
|
|
199
|
+
// As a security measure refuse to download from an absolute URL.
|
|
200
|
+
if (onError) {
|
|
201
|
+
onError(`The URL '${relativeUrl}' must be relative, not absolute`);
|
|
202
|
+
}
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
if (!this._authToken) {
|
|
206
|
+
if (onError) {
|
|
207
|
+
onError("Must authenticate first");
|
|
208
|
+
}
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
const instance = this;
|
|
212
|
+
|
|
213
|
+
const xhr = new XHRProvider();
|
|
214
|
+
this.xhr.push(xhr);
|
|
215
|
+
|
|
216
|
+
// Add '&asatt=1' to URL to request 'Content-Disposition: attachment' response header.
|
|
217
|
+
relativeUrl = addURLParam(relativeUrl, 'asatt', '1');
|
|
218
|
+
|
|
219
|
+
// Get data as blob (stored by the browser as a temporary file).
|
|
220
|
+
xhr.open('GET', relativeUrl, true);
|
|
221
|
+
xhr.setRequestHeader('X-Tinode-APIKey', this._apiKey);
|
|
222
|
+
xhr.setRequestHeader('X-Tinode-Auth', 'Token ' + this._authToken.token);
|
|
223
|
+
xhr.responseType = 'blob';
|
|
224
|
+
|
|
225
|
+
xhr.onprogress = function(e) {
|
|
226
|
+
if (onProgress) {
|
|
227
|
+
// Passing e.loaded instead of e.loaded/e.total because e.total
|
|
228
|
+
// is always 0 with gzip compression enabled by the server.
|
|
229
|
+
onProgress(e.loaded);
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
let toResolve = null;
|
|
234
|
+
let toReject = null;
|
|
235
|
+
|
|
236
|
+
const result = new Promise((resolve, reject) => {
|
|
237
|
+
toResolve = resolve;
|
|
238
|
+
toReject = reject;
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
// The blob needs to be saved as file. There is no known way to
|
|
242
|
+
// save the blob as file other than to fake a click on an <a href... download=...>.
|
|
243
|
+
xhr.onload = function() {
|
|
244
|
+
if (this.status == 200) {
|
|
245
|
+
const link = document.createElement('a');
|
|
246
|
+
// URL.createObjectURL is not available in non-browser environment. This call will fail.
|
|
247
|
+
link.href = window.URL.createObjectURL(new Blob([this.response], {
|
|
248
|
+
type: mimetype
|
|
249
|
+
}));
|
|
250
|
+
link.style.display = 'none';
|
|
251
|
+
link.setAttribute('download', filename);
|
|
252
|
+
document.body.appendChild(link);
|
|
253
|
+
link.click();
|
|
254
|
+
document.body.removeChild(link);
|
|
255
|
+
window.URL.revokeObjectURL(link.href);
|
|
256
|
+
if (toResolve) {
|
|
257
|
+
toResolve();
|
|
258
|
+
}
|
|
259
|
+
} else if (this.status >= 400 && toReject) {
|
|
260
|
+
// The this.responseText is undefined, must use this.response which is a blob.
|
|
261
|
+
// Need to convert this.response to JSON. The blob can only be accessed by the
|
|
262
|
+
// FileReader.
|
|
263
|
+
const reader = new FileReader();
|
|
264
|
+
reader.onload = function() {
|
|
265
|
+
try {
|
|
266
|
+
const pkt = JSON.parse(this.result, jsonParseHelper);
|
|
267
|
+
toReject(new CommError(pkt.ctrl.text, pkt.ctrl.code));
|
|
268
|
+
} catch (err) {
|
|
269
|
+
instance._tinode.logger("ERROR: Invalid server response in LargeFileHelper", this.result);
|
|
270
|
+
toReject(err);
|
|
271
|
+
}
|
|
272
|
+
};
|
|
273
|
+
reader.readAsText(this.response);
|
|
274
|
+
}
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
xhr.onerror = function(e) {
|
|
278
|
+
if (toReject) {
|
|
279
|
+
toReject(new Error("failed"));
|
|
280
|
+
}
|
|
281
|
+
if (onError) {
|
|
282
|
+
onError(e);
|
|
283
|
+
}
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
xhr.onabort = function() {
|
|
287
|
+
if (toReject) {
|
|
288
|
+
toReject(null);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
|
|
292
|
+
try {
|
|
293
|
+
xhr.send();
|
|
294
|
+
} catch (err) {
|
|
295
|
+
if (toReject) {
|
|
296
|
+
toReject(err);
|
|
297
|
+
}
|
|
298
|
+
if (onError) {
|
|
299
|
+
onError(err);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return result;
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Try to cancel all ongoing uploads or downloads.
|
|
307
|
+
* @memberof Tinode.LargeFileHelper#
|
|
308
|
+
*/
|
|
309
|
+
cancel() {
|
|
310
|
+
this.xhr.forEach(req => {
|
|
311
|
+
if (req.readyState < 4) {
|
|
312
|
+
req.abort();
|
|
313
|
+
}
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* To use LargeFileHelper in a non browser context, supply XMLHttpRequest provider.
|
|
318
|
+
* @static
|
|
319
|
+
* @memberof LargeFileHelper
|
|
320
|
+
* @param xhrProvider XMLHttpRequest provider, e.g. for node <code>require('xhr')</code>.
|
|
321
|
+
*/
|
|
322
|
+
static setNetworkProvider(xhrProvider) {
|
|
323
|
+
XHRProvider = xhrProvider;
|
|
324
|
+
}
|
|
325
|
+
}
|