200 lines
4.8 KiB
JavaScript
200 lines
4.8 KiB
JavaScript
/*
|
|
* IPS embed handling
|
|
* https://www.invisioncommunity.com
|
|
*/
|
|
( function () {
|
|
"use strict";
|
|
|
|
let _origin;
|
|
let _div;
|
|
let _embedId = '';
|
|
let _failSafe = null;
|
|
|
|
/**
|
|
* @type {ResizeObserver}
|
|
*/
|
|
let _ro;
|
|
|
|
/**
|
|
* Init method, called when the document is ready
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function init() {
|
|
// Check for postMessage and JSON support
|
|
if( !window.postMessage || !window.JSON.parse ){
|
|
return;
|
|
}
|
|
|
|
// Work out our URL
|
|
_div = ips.utils.get('ipsEmbed');
|
|
const url = new URL(window.location.href);
|
|
|
|
// Set our origin
|
|
_origin = url.origin;
|
|
ips.utils.log( "Origin in loader is " + _origin );
|
|
|
|
// Hide the content
|
|
_div.style.opacity = '0.00001';
|
|
|
|
// But if we're in the 'top frame', don't bother showing loading
|
|
try {
|
|
if( window.self === window.top ){
|
|
_div.style.opacity = 1;
|
|
_div.parentNode.className = '';
|
|
}
|
|
} catch (err) {}
|
|
|
|
// Start an emergency timeout, which we'll use to force-show the embed if the parent page
|
|
// isn't talking to us for some reason. This will force the embed to eventually show, albeit at the wrong size,
|
|
// instead of just being forever loading.
|
|
var counter = 0;
|
|
_failSafe = setInterval( function () {
|
|
if( counter >= 6 ){ // approx 6 seconds
|
|
ips.utils.log("Triggered failsafe timer");
|
|
_div.parentNode.className = '';
|
|
ips.utils.fadeIn( _div );
|
|
clearInterval( _failSafe );
|
|
}
|
|
counter++;
|
|
}, 1000 );
|
|
}
|
|
|
|
/**
|
|
* Starts our main loop, which posts messages to the parent frame as needed
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
function createResizeObserver() {
|
|
ips.utils.log("Creating resize observer for external embed...");
|
|
let debounceLastCall=0, debounceTimeout, debounceInterval=200, sentMessage=false;
|
|
const debouncePostMessage = (height) => {
|
|
clearTimeout(debounceTimeout);
|
|
const delta = Date.now() - debounceLastCall - debounceInterval;
|
|
debounceLastCall = Date.now();
|
|
debounceTimeout = setTimeout(
|
|
() => {
|
|
if (!sentMessage) {
|
|
sentMessage = true;
|
|
ips.utils.fadeIn(_div);
|
|
}
|
|
_div.parentNode.className = '';
|
|
_postMessage('height', {height})
|
|
},
|
|
Math.max(0, -delta)
|
|
);
|
|
}
|
|
|
|
_ro = new ResizeObserver((entries) => {
|
|
for (const entry of entries) {
|
|
if (entry.target === _div) {
|
|
if (typeof entry.borderBoxSize?.[0]?.blockSize === 'number') {
|
|
debouncePostMessage(entry.borderBoxSize[0].blockSize + 10);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
|
|
_ro.observe(
|
|
_div,
|
|
{box: 'border-box'}
|
|
);
|
|
|
|
// resize observers usually call the callback right when .observe() is called, but just in case we manually send the height if there hasn't been one
|
|
setTimeout(() => {
|
|
// still hasn't been called?
|
|
if (debounceLastCall === 0) {
|
|
const height = ips.utils.getObjHeight(_div);
|
|
ips.utils.log("Determined height as " + height + ' for external embed ID ' + _embedId );
|
|
|
|
// getting the height can force a reflow, so just double check it stil hasn't been called
|
|
if (debounceLastCall === 0) {
|
|
debounceLastCall = Date.now(); // do this outside the last call
|
|
debouncePostMessage(height);
|
|
}
|
|
}
|
|
}, 20);
|
|
}
|
|
|
|
/**
|
|
* Posts a message to the iframe
|
|
*
|
|
* @returns {void}
|
|
*/
|
|
var _postMessage = function (method, obj) {
|
|
// Send to parent window
|
|
window.top.postMessage(JSON.stringify( {...obj, method, embedId: _embedId}), _origin);
|
|
};
|
|
|
|
/**
|
|
* Events sent to the iframe
|
|
*/
|
|
var messageEvents = {
|
|
/**
|
|
* The parent is ready for messages
|
|
*
|
|
* @param {object} data Data from the iframe
|
|
* @returns {void}
|
|
*/
|
|
ready(data) {
|
|
_embedId = data.embedId;
|
|
_postMessage('ok');
|
|
|
|
createResizeObserver();
|
|
clearInterval( _failSafe ); // Stop our emergency timer
|
|
},
|
|
|
|
stop() {
|
|
_ro?.disconnect();
|
|
clearInterval( _failSafe ); // Stop our emergency timer
|
|
eventHandler.off( window, 'message', windowMessage );
|
|
}
|
|
};
|
|
|
|
/*******************************************************************************************/
|
|
/* Boring stuff below */
|
|
|
|
// Main message handler
|
|
ips.eventHandler.on( window, 'message', windowMessage );
|
|
|
|
function windowMessage (e) {
|
|
if( e.origin !== _origin ){
|
|
ips.utils.log( e.origin + ' does not equal ' + _origin );
|
|
return;
|
|
}
|
|
if (typeof e.data !== "string") {
|
|
return;
|
|
}
|
|
|
|
let pmData;
|
|
try {
|
|
pmData = JSON.parse(e.data);
|
|
if (typeof pmData !== 'object') {
|
|
return;
|
|
}
|
|
} catch (e) {
|
|
return; // we don't log here because the message could have come from somewhere else
|
|
}
|
|
|
|
try {
|
|
var method = pmData.method;
|
|
} catch (err) {
|
|
ips.utils.error("iframe: invalid data.");
|
|
ips.utils.error(err.message)
|
|
return;
|
|
}
|
|
|
|
if( method && typeof messageEvents[ method ] != 'undefined' ){
|
|
ips.utils.log("Called " + method );
|
|
messageEvents[ method ].call( this, pmData );
|
|
} else {
|
|
ips.utils.log("Method " + method + " doesn't exist");
|
|
}
|
|
}
|
|
|
|
|
|
ips.utils.contentLoaded( window, function () {
|
|
init();
|
|
});
|
|
})(); |