/* eslint-disable no-console */
(function (context) {
var $ = context.jQuery;
// eslint-disable-next-line no-unused-vars
var __ = context.wp.i18n.__;
var Buffer = context.buffer.Buffer;
var CtnFileHeader = context.CtnFileHeader;
var MessageChunker = context.MessageChunker;
var dataChunkSize = 10485760; // (10 MB) Size of binary data before any encoding. The actual number of bytes
// received depends on the encoding used. For base64, it will be 13,981,014
var initPollMsgProgressTime = 5000; // 5 sec.
var defPollMsgProgressTime = 30000; // 30 sec.
var backPollMsgProgressTime = 90000; // 1.5 min.
function CtnBlkDisplayMessage(uiContainer, props) {
this.uiContainer = uiContainer;
if (this.checkCtnApiProxyAvailable(this.uiContainer)) {
this.showSpinner = props.showSpinner;
this.spinnerColor = props.spinnerColor;
this.stripFileHeader = props.stripFileHeader;
this.limitMsg = props.limitMsg;
this.maxMsgLength = props.maxMsgLength;
this.messageId = undefined;
this.msgContainer = undefined;
this.divError = undefined;
this.txtError = undefined;
this.spinner = undefined;
this.readMessageChunk = readMessageChunk.bind(this);
this.cachedMessageId = undefined;
this.pollMsgProgressTimeout = undefined;
this.pollMsgProgressInterval = undefined;
this.pollMsgProgressTime = defPollMsgProgressTime;
this.pollMsgProgressHandler = processMessageProgress.bind(this);
this.notifyChannelOpen = false;
this.lastOpenNtfyChnlRetryTime = undefined;
this.minOpenNtfyChnlRetryInterval = 1000; // 1 sec. (in milliseconds)
this.setMessageElements();
this.setErrorPanel();
this.setUpNotification();
}
}
CtnBlkDisplayMessage.prototype.setUpNotification = function () {
var _self = this;
context.ctnApiProxy.on('comm-error', function (error) {
// Error communicating with Catenis notification process
console.error('Catenis notification process error:', error);
if (_self.notifyChannelOpen) {
var shouldRetryOpen = !_self.lastOpenNtfyChnlRetryTime || (Date.now() - _self.lastOpenNtfyChnlRetryTime >= _self.minOpenNtfyChnlRetryInterval);
if (shouldRetryOpen) {
// Make sure that notification channel is open
_self.lastOpenNtfyChnlRetryTime = Date.now();
context.setImmediate(openNotifyChannel);
}
}
});
// Prepare to open notification channel to monitor final message progress event.
var wsNotifyChannel = context.ctnApiProxy.createWsNotifyChannel('final-msg-progress');
wsNotifyChannel.on('open', function (error) {
if (error) {
// Error establishing underlying WebSocket connection
console.error('[' + wsNotifyChannel.eventName + '] - Error establishing underlying WebSocket connection:', error);
}
else {
// Catenis notification channel successfully open
console.log('[' + wsNotifyChannel.eventName + '] - Catenis notification channel successfully open');
_self.notifyChannelOpen = true;
}
});
wsNotifyChannel.on('error', function (error) {
// Error in the underlying WebSocket connection
console.error('[' + wsNotifyChannel.eventName + '] - Error in the underlying WebSocket connection:', error);
});
wsNotifyChannel.on('close', function (code, reason) {
// Underlying WebSocket connection has been closed
console.error('[' + wsNotifyChannel.eventName + '] - Underlying WebSocket connection has been closed; code: ' + code + ', reason: ' + reason);
_self.notifyChannelOpen = false;
var shouldRetry = !_self.lastOpenNtfyChnlRetryTime || (Date.now() - _self.lastOpenNtfyChnlRetryTime >= _self.minOpenNtfyChnlRetryInterval);
if (shouldRetry) {
// Reopen notification channel
_self.lastOpenNtfyChnlRetryTime = Date.now();
context.setImmediate(openNotifyChannel);
}
});
wsNotifyChannel.on('notify', function (eventData) {
_self.processFinalMessageProgress(eventData);
});
function openNotifyChannel() {
wsNotifyChannel.open(function (error) {
if (error) {
// Error sending command to open notification channel
console.error('Error opening Catenis notification channel:', error);
}
});
}
openNotifyChannel();
};
CtnBlkDisplayMessage.prototype.checkCtnApiProxyAvailable = function (uiContainer) {
var result = true;
if (typeof context.ctnApiProxy !== 'object') {
var elems = $('div.noctnapiproxy', uiContainer.parentElement);
if (elems.length > 0) {
var noCtnApiProxy = elems[0];
noCtnApiProxy.style.display = 'block';
}
uiContainer.style.display = 'none';
result = false;
}
return result;
};
CtnBlkDisplayMessage.prototype.setMessageElements = function () {
var elems = $('input[name="messageId"]', this.uiContainer);
if (elems.length > 0) {
this.messageId = elems[0];
}
elems = $('pre', this.uiContainer);
if (elems.length > 0) {
this.msgContainer = elems[0];
}
};
CtnBlkDisplayMessage.prototype.setErrorPanel = function () {
var elems = $('div.error', this.uiContainer);
if (elems.length > 0) {
this.divError = elems[0];
elems = $('p.error', this.divError);
if (elems.length > 0) {
this.txtError = elems[0];
}
}
};
CtnBlkDisplayMessage.prototype.checkRetrieveMessage = function () {
var messageId;
if (this.messageId && (messageId = this.messageId.value.trim())) {
this.clearResults();
if (this.showSpinner) {
this.displaySpinner();
}
// Start reading message
this.readMessageChunk(messageId);
}
};
CtnBlkDisplayMessage.prototype.checkNotifyMsgRead = function (messageId) {
if (this.messageId.ctnMsgReadNotify) {
// Dispatch event notifying that Catenis message had been read
$(this.messageId.ctnMsgReadNotify).trigger('ctn-msg-read', messageId);
delete this.messageId.ctnMsgReadNotify;
}
};
CtnBlkDisplayMessage.prototype.displayMessage = function (message) {
if (this.showSpinner) {
this.hideSpinner();
}
if (this.msgContainer) {
if (this.stripFileHeader) {
message = checkStripFileHeader(message);
}
var $msgContainer = $(this.msgContainer);
var onClickHandler = function (event) {
event.stopPropagation();
event.preventDefault();
// Delete message continuation info
$(event.target).parent().remove();
// Display the whole message
$msgContainer.text(message);
};
var truncatedMsg;
if (this.limitMsg && (truncatedMsg = truncateMessage(message, this.maxMsgLength))) {
// Prepare msg continuation
var $span = $(context.document.createElement('span'))
.html('... (show remaining ' + (message.length - truncatedMsg.length).toLocaleString() + ' characters)');
$('a', $span[0]).click(onClickHandler);
$msgContainer
.after($span)
.text(truncatedMsg);
}
else {
// Display the whole message
$msgContainer.text(message);
}
}
};
CtnBlkDisplayMessage.prototype.displaySpinner = function () {
if (!this.spinner) {
this.spinner = new context.Spin.Spinner({
className: 'msg-spinner',
color: this.spinnerColor
});
}
$(this.uiContainer).addClass('ctn-spinner');
this.spinner.spin(this.uiContainer);
};
CtnBlkDisplayMessage.prototype.hideSpinner = function () {
if (this.spinner) {
this.spinner.stop();
$(this.uiContainer).removeClass('ctn-spinner');
}
};
CtnBlkDisplayMessage.prototype.hideMessage = function () {
if (this.msgContainer) {
var $msgContainer = $(this.msgContainer);
$('span', $msgContainer.parent()[0]).remove();
$msgContainer.text('');
}
};
CtnBlkDisplayMessage.prototype.displayError = function (text) {
if (this.showSpinner) {
this.hideSpinner();
}
if (this.txtError) {
$(this.txtError).html(convertLineBreak(text));
this.divError.style.display = 'block';
}
};
CtnBlkDisplayMessage.prototype.hideError = function () {
if (this.txtError) {
$(this.txtError).html('');
this.divError.style.display = 'none';
}
};
CtnBlkDisplayMessage.prototype.clearResults = function () {
this.hideSpinner();
this.hideMessage();
this.hideError();
};
CtnBlkDisplayMessage.prototype.processFinalMessageProgress = function (eventData) {
if (this.cachedMessageId && eventData.ephemeralMessageId === this.cachedMessageId) {
// Message processing has been finalized. Clear pending message and stop polling
this.cachedMessageId = undefined;
this.stopPollingMessageProgress();
if (eventData.progress.success) {
// Message ready to be read. Start reading it
this.readMessageChunk(eventData.result.messageId, eventData.result.continuationToken);
}
else {
// Report error reading message
this.displayError('Error reading message: [' + eventData.progress.error.code + '] - ' + eventData.progress.error.message);
}
}
};
CtnBlkDisplayMessage.prototype.startPollingMessageProgress = function () {
if (!this.pollMsgProgressTimeout) {
// Set up initial timeout
this.pollMsgProgressTimeout = context.setTimeout(this.pollMsgProgressHandler, initPollMsgProgressTime);
}
if (!this.pollMsgProgressInterval) {
this.pollMsgProgressInterval = context.setInterval(this.pollMsgProgressHandler, this.pollMsgProgressTime);
}
}
CtnBlkDisplayMessage.prototype.stopPollingMessageProgress = function () {
if (this.pollMsgProgressInterval) {
context.clearInterval(this.pollMsgProgressInterval);
this.pollMsgProgressInterval = undefined;
}
if (this.pollMsgProgressTimeout) {
context.clearTimeout(this.pollMsgProgressTimeout);
this.pollMsgProgressTimeout = undefined;
}
}
function readMessageChunk(messageId, continuationToken, msgChunker) {
var options = continuationToken ? {
encoding: 'base64',
continuationToken: continuationToken
} : {
dataChunkSize: dataChunkSize,
async: true
};
var _self = this;
context.ctnApiProxy.readMessage(messageId, options, function (error, result) {
if (error) {
_self.displayError(error.toString());
}
else {
if (result.cachedMessageId) {
// Message processed asynchrnously. Save message reference
_self.cachedMessageId = result.cachedMessageId;
// Start polling for message progress
_self.pollMsgProgressTime = _self.notifyChannelOpen ? backPollMsgProgressTime : defPollMsgProgressTime;
_self.startPollingMessageProgress();
}
else {
// Accummulate message chunk
if (!msgChunker) {
msgChunker = new MessageChunker(options.encoding);
}
msgChunker.newMessageChunk(result.msgData);
if (result.continuationToken) {
// The whole message has not been received yet. Go get next chunk
context.setImmediate(_self.readMessageChunk, messageId, result.continuationToken, msgChunker);
}
else {
// Display complete message
_self.checkNotifyMsgRead(messageId);
_self.displayMessage(msgChunker.getMessage('utf8'));
}
}
}
})
}
function processMessageProgress() {
if (this.cachedMessageId) {
var _self = this;
context.ctnApiProxy.retrieveMessageProgress(this.cachedMessageId, function (error, result) {
if (error) {
// Error retrieving message progress. Clear pending message and stop polling
console.error('Error retrieving message progress:', error.toString());
_self.cachedMessageId = undefined;
_self.stopPollingMessageProgress();
}
else {
if (result.progress.done) {
// Message processing has been finalized. Clear pending message and stop polling
_self.cachedMessageId = undefined;
_self.stopPollingMessageProgress();
if (result.progress.success) {
// Message ready to be read. Start reading it
_self.readMessageChunk(result.result.messageId, result.result.continuationToken);
}
else {
// Report error reading message
_self.displayError('Error reading message: [' + result.progress.error.code + '] - ' + result.progress.error.message);
}
}
}
});
}
else {
// No cached message awaiting process. Stop polling
this.stopPollingMessageProgress();
}
}
function convertLineBreak(text) {
return text.replace(/\n/g, '
');
}
function checkStripFileHeader(message) {
var fileContents = Buffer.from(message);
var fileInfo = CtnFileHeader.decode(fileContents);
if (fileInfo) {
message = fileInfo.fileContents.toString();
}
return message;
}
function truncateMessage(message, maxLength) {
if (message.length > maxLength) {
var truncateLength = maxLength;
var lastCharCode = message.charCodeAt(maxLength - 1);
if (lastCharCode >= 0xd800 && lastCharCode <= 0xdbff) {
// Last character is first code unit of a UTF-16 surrogate pair.
// So add one more character, to include the whole pair
truncateLength++;
if (message.length === truncateLength) {
// No need to truncate message. Just return
return;
}
}
return message.substring(0, truncateLength);
}
}
context.CtnBlkDisplayMessage = CtnBlkDisplayMessage;
})(this);