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

ui template overhaul to simplify customization:

- add base.html template with head, header, footer optional customizations
- refactor all top-level templates to extend base.html, except frame_insert.html
- localization: add placeholder support for jinja2 localization extension, '{% trans %}' and _('') tags, placeholder null localization
- refactor new query UI to support localization
- update some text to match localized versions used in ukwa-pywb, update test
This commit is contained in:
Ilya Kreymer 2019-03-08 16:11:07 -08:00 committed by John Berlin
parent 1b0c9c6895
commit 3589240431
No known key found for this signature in database
GPG Key ID: 6EF5E4B442011B02
12 changed files with 337 additions and 321 deletions

View File

@ -71,7 +71,9 @@ class RewriterApp(object):
self.js_proxy_rw = RewriterWithJSProxy(replay_mod=self.replay_mod)
if not jinja_env:
jinja_env = JinjaEnv(globals={'static_path': 'static'})
jinja_env = JinjaEnv(globals={'static_path': 'static'},
extensions=['jinja2.ext.i18n', 'jinja2.ext.with_'])
jinja_env.jinja_env.install_null_translations()
self.jinja_env = jinja_env

View File

@ -18,3 +18,18 @@
.long-text {
word-wrap: break-word;
}
.inherit-height {
height: inherit;
}
.filter-list {
height: 140px;
max-height: 140px;
overflow-y: scroll
}
.show-optional-bad-input {
display: block;
}

View File

