mirror of
https://github.com/webrecorder/pywb.git
synced 2025-03-24 06:59:52 +01:00
Ensured that the banner does not become stuck displaying Loading... on non-html content fixes #417 (#418)
Changes: Reworked ContentFrame and the default banner to be ES5 classes. Introduced an optional relationship between ContentFrame and banners. If a banner is exposed then ContentFrame controls the initialization of the banner and routes any messages received from the replay iframe to the banner. When the replay iframe is navigated to a page and the replay iframe loads, the ContentFrame waits 2 seconds before checking to see if the banner still indicates it a loading state and if so updates the displayed information using the URL and timestamp the replay iframe was navigated to.
This commit is contained in:
parent
f7e8217e23
commit
2c8d607b18
@ -20,8 +20,134 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
|
|
||||||
// Creates the default pywb banner.
|
// Creates the default pywb banner.
|
||||||
|
|
||||||
(function() {
|
(function () {
|
||||||
function ts_to_date(ts, is_gmt) {
|
if (window.top !== window) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The default banner class
|
||||||
|
*/
|
||||||
|
function DefaultBanner() {
|
||||||
|
if (!(this instanceof DefaultBanner)) return new DefaultBanner();
|
||||||
|
this.banner = null;
|
||||||
|
this.captureInfo = null;
|
||||||
|
this.last_state = {};
|
||||||
|
this.state = null;
|
||||||
|
this.title = "";
|
||||||
|
this.loadingId = 'bannerLoading';
|
||||||
|
this.onMessage = this.onMessage.bind(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Functions required to be exposed by all banners
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Initialize (display) the banner
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.init = function () {
|
||||||
|
if (window.wbinfo) {
|
||||||
|
this.createBanner('_wb_plain_banner');
|
||||||
|
this.set_banner(
|
||||||
|
window.wbinfo.url,
|
||||||
|
window.wbinfo.timestamp,
|
||||||
|
window.wbinfo.is_live,
|
||||||
|
window.wbinfo.is_framed ? "" : document.title
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
this.createBanner('_wb_frame_top_banner');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Called by ContentFrame to detect if the banner is still showing
|
||||||
|
* that the page is loading
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.stillIndicatesLoading = function () {
|
||||||
|
return document.getElementById(this.loadingId) != null;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} url - The URL of the replayed page
|
||||||
|
* @param {?string} ts - The timestamp of the replayed page.
|
||||||
|
* If we are in live mode this is undefined/empty string
|
||||||
|
* @param {boolean} is_live - A bool indicating if we are operating in live mode
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.updateCaptureInfo = function (url, ts, is_live) {
|
||||||
|
if (is_live && !ts) {
|
||||||
|
ts = new Date().toISOString().replace(/[-T:.Z]/g, '')
|
||||||
|
}
|
||||||
|
this.set_banner(url, ts, is_live, null);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Called by ContentFrame when a message is received from the replay iframe
|
||||||
|
* @param {MessageEvent} event - The message event containing the message received
|
||||||
|
* from the replayed page
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.onMessage = function (event) {
|
||||||
|
var type = event.data.wb_type;
|
||||||
|
|
||||||
|
if (type === "load" || type === "replace-url") {
|
||||||
|
this.state = event.data;
|
||||||
|
this.last_state = this.state;
|
||||||
|
this.title = event.data.title || this.title;
|
||||||
|
} else if (type === "title") {
|
||||||
|
this.state = this.last_state;
|
||||||
|
this.title = event.data.title;
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// favicon update
|
||||||
|
if (type === 'load') {
|
||||||
|
var head = document.querySelector('head');
|
||||||
|
var oldLink = document.querySelectorAll("link[rel*='icon']");
|
||||||
|
var i = 0;
|
||||||
|
for (; i < oldLink.length; i++) {
|
||||||
|
head.removeChild(oldLink[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.icons) {
|
||||||
|
for (i = 0; i < this.state.icons.length; i++) {
|
||||||
|
var icon = this.state.icons[i];
|
||||||
|
var link = document.createElement('link');
|
||||||
|
link.rel = icon.rel;
|
||||||
|
link.href = icon.href;
|
||||||
|
head.appendChild(link);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.set_banner(this.state.url, this.state.ts, this.state.is_live, this.title);
|
||||||
|
};
|
||||||
|
|
||||||
|
// Functions internal to the default banner
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Creates the underlying HTML elements comprising the banner
|
||||||
|
* @param {string} bid - The id for the banner
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.createBanner = function (bid) {
|
||||||
|
this.banner = document.createElement("wb_div", true);
|
||||||
|
this.banner.setAttribute("id", bid);
|
||||||
|
this.banner.setAttribute("lang", "en");
|
||||||
|
this.captureInfo = document.createElement('span');
|
||||||
|
this.captureInfo.innerHTML = '<span id="' + this.loadingId + '">Loading...</span>';
|
||||||
|
this.captureInfo.id = '_wb_capture_info';
|
||||||
|
this.banner.appendChild(this.captureInfo);
|
||||||
|
document.body.insertBefore(this.banner, document.body.firstChild);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Converts a timestamp to a date string. If is_gmt is truthy then
|
||||||
|
* the returned data string will be the results of date.toGMTString otherwise
|
||||||
|
* its date.toLocaleString()
|
||||||
|
* @param {?string} ts - The timestamp to receive the correct date string for
|
||||||
|
* @param {boolean} is_gmt - Is the returned date string to be in GMT time
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
DefaultBanner.prototype.ts_to_date = function (ts, is_gmt) {
|
||||||
if (!ts) {
|
if (!ts) {
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
@ -31,11 +157,11 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
}
|
}
|
||||||
|
|
||||||
var datestr = (ts.substring(0, 4) + "-" +
|
var datestr = (ts.substring(0, 4) + "-" +
|
||||||
ts.substring(4, 6) + "-" +
|
ts.substring(4, 6) + "-" +
|
||||||
ts.substring(6, 8) + "T" +
|
ts.substring(6, 8) + "T" +
|
||||||
ts.substring(8, 10) + ":" +
|
ts.substring(8, 10) + ":" +
|
||||||
ts.substring(10, 12) + ":" +
|
ts.substring(10, 12) + ":" +
|
||||||
ts.substring(12, 14) + "-00:00");
|
ts.substring(12, 14) + "-00:00");
|
||||||
|
|
||||||
var date = new Date(datestr);
|
var date = new Date(datestr);
|
||||||
|
|
||||||
@ -44,22 +170,16 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
} else {
|
} else {
|
||||||
return date.toLocaleString();
|
return date.toLocaleString();
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
|
|
||||||
function init(bid) {
|
/**
|
||||||
var banner = document.createElement("wb_div", true);
|
* @desc Updates the contents displayed by the banner
|
||||||
|
* @param {?string} url - The URL of the replayed page to be displayed in the banner
|
||||||
banner.setAttribute("id", bid);
|
* @param {?string} ts - A timestamp to be displayed in the banner
|
||||||
banner.setAttribute("lang", "en");
|
* @param {boolean} is_live - Are we in live mode
|
||||||
|
* @param {?string} title - The title of the replayed page to be displayed in the banner
|
||||||
var text = "";
|
*/
|
||||||
text += "<span id='_wb_capture_info'>Loading...</span>";
|
DefaultBanner.prototype.set_banner = function (url, ts, is_live, title) {
|
||||||
|
|
||||||
banner.innerHTML = text;
|
|
||||||
document.body.insertBefore(banner, document.body.firstChild);
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_banner(url, ts, is_live, title) {
|
|
||||||
var capture_str;
|
var capture_str;
|
||||||
var title_str;
|
var title_str;
|
||||||
|
|
||||||
@ -67,11 +187,11 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var date_str = ts_to_date(ts, true);
|
var date_str = this.ts_to_date(ts, true);
|
||||||
|
|
||||||
if (title) {
|
if (title) {
|
||||||
capture_str = title;
|
capture_str = title;
|
||||||
} else {
|
} else {
|
||||||
capture_str = url;
|
capture_str = url;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -88,69 +208,12 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
|
|
||||||
title_str += " (" + date_str + ")";
|
title_str += " (" + date_str + ")";
|
||||||
capture_str += date_str;
|
capture_str += date_str;
|
||||||
|
this.captureInfo.innerHTML = capture_str;
|
||||||
document.querySelector("#_wb_capture_info").innerHTML = capture_str;
|
|
||||||
window.document.title = title_str;
|
window.document.title = title_str;
|
||||||
}
|
};
|
||||||
|
|
||||||
if (window.top != window) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var last_state = {};
|
|
||||||
|
|
||||||
window.addEventListener("load", function() {
|
|
||||||
if (window.wbinfo) {
|
|
||||||
init("_wb_plain_banner");
|
|
||||||
|
|
||||||
set_banner(window.wbinfo.url,
|
|
||||||
window.wbinfo.timestamp,
|
|
||||||
window.wbinfo.is_live,
|
|
||||||
window.wbinfo.is_framed ? "" : document.title);
|
|
||||||
} else {
|
|
||||||
init("_wb_frame_top_banner");
|
|
||||||
|
|
||||||
var state;
|
|
||||||
var title = "";
|
|
||||||
|
|
||||||
window.addEventListener("message", function(event) {
|
|
||||||
var type = event.data.wb_type;
|
|
||||||
|
|
||||||
if (type == "load" || type == "replace-url") {
|
|
||||||
state = event.data;
|
|
||||||
last_state = state;
|
|
||||||
title = event.data.title || title;
|
|
||||||
} else if (type == "title") {
|
|
||||||
state = last_state;
|
|
||||||
title = event.data.title;
|
|
||||||
} else {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// favicon update
|
|
||||||
if (type === 'load') {
|
|
||||||
var head = document.querySelector('head');
|
|
||||||
var oldLink = document.querySelectorAll("link[rel*='icon']");
|
|
||||||
for (var i = 0; i < oldLink.length; i++) {
|
|
||||||
head.removeChild(oldLink[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (state.icons) {
|
|
||||||
for (var i = 0; i < state.icons.length; i++) {
|
|
||||||
var icon = state.icons[i];
|
|
||||||
var link = document.createElement('link');
|
|
||||||
link.rel = icon.rel;
|
|
||||||
link.href = icon.href;
|
|
||||||
head.appendChild(link);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
set_banner(state.url, state.ts, state.is_live, title);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
// all banners will expose themselves by adding themselves as WBBanner on window
|
||||||
|
window.WBBanner = new DefaultBanner();
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|
||||||
|
@ -18,159 +18,250 @@ This file is part of pywb, https://github.com/webrecorder/pywb
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {Object} content_info - Information about the contents to be replayed
|
||||||
|
*/
|
||||||
function ContentFrame(content_info) {
|
function ContentFrame(content_info) {
|
||||||
|
if (!(this instanceof ContentFrame)) return new ContentFrame(content_info);
|
||||||
this.last_inner_hash = window.location.hash;
|
this.last_inner_hash = window.location.hash;
|
||||||
this.last_url = content_info.url;
|
this.last_url = content_info.url;
|
||||||
this.last_ts = content_info.request_ts;
|
this.last_ts = content_info.request_ts;
|
||||||
|
this.content_info = content_info;
|
||||||
this.init_iframe = function() {
|
|
||||||
if (typeof(content_info.iframe) === "string") {
|
|
||||||
this.iframe = document.querySelector(content_info.iframe);
|
|
||||||
} else {
|
|
||||||
this.iframe = content_info.iframe;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!this.iframe) {
|
|
||||||
console.warn("no iframe found " + content_info.iframe + " found");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extract_prefix();
|
|
||||||
|
|
||||||
this.load_url(content_info.url, content_info.request_ts);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.extract_prefix = function() {
|
|
||||||
this.app_prefix = content_info.app_prefix || content_info.prefix;
|
|
||||||
this.content_prefix = content_info.content_prefix || content_info.prefix;
|
|
||||||
|
|
||||||
if (this.app_prefix && this.content_prefix) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var inx = window.location.href.indexOf(content_info.url);
|
|
||||||
|
|
||||||
if (inx < 0) {
|
|
||||||
inx = window.location.href.indexOf("/http") + 1;
|
|
||||||
if (inx <= 0) {
|
|
||||||
inx = window.location.href.indexOf("///") + 1;
|
|
||||||
if (inx <= 0) {
|
|
||||||
console.warn("No Prefix Found!");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.prefix = window.location.href.substr(0, inx);
|
|
||||||
|
|
||||||
this.app_prefix = this.app_prefix || this.prefix;
|
|
||||||
this.content_prefix = this.content_prefix || this.prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.make_url = function(url, ts, content_url) {
|
|
||||||
var mod, prefix;
|
|
||||||
|
|
||||||
if (content_url) {
|
|
||||||
mod = "mp_";
|
|
||||||
prefix = this.content_prefix;
|
|
||||||
} else {
|
|
||||||
mod = "";
|
|
||||||
prefix = this.app_prefix;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ts || mod) {
|
|
||||||
mod += "/";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ts) {
|
|
||||||
return prefix + ts + mod + url;
|
|
||||||
} else {
|
|
||||||
return prefix + mod + url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handle_event = function(event) {
|
|
||||||
var frame_win = this.iframe.contentWindow;
|
|
||||||
if (event.source == window.parent) {
|
|
||||||
// Pass to replay frame
|
|
||||||
frame_win.postMessage(event.data, "*");
|
|
||||||
} else if (event.source == frame_win) {
|
|
||||||
|
|
||||||
// Check if iframe url change message
|
|
||||||
if (typeof(event.data) == "object" && event.data["wb_type"]) {
|
|
||||||
this.handle_message(event.data);
|
|
||||||
|
|
||||||
} else {
|
|
||||||
// Pass to parent
|
|
||||||
window.parent.postMessage(event.data, "*");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.handle_message = function(state) {
|
|
||||||
var type = state.wb_type;
|
|
||||||
|
|
||||||
if (type == "load" || type == "replace-url") {
|
|
||||||
this.set_url(state);
|
|
||||||
} else if (type == "hashchange") {
|
|
||||||
this.inner_hash_changed(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.set_url = function(state) {
|
|
||||||
if (state.url && (state.url != this.last_url || state.request_ts != this.last_ts)) {
|
|
||||||
var new_url = this.make_url(state.url, state.request_ts, false);
|
|
||||||
|
|
||||||
window.history.replaceState(state, "", new_url);
|
|
||||||
|
|
||||||
this.last_url = state.url;
|
|
||||||
this.last_ts = state.request_ts;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.load_url = function(newUrl, newTs) {
|
|
||||||
this.iframe.src = this.make_url(newUrl, newTs, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.inner_hash_changed = function(state) {
|
|
||||||
if (window.location.hash != state.hash) {
|
|
||||||
window.location.hash = state.hash;
|
|
||||||
}
|
|
||||||
this.last_inner_hash = state.hash;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.outer_hash_changed = function(event) {
|
|
||||||
if (window.location.hash == this.last_inner_hash) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.iframe) {
|
|
||||||
var message = {"wb_type": "outer_hashchange", "hash": window.location.hash}
|
|
||||||
|
|
||||||
this.iframe.contentWindow.postMessage(message, "*", undefined, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.close = function() {
|
|
||||||
window.removeEventListener("hashchange", this.outer_hash_changed);
|
|
||||||
window.removeEventListener("message", this.handle_event);
|
|
||||||
}
|
|
||||||
|
|
||||||
// bind event callbacks
|
// bind event callbacks
|
||||||
this.outer_hash_changed = this.outer_hash_changed.bind(this);
|
this.outer_hash_changed = this.outer_hash_changed.bind(this);
|
||||||
this.handle_event = this.handle_event.bind(this);
|
this.handle_event = this.handle_event.bind(this);
|
||||||
|
this.wbBanner = null;
|
||||||
|
this.checkBannerToId = null;
|
||||||
|
|
||||||
window.addEventListener("hashchange", this.outer_hash_changed, false);
|
window.addEventListener('hashchange', this.outer_hash_changed, false);
|
||||||
window.addEventListener("message", this.handle_event);
|
window.addEventListener('message', this.handle_event);
|
||||||
|
|
||||||
if (document.readyState === "complete") {
|
if (document.readyState === 'complete') {
|
||||||
this.init_iframe();
|
this.init_iframe();
|
||||||
} else {
|
} else {
|
||||||
document.addEventListener("DOMContentLoaded", this.init_iframe.bind(this), { once: true });
|
document.addEventListener('DOMContentLoaded', this.init_iframe.bind(this), {once: true});
|
||||||
}
|
}
|
||||||
|
|
||||||
window.__WB_pmw = function(win) {
|
window.__WB_pmw = function (win) {
|
||||||
this.pm_source = win;
|
this.pm_source = win;
|
||||||
return this;
|
return this;
|
||||||
}
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Initializes the replay iframe. If a banner exists (exposed on window as WBBanner)
|
||||||
|
* then the init function of the banner is called.
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.init_iframe = function () {
|
||||||
|
if (typeof (this.content_info.iframe) === 'string') {
|
||||||
|
this.iframe = document.querySelector(this.content_info.iframe);
|
||||||
|
} else {
|
||||||
|
this.iframe = this.content_info.iframe;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!this.iframe) {
|
||||||
|
console.warn('no iframe found ' + this.content_info.iframe + ' found');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.extract_prefix();
|
||||||
|
if (window.WBBanner) {
|
||||||
|
this.wbBanner = window.WBBanner;
|
||||||
|
this.wbBanner.init();
|
||||||
|
}
|
||||||
|
this.load_url(this.content_info.url, this.content_info.request_ts);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Initializes the prefixes used to load the pages to be replayed
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.extract_prefix = function () {
|
||||||
|
this.app_prefix = this.content_info.app_prefix || this.content_info.prefix;
|
||||||
|
this.content_prefix = this.content_info.content_prefix || this.content_info.prefix;
|
||||||
|
|
||||||
|
if (this.app_prefix && this.content_prefix) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var inx = window.location.href.indexOf(this.content_info.url);
|
||||||
|
|
||||||
|
if (inx < 0) {
|
||||||
|
inx = window.location.href.indexOf('/http') + 1;
|
||||||
|
if (inx <= 0) {
|
||||||
|
inx = window.location.href.indexOf('///') + 1;
|
||||||
|
if (inx <= 0) {
|
||||||
|
console.warn('No Prefix Found!');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.prefix = window.location.href.substr(0, inx);
|
||||||
|
|
||||||
|
this.app_prefix = this.app_prefix || this.prefix;
|
||||||
|
this.content_prefix = this.content_prefix || this.prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Returns an absolute URL (with correct prefix and replay modifier) given
|
||||||
|
* the replayed pages URL and optional timestamp and content_url
|
||||||
|
* @param {string} url - The URL of the replayed page
|
||||||
|
* @param {?string} ts - The timestamp of the replayed page
|
||||||
|
* @param {?boolean} content_url - Is the abs URL to be constructed using the content_prefix or app_prefix
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.make_url = function (url, ts, content_url) {
|
||||||
|
var mod, prefix;
|
||||||
|
|
||||||
|
if (content_url) {
|
||||||
|
mod = 'mp_';
|
||||||
|
prefix = this.content_prefix;
|
||||||
|
} else {
|
||||||
|
mod = '';
|
||||||
|
prefix = this.app_prefix;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts || mod) {
|
||||||
|
mod += '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ts) {
|
||||||
|
return prefix + ts + mod + url;
|
||||||
|
} else {
|
||||||
|
return prefix + mod + url;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Handles and routes all messages received from the replay iframe.
|
||||||
|
* @param {MessageEvent} event - A message event potentially containing a message from the replay iframe
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.handle_event = function (event) {
|
||||||
|
var frame_win = this.iframe.contentWindow;
|
||||||
|
if (event.source === window.parent) {
|
||||||
|
// Pass to replay frame
|
||||||
|
frame_win.postMessage(event.data, '*');
|
||||||
|
} else if (event.source === frame_win) {
|
||||||
|
// Check if iframe url change message
|
||||||
|
if (typeof (event.data) === 'object' && event.data['wb_type']) {
|
||||||
|
this.handle_message(event);
|
||||||
|
} else {
|
||||||
|
// Pass to parent
|
||||||
|
window.parent.postMessage(event.data, '*');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Handles messages intended for the content frame (indicated by data.wb_type). If a banner
|
||||||
|
* is exposed, calls the onMessage function of the exposed banner.
|
||||||
|
* @param {MessageEvent} event - The message event containing a message from the replay iframe
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.handle_message = function (event) {
|
||||||
|
if (this.wbBanner) {
|
||||||
|
this.wbBanner.onMessage(event);
|
||||||
|
}
|
||||||
|
var state = event.data;
|
||||||
|
var type = state.wb_type;
|
||||||
|
|
||||||
|
if (type === 'load' || type === 'replace-url') {
|
||||||
|
this.set_url(state);
|
||||||
|
} else if (type === 'hashchange') {
|
||||||
|
this.inner_hash_changed(state);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Updates the URL of the top frame
|
||||||
|
* @param {Object} state - The contents of a message rreceived from the replay iframe
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.set_url = function (state) {
|
||||||
|
if (state.url && (state.url !== this.last_url || state.request_ts !== this.last_ts)) {
|
||||||
|
var new_url = this.make_url(state.url, state.request_ts, false);
|
||||||
|
|
||||||
|
window.history.replaceState(state, '', new_url);
|
||||||
|
|
||||||
|
this.last_url = state.url;
|
||||||
|
this.last_ts = state.request_ts;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Checks to see if the banner is still indicating the replay iframe is still loading
|
||||||
|
* 2 seconds after the load event is fired by the replay iframe. If the banner is still
|
||||||
|
* indicating the replayed page is loading. Updates the displayed information using
|
||||||
|
* newURL and newTS
|
||||||
|
* @param {string} newUrl - The new URL of the replay iframe
|
||||||
|
* @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if
|
||||||
|
* operating in live mode
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.initBannerUpdateCheck = function (newUrl, newTs) {
|
||||||
|
if (!this.wbBanner) return;
|
||||||
|
var contentFrame = this;
|
||||||
|
var replayIframeLoaded = function () {
|
||||||
|
contentFrame.iframe.removeEventListener('load', replayIframeLoaded);
|
||||||
|
contentFrame.checkBannerToId = setTimeout(function () {
|
||||||
|
contentFrame.checkBannerToId = null;
|
||||||
|
if (contentFrame.wbBanner.stillIndicatesLoading()) {
|
||||||
|
contentFrame.wbBanner.updateCaptureInfo(
|
||||||
|
newUrl,
|
||||||
|
newTs,
|
||||||
|
contentFrame.content_prefix.indexOf('/live') !== -1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
};
|
||||||
|
if (this.checkBannerToId) {
|
||||||
|
clearTimeout(this.checkBannerToId);
|
||||||
|
}
|
||||||
|
this.iframe.addEventListener('load', replayIframeLoaded);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Navigates the replay iframe to a newURL and if a banner is exposed
|
||||||
|
* the initBannerUpdateCheck function is called.
|
||||||
|
* @param {string} newUrl - The new URL of the replay iframe
|
||||||
|
* @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if
|
||||||
|
* operating in live mode
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.load_url = function (newUrl, newTs) {
|
||||||
|
this.iframe.src = this.make_url(newUrl, newTs, true);
|
||||||
|
if (this.wbBanner) {
|
||||||
|
this.initBannerUpdateCheck(newUrl, newTs);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Updates this frames hash to the one inside the replay iframe
|
||||||
|
* @param {Object} state - The contents of message received from the replay iframe
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.inner_hash_changed = function (state) {
|
||||||
|
if (window.location.hash !== state.hash) {
|
||||||
|
window.location.hash = state.hash;
|
||||||
|
}
|
||||||
|
this.last_inner_hash = state.hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Updates the hash of the replay iframe on a hash change in this frame
|
||||||
|
* @param event
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.outer_hash_changed = function (event) {
|
||||||
|
if (window.location.hash === this.last_inner_hash) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.iframe) {
|
||||||
|
var message = {'wb_type': 'outer_hashchange', 'hash': window.location.hash};
|
||||||
|
|
||||||
|
this.iframe.contentWindow.postMessage(message, '*', undefined, true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @desc Cleans up any event listeners added by the content frame
|
||||||
|
*/
|
||||||
|
ContentFrame.prototype.close = function () {
|
||||||
|
window.removeEventListener('hashchange', this.outer_hash_changed);
|
||||||
|
window.removeEventListener('message', this.handle_event);
|
||||||
|
};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user