@taptalk.io/web-sdk
Version:
TapTalk.io Chat SDK for Web
1,418 lines (1,160 loc) • 263 kB
JavaScript
/* 22-04-2026 00:00 v1.41.0 */
// Changes:
// 1. fix compress image file
var define, CryptoJS;
var CryptoJS = require('./lib/crypto-js');
var md5 = require('./lib/md5');
var tapTalkRooms = {}; //room list with array of messages
var tapTalkRoomListHashmap = {}; //room list last message
var tapTalkRoomListHashmapPinned = {}; //room list last message - pinned
var tapTalkRoomListHashmapUnPinned = {}; //room list last message - unpinned
var tapTalkRoomListIDPinned = {}; //room list id pinned
// var tapTalkEmitMessageQueue = {}; //room list undelivered message
var tapRoomStatusListeners = [];
var tapMessageListeners = [];
var tapRoomListListeners = [];
var tapContactListeners = [];
var tapListener = [];
var taptalkContact = {};
var tapTalkRandomColors = ['#f99181', '#a914db', '#f26046', '#fb76ab', '#c4c9d1', '#4239be', '#9c89f1', '#f4c22c'];
var projectConfigs = null;
var expiredKey = [];
var refreshAccessTokenCallbackArray = [];
var isConnectRunning = false;
var isDoneFirstSetupRoomList = false;
var isWithoutRoomList = false;
var isNeedToCallApiUpdateRoomList = true;
var isFirstConnectedToWebSocket = false;
var taptalkStarMessageHashmap = {};
var taptalkUnreadMessageList = {};
var taptalkPinnedMessageHashmap = {};
var taptalkPinnedMessageIDHashmap = {};
var taptalkIndexedDBNotSupport = "Your browser doesn't support a stable version of IndexedDB. Such and such feature will not be available.";
var taptalkMessageReadCount = {};
const MAX_PINNED_ROOM = 10;
var db;
// window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
//initiate index db for local file(image, video, file)
function addFileToDB(fileID, base64, fileType) {
if (db) {
let tx = db.transaction(['files'], 'readwrite');
let store = tx.objectStore('files');
var objectStoreRequest = store.get(fileID);
objectStoreRequest.onsuccess = function (event) {
if (!objectStoreRequest.result) {
let file = { file: base64, type: fileType, timestamp: new Date().valueOf() };
store.add(file, fileID)
}
};
// tx.oncomplete = function() {
// }
tx.onerror = function (event) {
console.log('error storing note files' + event.target.errorCode);
}
} else {
console.log(taptalkIndexedDBNotSupport);
}
}
function deleteExpiredFileKey() {
if (db) {
let tx = db.transaction(['files'], 'readwrite');
let store = tx.objectStore('files');
if (expiredKey.length > 0) {
for (let i in expiredKey) {
store.delete(expiredKey[i])
}
}
} else {
console.log(taptalkIndexedDBNotSupport);
}
}
(function () {
if (!window.indexedDB) {
console.log(taptalkIndexedDBNotSupport);
} else {
var dbTapTalk = indexedDB.open('tapFiles', 1);
dbTapTalk.onupgradeneeded = function (event) {
db = event.target.result;
let notes = db.createObjectStore('files');
}
dbTapTalk.onsuccess = function (event) {
db = event.target.result;
let tx = db.transaction(['files'], 'readwrite');
let store = tx.objectStore('files');
var objectStoreRequest = store.getAll();
var objectKeyRequest = store.getAllKeys();
objectStoreRequest.onsuccess = function (event) {
if (!objectStoreRequest.result) {
let file = { file: base64, type: fileType, timestamp: new Date().valueOf() };
store.add(file, fileID)
}
};
objectKeyRequest.onsuccess = function (event) {
for (let i in objectKeyRequest.result) {
module.exports.tapCoreChatRoomManager.getFileFromDB(objectKeyRequest.result[i], function (data) {
//two weeks from now will be deleted
if ((new Date().valueOf() - data.timestamp) > 1576155138) {
expiredKey.push(objectKeyRequest.result[i]);
}
if (i === ((objectKeyRequest.result.length - 1).toString())) {
deleteExpiredFileKey();
}
})
}
};
}
dbTapTalk.onerror = function (event) {
console.log('error opening database ' + event.target.errorCode);
}
}
})();
var authenticationHeader = {
// "Content-Type": "application/json",
"App-Key": "",
"Authorization": "",
"Device-Identifier": "",
"Device-Model": navigator.appName,
"Device-Platform": "web",
// "Server-Key": ""
};
var baseApiUrl = "";
var webSocket = null;
const ROOM_TYPE = {
PERSONAL: 1,
GROUP: 2,
CHANNEL: 3
}
const KEY_PASSWORD_ENCRYPTOR = "kHT0sVGIKKpnlJE5BNkINYtuf19u6+Kk811iMuWQ5tM";
//listen connection status
window.addEventListener('offline', function () {
isNeedToCallApiUpdateRoomList = true;
});
//listen connection status
function bytesToSize(bytes) {
var sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
if (bytes == 0) return '0 Byte';
var i = parseInt(Math.floor(Math.log(bytes) / Math.log(1024)));
return (bytes / Math.pow(1024, i)).toFixed(2).replace('.00', '') + ' ' + sizes[i];
}
function getDeviceID() {
let localDeviceID = localStorage.getItem('tapTalk.DeviceID');
let md5DeviceID = md5(navigator.userAgent + "@" + new Date().valueOf());
let generateDeviceID = md5DeviceID.substring(0, 16) + "-" + guid();
if (localDeviceID !== null) {
return localDeviceID;
}
localStorage.setItem('tapTalk.DeviceID', generateDeviceID);
return generateDeviceID;
}
class WebWorker {
constructor(worker) {
const code = worker.toString();
const blob = new Blob(["(" + code + ")()"]);
return new Worker(URL.createObjectURL(blob));
}
}
// var reader = new FileReader();
const SOCKET_START_TYPING = "chat/startTyping";
const SOCKET_STOP_TYPING = "chat/stopTyping";
const EVENT_OPEN_ROOM = "chat/openRoom";
const SOCKET_CLOSE_ROOM = "chat/closeRoom";
const SOCKET_NEW_MESSAGE = "chat/sendMessage";
const SOCKET_UPDATE_MESSAGE = "chat/updateMessage";
const SOCKET_DELETE_MESSAGE = "chat/deleteMessage";
const SOCKET_OPEN_MESSAGE = "chat/openMessage";
const SOCKET_AUTHENTICATION = "user/authentication";
const SOCKET_USER_ONLINE_STATUS = "user/status";
const SOCKET_USER_UPDATED = "user/updated";
const SOCKET_USER_BLOCKED = "user/block";
const SOCKET_USER_UNBLOCKED = "user/unblock";
const SOCKET_CLEAR_CHAT_ROOM = "room/clearChat";
const SOCKET_SCHEDULED_MESSAGE = "room/scheduleMessage";
const CHAT_MESSAGE_TYPE_TEXT = 1001;
const CHAT_MESSAGE_TYPE_IMAGE = 1002;
const CHAT_MESSAGE_TYPE_VIDEO = 1003;
const CHAT_MESSAGE_TYPE_FILE = 1004;
const CHAT_MESSAGE_TYPE_LOCATION = 1005;
const CHAT_MESSAGE_TYPE_CONTACT = 1006;
const CHAT_MESSAGE_TYPE_STICKER = 1007;
const CHAT_MESSAGE_TYPE_VOICE = 1008;
const CHAT_MESSAGE_TYPE_AUDIO = 1009;
const CHAT_MESSAGE_TYPE_LINK = 1010;
const CHAT_MESSAGE_TYPE_PRODUCT = 2001;
const CHAT_MESSAGE_TYPE_CATEORY = 2002;
const CHAT_MESSAGE_TYPE_PAYMENT_CONFIRMATION = 2004;
const CHAT_MESSAGE_TYPE_SYSTEM_MESSAGE = 9001;
const CHAT_MESSAGE_TYPE_UNREAD_MESSAGE_IDENTIFIER = 9002;
const MESSAGE_ID = "0";
function doXMLHTTPRequest(method, header, url, data, isMultipart = false) {
return new Promise(function (resolve, reject) {
let xhr = new XMLHttpRequest();
xhr.open(method, url, true);
for (let headerVal in header) {
xhr.setRequestHeader(headerVal, header[headerVal]);
}
xhr.send(method === 'POST' && isMultipart ? data : JSON.stringify(data));
xhr.onload = function () {
if (xhr.status === 200) {
resolve(JSON.parse(xhr.response));
} else {
reject({
status: xhr.status,
statusText: xhr.statusText
});
}
};
xhr.onerror = function () {
reject({
status: xhr.status,
statusText: xhr.statusText
});
};
});
}
function doXMLHTTPRequestToBase64(method, header, url, data, message, onProgress) {
let sendProgressDownload = async (oEvent) => {
if (oEvent.lengthComputable) {
var percentComplete = oEvent.loaded / oEvent.total * 100;
onProgress(message, Math.round(percentComplete * 10) / 10, oEvent.loaded);
}
}
return new Promise(function (resolve, reject) {
let xhrBase64 = new XMLHttpRequest();
xhrBase64.addEventListener("progress", sendProgressDownload);
xhrBase64.open(method, url, true);
for (let headerVal in header) {
xhrBase64.setRequestHeader(headerVal, header[headerVal]);
}
xhrBase64.responseType = 'arraybuffer';
xhrBase64.send(JSON.stringify(data));
xhrBase64.onload = function () {
if (xhrBase64.status === 200) {
let convertToBase64 = () => {
let uInt8Array = new Uint8Array(xhrBase64.response);
let i = uInt8Array.length;
let binaryString = new Array(i);
while (i--) {
binaryString[i] = String.fromCharCode(uInt8Array[i]);
}
let data = binaryString.join('');
let base64 = window.btoa(data);
return base64;
};
if (xhrBase64.getResponseHeader('content-type') === "application/json") {
var enc = new TextDecoder("utf-8");
resolve(JSON.parse(enc.decode(xhrBase64.response)));
} else {
resolve({
base64: convertToBase64(),
contentType: xhrBase64.getResponseHeader('content-type')
});
}
} else {
reject({
status: xhrBase64.status,
statusText: xhrBase64.statusText
});
}
};
xhrBase64.onerror = function () {
reject({
status: xhrBase64.status,
statusText: xhrBase64.statusText
});
};
});
}
function doXMLHTTPRequestUpload(method, header, url, data, onProgress) {
let sendProgressUpload = async (oEvent) => {
if (oEvent.lengthComputable) {
var percentComplete = Math.round((oEvent.loaded / oEvent.total * 100) * 10) / 10;
onProgress(percentComplete, oEvent.loaded);
}
}
return new Promise(function (resolve, reject) {
let xhrUpload = new XMLHttpRequest();
xhrUpload.open(method, url, true);
for (let headerVal in header) {
xhrUpload.setRequestHeader(headerVal, header[headerVal]);
}
xhrUpload.upload.addEventListener("progress", sendProgressUpload);
xhrUpload.send(data);
xhrUpload.onload = function () {
if (xhrUpload.status === 200) {
resolve(JSON.parse(xhrUpload.response));
} else {
reject({
status: xhrUpload.status,
statusText: xhrUpload.statusText
});
}
};
xhrUpload.onerror = function () {
reject({
status: xhrUpload.status,
statusText: xhrUpload.statusText
});
};
});
}
function getLocalStorageObject(storage) {
return JSON.parse(decryptKey(localStorage.getItem(storage), KEY_PASSWORD_ENCRYPTOR));
}
function generateHeaderQuerystring() {
let keys = {
"content_type": authenticationHeader["Content-Type"],
"app_key": authenticationHeader["App-Key"],
"authorization": `Bearer ${getLocalStorageObject('TapTalk.UserData').accessToken}`,
"device_identifier": authenticationHeader["Device-Identifier"],
"device_model": authenticationHeader["Device-Model"],
"device_platform": "web",
}
var s = [];
for (var i in keys) {
s.push(i + "=" + encodeURIComponent(keys[i]));
}
return s.join("&");
}
function setUserDataStorage(response) {
let data = response;
data.logout = false;
return localStorage.setItem('TapTalk.UserData', encryptKey(JSON.stringify(data), KEY_PASSWORD_ENCRYPTOR));
}
function guid() {
let guidChar = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_";
let result = "";
let guidCharLength = guidChar.length;
for (var i = 0; i < 32; i++) {
result += guidChar.charAt(Math.floor(Math.random() * guidCharLength));
}
return result;
}
function isFileAllowed(fileType, file) {
let fileTypeAllowed = false;
for (let type in fileType) {
if (fileType[type] === file) {
fileTypeAllowed = true;
}
}
return fileTypeAllowed;
}
var tapReader = new FileReader();
tapReader.onload = function () {
var messages = this.result.split('\n');
for (let i in messages) {
var m = JSON.parse(messages[i]);
// console.log(m)
if (isDoneFirstSetupRoomList) {
handleEmit(m);
}
switch (m.eventName) {
case "chat/sendMessage":
for (let i in tapMessageListeners) {
tapMessageListeners[i].onReceiveNewMessage(m.data, m.isScheduled);
}
break;
case "chat/updateMessage":
for (let i in tapMessageListeners) {
tapMessageListeners[i].onReceiveUpdateMessage(m.data);
}
break;
case "chat/startTyping":
for (let i in tapRoomStatusListeners) {
tapRoomStatusListeners[i].onReceiveStartTyping(m.data.roomID, m.data.user);
}
break;
case "chat/stopTyping":
for (let i in tapRoomStatusListeners) {
tapRoomStatusListeners[i].onReceiveStopTyping(m.data.roomID, m.data.user);
}
break;
case "user/status":
for (let i in tapRoomStatusListeners) {
tapRoomStatusListeners[i].onReceiveOnlineStatus(m.data.user, m.data.isOnline, m.data.lastActive);
}
break;
case SOCKET_SCHEDULED_MESSAGE:
for (let i in tapRoomStatusListeners) {
tapRoomStatusListeners[i].onReceiveScheduledMessage(m.data.room, m.data.timestamp);
}
break;
case SOCKET_CLEAR_CHAT_ROOM:
for (let i in tapRoomListListeners) {
tapRoomListListeners[i].onChatRoomDeleted(m.data.room.roomID);
}
break;
case SOCKET_USER_BLOCKED:
for (let i in tapContactListeners) {
tapContactListeners[i].onContactBlocked(m.data.user);
}
break;
case SOCKET_USER_UNBLOCKED:
for (let i in tapContactListeners) {
tapContactListeners[i].onContactUnblocked(m.data.user);
}
break;
}
}
tapMsgQueue.processNext();
};
function handleEmit(emit) {
switch (emit.eventName) {
case "chat/sendMessage":
handleNewMessage(emit.data);
break;
case "chat/updateMessage":
handleUpdateMessage(emit.data);
break;
case SOCKET_CLEAR_CHAT_ROOM:
module.exports.tapCoreChatRoomManager.deleteMessageByRoomID(emit.data.room.roomID);
break;
}
}
var handleNewMessage = (message) => {
let _this = this;
let user = this.taptalk.getTaptalkActiveUser();
let removeRoom = (roomID) => {
delete tapTalkRoomListHashmap[roomID];
if (tapTalkRoomListHashmapPinned[roomID]) {
delete tapTalkRoomListHashmapPinned[roomID];
}
if (tapTalkRoomListHashmapUnPinned[roomID]) {
delete tapTalkRoomListHashmapUnPinned[roomID];
}
if (taptalkStarMessageHashmap[roomID]) {
delete taptalkStarMessageHashmap[roomID];
}
delete tapTalkRooms[roomID];
}
let mergeTaptalkRooms = (obj, src) => {
for (var key in src) {
if (src.hasOwnProperty(key)) obj[key] = src[key];
}
return obj;
}
if (user.userID !== message.user.userID) {
this.tapCoreMessageManager.markMessageAsDelivered([message.messageID]);
}
message.body = decryptKey(message.body, message.localID);
if (message.data !== "") {
message.data = JSON.parse(decryptKey(message.data, message.localID));
}
if (message.quote.content !== "") {
message.quote.content = decryptKey(message.quote.content, message.localID);
}
let isRoomExist = tapTalkRooms[message.room.roomID];
if (isRoomExist) {
if (!isRoomExist.messages[message.localID]) {
tapTalkRooms[message.room.roomID].messages = Object.assign({ [message.localID]: message }, tapTalkRooms[message.room.roomID].messages);
var currentIndex = tapTalkRooms[message.room.roomID];
delete tapTalkRooms[message.room.roomID];
tapTalkRooms = Object.assign({ [message.room.roomID]: currentIndex }, tapTalkRooms);
} else {
isRoomExist.messages[message.localID] = message;
}
} else {
var roomID = message.room.roomID;
var newRoom = {
[roomID]: {
messages: {},
hasMore: true,
lastUpdated: 0
}
}
newRoom[roomID].messages[message.localID] = message;
tapTalkRooms = mergeTaptalkRooms(newRoom, tapTalkRooms);
}
module.exports.tapCoreRoomListManager.setRoomListLastMessage(message, 'new emit');
//if delete room
if (message.action === 'room/delete' && message.type === CHAT_MESSAGE_TYPE_SYSTEM_MESSAGE) {
removeRoom(message.room.roomID);
}
//if leave group
if ((message.action === 'room/leave' && message.type === 9001) && module.exports.taptalk.getTaptalkActiveUser().userID === message.user.userID) {
removeRoom(message.room.roomID);
}
//handle pin - unpin
if (window.Worker) {
//new pinned
if (message.action === "message/pin" && taptalkPinnedMessageIDHashmap[message.room.roomID] && !taptalkPinnedMessageIDHashmap[message.room.roomID][message.data.messageID]) {
let _messagePin = { ...message };
let newMes = _messagePin.data;
newMes.body = decryptKey(newMes.body, newMes.localID);
newMes.created = newMes.createdTime;
if (newMes.data !== "") {
newMes.data = decryptKey(newMes.data, newMes.localID);
}
var newPinMessagePinned = new WebWorker(() => self.addEventListener('message', function (e) {
let { _pinnedMessage, _pinnedMessageID, _message, _roomID, isClose } = e.data;
if (!isClose) {
if (_pinnedMessageID[_roomID]) {
_pinnedMessageID[_roomID][_message.messageID] = true;
}
if (_pinnedMessage[_roomID]) {
_pinnedMessage[_roomID].messages.push(_message);
} else {
_pinnedMessage[_roomID] = {
hasMore: false,
messages: [_message],
pageNumber: 1,
totalItems: 1,
totalPages: 1
};
}
self.postMessage({
result: {
_taptalkPinnedMessageHashmap: _pinnedMessage,
_taptalkPinnedMessageIDHashmap: _pinnedMessageID
}
})
} else {
self.close();
}
}));
newPinMessagePinned.postMessage({
_pinnedMessage: taptalkPinnedMessageHashmap,
_pinnedMessageID: taptalkPinnedMessageIDHashmap,
_message: newMes,
_roomID: message.room.roomID
});
newPinMessagePinned.addEventListener('message', (e) => {
let { result } = e.data;
taptalkPinnedMessageIDHashmap = result._taptalkPinnedMessageIDHashmap;
taptalkPinnedMessageHashmap = result._taptalkPinnedMessageHashmap;
module.exports.taptalkHelper.orderArrayFromLargestToSmallest(taptalkPinnedMessageHashmap[message.room.roomID].messages, "created", "desc", (new_arr) => {
taptalkPinnedMessageHashmap[message.room.roomID].messages = new_arr;
});
newPinMessagePinned.postMessage({ isClose: true });
});
}
//new pinend
//new unpinned
if (message.action === "message/unpin" && taptalkPinnedMessageIDHashmap[message.room.roomID] && taptalkPinnedMessageIDHashmap[message.room.roomID][message.data.messageID]) {
var newUnpinMessagePinned = new WebWorker(() => self.addEventListener('message', function (e) {
let { _pinnedMessage, _pinnedMessageID, _message, _roomID, isClose } = e.data;
if (!isClose) {
let actionRemove = () => {
let indexMes = _pinnedMessage[_message.room.roomID].messages.findIndex(val => val.messageID === _message.data.messageID);
delete _pinnedMessageID[_message.room.roomID][_message.data.messageID];
if (indexMes !== -1) {
_pinnedMessage[_message.room.roomID].messages.splice(indexMes, 1);
}
}
if (_pinnedMessageID[_message.room.roomID]) {
actionRemove();
}
self.postMessage({
result: {
_taptalkPinnedMessageHashmap: _pinnedMessage,
_taptalkPinnedMessageIDHashmap: _pinnedMessageID
}
})
} else {
self.close();
}
}));
newUnpinMessagePinned.postMessage({
_pinnedMessage: taptalkPinnedMessageHashmap,
_pinnedMessageID: taptalkPinnedMessageIDHashmap,
_message: message
});
newUnpinMessagePinned.addEventListener('message', (e) => {
let { result } = e.data;
taptalkPinnedMessageIDHashmap = result._taptalkPinnedMessageIDHashmap;
taptalkPinnedMessageHashmap = result._taptalkPinnedMessageHashmap;
newUnpinMessagePinned.postMessage({ isClose: true });
});
}
//new unpinned
} else {
console.log("Worker is not supported");
}
//handle pin - unpin
if (message.room && tapTalkRoomListHashmap[message.room.roomID] && (message.action === "user/update" || message.action === "room/update")) {
// Handle room update
tapTalkRoomListHashmap[message.room.roomID].lastMessage.room = message.room;
}
}
var handleUpdateMessage = (message) => {
let isRoomExist = tapTalkRooms[message.room.roomID];
message.body = decryptKey(message.body, message.localID);
if (message.data !== "") {
message.data = JSON.parse(decryptKey(message.data, message.localID));
}
if (message.quote.content !== "") {
message.quote.content = decryptKey(message.quote.content, message.localID);
}
if (isRoomExist) {
tapTalkRooms[message.room.roomID].messages[message.localID] = message;
if (message.isRead) {
// for(var i in tapTalkRooms[message.room.roomID].messages) {
// tapTalkRooms[message.room.roomID].messages[i].isRead = true;
// }
tapTalkRooms[message.room.roomID].messages[message.localID].isRead = true
}
module.exports.tapCoreRoomListManager.setRoomListLastMessage(message, 'update emit', true);
}
if (window.Worker) {
//delete message pinned listener
if (message.isDeleted) {
var deleteMessagePinned = new WebWorker(() => self.addEventListener('message', function (e) {
let { _pinnedMessage, _pinnedMessageID, _message, isClose } = e.data;
if (!isClose) {
if (_pinnedMessageID[_message.room.roomID]) {
delete _pinnedMessageID[_message.room.roomID][_message.messageID];
}
if (_pinnedMessage[_message.room.roomID]) {
let _idx = _pinnedMessage[_message.room.roomID].messages.findIndex(v => v.messageID === _message.messageID);
if (_idx !== -1) {
_pinnedMessage[_message.room.roomID].messages.splice(_idx, 1);
}
}
self.postMessage({
result: {
_taptalkPinnedMessageHashmap: _pinnedMessage,
_taptalkPinnedMessageIDHashmap: _pinnedMessageID
}
})
} else {
self.close();
}
}));
deleteMessagePinned.postMessage({
_pinnedMessage: taptalkPinnedMessageHashmap,
_pinnedMessageID: taptalkPinnedMessageIDHashmap,
_message: message
});
deleteMessagePinned.addEventListener('message', (e) => {
let { result } = e.data;
taptalkPinnedMessageHashmap = result._taptalkPinnedMessageHashmap;
taptalkPinnedMessageIDHashmap = result._taptalkPinnedMessageIDHashmap;
deleteMessagePinned.postMessage({ isClose: true });
});
}
//delete message pinned listener
//edit message pinned listener
if (Object.keys(taptalkPinnedMessageHashmap).length > 0 && Object.keys(taptalkPinnedMessageIDHashmap).length > 0) {
var editMessagePinned = new WebWorker(() => self.addEventListener('message', function (e) {
let { _pinnedMessage, _pinnedMessageID, _message, isClose } = e.data;
if (!isClose) {
if (_pinnedMessage[_message.room.roomID]) {
let _idx = _pinnedMessage[_message.room.roomID].messages.findIndex(v => v.messageID === _message.messageID);
if (_idx !== -1) {
_pinnedMessage[_message.room.roomID].messages[_idx] = _message;
}
}
self.postMessage({
result: {
_taptalkPinnedMessageHashmap: _pinnedMessage,
_taptalkPinnedMessageIDHashmap: _pinnedMessageID
}
})
} else {
self.close();
}
}));
editMessagePinned.postMessage({
_pinnedMessage: taptalkPinnedMessageHashmap,
_pinnedMessageID: taptalkPinnedMessageIDHashmap,
_message: message
});
editMessagePinned.addEventListener('message', (e) => {
let { result } = e.data;
taptalkPinnedMessageHashmap = result._taptalkPinnedMessageHashmap;
taptalkPinnedMessageIDHashmap = result._taptalkPinnedMessageIDHashmap;
editMessagePinned.postMessage({ isClose: true });
});
}
//edit message pinned listener
} else {
console.log("Worker is not supported");
}
}
class TapMessageQueue {
constructor() {
this.queue = [];
this.isRunning = false;
this.callback = null;
}
setCallback(callback) {
if (typeof (callback) !== "function") {
throw new Error("callback must be function");
}
this.callback = callback;
}
addToQueue(item) {
this.queue.push(item);
if (!this.isRunning) {
this.isRunning = true;
this.processNext();
}
}
processNext(stopIfEmpty) {
if (this.queue.length != 0) {
this.callback(this.queue.shift());
} else if (!stopIfEmpty) {
setTimeout(() => {
if (this.queue.length > 0) {
this.processNext();
} else {
this.isRunning = false;
}
}, 100);
}
// else {
// this.isRunning = false;
// }
}
}
var tapMsgQueue = new TapMessageQueue();
tapMsgQueue.setCallback((emit) => {
tapReader.readAsText(emit);
});
class TapEmitMessageQueue {
constructor() {
this.emitQueue = [];
this.isRunningMessageQueue = false;
}
runEmitQueue() {
if (!navigator.onLine || !module.exports.taptalk.isConnected()) {
this.isRunningMessageQueue = false;
} else {
this.isRunningMessageQueue = true;
}
if (this.emitQueue.length > 0 && this.isRunningMessageQueue) {
webSocket.send(this.emitQueue[0]);
this.emitQueue.shift();
this.runEmitQueue();
} else {
this.isRunningMessageQueue = false;
return;
}
}
pushEmitQueue(emit) {
this.emitQueue.push(emit);
if (!this.isRunningMessageQueue) {
this.runEmitQueue();
}
}
}
var tapEmitMsgQueue = new TapEmitMessageQueue();
class TapScheduledMessageQueue {
constructor() {
this.scheduledItemQueue = [];
this.isRunningScheduledMessageQueue = false;
}
runScheduledMessageQueue() {
if (!navigator.onLine || !module.exports.taptalk.isConnected()) {
this.isRunningScheduledMessageQueue = false;
}
else {
this.isRunningScheduledMessageQueue = true;
}
if (this.scheduledItemQueue.length > 0 && this.isRunningScheduledMessageQueue) {
const message = this.scheduledItemQueue[0].message;
const scheduledTime = this.scheduledItemQueue[0].scheduledTime;
const callback = this.scheduledItemQueue[0].callback;
let handleScheduledMessageFinished = () => {
this.scheduledItemQueue.shift();
this.runScheduledMessageQueue();
}
if (scheduledTime > new Date().valueOf()) {
module.exports.tapCoreMessageManager.createScheduledMessage(message, scheduledTime, {
onSuccess: (response) => {
callback.onSuccess(response);
handleScheduledMessageFinished();
},
onError: (errCode, errMes) => {
callback.onError(errCode, errMes);
handleScheduledMessageFinished();
}
}, false);
}
else {
module.exports.tapCoreMessageManager.sendCustomMessage(message, () => {
callback.onSuccess({
success: false,
message: "Message was sent",
createdItem: message
});
handleScheduledMessageFinished();
});
}
}
else {
this.isRunningScheduledMessageQueue = false;
return;
}
}
pushScheduledMessageQueue(message, scheduledTime, callback) {
const scheduledItem = {
message: message,
scheduledTime: scheduledTime,
callback: callback
};
this.scheduledItemQueue.push(scheduledItem);
if (!this.isRunningScheduledMessageQueue) {
this.runScheduledMessageQueue();
}
}
}
var scheduledMessageQueue = new TapScheduledMessageQueue();
//image compress
var urlToFile = (url, filename, mimeType) => {
return (
fetch(url)
.then(function (res) { return res.arrayBuffer(); })
.then(function (buf) { return new File([buf], filename, { type: mimeType }); })
);
};
let compressImageFile = (file, widthVal, heightVal) => {
return new Promise(function (resolve, reject) {
let fileName = file.name;
let reader = new FileReader();
let readerCanvasImage = new FileReader();
reader.readAsDataURL(file);
reader.onload = event => {
let img = new Image();
img.src = event.target.result;
img.onload = () => {
let elem = document.createElement('canvas');
elem.width = widthVal;
elem.height = heightVal;
let ctx = elem.getContext('2d');
ctx.drawImage(img, 0, 0, widthVal, heightVal);
ctx.canvas.toBlob((blob) => {
let finalFile;
if (!blob) {
console.warn("Compression failed, using original file");
// 🔥 fallback to original file
finalFile = file;
} else {
finalFile = new File([blob], fileName, {
type: file.type,
lastModified: Date.now()
});
}
readerCanvasImage.readAsDataURL(finalFile);
}, file.type, 0.6);
},
reader.onerror = error => console.log(error);
};
readerCanvasImage.onload = event => {
urlToFile(event.target.result, file.name, file.type)
.then((file) => {
resolve({
file: file,
src: event.target.result
})
});
}
})
}
exports.taptalkHelper = {
orderArrayFromLargestToSmallest: (array, key, dir, callback) => {
if (window.Worker) {
var orderArrayFromLargestToSmallestWorker = new WebWorker(() => self.addEventListener('message', function (e) {
let { _array, _key, _dir, isClose } = e.data;
if (!isClose) {
let sortArray = (a, k) => {
var temp = 0;
for (var i = 0; i < a.length; i++) {
for (var j = i; j < a.length; j++) {
if (_dir === "desc") {
if (a[j][k] > a[i][k]) {
temp = a[j];
a[j] = a[i];
a[i] = temp;
}
} else {
if (a[j][k] < a[i][k]) {
temp = a[j];
a[j] = a[i];
a[i] = temp;
}
}
}
}
return a;
}
let resultNewArray = sortArray(_array, _key);
self.postMessage({
result: {
newArray: resultNewArray,
error: ""
}
})
} else {
self.close();
}
}));
orderArrayFromLargestToSmallestWorker.postMessage({
_array: array,
_key: key,
_dir: dir
});
orderArrayFromLargestToSmallestWorker.addEventListener('message', (e) => {
let { result } = e.data;
callback(result.newArray);
if (result.error !== "") {
console.log("Room not found")
}
orderArrayFromLargestToSmallestWorker.postMessage({ isClose: true });
});
} else {
console.log("Worker is not supported");
}
},
helperDecryptKey(str, key) {
return decryptKey(str, key);
},
helperEncryptKey(str, key) {
return encryptKey(str, key);
},
getUrlsFromString(string) {
// const expression = /[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?/gi;
// const expression = /(\b((https?|ftp|file):\/\/)?[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
const expression = /(\b((https?|ftp|file):\/\/)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)?)/gi;
const regex = new RegExp(expression);
const urls = string.match(regex);
if (urls !== null && urls !== undefined) {
return urls;
}
return [];
}
}
exports.taptalk = {
forTesting: () => {
let data = {
// _tapTalkEmitMessageQueue: tapTalkEmitMessageQueue,
_taptalkRooms: tapTalkRooms,
_tapTalkRoomListHashmap: tapTalkRoomListHashmap,
_tapTalkRoomListHashmapPinned: tapTalkRoomListHashmapPinned,
_tapTalkRoomListHashmapUnPinned: tapTalkRoomListHashmapUnPinned,
_emitQueuue: tapEmitMsgQueue.emitQueue
}
return data;
},
init: (appID, appSecret, baseUrlApi, withOutRoomList = false) => {
authenticationHeader["App-Key"] = btoa(`${appID}:${appSecret}`);
// authenticationHeader["Server-Key"] = btoa(`${serverID}:${serverSecret}`);
authenticationHeader["Device-Identifier"] = getDeviceID();
baseApiUrl = baseUrlApi;
if (withOutRoomList) {
isDoneFirstSetupRoomList = true;
isWithoutRoomList = true;
}
this.taptalk.refreshProjectConfigs();
},
getDeviceID: () => {
let localDeviceID = localStorage.getItem('tapTalk.DeviceID');
let md5DeviceID = md5(navigator.userAgent + "@" + new Date().valueOf());
let generateDeviceID = md5DeviceID.substring(0, 16) + "-" + guid();
if (localDeviceID !== null) {
return localDeviceID;
}
localStorage.setItem('tapTalk.DeviceID', generateDeviceID);
return generateDeviceID;
},
addTapListener: (callback) => {
tapListener.push(callback);
},
checkErrorResponse: (response, callbackOnMethod = null, callbackAfterRefresh = null) => {
if (response.status !== 200) {
if (response.status === 401) {
if (response.error.code === "40104") {
this.taptalk.refreshAccessToken(callbackAfterRefresh);
} else {
refreshAccessTokenCallbackArray = [];
for (let i in tapListener) {
Object.keys(tapListener[i]).map((_callback) => {
if (_callback === 'onTapTalkRefreshTokenExpired') {
tapListener[i][_callback]();
}
})
}
}
} else {
if (callbackOnMethod !== null) {
callbackOnMethod.onError(response.error.code, response.error.message)
}
}
}
},
authenticateWithAuthTicket: (authTicket, connectOnSuccess, callback) => {
let url = `${baseApiUrl}/v1/auth/access_token/request`;
let _this = this;
setTimeout(() => {
authenticationHeader["Authorization"] = `Bearer ${authTicket}`;
doXMLHTTPRequest('POST', authenticationHeader, url, "")
.then(function (response) {
if (response.error.code === "") {
setUserDataStorage(response.data);
callback.onSuccess('Request access token success');
connectOnSuccess && _this.taptalk.testAccessToken(callback);
} else {
callback.onError(response.error.code, response.error.message);
}
})
.catch(function (err) {
console.error('there was an error!', err);
});
}, 300);
},
testAccessToken: (callback) => {
authenticationHeader["Authorization"] = `Bearer ${getLocalStorageObject('TapTalk.UserData').accessToken}`;
let url = `${baseApiUrl}/connect?check=1`;
let _this = this;
doXMLHTTPRequest('GET', authenticationHeader, url, "")
.then(function (response) {
if (response.error.code === "") {
// _this.connect(callback);
callback.onSuccess();
} else {
_this.taptalk.checkErrorResponse(response, callback, () => {
_this.taptalk.testAccessToken(callback)
});
}
})
.catch(function (err) {
console.error('Augh, there was an error!', err);
setTimeout(() => {
_this.taptalk.testAccessToken(callback);
}, 1000)
});
},
connect: (callback) => {
if (!isConnectRunning) {
isConnectRunning = true;
this.taptalk.testAccessToken({
onSuccess: () => {
if (window["WebSocket"]) {
authenticationHeader["Authorization"] = `Bearer ${getLocalStorageObject('TapTalk.UserData').accessToken}`;
var protocol = "";
protocol = baseApiUrl.includes("https") ? "https" : "http";
var url = ``;
if (protocol === "https") {
url = `wss://${baseApiUrl.replace('https://', '')}/connect?${generateHeaderQuerystring()}`
} else {
url = `ws://${baseApiUrl.replace('http://', '')}/connect?${generateHeaderQuerystring()}`;
}
webSocket = new WebSocket(url);
webSocket.onopen = function () {
callback.onSuccess('Successfully connected to TapTalk.io server');
tapEmitMsgQueue.runEmitQueue();
if (tapUplQueue.queue.length > 0) {
tapUplQueue.processNext();
}
scheduledMessageQueue.runScheduledMessageQueue();
isFirstConnectedToWebSocket = true;
}
webSocket.onclose = function () {
callback.onClose('Disconnected from TapTalk.io server');
};
webSocket.onerror = function () {
callback.onError('Error while connecting to web socket');
}
webSocket.onmessage = function (evt) {
if (isFirstConnectedToWebSocket) {
tapMsgQueue.addToQueue(evt.data);
}
};
isConnectRunning = false;
} else {
isConnectRunning = false;
alert("Your browser does not support WebSockets.");
callback(null, 'cannot connect to websocket');
}
},
onError: (errorCode, errorMessage) => {
isConnectRunning = false;
callback.onError((errorCode, errorMessage));
}
})
}
},
disconnect: () => {
return webSocket ? webSocket.close() : false;
},
isConnected: () => {
return webSocket ? webSocket.readyState === 1 : false;
},
refreshAccessToken: (callback) => {
let runCallbackRefreshToken = () => {
if (refreshAccessTokenCallbackArray.length > 0) {
refreshAccessTokenCallbackArray[0]();
refreshAccessTokenCallbackArray.shift();
runCallbackRefreshToken();
} else {
return;
}
};
refreshAccessTokenCallbackArray.push(callback);
if (this.taptalk.isAuthenticated()) {
if (refreshAccessTokenCallbackArray.length < 2) {
let url = `${baseApiUrl}/v1/auth/access_token/refresh`;
setTimeout(() => {
authenticationHeader["Authorization"] = `Bearer ${getLocalStorageObject('TapTalk.UserData').refreshToken}`;
doXMLHTTPRequest('POST', authenticationHeader, url, "")
.then(function (response) {
if (response.error.code === "") {
setUserDataStorage(response.data);
runCallbackRefreshToken();
} else {
refreshAccessTokenCallbackArray = [];
for (let i in tapListener) {
Object.keys(tapListener[i]).map((callback) => {
if (callback === 'onTapTalkRefreshTokenExpired') {
tapListener[i][callback]();
}
})
}
}
})
.catch(function (err) {
console.error('there was an error!', err);
});
}, 300);
}
} else {
return;
}
},
isAuthenticated: () => {
return (
getLocalStorageObject("TapTalk.UserData") ?
getLocalStorageObject("TapTalk.UserData").accessToken ? true : false
:
false
)
},
clearTaptalkChatData: () => {
localStorage.removeItem('TapTalk.UserData');
tapTalkRooms = {}; //room list with array of messages
tapTalkRoomListHashmap = {}; //room list last message
tapTalkRoomListHashmapPinned = {}; //room list last message - pinned
tapTalkRoomListHashmapUnPinned = {}; //room list last message - unpinned
tapRoomStatusListeners = [];
tapMessageListeners = [];
tapRoomListListeners = [];
tapContactListeners = [];
tapListener = [];
taptalkContact = {};
projectConfigs = null;
expiredKey = [];
refreshAccessTokenCallbackArray = [];
isConnectRunning = false;
isDoneFirstSetupRoomList = false;
isNeedToCallApiUpdateRoomList = true;
isFirstConnectedToWebSocket = false;
},
logoutAndClearAllTapTalkData: (callback) => {
let url = `${baseApiUrl}/v1/client/logout`;
let _this = this;
if (this.taptalk.isAuthenticated()) {
authenticationHeader["Authorization"] = `Bearer ${getLocalStorageObject('TapTalk.UserData').accessToken}`;
doXMLHTTPRequest('POST', authenticationHeader, url, "")
.then(function (response) {
// if(response.error.code === "") {
// callback.onSuccess("Logged out successfully");
// }else {