diff --git a/qutebrowser/browser/qutescheme.py b/qutebrowser/browser/qutescheme.py index 018582abf..e5b581fca 100644 --- a/qutebrowser/browser/qutescheme.py +++ b/qutebrowser/browser/qutescheme.py @@ -24,12 +24,12 @@ Module attributes: _HANDLERS: The handlers registered via decorators. """ +import json import sys import time -import datetime import urllib.parse -from PyQt5.QtCore import QUrlQuery +from PyQt5.QtCore import QUrl, QUrlQuery import qutebrowser from qutebrowser.utils import (version, utils, jinja, log, message, docutils, @@ -165,83 +165,77 @@ def qute_bookmarks(_url): @add_handler('history') # noqa def qute_history(url): - """Handler for qute:history. Display history.""" - # Get current date from query parameter, if not given choose today. - curr_date = datetime.date.today() - try: - query_date = QUrlQuery(url).queryItemValue("date") - if query_date: - curr_date = datetime.datetime.strptime(query_date, "%Y-%m-%d") - curr_date = curr_date.date() - except ValueError: - log.misc.debug("Invalid date passed to qute:history: " + query_date) + """Handler for qute:history. Display and serve history.""" + def history_iter(start_time, reverse=False): + """Iterate through the history and get items we're interested. - one_day = datetime.timedelta(days=1) - next_date = curr_date + one_day - prev_date = curr_date - one_day - - def history_iter(reverse): - """Iterate through the history and get items we're interested in.""" - curr_timestamp = time.mktime(curr_date.timetuple()) + Keyword arguments: + reverse -- whether to reverse the history_dict before iterating. + start_time -- select history starting from this timestamp. + """ history = objreg.get('web-history').history_dict.values() if reverse: history = reversed(history) + end_time = start_time - 86400.0 # end is 24hrs earlier than start + for item in history: - # If we can't apply the reverse performance trick below, - # at least continue as early as possible with old items. - # This gets us down from 550ms to 123ms with 500k old items on my - # machine. - if item.atime < curr_timestamp and not reverse: - continue + # Abort/continue as early as possible + item_newer = item.atime > start_time + item_older = item.atime < end_time + if reverse: + # history_dict is reversed, we are going back in history. + # so: + # abort if item is older than start_time+24hr + # skip if item is newer than start + if item_older: + return + if item_newer: + continue + else: + # history_dict is not reversed, we are going forward in history. + # so: + # abort if item is newer than start_time + # skip if item is older than start_time+24hrs + if item_older: + continue + if item_newer: + return - # Convert timestamp - try: - item_atime = datetime.datetime.fromtimestamp(item.atime) - except (ValueError, OSError, OverflowError): - log.misc.debug("Invalid timestamp {}.".format(item.atime)) - continue - - if reverse and item_atime.date() < curr_date: - # If we could reverse the history in-place, and this entry is - # older than today, only older entries will follow, so we can - # abort here. - return - - # Skip items not on curr_date + # Skip items not within start_time and end_time # Skip redirects # Skip qute:// links + is_in_window = item.atime > end_time and item.atime <= start_time is_internal = item.url.scheme() == 'qute' - is_not_today = item_atime.date() != curr_date - if item.redirect or is_internal or is_not_today: + if item.redirect or is_internal or not is_in_window: continue # Use item's url as title if there's no title. item_url = item.url.toDisplayString() item_title = item.title if item.title else item_url - display_atime = item_atime.strftime("%X") + item_time = int(item.atime) - yield (item_url, item_title, display_atime) + yield {"url": item_url, "title": item_title, "time": item_time} - if sys.hexversion >= 0x03050000: - # On Python >= 3.5 we can reverse the ordereddict in-place and thus - # apply an additional performance improvement in history_iter. - # On my machine, this gets us down from 550ms to 72us with 500k old - # items. - history = list(history_iter(reverse=True)) + if QUrl(url).path() == '/data': + # Use start_time in query or current time. + start_time = QUrlQuery(url).queryItemValue("start_time") + start_time = float(start_time) if start_time else time.time() + + if sys.hexversion >= 0x03050000: + # On Python >= 3.5 we can reverse the ordereddict in-place and thus + # apply an additional performance improvement in history_iter. + # On my machine, this gets us down from 550ms to 72us with 500k old + # items. + history = list(history_iter(start_time, reverse=True)) + else: + # On Python 3.4, we can't do that, so we'd need to copy the entire + # history to a list. There, filter first and then reverse it here. + history = reversed(list(history_iter(start_time, reverse=False))) + + return 'text/html', json.dumps(history) else: - # On Python 3.4, we can't do that, so we'd need to copy the entire - # history to a list. There, filter first and then reverse it here. - history = reversed(list(history_iter(reverse=False))) - - html = jinja.render('history.html', - title='History', - history=history, - curr_date=curr_date, - next_date=next_date, - prev_date=prev_date, - today=datetime.date.today()) - return 'text/html', html + return 'text/html', jinja.render('history.html', title='History') @add_handler('pyeval') diff --git a/qutebrowser/html/history.html b/qutebrowser/html/history.html index 94f82182f..ba64316e4 100644 --- a/qutebrowser/html/history.html +++ b/qutebrowser/html/history.html @@ -16,43 +16,165 @@ td.time { white-space: nowrap; } +table { + margin-bottom: 30px; +} + .date { - color: #888; - font-size: 14pt; - padding-left: 25px; -} - -.pagination-link { - display: inline-block; - margin-bottom: 10px; - margin-top: 10px; - padding-right: 10px; -} - -.pagination-link > a { - color: #333; + color: #555; + font-size: 12pt; + padding-bottom: 15px; font-weight: bold; + text-align: left; +} + +.session-separator { + color: #aaa; + height: 40px; + text-align: center; +} + +{% endblock %} + +{% block script %} +/** + * Container for global stuff + */ +var global = { + // The last history item that was seen. + lastItem: null, + // The cutoff interval for session-separator (30 minutes) + SESSION_CUTOFF: 30*60 +}; + +/** + * Finds or creates the session table>tbody to which item with given date + * should be added. + * + * @param {Date} date - the date of the item being added. + */ +var getSessionNode = function(date) { + var histContainer = document.getElementById('hist-container'); + + // Find/create table + var tableId = "hist-" + date.getDate() + date.getMonth() + date.getYear(); + var table = document.getElementById(tableId); + if (table === null) { + table = document.createElement("table"); + table.id = tableId; + + caption = document.createElement("caption"); + caption.className = "date"; + var options = {weekday: 'long', year: 'numeric', month: 'long', day: 'numeric'}; + caption.innerHTML = date.toLocaleDateString('en-US', options); + table.appendChild(caption); + + // Add table to page + histContainer.appendChild(table); + } + + // Find/create tbody + var tbody = table.lastChild; + if (tbody.tagName !== "TBODY") { // this is the caption + tbody = document.createElement("tbody"); + table.appendChild(tbody); + } + + // Create session-separator and new tbody if necessary + if (tbody.lastChild !== null && global.lastItem !== null) { + lastItemDate = new Date(parseInt(global.lastItem.time)*1000); + var interval = (lastItemDate.getTime() - date.getTime())/1000.00; + if (interval > global.SESSION_CUTOFF) { + // Add session-separator + var sessionSeparator = document.createElement('td'); + sessionSeparator.className = "session-separator"; + sessionSeparator.colSpan = 2; + sessionSeparator.innerHTML = "§" + table.appendChild(document.createElement('tr')); + table.lastChild.appendChild(sessionSeparator); + + // Create new tbody + tbody = document.createElement("tbody"); + table.appendChild(tbody); + } + } + + return tbody; +} + +/** + * Given a history item, create and return