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

Frame insert refactor (#246)

refactor frame/head insert templates:
ContentFrame:
- content iframe inited with new ContentFrame() which creates iframe
- wb_frame.js: contains ContentFrame system for initing, updating, closing content frame for replayed content.
- wb_frame.js: supports 'app_prefix' and 'content_prefix' or default 'prefix' for replay content
- window.location.hash passed added to init url.
- frame insert and head insert: simplify, remove 'wbrequest'
- frame insert: global wbinfo object no longer needed in top frame, each ContentFrame self-contained.
- wombat.js: next_parent() check does not assume wbinfo is present in top frame
- vidrw.js: only init if wbinfo is present

Banner:
- wb.js no longer needed, frame check/redirect folded into wombat.js
- default banner self-contained in default_banner.js/default_banner.css, handles both frame and frameless case
- rename wb.css -> default_banner.css
- banner html passed in as 'banner_html' variable to be optionally included, supports per collection banner html.
- templateview: BaseInsertView can accept an option 'banner view', used by HeadInsertView and TopFrameView

Tests:
- tests: test_auto_colls uses shared app to test dynamic changes, testing both frame and non-frame access, added per-collection banner html check.
This commit is contained in:
Ilya Kreymer 2017-09-30 21:09:38 -07:00 committed by GitHub
parent d533e5345a
commit aa0a019567
17 changed files with 401 additions and 541 deletions

View File

@ -71,13 +71,15 @@ class RewriterApp(object):
self.jinja_env = jinja_env
self.banner_view = BaseInsertView(self.jinja_env, self._html_templ('banner_html'))
self.head_insert_view = HeadInsertView(self.jinja_env,
self._html_templ('head_insert_html'),
self._html_templ('banner_html'))
self.banner_view)
self.frame_insert_view = TopFrameView(self.jinja_env,
self._html_templ('frame_insert_html'),
self._html_templ('banner_html'))
self.banner_view)
self.error_view = BaseInsertView(self.jinja_env, self._html_templ('error_html'))
self.not_found_view = BaseInsertView(self.jinja_env, self._html_templ('not_found_html'))

View File

@ -28,15 +28,13 @@ class TestRewriterApp(FakeRedisTests, BaseTestClass):
assert '"http://localhost:80/live/mp_/http://www.iana.org/domains/example"' in resp.text
assert 'wbinfo.url = "http://example.com/"'
assert '"http://example.com/"'
def test_top_frame(self):
resp = self.testapp.get('/live/http://example.com/')
resp.charset = 'utf-8'
assert '"http://localhost:80/live/mp_/http://example.com/"' in resp.text
assert 'wbinfo.capture_url = "http://example.com/"' in resp.text
assert '"http://example.com/"' in resp.text
#def test_cookie_track_1(self):
# resp = self.testapp.get('/live/mp_/https://twitter.com/')

View File

@ -109,10 +109,10 @@ class JinjaEnv(object):
# ============================================================================
class BaseInsertView(object):
def __init__(self, jenv, insert_file, banner_file=''):
def __init__(self, jenv, insert_file, banner_view=None):
self.jenv = jenv
self.insert_file = insert_file
self.banner_file = banner_file
self.banner_view = banner_view
def render_to_string(self, env, **kwargs):
template = None
@ -134,6 +134,7 @@ class BaseInsertView(object):
params = env.get('webrec.template_params')
if params:
kwargs.update(params)
kwargs['env'] = env
return template.render(**kwargs)
@ -149,27 +150,25 @@ class HeadInsertView(BaseInsertView):
coll='',
include_ts=True):
url = wb_url.get_url()
include_wombat = not wb_url.is_banner_only
wbrequest = {'host_prefix': host_prefix,
'wb_prefix': wb_prefix,
'wb_url': wb_url,
'coll': coll,
'env': env,
'options': {'is_framed': is_framed},
'rewrite_opts': {}
}
params = {'host_prefix': host_prefix,
'wb_prefix': wb_prefix,
'wb_url': wb_url,
'coll': coll,
'is_framed': 'true' if is_framed else 'false',
'top_url': top_url,
}
def make_head_insert(rule, cdx):
return (self.render_to_string(env, wbrequest=wbrequest,
cdx=cdx,
top_url=top_url,
include_ts=include_ts,
include_wombat=include_wombat,
banner_html=self.banner_file,
rule=rule))
params['wombat_ts'] = cdx['timestamp'] if include_ts else ''
params['wombat_sec'] = timestamp_to_sec(cdx['timestamp'])
params['is_live'] = 'true' if cdx.get('is_live') else 'false'
if self.banner_view:
banner_html = self.banner_view.render_to_string(env, cdx=cdx, **params)
params['banner_html'] = banner_html
return self.render_to_string(env, cdx=cdx, **params)
return make_head_insert
@ -198,25 +197,27 @@ class TopFrameView(BaseInsertView):
else:
iframe_url = wb_prefix + embed_url
wbrequest = {'host_prefix': host_prefix,
'wb_prefix': wb_prefix,
'wb_url': wb_url,
'coll': coll,
params = {'host_prefix': host_prefix,
'wb_prefix': wb_prefix,
'wb_url': wb_url,
'coll': coll,
'options': {'frame_mod': frame_mod,
'replay_mod': replay_mod},
}
'options': {'frame_mod': frame_mod,
'replay_mod': replay_mod},
params = dict(embed_url=embed_url,
iframe_url=iframe_url,
wbrequest=wbrequest,
timestamp=timestamp,
url=wb_url.get_url(),
banner_html=self.banner_file)
'embed_url': embed_url,
'iframe_url': iframe_url,
'timestamp': timestamp,
'url': wb_url.get_url()
}
if extra_params:
params.update(extra_params)
if self.banner_view:
banner_html = self.banner_view.render_to_string(env, **params)
params['banner_html'] = banner_html
return self.render_to_string(env, **params)

