(function (root, factory) {
if (typeof define === 'function' && define.amd) {
define(["converse"], factory);
} else {
factory(converse);
}
}(this, function (converse) {
let baseMeetUrl;
converse.plugins.add("olmeet", { initialize });
const MEET_START_OPTIONS = {
INTO_CHAT_WINDOW: "into_chat_window",
INTO_NEW_TAB: "into_new_tab",
BASE_URL: "https://meet.jit.si"
};
function handleMessageNotification(_converse, data) {
console.debug("messageNotification", data);
const chatbox = data.chatbox;
const bodyElement = data.stanza.querySelector("body");
const { __ } = _converse;
if (bodyElement) {
const body = bodyElement.innerHTML;
const url = baseMeetUrl;
const pos = body.indexOf(url + "/");
if (pos > -1) {
const room = body.substring(pos + url.length + 1);
const label = pos > 0 ? body.substring(0, pos) : __("New meeting");
const from = chatbox.getDisplayName().trim();
const avatar = _converse.api.settings.get("notification_icon");
if (chatbox.vcard.attributes.image) {
avatar = chatbox.vcard.attributes.image;
}
const prompt = new Notification(from, {
body: label + " " + room,
lang: _converse.locale,
icon: avatar,
requireInteraction: true,
});
prompt.onclick = function (event) {
event.preventDefault();
const box_jid = Strophe.getBareJidFromJid(
chatbox.get("contact_jid") ||
chatbox.get("jid") ||
chatbox.get("from")
);
const view = _converse.chatboxviews.get(box_jid);
if (view) {
doLocalVideo(view, room, `${url}/${room}`, label);
}
};
}
}
}
function parseStanza(_converse, stanza, attrs) {
const type = attrs.type;
const from = (type == "chat") ? attrs.from : attrs.from_muc;
const view = _converse.chatboxviews.get(from);
if (view) {
const model = view.model;
const accept = stanza.querySelector('accept');
const invite = stanza.querySelector('invite');
const retract = stanza.querySelector('retract');
if (invite) {
const uri = invite.querySelector('external').getAttribute("uri");
console.debug("online meeting invite", uri);
}
else
if (accept) {
const id = accept.getAttribute("id");
console.debug("online meeting accept", id);
}
else
if (retract) {
const id = retract.getAttribute("id");
console.debug("online meeting retract", id);
}
}
return attrs;
}
function getToolbarButtons(_converse, toolbar_el, buttons) {
const { html } = env;
const { __ } = _converse;
console.debug("getToolbarButtons", toolbar_el.model.get("jid"));
let style = "width:18px; height:18px; fill:var(--chat-color);";
if (toolbar_el.model.get("type") === "chatroom") {
style = "width:18px; height:18px; fill:var(--muc-color);";
}
buttons.push(html`
`);
return buttons;
}
function afterMessageBodyTransformed(_converse, text) {
const { api, __ } = _converse;
const pos = text.indexOf(baseMeetUrl);
if (pos > -1) {
console.debug("afterMessageBodyTransformed", text);
const { html } = env;
const url = text.substring(pos);
const link_room = url.substring(url.lastIndexOf("/") + 1);
text.references = [];
text.addTemplateResult(
0,
text.length,
html`
${__('A new meeting started:')} ${link_room}
`
);
}
}
function __displayError(error) {
alert(error);
}
function getChatViewFromElement(el) {
return (
el.closest("converse-chat.chatbox") ||
el.closest("converse-muc.chatbox")
);
}
function performVideo(_converse, ev) {
ev.stopPropagation();
ev.preventDefault();
const { __ } = _converse;
const chatView = getChatViewFromElement(ev.currentTarget);
const olmeet_confirm = __("Would you like to start a meeting?");
if (confirm(olmeet_confirm)) {
doVideo(_converse, chatView);
}
}
function clickVideo(_converse, ev) {
ev.stopPropagation();
ev.preventDefault();
const url = ev.target.getAttribute("data-url");
const room = ev.target.getAttribute("data-room");
if (ev.currentTarget) {
const chatView = getChatViewFromElement(ev.currentTarget);
doLocalVideo(_converse, chatView, room, url);
}
}
function doVideo(_converse, view) {
const { api } = _converse;
const room = Strophe.getNodeFromJid(view.model.attributes.jid).toLowerCase().replace(/[\\]/g, "") + "-" + Math.random().toString(36).substr(2, 9);
const url = baseMeetUrl + "/" + room;
const model = view.model;
console.debug("doVideo", room, url, view, model);
const type = (model.get('type') == 'chatroom') ? 'groupchat' : 'chat';
const target = (model.get('type') == 'chatbox') ? model.get('jid') : (model.get('type') == 'chatroom' ? model.get('jid') : model.get('from'));
const msg = converse.env.stx`${url}`;
_converse.api.send(msg);
const startOption = api.settings.get("olmeet_start_option");
if (startOption === MEET_START_OPTIONS.INTO_CHAT_WINDOW) {
doLocalVideo(_converse, view, room, url);
} else if (startOption === MEET_START_OPTIONS.INTO_NEW_TAB) {
doNewTabVideo(url);
}
};
function doNewTabVideo(url) {
console.debug("doNewTabVideo", url);
const newTabVideoLink = document.createElement("a");
Object.assign(newTabVideoLink, {
target: "_blank",
rel: "noopener noreferrer",
href: url,
}).click();
};
function doLocalVideo(_converse, view, room, url, label) {
const { api } = _converse;
const chatModel = view.model;
console.debug("doLocalVideo", view, room, url, label);
const modal = api.settings.get("olmeet_modal") === true;
if (modal) {
const model = new converse.env.Model();
model.set({ view, url, label, room });
api.modal.show('converse-olmeet-dialog', { model });
} else {
const isOverlayedDisplay = _converse.api.settings.get("view_mode") === "overlayed";
const headDisplayToggle =
isOverlayedDisplay ||
_converse.api.settings.get("olmeet_head_display_toggle") ===
true;
const div = view.querySelector(headDisplayToggle ? ".chat-body" : ".box-flyout");
if (div) {
const jid = view.getAttribute("jid");
if (
Array.from(
document.querySelectorAll("iframe.olmeet")
).filter((f) => f.__jid === jid).length > 0
) {
__displayError(__("A meet is already running into room"));
return;
}
const toggleHandler = () => olFrame.toggleHideShow();
const dynamicDisplayManager = new (function () {
let __resizeHandler;
let __resizeWatchImpl;
this.start = function () {
const $chatBox = document.querySelector(
".converse-chatboxes"
);
const $anchor = document.querySelector(
"#conversejs.conversejs"
);
__resizeHandler = function () {
const currentView = _converse.chatboxviews.get(jid);
if (currentView && headDisplayToggle) {
const head = currentView.querySelector(".chat-head");
head.removeEventListener("dblclick", toggleHandler);
head.addEventListener("dblclick", toggleHandler);
}
const currentDiv =
currentView &&
currentView.querySelector(
headDisplayToggle
? ".chat-body"
: ".box-flyout"
);
let top = currentDiv ? currentDiv.offsetTop : 0;
let left = currentDiv ? currentDiv.offsetLeft : 0;
let width = currentDiv ? currentDiv.offsetWidth : 0;
let height = currentDiv ? currentDiv.offsetHeight : 0;
let current = currentDiv && currentDiv.offsetParent;
while (current && current !== $anchor) {
top += current.offsetTop;
left += current.offsetLeft;
current = current.offsetParent;
}
olFrame.style.top = top + "px";
olFrame.style.left = left + "px";
olFrame.style.width = width + "px";
olFrame.style.height = height + "px";
};
__resizeWatchImpl = new (function () {
let __resizeObserver;
if (
isOverlayedDisplay &&
typeof ResizeObserver === "function"
) {
__resizeObserver = new ResizeObserver(
function (entries) {
if (entries.length > 0) {
__resizeHandler();
}
}
);
}
const __resizeWatchEvents = [
"controlBoxOpened",
"controlBoxClosed",
"chatBoxBlurred",
"chatBoxFocused",
"chatBoxMinimized",
"chatBoxMaximized",
"chatBoxViewInitialized",
"chatRoomViewInitialized",
];
const __startResize = function () {
olFrame.style.pointerEvents = "none";
document.addEventListener("mousemove", __deferredResize);
};
const __endResize = function () {
olFrame.style.pointerEvents = "";
document.removeEventListener("mousemove", __deferredResize);
};
let timeoutId;
const __deferredResize = function () {
clearTimeout(timeoutId);
timeoutId = setTimeout(__resizeHandler, 0);
};
this.start = function () {
_converse.api.listen.on("startDiagonalResize", __startResize);
_converse.api.listen.on("startHorizontalResize", __startResize);
_converse.api.listen.on("startVerticalResize", __startResize);
document.addEventListener("mouseup", __endResize);
window.addEventListener("resize", __resizeHandler);
__resizeWatchEvents.forEach((c) => _converse.api.listen.on(c, __deferredResize));
if (__resizeObserver) {
__resizeObserver.observe(div);
__resizeObserver.observe($anchor);
__resizeObserver.observe($chatBox);
}
};
this.close = function () {
_converse.api.listen.not("startDiagonalResize", __startResize);
_converse.api.listen.not("startHorizontalResize", __startResize);
_converse.api.listen.not("startVerticalResize", __startResize);
document.removeEventListener("mouseup", __endResize);
window.removeEventListener("resize", __resizeHandler);
__resizeWatchEvents.forEach((c) => _converse.api.listen.not(c, __deferredResize));
if (__resizeObserver) {
__resizeObserver.disconnect();
}
};
})();
olFrame.style.position = "absolute";
$anchor.appendChild(olFrame);
__resizeWatchImpl.start();
_converse.api.listen.on("chatBoxClosed", closeOnline);
this.triggerChange();
};
this.triggerChange = function () {
__resizeHandler();
};
this.close = function () {
__resizeWatchImpl.close();
_converse.api.listen.not("chatBoxClosed", closeOnline);
};
})();
const olFrame = document.createElement("iframe");
let firstTime = true;
function closeOnline (currentModel) {
dynamicDisplayManager.triggerChange();
if (currentModel && currentModel.cid !== chatModel.cid) {
return;
}
dynamicDisplayManager.close();
olFrame.remove();
};
function olIframeCloseHandler() {
console.debug("doVideo - load", this);
if (!firstTime) {
// meeting closed and root url is loaded
closeOnline();
}
if (firstTime) {
firstTime = false; // ignore when ol-meet room url is loaded
}
};
olFrame.toggleHideShow = function () {
if (olFrame.style.display === "none") {
olFrame.show();
} else {
olFrame.hide();
}
};
olFrame.show = () => {
olFrame.style.display = "";
};
olFrame.hide = () => {
olFrame.style.display = "none";
};
olFrame.__jid = jid;
olFrame.addEventListener("load", olIframeCloseHandler);
olFrame.setAttribute("src", url);
olFrame.setAttribute("class", "olmeet");
olFrame.setAttribute("allow", "microphone; camera; display-capture;");
olFrame.setAttribute("frameborder", "0");
olFrame.setAttribute("seamless", "seamless");
olFrame.setAttribute("allowfullscreen", "true");
olFrame.setAttribute("scrolling", "no");
olFrame.setAttribute("style", "z-index:1049;width:100%;height:100%;");
dynamicDisplayManager.start();
olFrame.contentWindow.addEventListener(
"message",
function (event) {
if (baseMeetUrl.indexOf(event.origin) === 0 && typeof event.data === "string") {
let data = JSON.parse(event.data);
let olEvent = data["olmeet_event"];
if ("close" === olEvent) {
closeOnline();
}
}
},
false
);
}
}
};
async function handleConnected() {
const features = await _converse.api.disco.getFeatures(await _converse.api.connection.get().domain);
console.debug("connected features", features);
let jitsiAvailable = false;
let galeneAvailable = false;
let ohunAvailable = false;
features.each(feature => {
const fieldname = feature.get('var');
console.debug("connected feature", fieldname);
if (fieldname == "urn:xmpp:http:online-meetings#jitsi") jitsiAvailable = true;
if (fieldname == "urn:xmpp:http:online-meetings#galene") galeneAvailable = true;
if (fieldname == "urn:xmpp:http:online-meetings#ohun") ohunAvailable = true;
});
baseMeetUrl = _converse.api.settings.get("olmeet_url");
if (jitsiAvailable) {
const res = await _converse.api.sendIQ(converse.env.$iq({type: 'get'}).c('query', {type: 'jitsi', xmlns: 'urn:xmpp:http:online-meetings:0'}));
console.debug('handleConnected query jitsi response', res);
if (res.querySelector('url')) baseMeetUrl = res.querySelector('url').innerHTML;
}
else
if (galeneAvailable) {
const res = await _converse.api.sendIQ(converse.env.$iq({type: 'get'}).c('query', {type: 'galene', xmlns: 'urn:xmpp:http:online-meetings:0'}));
console.debug('handleConnected query galene response', res);
if (res.querySelector('url')) baseMeetUrl = res.querySelector('url').innerHTML;
}
else
if (ohunAvailable) {
const res = await _converse.api.sendIQ(converse.env.$iq({type: 'get'}).c('query', {type: 'ohun', xmlns: 'urn:xmpp:http:online-meetings:0'}));
console.debug('handleConnected query ohun response', res);
if (res.querySelector('url')) baseMeetUrl = res.querySelector('url').innerHTML;
}
}
function initialize() {
Strophe = converse.env.Strophe;
env = converse.env;
const _converse = this._converse;
const { api, __ } = _converse;
const { BaseModal } = _converse.exports;
const { html, render } = converse.env;
api.settings.extend({
olmeet_start_option: MEET_START_OPTIONS.INTO_CHAT_WINDOW,
olmeet_head_display_toggle: true,
olmeet_modal: false,
olmeet_url: MEET_START_OPTIONS.BASE_URL,
});
api.listen.on('connected', (data) => handleConnected(_converse));
api.listen.on("messageNotification", (data) => handleMessageNotification(_converse, data));
api.listen.on( "getToolbarButtons", (toolbar_el, buttons) => getToolbarButtons(_converse, toolbar_el, buttons));
api.listen.on("afterMessageBodyTransformed", (text) => afterMessageBodyTransformed(_converse, text));
api.listen.on('parseMessage', (stanza, attrs) => parseStanza(_converse, stanza, attrs));
api.listen.on('parseMUCMessage', (stanza, attrs) => parseStanza(_converse, stanza, attrs));
class MeetDialog extends BaseModal {
initialize() {
super.initialize();
this.listenTo(this.model, "change", () => this.requestUpdate());
this.addEventListener('hidden.bs.modal', () => render('', this));
}
getModalTitle () {
return __('Meeting room: %1$s', this.model.get('room'));
}
renderModal() {
return html`
`;
}
}
api.elements.define('converse-olmeet-dialog', MeetDialog);
console.debug("olmeet plugin is ready");
}
}));