@ -65,26 +65,7 @@ function RenderCalendar(init) {
'=!=': 'Is Not',
'=!~': 'Does Not Begins With'
};
this.text = {
months: {
'01': 'January',
'02': 'February',
'03': 'March',
'04': 'April',
'05': 'May',
'06': 'June',
'07': 'July',
'08': 'August',
'09': 'September',
'10': 'October',
'11': 'November',
'12': 'December'
},
version: 'capture',
versions: 'captures',
result: 'result',
results: 'results'
};
this.text = init.text;
this.versionString = null;
}
@ -268,10 +249,16 @@ RenderCalendar.prototype.makeCDXRequest = function() {
var queryWorker = new window.Worker(this.staticPrefix + '/queryWorker.js');
var cdxRecordMsg = 'cdxRecord';
var done = 'finished';
var months = this.text.months;
queryWorker.onmessage = function(msg) {
var data = msg.data;
var terminate = false;
if (data.type === cdxRecordMsg) {
data.timeInfo.month = months[data.timeInfo.month];
// render the results sent to us from the worker
renderCal.displayedCountStr(
data.recordCount,
@ -599,14 +586,9 @@ RenderCalendar.prototype.renderDateCalPart = function(
* Updates the advanced view with the supplied cdx information
* @param {Object} cdxObj - CDX object for this capture
*/
RenderCalendar.prototype.renderAdvancedSearchPart = function(cdxObj) {
RenderCalendar.prototype.renderAdvancedSearchPart = function (cdxObj) {
// display the URL of the result
var displayedInfo = [
{
tag: 'small',
innerText: 'Date Time: ' + this.tsToDate(cdxObj.timestamp)
}
];
var displayedInfo = [{ tag: 'small', innerText: this.text.dateTime + this.ts_to_date(cdxObj.timestamp) }];
// display additional information about the result under the URL
if (cdxObj.mime) {
displayedInfo.push({
@ -626,7 +608,7 @@ RenderCalendar.prototype.renderAdvancedSearchPart = function(cdxObj) {
href: this.prefix + '*' + '/' + cdxObj.url,
target: '_blank'
},
child: { tag: 'small', innerText: 'View All Captures' }
child: { tag: 'small', innerText: this.text.viewAllCaptures }
});
this.createAndAddElementTo(this.containers.advancedResultsList, {
tag: 'li',

View File

@ -1,18 +1,4 @@
var colon = ':';
var monthToText = {
'01': 'January',
'02': 'February',
'03': 'March',
'04': 'April',
'05': 'May',
'06': 'June',
'07': 'July',
'08': 'August',
'09': 'September',
'10': 'October',
'11': 'November',
'12': 'December'
};
var recordCount = 0;
@ -166,7 +152,7 @@ function handleCDXRecord(binaryCDXRecord) {
record: cdxRecord,
timeInfo: {
year: ts.substring(0, 4),
month: monthToText[ts.substring(4, 6)],
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 +

175
pywb/static/search.js Normal file
View File

@ -0,0 +1,175 @@
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'
};
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)) {
dtBadNotice.classList.remove(showBadDateTimeClass);
} else if (!validInput) {
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);
}
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 {
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);
});
});

35
pywb/templates/base.html Normal file
View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html lang="{{ env.pywb_lang | default('en') }}">
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8"/>
<title>{% block title %}{% endblock %}</title>
<!-- jquery and bootstrap dependencies query view -->
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css"/>
<link rel="stylesheet" href="{{ static_prefix }}/css/query.css">
<link rel="stylesheet" href="{{ static_prefix }}/css/font-awesome.min.css">
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
{% block head %}
{% include 'head.html' ignore missing %}
{% endblock %}
</head>
<body>
{% block header %}
{% include 'header.html' ignore missing %}
{% endblock %}
<section>
{% block body %}
{% endblock %}
</section>
{% block footer %}
{% include 'footer.html' ignore missing %}
{% endblock footer %}
</body>
</html>

View File

@ -1,26 +1,33 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8">
<title>Pywb Error</title>
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css">
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
</head>
<body>
{% extends "base.html" %}
{% block title %}Pywb Error{% endblock %}
{% block body %}
<div class="container text-danger">
<div class="row justify-content-center">
<h2 class="display-2">Pywb Error</h2>
</div>
<div class="row">
<div class="col-12 text-center">
{% if err_status == 451 %}
<p class="lead">Access Blocked to {{ err_msg }}</p>
{% elif err_status == 404 and err_details == 'coll_not_found' %}
<p>Collection not found: <b>{{ err_msg }}</b></p>
<p><a href="/">See list of valid collections</a></p>
{% elif err_status == 404 and err_details == 'static_file_not_found' %}
<p>Static file not found: <b>{{ err_msg }}</b></p>
{% else %}
<p class="lead">{{ err_msg }}</p>
{% if err_details %}
<p class="lead">Error Details:</p>
<pre>{{ err_details }}</pre>
{% endif %}
{% endif %}
</div>
</div>
</div>
</body>
</html>
{% endblock %}

View File

@ -1,13 +1,5 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8">
<title>Pywb Wayback Machine Home Page</title>
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css">
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
</head>
<body>
{% extends "base.html" %}
{% block body %}
<div class="container">
<div class="row">
<h2 class="display-2">Pywb Wayback Machine</h2>
@ -26,5 +18,4 @@
</ul>
</div>
</div>
</body>
</html>
{% endblock %}

View File

@ -1,27 +1,23 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8">
<title>URL Not Found</title>
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css">
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
</head>
<body>
{% extends "base.html" %}
{% block title %}URL Not Found{% endblock %}
{% block body %}
<div class="container">
<div class="row justify-content-center">
<h2 class="display-2">URL Not Found</h2>
<p class="lead">
The url <b>{{ url }}</b> could not be found in this collection.
<div class="col-12">
<h4 class="display-2">URL Not Found</h4>
</div>
<p>
The url <b>{{ url }}</b> could not be found in this collection.
</p>
{% if wbrequest and wbrequest.env.pywb_proxy_magic and url %}
<p>
<a href="//select.{{ wbrequest and wbrequest.env.pywb_proxy_magic }}/{{ url }}">
Try Different Collection
</a>
</p>
{% if wbrequest and wbrequest.env.pywb_proxy_magic and url %}
<p>
<a href="//select.{{ wbrequest and wbrequest.env.pywb_proxy_magic }}/{{ url }}">
Try Different Collection
</a>
</p>
{% endif %}
{% endif %}
</div>
</div>
</body>
</html>
{% endblock %}

View File

@ -1,28 +1,50 @@
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8">
<title>Pywb Query Results</title>
<!-- jquery and bootstrap dependencies query view -->
<link rel="stylesheet" href="{{ static_prefix }}/css/query.css">
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css">
<link rel="stylesheet" href="{{ static_prefix }}/css/font-awesome.min.css">
</head>
<body>
{% extends "base.html" %}
{% block title %}
{{ _('Search Results') }}
{% endblock %}
{% block head %}
{{ super() }}
<script src="{{ static_prefix }}/query.js"></script>
{% endblock %}
{% block body %}
<div class="container-fluid">
<div class="row justify-content-center">
<h4 class="display-4 p-0">Pywb Query Results</h4>
<h4 class="display-4 p-0">{{ _('Search Results') }}</h4>
</div>
</div>
<div class="container">
<div class="row justify-content-center mt-1" id="display-query-type-info"></div>
</div>
<div class="container mt-3 q-display" id="captures"></div>
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
<script src="{{ static_prefix }}/query.js"></script>
<script>
var renderCal = new RenderCalendar({ prefix: "{{ prefix }}", staticPrefix: "{{ static_prefix }}" });
var text = {
months: {
'01': "{{ _('January') }}",
'02': "{{ _('February') }}",
'03': "{{ _('March') }}",
'04': "{{ _('April') }}",
'05': "{{ _('May') }}",
'06': "{{ _('June') }}",
'07': "{{ _('July') }}",
'08': "{{ _('August') }}",
'09': "{{ _('September') }}",
'10': "{{ _('October') }}",
'11': "{{ _('November') }}",
'12': "{{ _('December') }}",
},
version: "{{ _('capture') }}",
versions: "{{ _('captures') }}",
result: "{{ _('result') }}",
results: "{{ _('results') }}",
viewAllCaptures: "{{ _('View All Captures') }}",
dateTime: "{{ _('Date Time: ') }}",
};
var renderCal = new RenderCalendar({ prefix: "{{ prefix }}", staticPrefix: "{{ static_prefix }}", text: text });
renderCal.init();
</script>
</body>
</html>
{% endblock %}

View File

@ -1,31 +1,19 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8;charset=utf-8">
<title>Pywb {% if metadata %} {{ metadata.title if metadata.title else coll }} {% else %} {{ coll }} {% endif %}
Collection Search Page</title>
<link rel="stylesheet" href="{{ static_prefix }}/css/bootstrap.min.css">
<style>
.inherit-height {
height: inherit;
}
{% extends "base.html" %}
.filter-list {
height: 140px;
max-height: 140px;
overflow-y: scroll
}
{% block head %}
{{ super() }}
<script>
// TODO: cleanup
window.wb_prefix = "{{ wb_prefix }}";
</script>
<script src="{{ static_prefix }}/search.js"></script>
{% endblock %}
.show-optional-bad-input {
display: block;
}
</style>
</head>
<body style="overflow: hidden">
{% block body %}
<div class="container-fluid">
<div class="row justify-content-center">
<h4 class="display-4">
Collection Search Page
Collection {{ coll }} Search Page
</h4>
</div>
</div>
@ -33,12 +21,9 @@
<form class="needs-validation" id="search-form" novalidate>
<div class="form-row">
<div class="col-12">
<label for="search-url" class="lead" aria-label="Search For Col">Search the
<b>{% if metadata %}
{{ metadata.title if metadata.title else coll }}
{% else %}
{{ coll }}
{% endif %}</b> collection:
<label for="search-url" class="lead" aria-label="Search For Col">
{% set coll_title = metadata.title if metadata and metadata.title else coll %}
{% trans %}Search the {{ coll_title }} collection by url:{% endtrans %}
</label>
<input aria-label="url" aria-required="true" class="form-control form-control-lg" id="search-url"
name="search" placeholder="Enter a URL to search for"
@ -57,7 +42,7 @@
</div>
<div class="col-7">
<button type="submit" class="btn btn-outline-primary float-right" role="button" aria-label="Search">
Search
{% trans %}Search{% endtrans %}
</button>
<button class="btn btn-outline-info float-right mr-3" type="button" role="button"
data-toggle="collapse" data-target="#advancedOptions"
@ -195,184 +180,4 @@
</div>
</div>
{% endif %}
<script src="{{ static_prefix }}/js/jquery-latest.min.js"></script>
<script src="{{ static_prefix }}/js/bootstrap.min.js"></script>
<script>
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'
};
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)) {
dtBadNotice.classList.remove(showBadDateTimeClass);
} else if (!validInput) {
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);
}
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 = ['{{ 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 {
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);
});
});
</script>
</body>
</html>
{% endblock %}

View File

@ -341,14 +341,14 @@ class TestManagedColls(CollsDirMixin, BaseConfigTest):
with open(filename, 'r+b') as fh:
buf = fh.read()
buf = buf.replace(b'</html>', b'Custom Test Homepage</html>')
buf = buf.replace(b'Pywb Wayback Machine', b'Custom Test Homepage')
fh.seek(0)
fh.write(buf)
resp = self.testapp.get('/')
resp.charset = 'utf-8'
assert resp.content_type == 'text/html'
assert 'Custom Test Homepage</html>' in resp.text, resp.text
assert 'Custom Test Homepage' in resp.text, resp.text
@patch('pywb.manager.manager.get_input', lambda x: 'y')
def test_add_template_input_yes(self):