View File

@ -28,7 +28,7 @@
height: 40px !important;
}
.wb_iframe_div
#wb_iframe_div
{
position: absolute;
width: 100%;

View File

@ -19,43 +19,85 @@ This file is part of pywb, https://github.com/ikreymer/pywb
*/
// Creates the default pywb banner.
// Override this function/script to create a different type of banner
(function() {
function ts_to_date(ts, is_gmt) {
if (!ts) {
return "";
}
_wb_js.create_banner_element = function(banner_id)
{
if (ts.length < 14) {
ts += "00000000000000".substr(ts.length);
}
var banner = document.createElement("wb_div", true);
banner.setAttribute("id", banner_id);
banner.setAttribute("lang", "en");
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");
var text;
var date = new Date(datestr);
if (wbinfo.is_frame) {
text = _wb_js.banner_labels.LOADING_MSG;
} else if (wbinfo.is_live) {
text = _wb_js.banner_labels.LIVE_MSG;
} else {
text = _wb_js.banner_labels.REPLAY_MSG;
}
text = "<span id='_wb_label'>" + text + "</span>";
var capture_str = "";
if (wbinfo && wbinfo.timestamp) {
capture_str = _wb_js.ts_to_date(wbinfo.timestamp, true);
if (is_gmt) {
return date.toGMTString();
} else {
return date.toLocaleString();
}
}
text += "<b id='_wb_capture_info'>" + capture_str + "</b>";
function init(bid) {
var banner = document.createElement("wb_div", true);
if (wbinfo.proxy_magic && wbinfo.url) {
var select_url = wbinfo.proxy_magic + "/" + wbinfo.url;
var query_url = wbinfo.proxy_magic + "/*/" + wbinfo.url;
text += '&nbsp;<a href="//query.' + query_url + '">All Capture Times</a>';
text += '<br/>'
text += 'From collection <b>"' + wbinfo.coll + '"</b>&nbsp;<a href="//select.' + select_url + '">All Collections</a>';
banner.setAttribute("id", bid);
banner.setAttribute("lang", "en");
var text = "<span id='_wb_capture_info'>Loading...</span>";
banner.innerHTML = text;
document.body.insertBefore(banner, document.body.firstChild);
}
banner.innerHTML = text;
document.body.insertBefore(banner, document.body.firstChild);
}
function set_banner(url, ts, is_live) {
var capture_str;
if (!ts) {
return;
}
if (is_live) {
capture_str = "This is a <b>live</b> page from ";
} else {
capture_str = "This is an <b>archived</b> page from ";
}
capture_str += ts_to_date(ts, true);
document.querySelector("#_wb_capture_info").innerHTML = capture_str;
}
if (window.top != window) {
return;
}
window.addEventListener("load", function() {
if (window.wbinfo) {
init("_wb_plain_banner");
set_banner(window.wbinfo.url,
window.wbinfo.timestamp,
window.wbinfo.is_live);
} else {
init("_wb_frame_top_banner");
window.addEventListener("message", function(event) {
var state = event.data;
if (state.wb_type) {
set_banner(state.url, state.ts, state.is_live);
}
});
}
});
})();

View File

@ -34,7 +34,7 @@ if (window.location.hash) {
if (_pywbvid == "html" || _pywbvid == "flash") {
var YT_W_E_RX = /^(https?:\/\/.*youtube.com)\/(watch|embed).*$/;
if (wbinfo.url.match(YT_W_E_RX)) {
if (window.wbinfo && window.wbinfo.url.match(YT_W_E_RX)) {
// special case: prevent yt player from being inited
Object.defineProperty(window, 'yt', {writeable: false});
Object.defineProperty(window, 'ytplayer', {writeable: false});
@ -45,6 +45,10 @@ if (window.location.hash) {
__wbvidrw = (function() {
if (!window.wbinfo) {
return;
}
var checked_embeds = false;
var FLASH_PLAYER = wbinfo.static_prefix + "/flowplayer/flowplayer-3.2.18.swf";

View File

@ -1,176 +0,0 @@
/*
Copyright(c) 2013-2014 Ilya Kreymer. Released under the GNU General Public License.
This file is part of pywb, https://github.com/ikreymer/pywb
pywb is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
pywb is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with pywb. If not, see <http://www.gnu.org/licenses/>.
*/
function __WbJsInit() {
var bid = undefined;
function init_banner() {
var PLAIN_BANNER_ID = "_wb_plain_banner";
var FRAME_BANNER_ID = "_wb_frame_top_banner";
if (!window.wbinfo || (window.__WB_replay_top && window != window.__WB_replay_top)) {
return;
}
if (!bid) {
if (wbinfo.is_frame) {
bid = FRAME_BANNER_ID;
} else {
bid = PLAIN_BANNER_ID;
}
}
if (!document || !document.body) {
return;
}
if (document.getElementById(bid) != null) {
return;
}
var res = _wb_js.create_banner_element(bid);
if (res) {
bid = res;
}
}
this.banner_labels = {LOADING_MSG: "Loading...",
REPLAY_MSG: "This is an <b>archived</b> page from ",
LIVE_MSG: "This is a <b>live</b> page loaded on "};
this.create_banner_element = function() {
// No banner by default
return null;
}
this.ts_to_date = function(ts, is_gmt)
{
if (!ts) {
return "";
}
if (ts.length < 14) {
ts += "00000000000000".substr(ts.length);
}
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");
var date = new Date(datestr);
if (is_gmt) {
return date.toGMTString();
} else {
return date.toLocaleString();
}
}
function add_event(name, func, object) {
if (object.addEventListener) {
object.addEventListener(name, func);
return true;
} else if (object.attachEvent) {
object.attachEvent("on" + name, func);
return true;
} else {
return false;
}
}
function remove_event(name, func, object) {
if (object.removeEventListener) {
object.removeEventListener(name, func);
return true;
} else if (object.detachEvent) {
object.detachEvent("on" + name, func);
return true;
} else {
return false;
}
}
function notify_top(event) {
if (!window.__WB_top_frame) {
return;
}
if (!window.WB_wombat_location) {
return;
}
if (typeof(window.WB_wombat_location.href) != "string") {
return;
}
var message = {
"url": window.WB_wombat_location.href,
"ts": wbinfo.timestamp,
"request_ts": wbinfo.request_ts,
"is_live": wbinfo.is_live,
"title": document ? document.title : "",
"readyState": document.readyState,
"wb_type": "load",
}
window.__WB_top_frame.postMessage(message, "*");
//remove_event("readystatechange", notify_top, document);
}
this.load = function() {
if (window._wb_js_inited) {
return;
}
window._wb_js_inited = true;
// Non-Framed Replay OR top frame for framed replay!
if (window.wbinfo && !window.__WB_top_frame) {
if (wbinfo.is_framed && wbinfo.mod != "bn_") {
var hash = window.location.hash;
var loc = window.location.href.replace(window.location.hash, "");
loc = decodeURI(loc);
var top_url = wbinfo.top_url;
if (wbinfo.top_url && (loc != decodeURI(wbinfo.top_url))) {
// Auto-redirect to top frame
window.location.replace(wbinfo.top_url + hash);
return;
}
}
// Init Banner (no frame or top frame)
add_event("readystatechange", init_banner, document);
// Framed Replay
} else if (window.__WB_top_frame) {
add_event("readystatechange", notify_top, document);
}
}
};
_wb_js = new __WbJsInit();

View File

@ -17,100 +17,80 @@ This file is part of pywb, https://github.com/ikreymer/pywb
along with pywb. If not, see <http://www.gnu.org/licenses/>.
*/
var LIVE_COOKIE_REGEX = /pywb.timestamp=([\d]{1,14})/;
var TS_REGEX = /\/([\d]{1,14})(?:\w+_)?\/(?:\w+[:])?\/\//;
function ContentFrame(content_info) {
this.last_inner_hash = window.location.hash;
this.last_url = content_info.url;
this.last_ts = content_info.request_ts;
//var curr_state = {};
var IFRAME_ID = "replay_iframe";
var last_inner_hash = undefined;
function make_url(url, ts, mod, prefix)
{
if (ts || mod) {
mod += "/";
}
prefix = prefix || wbinfo.prefix;
if (ts) {
return prefix + ts + mod + url;
} else {
return prefix + mod + url;
}
}
function pop_state(state) {
set_state(state);
}
function extract_ts(url)
{
var result = url.match(TS_REGEX);
if (!result) {
return "";
}
return result[1];
}
function extract_replay_url(url) {
var inx = url.indexOf("/http:");
if (inx < 0) {
inx = url.indexOf("/https:");
if (inx < 0) {
return "";
}
}
return url.substring(inx + 1);
}
function set_state(state) {
var capture_info = document.getElementById("_wb_capture_info");
if (capture_info) {
capture_info.innerHTML = state.capture_str;
}
var label = document.getElementById("_wb_label");
if (label && window._wb_js) {
if (state.is_live) {
label.innerHTML = _wb_js.banner_labels.LIVE_MSG;
this.init_iframe = function() {
if (typeof(content_info.iframe) === "string") {
this.iframe = document.querySelector(content_info.iframe);
} else {
label.innerHTML = _wb_js.banner_labels.REPLAY_MSG;
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;
}
}
//curr_state = state;
}
window.onpopstate = function(event) {
var state = event.state;
if (state) {
pop_state(state);
}
}
function extract_ts_cookie(value) {
var result = value.match(LIVE_COOKIE_REGEX);
if (result) {
return result[1];
} else {
return "";
}
}
function init_pm(frame) {
if (!frame) {
return;
}
var frame_win = frame.contentWindow;
window.addEventListener("message", function(event) {
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, "*");
@ -118,101 +98,79 @@ function init_pm(frame) {
// Check if iframe url change message
if (typeof(event.data) == "object" && event.data["wb_type"]) {
handle_message(event.data);
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
this.outer_hash_changed = this.outer_hash_changed.bind(this);
this.handle_event = this.handle_event.bind(this);
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 });
}
window.__WB_pmw = function(win) {
this.pm_source = win;
return this;
}
}
function handle_message(state) {
var type = state.wb_type;
if (type == "load" || type == "replace-url") {
update_wb_url(state);
} else if (type == "hashchange") {
inner_hash_changed(state);
}
}
function update_wb_url(state) {
if (window._wb_js) {
state['capture_str'] = _wb_js.ts_to_date(state.ts, true);
}
if (!state.url) {
return;
}
var canon_url = make_url(state.url, state.request_ts, "", wbinfo.outer_prefix);
window.history.replaceState(state, "", canon_url);
set_state(state);
}
function inner_hash_changed(state) {
if (window.location.hash != state.hash) {
window.location.hash = state.hash;
}
last_inner_hash = state.hash;
}
function outer_hash_changed(event) {
if (window.location.hash == last_inner_hash) {
return;
}
var frame = document.getElementById(IFRAME_ID);
if (frame) {
var message = {"wb_type": "outer_hashchange", "hash": window.location.hash}
frame.contentWindow.postMessage(message, "*", undefined, true);
}
}
function init_hash_connect() {
var frame = document.getElementById(IFRAME_ID);
if (!frame) {
return;
}
if (window.location.hash) {
var curr_url = wbinfo.capture_url + window.location.hash;
frame.src = make_url(curr_url, wbinfo.request_ts, wbinfo.replay_mod);
last_inner_hash = window.location.hash;
//frame.location.href = make_url(curr_url, wbinfo.request_ts, wbinfo.replay_mod);
//frame.location.hash = window.location.hash;
}
if ("onhashchange" in window) {
window.addEventListener("hashchange", outer_hash_changed, false);
}
// Init Post Message connect
init_pm(frame);
}
document.addEventListener("DOMContentLoaded", init_hash_connect);
// Load Banner
if (window._wb_js) {
_wb_js.load();
}

View File

@ -3077,9 +3077,59 @@ var _WBWombat = function($wbwindow, wbinfo) {
return res;
}
if (wbinfo.is_framed && wbinfo.mod != "bn_") {
init_frame_notify();
}
return obj;
}
function init_frame_notify() {
function notify_top(event) {
if (!window.__WB_top_frame) {
var hash = window.location.hash;
//var loc = window.location.href.replace(window.location.hash, "");
//loc = decodeURI(loc);
//if (wbinfo.top_url && (loc != decodeURI(wbinfo.top_url))) {
// Auto-redirect to top frame
window.location.replace(wbinfo.top_url + hash);
//}
return;
}
if (!window.WB_wombat_location) {
return;
}
if (typeof(window.WB_wombat_location.href) != "string") {
return;
}
var message = {
"url": window.WB_wombat_location.href,
"ts": wbinfo.timestamp,
"request_ts": wbinfo.request_ts,
"is_live": wbinfo.is_live,
"title": document ? document.title : "",
"readyState": document.readyState,
"wb_type": "load"
}
window.__WB_top_frame.postMessage(message, wbinfo.top_host);
}
if (document.readyState == "complete") {
notify_top();
} else if (window.addEventListener) {
document.addEventListener("readystatechange", notify_top);
} else if (window.attachEvent) {
document.attachEvent("onreadystatechange", notify_top);
}
}
function init_top_frame($wbwindow) {
// proxy mode
if (wb_is_proxy) {
@ -3099,9 +3149,8 @@ var _WBWombat = function($wbwindow, wbinfo) {
return (win._wb_wombat != undefined);
} else {
// otherwise, ensure that it is not a top container frame
return !win.wbinfo.is_frame;
return win.wbinfo.is_framed
}
} catch (e) {
return false;
}

View File

@ -1,4 +1,4 @@
<!-- default banner, create through js -->
<script src='{{ wbrequest.host_prefix }}/{{ static_path }}/default_banner.js'> </script>
<link rel='stylesheet' href='{{ wbrequest.host_prefix }}/{{ static_path }}/wb.css'/>
<script src='{{ host_prefix }}/{{ static_path }}/default_banner.js'> </script>
<link rel='stylesheet' href='{{ host_prefix }}/{{ static_path }}/default_banner.css'/>

View File

@ -12,26 +12,21 @@ html, body
}
</style>
<script>
wbinfo = {}
wbinfo.prefix = "{{ wbrequest.wb_prefix }}";
wbinfo.capture_url = "{{ url }}";
wbinfo.timestamp = "{{ wbrequest.wb_url.timestamp }}";
wbinfo.is_frame = true;
wbinfo.frame_mod = "{{ wbrequest.options.frame_mod }}";
wbinfo.replay_mod = "{{ wbrequest.options.replay_mod }}";
</script>
<script src='{{ wbrequest.host_prefix }}/{{ static_path }}/wb.js'> </script>
<link rel='stylesheet' href='{{ wbrequest.host_prefix }}/{{ static_path }}/wb.css'/>
<link rel='stylesheet' href='{{ wbrequest.host_prefix }}/{{ static_path }}/scroll-webkit.css'/>
<script src='{{ host_prefix }}/{{ static_path }}/wb_frame.js'> </script>
{% include banner_html ignore missing %}
{{ banner_html }}
<script src='{{ wbrequest.host_prefix }}/{{ static_path }}/wb_frame.js'> </script>
</head>
<body style="margin: 0px; padding: 0px;">
<div class="wb_iframe_div">
<iframe id="replay_iframe" src="{{ iframe_url }}" onload="" frameborder="0" seamless="seamless" scrolling="yes" class="wb_iframe"></iframe>
<div id="wb_iframe_div">
<iframe id="replay_iframe" frameborder="0" seamless="seamless" scrolling="yes" class="wb_iframe"></iframe>
</div>
<script>
var cframe = new ContentFrame({"url": "{{ url }}" + window.location.hash,
"prefix": "{{ wb_prefix }}",
"request_ts": "{{ wb_url.timestamp }}",
"iframe": "#replay_iframe"});
</script>
</body>
</html>

View File

@ -1,55 +1,40 @@
<!-- WB Insert -->
<script src='{{ host_prefix }}/{{ static_path }}/wombat.js'> </script>
<script>
{% set urlsplit = cdx.url | urlsplit %}
wbinfo = {}
wbinfo.url = "{{ cdx.url }}";
wbinfo.timestamp = "{{ cdx.timestamp }}";
wbinfo.request_ts = "{{ wbrequest.wb_url.timestamp }}";
wbinfo.prefix = decodeURI("{{ wbrequest.wb_prefix }}");
wbinfo.mod = "{{ wbrequest.wb_url.mod }}";
wbinfo.request_ts = "{{ wb_url.timestamp }}";
wbinfo.prefix = decodeURI("{{ wb_prefix }}");
wbinfo.mod = "{{ wb_url.mod }}";
wbinfo.top_url = "{{ top_url }}";
wbinfo.is_framed = {{ "true" if wbrequest.options.is_framed else "false" }};
wbinfo.is_live = {{ "true" if cdx.is_live else "false" }};
wbinfo.coll = "{{ wbrequest.coll }}";
wbinfo.proxy_magic = "{{ wbrequest.env.pywb_proxy_magic }}";
wbinfo.static_prefix = "{{ wbrequest.host_prefix }}/{{ static_path }}";
</script>
wbinfo.is_framed = {{ is_framed }};
wbinfo.is_live = {{ is_live }};
wbinfo.coll = "{{ coll }}";
wbinfo.proxy_magic = "{{ env.pywb_proxy_magic }}";
wbinfo.static_prefix = "{{ host_prefix }}/{{ static_path }}";
{% if rule.js_rewrite_location != 'urls' and include_wombat %}
<script src='{{ wbrequest.host_prefix }}/{{ static_path }}/wombat.js'> </script>
<script>
{% set urlsplit = cdx.url | urlsplit %}
wbinfo.wombat_ts = "{{ wbrequest.wb_url.timestamp if include_ts else ''}}";
{% if not wb_url.is_banner_only %}
wbinfo.wombat_ts = "{{ wombat_ts }}";
wbinfo.wombat_sec = "{{ wombat_sec }}";
wbinfo.wombat_scheme = "{{ urlsplit.scheme }}";
wbinfo.wombat_host = "{{ urlsplit.netloc }}";
wbinfo.wombat_sec = "{{ cdx.timestamp | format_ts('%s') }}";
wbinfo.wombat_opts = {{ wbrequest.rewrite_opts.client | tojson if wbrequest.rewrite_opts.client else '{}' }};
wbinfo.wombat_opts = {};
if (window && window._WBWombat && !window._wb_js_inited && !window._wb_wombat) {
if (window && window._WBWombat && !window._wb_wombat) {
window._wb_wombat = new _WBWombat(window, wbinfo);
} else {
} else if (window._wb_wombat) {
window._wb_wombat.init_paths(wbinfo);
} else {
console.warn("_wb_wombat missing!");
}
{% endif %}
</script>
{% endif %}
<script src='{{ wbrequest.host_prefix }}/{{ static_path }}/wb.js'> </script>
{% include banner_html ignore missing %}
<!-- load banner -->
<script> if (window._wb_js) { _wb_js.load(); }</script>
{#
<!-- workaround for chrome iframe scrolling issue -->
{% if wbrequest.options.is_framed %}
<style type="text/css">
* {
-webkit-transform: none !important;
}
</style>
{% endif %}
#}
{{ banner_html }}
<!-- End WB Insert -->

View File

@ -1,4 +1,4 @@
from .base_config_test import CollsDirMixin
from .base_config_test import BaseConfigTest, CollsDirMixin, fmod
import os
import tempfile
@ -38,7 +38,11 @@ AUTOINDEX_FILE = 'autoindex.cdxj'
#=============================================================================
class TestManagedColls(CollsDirMixin, BaseTestClass):
class TestManagedColls(CollsDirMixin, BaseConfigTest):
@classmethod
def setup_class(cls):
super(TestManagedColls, cls).setup_class('config_test.yaml')
def _check_dirs(self, base, dirlist):
for dir_ in dirlist:
assert os.path.isdir(os.path.join(base, dir_))
@ -46,11 +50,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
def _get_sample_warc(self, name):
return os.path.join(get_test_dir(), 'warcs', name)
def _create_app(self):
config_file = 'config_test.yaml'
config_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), config_file)
self.testapp = webtest.TestApp(FrontEndApp(config_file=config_file))
@patch('pywb.apps.cli.BaseCli.run_gevent', lambda *args, **kwargs: None)
def test_run_cli(self):
""" test new wayback cli interface
@ -89,8 +88,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
main(['add', 'test', warc1])
self._create_app()
resp = self.testapp.get('/test/20140103030321/http://example.com?example=1')
def test_add_warcs_replay(self, fmod):
resp = self.get('/test/20140103030321{0}/http://example.com/?example=1', fmod)
assert resp.status_int == 200
def test_another_coll(self):
@ -102,8 +101,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
main(['add', 'foo', warc1])
self._create_app()
resp = self.testapp.get('/foo/20140103030321/http://example.com?example=1')
def test_another_coll_replay(self, fmod):
resp = self.get('/foo/20140103030321{0}/http://example.com/?example=1', fmod)
assert resp.status_int == 200
def test_add_more_warcs(self):
@ -121,9 +120,9 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
with raises(IOError):
main(['add', 'test', 'non-existent-file.warc.gz'])
def test_add_more_warcs_replay(self, fmod):
# check new cdx
self._create_app()
resp = self.testapp.get('/test/20140126200624/http://www.iana.org/')
resp = self.get('/test/20140126200624{0}/http://www.iana.org/', fmod)
assert resp.status_int == 200
def test_add_custom_nested_warcs(self):
@ -165,11 +164,11 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
assert '334' in nested_cdx_index
assert 'A/iana.warc.gz' in nested_cdx_index
self._create_app()
resp = self.testapp.get('/nested/20140126200624/http://www.iana.org/')
def test_nested_replay(self, fmod):
resp = self.get('/nested/20140126200624{0}/http://www.iana.org/', fmod)
assert resp.status_int == 200
resp = self.testapp.get('/nested/20140103030321/http://example.com?example=1')
resp = self.get('/nested/20140103030321{0}/http://example.com/?example=1', fmod)
assert resp.status_int == 200
def test_merge_vs_reindex_equality(self):
@ -202,7 +201,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
with open(a_static, 'w+b') as fh:
fh.write(b'/* Some JS File */')
self._create_app()
resp = self.testapp.get('/static/_/test/abc.js')
assert resp.status_int == 200
assert resp.content_type == 'application/javascript'
@ -217,7 +215,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
with open(a_static, 'w+b') as fh:
fh.write(b'/* Some CSS File */')
self._create_app()
resp = self.testapp.get('/static/foo.css')
assert resp.status_int == 200
assert resp.content_type == 'text/css'
@ -230,7 +227,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
"""
main(['metadata', 'foo', '--set', 'title=Collection Title'])
self._create_app()
resp = self.testapp.get('/')
assert resp.status_int == 200
assert resp.content_type == 'text/html'
@ -242,7 +238,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
resp.charset = 'utf-8'
assert '(Collection Title)' in resp.text
def test_other_metadata_search_page(self):
main(['metadata', 'foo', '--set',
'desc=Some Description Text',
@ -251,7 +246,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
with raises(ValueError):
main(['metadata', 'foo', '--set', 'name_only'])
self._create_app()
resp = self.testapp.get('/foo/')
resp.charset = 'utf-8'
assert resp.status_int == 200
@ -268,18 +262,32 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
def test_custom_template_search(self):
""" Test manually added custom search template search.html
"""
a_static = os.path.join(self.root_dir, COLLECTIONS, 'test', 'templates', 'search.html')
custom_search = os.path.join(self.root_dir, COLLECTIONS, 'test',
'templates', 'search.html')
with open(a_static, 'w+b') as fh:
with open(custom_search, 'w+b') as fh:
fh.write(b'pywb custom search page')
self._create_app()
resp = self.testapp.get('/test/')
resp.charset = 'utf-8'
assert resp.status_int == 200
assert resp.content_type == 'text/html'
assert 'pywb custom search page' in resp.text
def test_add_custom_banner(self):
""" Test adding custom banner.html per-collection template
"""
banner_file = os.path.join(self.root_dir, COLLECTIONS, 'test',
'templates', 'banner.html')
with open(banner_file, 'w+b') as fh:
fh.write(b'<div>Custom Banner Here!</div>')
def test_add_custom_banner_replay(self, fmod):
resp = self.get('/test/20140103030321/http://example.com/?example=1', fmod)
assert '<div>Custom Banner Here!</div>' in resp.text
def test_more_custom_templates(self):
"""
Test custom templates and metadata
@ -287,7 +295,7 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
Add custom metadata and test its presence in custom search page
"""
custom_search = os.path.join(self.root_dir, COLLECTIONS, 'test',
'templates', 'search.html')
'templates', 'search.html')
# add metadata
main(['metadata', 'test', '--set', 'some=value'])
@ -296,7 +304,9 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
fh.write(b'overriden search page: ')
fh.write(b'{{ metadata | tojson }}\n')
self._create_app()
# force clear of jinja env cache to reload
self.app.rewriterapp.jinja_env.jinja_env.cache = {}
resp = self.testapp.get('/test/')
resp.charset = 'utf-8'
assert resp.status_int == 200
@ -304,7 +314,8 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
assert 'overriden search page: ' in resp.text
assert '"some": "value"' in resp.text
resp = self.testapp.get('/test/20140103030321/http://example.com?example=1')
def test_more_custom_templates_replay(self, fmod):
resp = self.get('/test/20140103030321{0}/http://example.com/?example=1', fmod)
assert resp.status_int == 200
def test_add_default_coll_templates(self):
@ -334,7 +345,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
fh.seek(0)
fh.write(buf)
self._create_app()
resp = self.testapp.get('/')
resp.charset = 'utf-8'
assert resp.content_type == 'text/html'
@ -379,8 +389,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
"""
shutil.rmtree(os.path.join(self.root_dir, COLLECTIONS, 'foo', 'templates'))
self._create_app()
resp = self.testapp.get('/foo/')
resp.charset = 'utf-8'
assert resp.status_int == 200
@ -559,7 +567,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
# No Statics -- ignorable
shutil.rmtree(os.path.join(colls, 'foo', 'static'))
self._create_app()
# No WARCS
warcs_path = os.path.join(colls, 'foo', ARCHIVE_DIR)
@ -572,16 +579,10 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
cdx_path = os.path.join(colls, 'foo', INDEX_DIR)
shutil.rmtree(cdx_path)
#with raises(Exception):
# self._create_app()
# CDX a file not a dir
with open(cdx_path, 'w+b') as fh:
fh.write(b'foo\n')
#with raises(Exception):
# self._create_app()
shutil.rmtree(colls)
# No Collections to list
@ -589,7 +590,6 @@ class TestManagedColls(CollsDirMixin, BaseTestClass):
main(['list'])
# No Collections
self._create_app()
resp = self.testapp.get('/test/', status=404)
assert resp.status_int == 404

View File

@ -74,15 +74,16 @@ class TestWbIntegration(BaseConfigTest):
def test_replay_top_frame(self):
resp = self.testapp.get('/pywb/20140127171238/http://www.iana.org/')
assert '<iframe ' in resp.text
assert '/pywb/20140127171238mp_/http://www.iana.org/' in resp.text, resp.text
assert 'new ContentFrame' in resp.text
assert '"20140127171238"' in resp.text
assert 'http://www.iana.org/' in resp.text, resp.text
def test_replay_content(self, fmod):
resp = self.get('/pywb/20140127171238{0}/http://www.iana.org/', fmod)
self._assert_basic_html(resp)
assert '"20140127171238"' in resp.text, resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert 'new _WBWombat' in resp.text, resp.text
assert '/pywb/20140127171238{0}/http://www.iana.org/time-zones"'.format(fmod) in resp.text
@ -112,7 +113,7 @@ class TestWbIntegration(BaseConfigTest):
self._assert_basic_html(resp)
assert '"20140103030321"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert '/pywb-cdxj/20140103030321{0}/http://www.iana.org/domains/example'.format(fmod) in resp.text
def test_replay_cdxj_revisit(self, fmod):
@ -120,7 +121,7 @@ class TestWbIntegration(BaseConfigTest):
self._assert_basic_html(resp)
assert '"20140103030341"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert '/pywb-cdxj/20140103030341{0}/http://www.iana.org/domains/example'.format(fmod) in resp.text
def test_zero_len_revisit(self, fmod):
@ -128,7 +129,7 @@ class TestWbIntegration(BaseConfigTest):
self._assert_basic_html(resp)
assert '"20140603030341"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert '/pywb/20140603030341{0}/http://www.iana.org/domains/example'.format(fmod) in resp.text
def test_replay_url_agnostic_revisit(self, fmod):
@ -136,7 +137,7 @@ class TestWbIntegration(BaseConfigTest):
self._assert_basic_html(resp)
assert '"20130729195151"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert '/pywb/20130729195151{0}/http://www.iana.org/domains/example"'.format(fmod) in resp.text
def test_video_info_not_found(self):
@ -155,8 +156,8 @@ class TestWbIntegration(BaseConfigTest):
def test_replay_banner_only(self):
resp = self.testapp.get('/pywb/20140126201054bn_/http://www.iana.org/domains/reserved')
# wb.js header insertion
assert 'wb.js' in resp.text
# wombat.js header insertion
assert 'wombat.js' in resp.text
# no wombat present
assert '_WBWombat' not in resp.text
@ -169,7 +170,7 @@ class TestWbIntegration(BaseConfigTest):
resp = self.testapp.get('/pywb/20140127171251id_/http://example.com/')
# no wb header insertion
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
assert resp.content_length == 1270, resp.content_length
@ -185,7 +186,7 @@ class TestWbIntegration(BaseConfigTest):
assert resp.headers['Content-Range'] == 'bytes 0-200/1270', resp.headers['Content-Range']
assert resp.content_length == 201, resp.content_length
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
def _test_replay_content_ignore_range(self):
headers = [('Range', 'bytes=0-200')]
@ -198,7 +199,7 @@ class TestWbIntegration(BaseConfigTest):
assert resp.content_length == 1270, resp.content_length
# identity, no header insertion
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
def _test_replay_range_cache_content_bound_end(self):
headers = [('Range', 'bytes=10-10000')]
@ -210,7 +211,7 @@ class TestWbIntegration(BaseConfigTest):
assert resp.content_length == 1260, resp.content_length
assert len(resp.text) == resp.content_length
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
def _test_replay_redir_no_cache(self):
headers = [('Range', 'bytes=10-10000')]
@ -223,7 +224,7 @@ class TestWbIntegration(BaseConfigTest):
resp = self.testapp.get('/pywb/20140216050221id_/http://arc.gz.test.example.com/')
# no wb header insertion
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
# original unrewritten url present
assert '"http://www.iana.org/domains/example"' in resp.text
@ -232,7 +233,7 @@ class TestWbIntegration(BaseConfigTest):
resp = self.testapp.get('/pywb/20140216050221id_/http://arc.test.example.com/')
# no wb header insertion
assert 'wb.js' not in resp.text
assert 'wombat.js' not in resp.text
# original unrewritten url present
assert '"http://www.iana.org/domains/example"' in resp.text
@ -412,14 +413,14 @@ class TestWbIntegration(BaseConfigTest):
assert resp.status_int == 404
def test_static_content(self):
resp = self.testapp.get('/static/wb.css')
resp = self.testapp.get('/static/default_banner.css')
assert resp.status_int == 200
assert resp.content_type == 'text/css'
assert resp.content_length > 0
def test_static_content_filewrapper(self):
from wsgiref.util import FileWrapper
resp = self.testapp.get('/static/wb.css', extra_environ = {'wsgi.file_wrapper': FileWrapper})
resp = self.testapp.get('/static/default_banner.css', extra_environ = {'wsgi.file_wrapper': FileWrapper})
assert resp.status_int == 200
assert resp.content_type == 'text/css'
assert resp.content_length > 0

View File

@ -28,8 +28,9 @@ class TestLiveRewriter(BaseConfigTest):
resp = self.testapp.get('/live/http://example.com/')
assert resp.status_int == 200
resp.charset = 'utf-8'
assert '<iframe ' in resp.text
assert 'src="http://localhost:80/live/mp_/http://example.com/"' in resp.text, resp.text
#assert '<iframe ' in resp.text
assert '"http://localhost:80/live/"' in resp.text, resp.text
assert '"http://example.com/"' in resp.text, resp.text
def test_live_invalid(self, fmod_sl):
resp = self.get('/live/{0}http://abcdef', fmod_sl, status=307)

View File

@ -60,8 +60,8 @@ class TestMemento(MementoMixin, BaseConfigTest):
assert self.make_timegate_link(url, 'mp_') in links
# Body
assert '<iframe ' in resp.text
assert '/pywb/20140127171238mp_/http://www.iana.org/' in resp.text, resp.text
assert '"20140127171238"' in resp.text
assert '"http://www.iana.org/"' in resp.text, resp.text
def test_memento_content_replay_exact(self, fmod):
resp = self.get('/pywb/20140127171238{0}/http://www.iana.org/', fmod)
@ -72,7 +72,7 @@ class TestMemento(MementoMixin, BaseConfigTest):
# Body
assert '"20140127171238"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert 'new _WBWombat' in resp.text, resp.text
assert '/pywb/20140127171238{0}/http://www.iana.org/time-zones"'.format(fmod) in resp.text

View File

@ -12,7 +12,7 @@ class TestRootColl(BaseConfigTest):
# Body
assert '"20140127171238"' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert 'new _WBWombat' in resp.text, resp.text
assert '/20140127171238{0}/http://www.iana.org/time-zones"'.format(fmod) in resp.text
@ -22,7 +22,7 @@ class TestRootColl(BaseConfigTest):
# Body
assert 'request_ts = ""' in resp.text
assert 'wb.js' in resp.text
assert 'wombat.js' in resp.text
assert 'new _WBWombat' in resp.text, resp.text
assert '/{0}http://www.iana.org/time-zones"'.format(fmod_slash) in resp.text