diff --git a/pywb/apps/rewriterapp.py b/pywb/apps/rewriterapp.py index 8cc6730c..c7f313e6 100644 --- a/pywb/apps/rewriterapp.py +++ b/pywb/apps/rewriterapp.py @@ -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 diff --git a/pywb/static/css/query.css b/pywb/static/css/query.css index 7c549fb0..81906938 100644 --- a/pywb/static/css/query.css +++ b/pywb/static/css/query.css @@ -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; +} + diff --git a/pywb/static/query.js b/pywb/static/query.js index db0ceed8..6bba8fb0 100644 --- a/pywb/static/query.js +++ b/pywb/static/query.js @@ -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', diff --git a/pywb/static/queryWorker.js b/pywb/static/queryWorker.js index b514a635..464390d0 100644 --- a/pywb/static/queryWorker.js +++ b/pywb/static/queryWorker.js @@ -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 + diff --git a/pywb/static/search.js b/pywb/static/search.js new file mode 100644 index 00000000..99f7381c --- /dev/null +++ b/pywb/static/search.js @@ -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); + }); + }); diff --git a/pywb/templates/base.html b/pywb/templates/base.html new file mode 100644 index 00000000..f9bf5b02 --- /dev/null +++ b/pywb/templates/base.html @@ -0,0 +1,35 @@ + + + + + + {% block title %}{% endblock %} + + + + + + + + + + {% block head %} + {% include 'head.html' ignore missing %} + {% endblock %} + + + + {% block header %} + {% include 'header.html' ignore missing %} + {% endblock %} + +
+ {% block body %} + {% endblock %} +
+ + {% block footer %} + {% include 'footer.html' ignore missing %} + {% endblock footer %} + + diff --git a/pywb/templates/error.html b/pywb/templates/error.html index 2372570b..c0745395 100644 --- a/pywb/templates/error.html +++ b/pywb/templates/error.html @@ -1,26 +1,33 @@ - - - - - Pywb Error - - - - - +{% extends "base.html" %} +{% block title %}Pywb Error{% endblock %} +{% block body %}

Pywb Error

+ {% if err_status == 451 %} +

Access Blocked to {{ err_msg }}

+ + {% elif err_status == 404 and err_details == 'coll_not_found' %} +

Collection not found: {{ err_msg }}

+ +

See list of valid collections

+ + {% elif err_status == 404 and err_details == 'static_file_not_found' %} +

Static file not found: {{ err_msg }}

+ + {% else %} +

{{ err_msg }}

+ {% if err_details %}

Error Details:

{{ err_details }}
{% endif %} + {% endif %}
- - \ No newline at end of file +{% endblock %} diff --git a/pywb/templates/index.html b/pywb/templates/index.html index 87b18d7e..ecd1c380 100644 --- a/pywb/templates/index.html +++ b/pywb/templates/index.html @@ -1,13 +1,5 @@ - - - - - Pywb Wayback Machine Home Page - - - - - +{% extends "base.html" %} +{% block body %}

Pywb Wayback Machine

@@ -26,5 +18,4 @@
- - +{% endblock %} diff --git a/pywb/templates/not_found.html b/pywb/templates/not_found.html index 945db20d..4cfc4728 100644 --- a/pywb/templates/not_found.html +++ b/pywb/templates/not_found.html @@ -1,27 +1,23 @@ - - - - - URL Not Found - - - - - +{% extends "base.html" %} + +{% block title %}URL Not Found{% endblock %} + +{% block body %}
-
-

URL Not Found

-

- The url {{ url }} could not be found in this collection. +

+

URL Not Found

+
+

+ The url {{ url }} could not be found in this collection. +

+ {% if wbrequest and wbrequest.env.pywb_proxy_magic and url %} +

+ + Try Different Collection +

- {% if wbrequest and wbrequest.env.pywb_proxy_magic and url %} -

- - Try Different Collection - -

- {% endif %} + {% endif %}
- - +{% endblock %} + diff --git a/pywb/templates/query.html b/pywb/templates/query.html index 0be75843..bd50df43 100644 --- a/pywb/templates/query.html +++ b/pywb/templates/query.html @@ -1,28 +1,50 @@ - - - - Pywb Query Results - - - - - - +{% extends "base.html" %} + +{% block title %} +{{ _('Search Results') }} +{% endblock %} + +{% block head %} +{{ super() }} + +{% endblock %} + + +{% block body %}
-

Pywb Query Results

+

{{ _('Search Results') }}

- - - - - +{% endblock %} diff --git a/pywb/templates/search.html b/pywb/templates/search.html index 2b82d30d..151e89d2 100644 --- a/pywb/templates/search.html +++ b/pywb/templates/search.html @@ -1,31 +1,19 @@ - - - - - Pywb {% if metadata %} {{ metadata.title if metadata.title else coll }} {% else %} {{ coll }} {% endif %} - Collection Search Page - - - - +{% block body %}

- Collection Search Page + Collection {{ coll }} Search Page

@@ -33,12 +21,9 @@
-