1
0
mirror of https://github.com/webrecorder/pywb.git synced 2025-03-15 00:03:28 +01:00

static files:

- re-formatted: default_banner.js, queryWorker.js, search.js, wb_frame.js
This commit is contained in:
John Berlin 2019-09-04 14:59:50 -04:00
parent ae78a955de
commit 69f7f02006
No known key found for this signature in database
GPG Key ID: 6EF5E4B442011B02
4 changed files with 528 additions and 496 deletions

View File

@ -20,200 +20,211 @@ This file is part of pywb, https://github.com/webrecorder/pywb
// Creates the default pywb banner. // Creates the default pywb banner.
(function () { (function() {
if (window.top !== window) { if (window.top !== window) {
return; 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
* The default banner class if (type === 'load') {
*/ var head = document.querySelector('head');
function DefaultBanner() { var oldLink = document.querySelectorAll("link[rel*='icon']");
if (!(this instanceof DefaultBanner)) return new DefaultBanner(); var i = 0;
this.banner = null; for (; i < oldLink.length; i++) {
this.captureInfo = null; head.removeChild(oldLink[i]);
this.last_state = {}; }
this.state = null;
this.title = ""; if (this.state.icons) {
this.loadingId = 'bannerLoading'; for (i = 0; i < this.state.icons.length; i++) {
this.onMessage = this.onMessage.bind(this); var icon = this.state.icons[i];
var link = document.createElement('link');
link.rel = icon.rel;
link.href = icon.href;
head.appendChild(link);
}
}
} }
// Functions required to be exposed by all banners this.set_banner(
this.state.url,
this.state.ts,
this.state.is_live,
this.title
);
};
/** // Functions internal to the default banner
* @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 * @desc Creates the underlying HTML elements comprising the banner
* that the page is loading * @param {string} bid - The id for the banner
* @returns {boolean} */
*/ DefaultBanner.prototype.createBanner = function(bid) {
DefaultBanner.prototype.stillIndicatesLoading = function () { this.banner = document.createElement('wb_div', true);
return document.getElementById(this.loadingId) != null; 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);
};
/** /**
* @param {string} url - The URL of the replayed page * @desc Converts a timestamp to a date string. If is_gmt is truthy then
* @param {?string} ts - The timestamp of the replayed page. * the returned data string will be the results of date.toGMTString otherwise
* If we are in live mode this is undefined/empty string * its date.toLocaleString()
* @param {boolean} is_live - A bool indicating if we are operating in live mode * @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
DefaultBanner.prototype.updateCaptureInfo = function (url, ts, is_live) { * @returns {string}
if (is_live && !ts) { */
ts = new Date().toISOString().replace(/[-T:.Z]/g, '') DefaultBanner.prototype.ts_to_date = function(ts, is_gmt) {
} if (!ts) {
this.set_banner(url, ts, is_live, null); return '';
}; }
/** if (ts.length < 14) {
* @desc Called by ContentFrame when a message is received from the replay iframe ts += '00000000000000'.substr(ts.length);
* @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") { var datestr =
this.state = event.data; ts.substring(0, 4) +
this.last_state = this.state; '-' +
this.title = event.data.title || this.title; ts.substring(4, 6) +
} else if (type === "title") { '-' +
this.state = this.last_state; ts.substring(6, 8) +
this.title = event.data.title; 'T' +
} else { ts.substring(8, 10) +
return; ':' +
} ts.substring(10, 12) +
':' +
ts.substring(12, 14) +
'-00:00';
// favicon update var date = new Date(datestr);
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) { if (is_gmt) {
for (i = 0; i < this.state.icons.length; i++) { return date.toGMTString();
var icon = this.state.icons[i]; } else {
var link = document.createElement('link'); return date.toLocaleString();
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); /**
}; * @desc Updates the contents displayed by the banner
* @param {?string} url - The URL of the replayed page to be displayed in the banner
* @param {?string} ts - A timestamp to be displayed in the banner
* @param {boolean} is_live - Are we in live mode
* @param {?string} title - The title of the replayed page to be displayed in the banner
*/
DefaultBanner.prototype.set_banner = function(url, ts, is_live, title) {
var capture_str;
var title_str;
// Functions internal to the default banner if (!ts) {
return;
}
/** var date_str = this.ts_to_date(ts, true);
* @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);
};
/** if (title) {
* @desc Converts a timestamp to a date string. If is_gmt is truthy then capture_str = title;
* the returned data string will be the results of date.toGMTString otherwise } else {
* its date.toLocaleString() capture_str = url;
* @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) {
return "";
}
if (ts.length < 14) { title_str = capture_str;
ts += "00000000000000".substr(ts.length); capture_str = "<b id='title_or_url'>" + capture_str + '</b>';
}
var datestr = (ts.substring(0, 4) + "-" + if (is_live) {
ts.substring(4, 6) + "-" + title_str = ' pywb Live: ' + title_str;
ts.substring(6, 8) + "T" + capture_str += '<i>Live on&nbsp;</i>';
ts.substring(8, 10) + ":" + } else {
ts.substring(10, 12) + ":" + title_str += 'pywb Archived: ' + title_str;
ts.substring(12, 14) + "-00:00"); capture_str += '<i>Archived on&nbsp;</i>';
}
var date = new Date(datestr); title_str += ' (' + date_str + ')';
capture_str += date_str;
this.captureInfo.innerHTML = capture_str;
window.document.title = title_str;
};
if (is_gmt) { // all banners will expose themselves by adding themselves as WBBanner on window
return date.toGMTString(); window.WBBanner = new DefaultBanner();
} else {
return date.toLocaleString();
}
};
/**
* @desc Updates the contents displayed by the banner
* @param {?string} url - The URL of the replayed page to be displayed in the banner
* @param {?string} ts - A timestamp to be displayed in the banner
* @param {boolean} is_live - Are we in live mode
* @param {?string} title - The title of the replayed page to be displayed in the banner
*/
DefaultBanner.prototype.set_banner = function (url, ts, is_live, title) {
var capture_str;
var title_str;
if (!ts) {
return;
}
var date_str = this.ts_to_date(ts, true);
if (title) {
capture_str = title;
} else {
capture_str = url;
}
title_str = capture_str;
capture_str = "<b id='title_or_url'>" + capture_str + "</b>";
if (is_live) {
title_str = " pywb Live: " + title_str;
capture_str += "<i>Live on&nbsp;</i>";
} else {
title_str += "pywb Archived: " + title_str;
capture_str += "<i>Archived on&nbsp;</i>";
}
title_str += " (" + date_str + ")";
capture_str += date_str;
this.captureInfo.innerHTML = capture_str;
window.document.title = title_str;
};
// all banners will expose themselves by adding themselves as WBBanner on window
window.WBBanner = new DefaultBanner();
})(); })();

