From 69f7f0200623aa9c81bfb69784989bd9baf3f21c Mon Sep 17 00:00:00 2001 From: John Berlin Date: Wed, 4 Sep 2019 14:59:50 -0400 Subject: [PATCH] static files: - re-formatted: default_banner.js, queryWorker.js, search.js, wb_frame.js --- pywb/static/default_banner.js | 365 +++++++++++++++++----------------- pywb/static/queryWorker.js | 23 ++- pywb/static/search.js | 336 ++++++++++++++++--------------- pywb/static/wb_frame.js | 300 ++++++++++++++-------------- 4 files changed, 528 insertions(+), 496 deletions(-) diff --git a/pywb/static/default_banner.js b/pywb/static/default_banner.js index 5def7ff2..58b43c2a 100644 --- a/pywb/static/default_banner.js +++ b/pywb/static/default_banner.js @@ -20,200 +20,211 @@ This file is part of pywb, https://github.com/webrecorder/pywb // Creates the default pywb banner. -(function () { - if (window.top !== window) { - return; +(function() { + 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; } - /** - * 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); + // 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); + } + } } - // Functions required to be exposed by all banners + this.set_banner( + this.state.url, + this.state.ts, + this.state.is_live, + this.title + ); + }; - /** - * @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'); - } - }; + // Functions internal to the default 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; - }; + /** + * @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 = + 'Loading...'; + 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 - * @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 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) { + return ''; + } - /** - * @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 (ts.length < 14) { + ts += '00000000000000'.substr(ts.length); + } - 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; - } + var datestr = + ts.substring(0, 4) + + '-' + + ts.substring(4, 6) + + '-' + + ts.substring(6, 8) + + 'T' + + ts.substring(8, 10) + + ':' + + ts.substring(10, 12) + + ':' + + ts.substring(12, 14) + + '-00:00'; - // 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]); - } + var date = new Date(datestr); - 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); - } - } - } + if (is_gmt) { + return date.toGMTString(); + } else { + return date.toLocaleString(); + } + }; - 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; + } - /** - * @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 = 'Loading...'; - this.captureInfo.id = '_wb_capture_info'; - this.banner.appendChild(this.captureInfo); - document.body.insertBefore(this.banner, document.body.firstChild); - }; + var date_str = this.ts_to_date(ts, true); - /** - * @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) { - return ""; - } + if (title) { + capture_str = title; + } else { + capture_str = url; + } - if (ts.length < 14) { - ts += "00000000000000".substr(ts.length); - } + title_str = capture_str; + capture_str = "" + capture_str + ''; - var datestr = (ts.substring(0, 4) + "-" + - ts.substring(4, 6) + "-" + - ts.substring(6, 8) + "T" + - ts.substring(8, 10) + ":" + - ts.substring(10, 12) + ":" + - ts.substring(12, 14) + "-00:00"); + if (is_live) { + title_str = ' pywb Live: ' + title_str; + capture_str += 'Live on '; + } else { + title_str += 'pywb Archived: ' + title_str; + capture_str += 'Archived on '; + } - 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) { - return date.toGMTString(); - } 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 = "" + capture_str + ""; - - if (is_live) { - title_str = " pywb Live: " + title_str; - capture_str += "Live on "; - } else { - title_str += "pywb Archived: " + title_str; - capture_str += "Archived on "; - } - - 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(); + // all banners will expose themselves by adding themselves as WBBanner on window + window.WBBanner = new DefaultBanner(); })(); - - diff --git a/pywb/static/queryWorker.js b/pywb/static/queryWorker.js index 464390d0..4e5cc04c 100644 --- a/pywb/static/queryWorker.js +++ b/pywb/static/queryWorker.js @@ -18,7 +18,7 @@ var decoder = new TextDecoder('utf-8'); */ var bufferedPreviousChunk = null; -self.onmessage = function (event) { +self.onmessage = function(event) { var data = event.data; if (data.type === 'query') { fetch(data.queryURL) @@ -42,7 +42,8 @@ function defaultErrorCatcher(error) { */ function consumeResponseBodyAsStream(response) { var reader = response.body.getReader(); - reader.read() + reader + .read() .then(function consumeStream(result) { if (result.done) { if (bufferedPreviousChunk) { @@ -58,7 +59,10 @@ function consumeResponseBodyAsStream(response) { return; } transformChunk(result.value); - reader.read().then(consumeStream).catch(defaultErrorCatcher); + reader + .read() + .then(consumeStream) + .catch(defaultErrorCatcher); }) .catch(defaultErrorCatcher); } @@ -154,8 +158,11 @@ function handleCDXRecord(binaryCDXRecord) { year: ts.substring(0, 4), month: ts.substring(4, 6), day: day.charAt(0) === '0' ? day.charAt(1) : day, - time: ts.substring(8, 10) + colon + - ts.substring(10, 12) + colon + + time: + ts.substring(8, 10) + + colon + + ts.substring(10, 12) + + colon + ts.substring(12, 14) }, wasError: false, @@ -163,9 +170,3 @@ function handleCDXRecord(binaryCDXRecord) { recordCountFormatted: recordCount.toLocaleString() }); } - - - - - - diff --git a/pywb/static/search.js b/pywb/static/search.js index 99f7381c..92bae699 100644 --- a/pywb/static/search.js +++ b/pywb/static/search.js @@ -1,175 +1,191 @@ - var dtRE = /^\d{4,14}$/; - var didSetWasValidated = false; - var showBadDateTimeClass = 'show-optional-bad-input'; - var filterMods = { - '=': 'Contains', - '==': 'Matches Exactly', - '=~': 'Matches Regex', - '=!': 'Does Not Contains', - '=!=': 'Is Not', - '=!~': 'Does Not Begins With' - }; +var dtRE = /^\d{4,14}$/; +var didSetWasValidated = false; +var showBadDateTimeClass = 'show-optional-bad-input'; +var filterMods = { + '=': 'Contains', + '==': 'Matches Exactly', + '=~': 'Matches Regex', + '=!': 'Does Not Contains', + '=!=': 'Is Not', + '=!~': 'Does Not Begins With' +}; - var elemIds = { - filtering: { - by: 'filter-by', - modifier: 'filter-modifier', - expression: 'filter-expression', - list: 'filter-list', - nothing: 'filtering-nothing', - add: 'add-filter', - clear: 'clear-filters' - }, - dateTime: { - from: 'dt-from', - fromBad: 'dt-from-bad', - to: 'dt-to', - toBad: 'dt-to-bad' - }, - match: 'match-type-select', - url: 'search-url', - form: 'search-form', - resultsNewWindow: 'open-results-new-window' - }; +var elemIds = { + filtering: { + by: 'filter-by', + modifier: 'filter-modifier', + expression: 'filter-expression', + list: 'filter-list', + nothing: 'filtering-nothing', + add: 'add-filter', + clear: 'clear-filters' + }, + dateTime: { + from: 'dt-from', + fromBad: 'dt-from-bad', + to: 'dt-to', + toBad: 'dt-to-bad' + }, + match: 'match-type-select', + url: 'search-url', + form: 'search-form', + resultsNewWindow: 'open-results-new-window' +}; - - function makeCheckDateRangeChecker(dtInputId, dtBadNotice) { - var dtInput = document.getElementById(dtInputId); - dtInput.onblur = function () { - if (dtInput.validity.valid && dtBadNotice.classList.contains(showBadDateTimeClass)) { - return dtBadNotice.classList.remove(showBadDateTimeClass); - } - if (dtInput.validity.valueMissing) { - if (dtBadNotice.classList.contains(showBadDateTimeClass)) { - dtBadNotice.classList.remove(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)) { +function makeCheckDateRangeChecker(dtInputId, dtBadNotice) { + var dtInput = document.getElementById(dtInputId); + dtInput.onblur = function() { + if ( + dtInput.validity.valid && + dtBadNotice.classList.contains(showBadDateTimeClass) + ) { + return dtBadNotice.classList.remove(showBadDateTimeClass); + } + if (dtInput.validity.valueMissing) { + if (dtBadNotice.classList.contains(showBadDateTimeClass)) { dtBadNotice.classList.remove(showBadDateTimeClass); - } else if (!validInput) { + } + return; + } + if (dtInput.validity.badInput) { + if (!dtBadNotice.classList.contains(showBadDateTimeClass)) { dtBadNotice.classList.add(showBadDateTimeClass); } - }; - } - - 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); + return; } - 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 = '×'; - 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); + var validInput = dtRE.test(dtInput.value); + if (validInput && dtBadNotice.classList.contains(showBadDateTimeClass)) { + dtBadNotice.classList.remove(showBadDateTimeClass); + } else if (!validInput) { + dtBadNotice.classList.add(showBadDateTimeClass); } - 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()); - } +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'); + 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 = '×'; + 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) { - query.push('matchType=' + matchType.trim()); - } - var fromT = document.getElementById(elemIds.dateTime.from).value; - if (fromT) { - query.push('from=' + fromT.trim()); - } - var toT = document.getElementById(elemIds.dateTime.to).value; - if (toT) { - query.push('to=' + toT.trim()); - } - var builtQuery = query.join('&'); - if (document.getElementById(elemIds.resultsNewWindow).checked) { - try { - var win = window.open(builtQuery); - win.focus(); - } catch (e) { - document.location.href = builtQuery; - } - } else { + } + var matchType = document.getElementById(elemIds.match).value; + if (matchType) { + query.push('matchType=' + matchType.trim()); + } + var fromT = document.getElementById(elemIds.dateTime.from).value; + if (fromT) { + query.push('from=' + fromT.trim()); + } + var toT = document.getElementById(elemIds.dateTime.to).value; + if (toT) { + query.push('to=' + toT.trim()); + } + var builtQuery = query.join('&'); + if (document.getElementById(elemIds.resultsNewWindow).checked) { + try { + var win = window.open(builtQuery); + win.focus(); + } catch (e) { document.location.href = builtQuery; } + } else { + document.location.href = builtQuery; } +} - $(document).ready(function () { - $('[data-toggle="tooltip"]').tooltip({ - container: 'body', - 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); - }); +$(document).ready(function() { + $('[data-toggle="tooltip"]').tooltip({ + container: 'body', + 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); + }); +}); diff --git a/pywb/static/wb_frame.js b/pywb/static/wb_frame.js index e72f2c26..8a9f92da 100644 --- a/pywb/static/wb_frame.js +++ b/pywb/static/wb_frame.js @@ -17,88 +17,90 @@ This file is part of pywb, https://github.com/webrecorder/pywb along with pywb. If not, see . */ - /** * @param {Object} content_info - Information about the contents to be replayed */ function ContentFrame(content_info) { - if (!(this instanceof ContentFrame)) return new ContentFrame(content_info); - this.last_inner_hash = window.location.hash; - this.last_url = content_info.url; - this.last_ts = content_info.request_ts; - this.content_info = content_info; - // bind event callbacks - this.outer_hash_changed = this.outer_hash_changed.bind(this); - this.handle_event = this.handle_event.bind(this); - this.wbBanner = null; - this.checkBannerToId = null; + if (!(this instanceof ContentFrame)) return new ContentFrame(content_info); + this.last_inner_hash = window.location.hash; + this.last_url = content_info.url; + this.last_ts = content_info.request_ts; + this.content_info = content_info; + // bind event callbacks + this.outer_hash_changed = this.outer_hash_changed.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('message', this.handle_event); + window.addEventListener('hashchange', this.outer_hash_changed, false); + window.addEventListener('message', this.handle_event); - if (document.readyState === 'complete') { - this.init_iframe(); - } else { - document.addEventListener('DOMContentLoaded', this.init_iframe.bind(this), {once: true}); - } + if (document.readyState === 'complete') { + this.init_iframe(); + } else { + document.addEventListener('DOMContentLoaded', this.init_iframe.bind(this), { + once: true + }); + } - window.__WB_pmw = function (win) { - this.pm_source = win; - return this; - }; + window.__WB_pmw = function(win) { + this.pm_source = win; + 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; - } +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; - } + 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); + 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; +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; + 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!'); + } } + } - var inx = window.location.href.indexOf(this.content_info.url); + this.prefix = window.location.href.substr(0, inx); - 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.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 * @returns {string} */ -ContentFrame.prototype.make_url = function (url, ts, content_url) { - var mod, prefix; +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 (content_url) { + mod = 'mp_'; + prefix = this.content_prefix; + } else { + mod = ''; + prefix = this.app_prefix; + } - if (ts || mod) { - mod += '/'; - } + if (ts || mod) { + mod += '/'; + } - if (ts) { - return prefix + ts + mod + url; - } else { - return prefix + mod + url; - } + 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, '*'); - } +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, '*'); } + } }; /** @@ -156,33 +158,36 @@ ContentFrame.prototype.handle_event = function (event) { * 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; +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); - } + 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); +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); + window.history.replaceState(state, '', new_url); - this.last_url = state.url; - this.last_ts = state.request_ts; - } + this.last_url = state.url; + 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 * 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); +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. @@ -224,44 +228,44 @@ ContentFrame.prototype.initBannerUpdateCheck = function (newUrl, newTs) { * @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); - } +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; +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; - } +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}; + if (this.iframe) { + 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 */ -ContentFrame.prototype.close = function () { - window.removeEventListener('hashchange', this.outer_hash_changed); - window.removeEventListener('message', this.handle_event); +ContentFrame.prototype.close = function() { + window.removeEventListener('hashchange', this.outer_hash_changed); + window.removeEventListener('message', this.handle_event); };