View File

@ -18,7 +18,7 @@ var decoder = new TextDecoder('utf-8');
*/ */
var bufferedPreviousChunk = null; var bufferedPreviousChunk = null;
self.onmessage = function (event) { self.onmessage = function(event) {
var data = event.data; var data = event.data;
if (data.type === 'query') { if (data.type === 'query') {
fetch(data.queryURL) fetch(data.queryURL)
@ -42,7 +42,8 @@ function defaultErrorCatcher(error) {
*/ */
function consumeResponseBodyAsStream(response) { function consumeResponseBodyAsStream(response) {
var reader = response.body.getReader(); var reader = response.body.getReader();
reader.read() reader
.read()
.then(function consumeStream(result) { .then(function consumeStream(result) {
if (result.done) { if (result.done) {
if (bufferedPreviousChunk) { if (bufferedPreviousChunk) {
@ -58,7 +59,10 @@ function consumeResponseBodyAsStream(response) {
return; return;
} }
transformChunk(result.value); transformChunk(result.value);
reader.read().then(consumeStream).catch(defaultErrorCatcher); reader
.read()
.then(consumeStream)
.catch(defaultErrorCatcher);
}) })
.catch(defaultErrorCatcher); .catch(defaultErrorCatcher);
} }
@ -154,8 +158,11 @@ function handleCDXRecord(binaryCDXRecord) {
year: ts.substring(0, 4), year: ts.substring(0, 4),
month: ts.substring(4, 6), month: ts.substring(4, 6),
day: day.charAt(0) === '0' ? day.charAt(1) : day, day: day.charAt(0) === '0' ? day.charAt(1) : day,
time: ts.substring(8, 10) + colon + time:
ts.substring(10, 12) + colon + ts.substring(8, 10) +
colon +
ts.substring(10, 12) +
colon +
ts.substring(12, 14) ts.substring(12, 14)
}, },
wasError: false, wasError: false,
@ -163,9 +170,3 @@ function handleCDXRecord(binaryCDXRecord) {
recordCountFormatted: recordCount.toLocaleString() recordCountFormatted: recordCount.toLocaleString()
}); });
} }

View File

@ -1,175 +1,191 @@
var dtRE = /^\d{4,14}$/; var dtRE = /^\d{4,14}$/;
var didSetWasValidated = false; var didSetWasValidated = false;
var showBadDateTimeClass = 'show-optional-bad-input'; var showBadDateTimeClass = 'show-optional-bad-input';
var filterMods = { var filterMods = {
'=': 'Contains', '=': 'Contains',
'==': 'Matches Exactly', '==': 'Matches Exactly',
'=~': 'Matches Regex', '=~': 'Matches Regex',
'=!': 'Does Not Contains', '=!': 'Does Not Contains',
'=!=': 'Is Not', '=!=': 'Is Not',
'=!~': 'Does Not Begins With' '=!~': 'Does Not Begins With'
}; };
var elemIds = { var elemIds = {
filtering: { filtering: {
by: 'filter-by', by: 'filter-by',
modifier: 'filter-modifier', modifier: 'filter-modifier',
expression: 'filter-expression', expression: 'filter-expression',
list: 'filter-list', list: 'filter-list',
nothing: 'filtering-nothing', nothing: 'filtering-nothing',
add: 'add-filter', add: 'add-filter',
clear: 'clear-filters' clear: 'clear-filters'
}, },
dateTime: { dateTime: {
from: 'dt-from', from: 'dt-from',
fromBad: 'dt-from-bad', fromBad: 'dt-from-bad',
to: 'dt-to', to: 'dt-to',
toBad: 'dt-to-bad' toBad: 'dt-to-bad'
}, },
match: 'match-type-select', match: 'match-type-select',
url: 'search-url', url: 'search-url',
form: 'search-form', form: 'search-form',
resultsNewWindow: 'open-results-new-window' resultsNewWindow: 'open-results-new-window'
}; };
function makeCheckDateRangeChecker(dtInputId, dtBadNotice) {
function makeCheckDateRangeChecker(dtInputId, dtBadNotice) { var dtInput = document.getElementById(dtInputId);
var dtInput = document.getElementById(dtInputId); dtInput.onblur = function() {
dtInput.onblur = function () { if (
if (dtInput.validity.valid && dtBadNotice.classList.contains(showBadDateTimeClass)) { dtInput.validity.valid &&
return dtBadNotice.classList.remove(showBadDateTimeClass); dtBadNotice.classList.contains(showBadDateTimeClass)
} ) {
if (dtInput.validity.valueMissing) { return dtBadNotice.classList.remove(showBadDateTimeClass);
if (dtBadNotice.classList.contains(showBadDateTimeClass)) { }
dtBadNotice.classList.remove(showBadDateTimeClass); if (dtInput.validity.valueMissing) {
} if (dtBadNotice.classList.contains(showBadDateTimeClass)) {
return;
}
if (dtInput.validity.badInput) {
if (!dtBadNotice.classList.contains(showBadDateTimeClass)) {
dtBadNotice.classList.add(showBadDateTimeClass);
}
return;
}
var validInput = dtRE.test(dtInput.value);
if (validInput && dtBadNotice.classList.contains(showBadDateTimeClass)) {
dtBadNotice.classList.remove(showBadDateTimeClass); dtBadNotice.classList.remove(showBadDateTimeClass);
} else if (!validInput) { }
return;
}
if (dtInput.validity.badInput) {
if (!dtBadNotice.classList.contains(showBadDateTimeClass)) {
dtBadNotice.classList.add(showBadDateTimeClass); dtBadNotice.classList.add(showBadDateTimeClass);
} }
}; return;
}
function createAndAddNoFilter(filterList) {
var nothing = document.createElement('li');
nothing.innerText = 'No Filter';
nothing.id = elemIds.filtering.nothing;
filterList.appendChild(nothing);
}
function addFilter(event) {
var by = document.getElementById(elemIds.filtering.by).value;
if (!by) return;
var modifier = document.getElementById(elemIds.filtering.modifier).value;
var expr = document.getElementById(elemIds.filtering.expression).value;
if (!expr) return;
var filterExpr = 'filter' + modifier + by + ':' + expr;
var filterList = document.getElementById(elemIds.filtering.list);
var filterNothing = document.getElementById(elemIds.filtering.nothing);
if (filterNothing) {
filterList.removeChild(filterNothing);
} }
var li = document.createElement('li'); var validInput = dtRE.test(dtInput.value);
li.innerText = 'By ' + by[0].toUpperCase() + by.substr(1) + ' ' + filterMods[modifier] + ' ' + expr; if (validInput && dtBadNotice.classList.contains(showBadDateTimeClass)) {
li.dataset.filter = filterExpr; dtBadNotice.classList.remove(showBadDateTimeClass);
var nukeButton = document.createElement('button'); } else if (!validInput) {
nukeButton.type = 'button'; dtBadNotice.classList.add(showBadDateTimeClass);
nukeButton.role = 'button';
nukeButton.className = 'btn btn-outline-danger close';
nukeButton.setAttribute('aria-label', 'Remove Filter');
var buttonX = document.createElement('span');
buttonX.className = 'px-2';
buttonX.innerHTML = '&times;';
buttonX.setAttribute('aria-hidden', 'true');
nukeButton.appendChild(buttonX);
nukeButton.onclick = function () {
filterList.removeChild(li);
if (filterList.children.length === 0) {
createAndAddNoFilter(filterList);
}
};
li.appendChild(nukeButton);
filterList.appendChild(li);
}
function clearFilters(event) {
if (document.getElementById(elemIds.filtering.nothing)) return;
var filterList = document.getElementById(elemIds.filtering.list);
while (filterList.firstElementChild) {
filterList.firstElementChild.onclick = null;
filterList.removeChild(filterList.firstElementChild);
} }
createAndAddNoFilter(filterList); };
} }
function performQuery(url) { function createAndAddNoFilter(filterList) {
var query = [window.wb_prefix + '*?url=' + url]; var nothing = document.createElement('li');
var filterExpressions = document.getElementById(elemIds.filtering.list).children; nothing.innerText = 'No Filter';
if (filterExpressions.length) { nothing.id = elemIds.filtering.nothing;
for (var i = 0; i < filterExpressions.length; ++i) { filterList.appendChild(nothing);
var fexpr = filterExpressions[i]; }
if (fexpr.dataset && fexpr.dataset.filter) {
query.push(fexpr.dataset.filter.trim()); function addFilter(event) {
} var by = document.getElementById(elemIds.filtering.by).value;
if (!by) return;
var modifier = document.getElementById(elemIds.filtering.modifier).value;
var expr = document.getElementById(elemIds.filtering.expression).value;
if (!expr) return;
var filterExpr = 'filter' + modifier + by + ':' + expr;
var filterList = document.getElementById(elemIds.filtering.list);
var filterNothing = document.getElementById(elemIds.filtering.nothing);
if (filterNothing) {
filterList.removeChild(filterNothing);
}
var li = document.createElement('li');
li.innerText =
'By ' +
by[0].toUpperCase() +
by.substr(1) +
' ' +
filterMods[modifier] +
' ' +
expr;
li.dataset.filter = filterExpr;
var nukeButton = document.createElement('button');
nukeButton.type = 'button';
nukeButton.role = 'button';
nukeButton.className = 'btn btn-outline-danger close';
nukeButton.setAttribute('aria-label', 'Remove Filter');
var buttonX = document.createElement('span');
buttonX.className = 'px-2';
buttonX.innerHTML = '&times;';
buttonX.setAttribute('aria-hidden', 'true');
nukeButton.appendChild(buttonX);
nukeButton.onclick = function() {
filterList.removeChild(li);
if (filterList.children.length === 0) {
createAndAddNoFilter(filterList);
}
};
li.appendChild(nukeButton);
filterList.appendChild(li);
}
function clearFilters(event) {
if (document.getElementById(elemIds.filtering.nothing)) return;
var filterList = document.getElementById(elemIds.filtering.list);
while (filterList.firstElementChild) {
filterList.firstElementChild.onclick = null;
filterList.removeChild(filterList.firstElementChild);
}
createAndAddNoFilter(filterList);
}
function performQuery(url) {
var query = [window.wb_prefix + '*?url=' + url];
var filterExpressions = document.getElementById(elemIds.filtering.list)
.children;
if (filterExpressions.length) {
for (var i = 0; i < filterExpressions.length; ++i) {
var fexpr = filterExpressions[i];
if (fexpr.dataset && fexpr.dataset.filter) {
query.push(fexpr.dataset.filter.trim());
} }
} }
var matchType = document.getElementById(elemIds.match).value; }
if (matchType) { var matchType = document.getElementById(elemIds.match).value;
query.push('matchType=' + matchType.trim()); if (matchType) {
} query.push('matchType=' + matchType.trim());
var fromT = document.getElementById(elemIds.dateTime.from).value; }
if (fromT) { var fromT = document.getElementById(elemIds.dateTime.from).value;
query.push('from=' + fromT.trim()); if (fromT) {
} query.push('from=' + fromT.trim());
var toT = document.getElementById(elemIds.dateTime.to).value; }
if (toT) { var toT = document.getElementById(elemIds.dateTime.to).value;
query.push('to=' + toT.trim()); if (toT) {
} query.push('to=' + toT.trim());
var builtQuery = query.join('&'); }
if (document.getElementById(elemIds.resultsNewWindow).checked) { var builtQuery = query.join('&');
try { if (document.getElementById(elemIds.resultsNewWindow).checked) {
var win = window.open(builtQuery); try {
win.focus(); var win = window.open(builtQuery);
} catch (e) { win.focus();
document.location.href = builtQuery; } catch (e) {
}
} else {
document.location.href = builtQuery; document.location.href = builtQuery;
} }
} else {
document.location.href = builtQuery;
} }
}
$(document).ready(function () { $(document).ready(function() {
$('[data-toggle="tooltip"]').tooltip({ $('[data-toggle="tooltip"]').tooltip({
container: 'body', container: 'body',
delay: { show: 1000 } delay: { show: 1000 }
});
makeCheckDateRangeChecker(elemIds.dateTime.from, document.getElementById(elemIds.dateTime.fromBad));
makeCheckDateRangeChecker(elemIds.dateTime.to, document.getElementById(elemIds.dateTime.toBad));
document.getElementById(elemIds.filtering.add).onclick = addFilter;
document.getElementById(elemIds.filtering.clear).onclick = clearFilters;
var searchURLInput = document.getElementById(elemIds.url);
var form = document.getElementById(elemIds.form);
form.addEventListener('submit', function (event) {
event.preventDefault();
event.stopPropagation();
var url = searchURLInput.value;
if (!url) {
if (!didSetWasValidated) {
form.classList.add('was-validated');
didSetWasValidated = true;
}
return;
}
performQuery(url);
});
}); });
makeCheckDateRangeChecker(
elemIds.dateTime.from,
document.getElementById(elemIds.dateTime.fromBad)
);
makeCheckDateRangeChecker(
elemIds.dateTime.to,
document.getElementById(elemIds.dateTime.toBad)
);
document.getElementById(elemIds.filtering.add).onclick = addFilter;
document.getElementById(elemIds.filtering.clear).onclick = clearFilters;
var searchURLInput = document.getElementById(elemIds.url);
var form = document.getElementById(elemIds.form);
form.addEventListener('submit', function(event) {
event.preventDefault();
event.stopPropagation();
var url = searchURLInput.value;
if (!url) {
if (!didSetWasValidated) {
form.classList.add('was-validated');
didSetWasValidated = true;
}
return;
}
performQuery(url);
});
});

View File

@ -17,88 +17,90 @@ This file is part of pywb, https://github.com/webrecorder/pywb
along with pywb. If not, see <http://www.gnu.org/licenses/>. along with pywb. If not, see <http://www.gnu.org/licenses/>.
*/ */
/** /**
* @param {Object} content_info - Information about the contents to be replayed * @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); 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.content_info = content_info;
// 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.wbBanner = null;
this.checkBannerToId = 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) * @desc Initializes the replay iframe. If a banner exists (exposed on window as WBBanner)
* then the init function of the banner is called. * then the init function of the banner is called.
*/ */
ContentFrame.prototype.init_iframe = function () { ContentFrame.prototype.init_iframe = function() {
if (typeof (this.content_info.iframe) === 'string') { if (typeof this.content_info.iframe === 'string') {
this.iframe = document.querySelector(this.content_info.iframe); this.iframe = document.querySelector(this.content_info.iframe);
} else { } else {
this.iframe = this.content_info.iframe; this.iframe = this.content_info.iframe;
} }
if (!this.iframe) { if (!this.iframe) {
console.warn('no iframe found ' + this.content_info.iframe + ' found'); console.warn('no iframe found ' + this.content_info.iframe + ' found');
return; return;
} }
this.extract_prefix(); this.extract_prefix();
if (window.WBBanner) { if (window.WBBanner) {
this.wbBanner = window.WBBanner; this.wbBanner = window.WBBanner;
this.wbBanner.init(); this.wbBanner.init();
} }
this.load_url(this.content_info.url, this.content_info.request_ts); this.load_url(this.content_info.url, this.content_info.request_ts);
}; };
/** /**
* @desc Initializes the prefixes used to load the pages to be replayed * @desc Initializes the prefixes used to load the pages to be replayed
*/ */
ContentFrame.prototype.extract_prefix = function () { ContentFrame.prototype.extract_prefix = function() {
this.app_prefix = this.content_info.app_prefix || this.content_info.prefix; this.app_prefix = this.content_info.app_prefix || this.content_info.prefix;
this.content_prefix = this.content_info.content_prefix || this.content_info.prefix; this.content_prefix =
this.content_info.content_prefix || this.content_info.prefix;
if (this.app_prefix && this.content_prefix) { if (this.app_prefix && this.content_prefix) {
return; 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!');
}
} }
}
var inx = window.location.href.indexOf(this.content_info.url); this.prefix = window.location.href.substr(0, inx);
if (inx < 0) { this.app_prefix = this.app_prefix || this.prefix;
inx = window.location.href.indexOf('/http') + 1; this.content_prefix = this.content_prefix || this.prefix;
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;
}; };
/** /**
@ -109,46 +111,46 @@ ContentFrame.prototype.extract_prefix = function () {
* @param {?boolean} content_url - Is the abs URL to be constructed using the content_prefix or app_prefix * @param {?boolean} content_url - Is the abs URL to be constructed using the content_prefix or app_prefix
* @returns {string} * @returns {string}
*/ */
ContentFrame.prototype.make_url = function (url, ts, content_url) { ContentFrame.prototype.make_url = function(url, ts, content_url) {
var mod, prefix; var mod, prefix;
if (content_url) { if (content_url) {
mod = 'mp_'; mod = 'mp_';
prefix = this.content_prefix; prefix = this.content_prefix;
} else { } else {
mod = ''; mod = '';
prefix = this.app_prefix; prefix = this.app_prefix;
} }
if (ts || mod) { if (ts || mod) {
mod += '/'; mod += '/';
} }
if (ts) { if (ts) {
return prefix + ts + mod + url; return prefix + ts + mod + url;
} else { } else {
return prefix + mod + url; return prefix + mod + url;
} }
}; };
/** /**
* @desc Handles and routes all messages received from the replay iframe. * @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 * @param {MessageEvent} event - A message event potentially containing a message from the replay iframe
*/ */
ContentFrame.prototype.handle_event = function (event) { ContentFrame.prototype.handle_event = function(event) {
var frame_win = this.iframe.contentWindow; var frame_win = this.iframe.contentWindow;
if (event.source === window.parent) { if (event.source === window.parent) {
// Pass to replay frame // Pass to replay frame
frame_win.postMessage(event.data, '*'); frame_win.postMessage(event.data, '*');
} else if (event.source === frame_win) { } else if (event.source === frame_win) {
// Check if iframe url change message // Check if iframe url change message
if (typeof (event.data) === 'object' && event.data['wb_type']) { if (typeof event.data === 'object' && event.data['wb_type']) {
this.handle_message(event); this.handle_message(event);
} else { } else {
// Pass to parent // Pass to parent
window.parent.postMessage(event.data, '*'); window.parent.postMessage(event.data, '*');
}
} }
}
}; };
/** /**
@ -156,33 +158,36 @@ ContentFrame.prototype.handle_event = function (event) {
* is exposed, calls the onMessage function of the exposed banner. * is exposed, calls the onMessage function of the exposed banner.
* @param {MessageEvent} event - The message event containing a message from the replay iframe * @param {MessageEvent} event - The message event containing a message from the replay iframe
*/ */
ContentFrame.prototype.handle_message = function (event) { ContentFrame.prototype.handle_message = function(event) {
if (this.wbBanner) { if (this.wbBanner) {
this.wbBanner.onMessage(event); this.wbBanner.onMessage(event);
} }
var state = event.data; var state = event.data;
var type = state.wb_type; var type = state.wb_type;
if (type === 'load' || type === 'replace-url') { if (type === 'load' || type === 'replace-url') {
this.set_url(state); this.set_url(state);
} else if (type === 'hashchange') { } else if (type === 'hashchange') {
this.inner_hash_changed(state); this.inner_hash_changed(state);
} }
}; };
/** /**
* @desc Updates the URL of the top frame * @desc Updates the URL of the top frame
* @param {Object} state - The contents of a message rreceived from the replay iframe * @param {Object} state - The contents of a message rreceived from the replay iframe
*/ */
ContentFrame.prototype.set_url = function (state) { ContentFrame.prototype.set_url = function(state) {
if (state.url && (state.url !== this.last_url || state.request_ts !== this.last_ts)) { if (
var new_url = this.make_url(state.url, state.request_ts, false); 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); window.history.replaceState(state, '', new_url);
this.last_url = state.url; this.last_url = state.url;
this.last_ts = state.request_ts; this.last_ts = state.request_ts;
} }
}; };
/** /**
@ -194,29 +199,28 @@ ContentFrame.prototype.set_url = function (state) {
* @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if * @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if
* operating in live mode * operating in live mode
*/ */
ContentFrame.prototype.initBannerUpdateCheck = function (newUrl, newTs) { ContentFrame.prototype.initBannerUpdateCheck = function(newUrl, newTs) {
if (!this.wbBanner) return; if (!this.wbBanner) return;
var contentFrame = this; var contentFrame = this;
var replayIframeLoaded = function () { var replayIframeLoaded = function() {
contentFrame.iframe.removeEventListener('load', replayIframeLoaded); contentFrame.iframe.removeEventListener('load', replayIframeLoaded);
contentFrame.checkBannerToId = setTimeout(function () { contentFrame.checkBannerToId = setTimeout(function() {
contentFrame.checkBannerToId = null; contentFrame.checkBannerToId = null;
if (contentFrame.wbBanner.stillIndicatesLoading()) { if (contentFrame.wbBanner.stillIndicatesLoading()) {
contentFrame.wbBanner.updateCaptureInfo( contentFrame.wbBanner.updateCaptureInfo(
newUrl, newUrl,
newTs, newTs,
contentFrame.content_prefix.indexOf('/live') !== -1 contentFrame.content_prefix.indexOf('/live') !== -1
); );
} }
}, 2000); }, 2000);
}; };
if (this.checkBannerToId) { if (this.checkBannerToId) {
clearTimeout(this.checkBannerToId); clearTimeout(this.checkBannerToId);
} }
this.iframe.addEventListener('load', replayIframeLoaded); this.iframe.addEventListener('load', replayIframeLoaded);
}; };
/** /**
* @desc Navigates the replay iframe to a newURL and if a banner is exposed * @desc Navigates the replay iframe to a newURL and if a banner is exposed
* the initBannerUpdateCheck function is called. * the initBannerUpdateCheck function is called.
@ -224,44 +228,44 @@ ContentFrame.prototype.initBannerUpdateCheck = function (newUrl, newTs) {
* @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if * @param {?string} newTs - The new timestamp of the replay iframe. Is falsy if
* operating in live mode * operating in live mode
*/ */
ContentFrame.prototype.load_url = function (newUrl, newTs) { ContentFrame.prototype.load_url = function(newUrl, newTs) {
this.iframe.src = this.make_url(newUrl, newTs, true); this.iframe.src = this.make_url(newUrl, newTs, true);
if (this.wbBanner) { if (this.wbBanner) {
this.initBannerUpdateCheck(newUrl, newTs); this.initBannerUpdateCheck(newUrl, newTs);
} }
}; };
/** /**
* @desc Updates this frames hash to the one inside the replay iframe * @desc Updates this frames hash to the one inside the replay iframe
* @param {Object} state - The contents of message received from the replay iframe * @param {Object} state - The contents of message received from the replay iframe
*/ */
ContentFrame.prototype.inner_hash_changed = function (state) { ContentFrame.prototype.inner_hash_changed = function(state) {
if (window.location.hash !== state.hash) { if (window.location.hash !== state.hash) {
window.location.hash = state.hash; window.location.hash = state.hash;
} }
this.last_inner_hash = state.hash; this.last_inner_hash = state.hash;
}; };
/** /**
* @desc Updates the hash of the replay iframe on a hash change in this frame * @desc Updates the hash of the replay iframe on a hash change in this frame
* @param event * @param event
*/ */
ContentFrame.prototype.outer_hash_changed = function (event) { ContentFrame.prototype.outer_hash_changed = function(event) {
if (window.location.hash === this.last_inner_hash) { if (window.location.hash === this.last_inner_hash) {
return; return;
} }
if (this.iframe) { if (this.iframe) {
var message = {'wb_type': 'outer_hashchange', 'hash': window.location.hash}; var message = { wb_type: 'outer_hashchange', hash: window.location.hash };
this.iframe.contentWindow.postMessage(message, '*', undefined, true); this.iframe.contentWindow.postMessage(message, '*', undefined, true);
} }
}; };
/** /**
* @desc Cleans up any event listeners added by the content frame * @desc Cleans up any event listeners added by the content frame
*/ */
ContentFrame.prototype.close = function () { ContentFrame.prototype.close = function() {
window.removeEventListener('hashchange', this.outer_hash_changed); window.removeEventListener('hashchange', this.outer_hash_changed);
window.removeEventListener('message', this.handle_event); window.removeEventListener('message', this.handle_event);
}; };