/** * Copyright 2017-2021 Florian Bruhin (The-Compiler) * Copyright 2017 Imran Sobir * * This file is part of qutebrowser. * * qutebrowser 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. * * qutebrowser 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 qutebrowser. If not, see . */ "use strict"; window.loadHistory = (function() { // Date of last seen item. let lastItemDate = null; // Each request for new items includes the time of the last item and an // offset. The offset is equal to the number of items from the previous // request that had time=nextTime, and causes the next request to skip // those items to avoid duplicates. let nextTime = null; let nextOffset = 0; // The URL to fetch data from. const DATA_URL = "qute://history/data"; // Various fixed elements const EOF_MESSAGE = document.getElementById("eof"); const LOAD_LINK = document.getElementById("load"); const HIST_CONTAINER = document.getElementById("hist-container"); /** * 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. * @returns {Element} the element to which new rows should be added. */ function getSessionNode(date) { // Find/create table const tableId = ["hist", date.getDate(), date.getMonth(), date.getYear()].join("-"); let table = document.getElementById(tableId); if (table === null) { table = document.createElement("table"); table.id = tableId; // Caption contains human-readable date const caption = document.createElement("caption"); caption.className = "date"; const options = { "weekday": "long", "year": "numeric", "month": "long", "day": "numeric", }; caption.innerHTML = date.toLocaleDateString("en-US", options); table.appendChild(caption); // Add table to page HIST_CONTAINER.appendChild(table); } // Find/create tbody let tbody = table.lastChild; if (tbody.tagName !== "TBODY") { tbody = document.createElement("tbody"); table.appendChild(tbody); } // Create session-separator and new tbody if necessary if (tbody.lastChild !== null && lastItemDate !== null && window.GAP_INTERVAL > 0) { const interval = lastItemDate.getTime() - date.getTime(); if (interval > window.GAP_INTERVAL) { // Add session-separator const 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 for it. * * @param {string} itemUrl - The url for this item. * @param {string} itemTitle - The title for this item. * @param {string} itemTime - The formatted time for this item. * @returns {Element} the completed tr. */ function makeHistoryRow(itemUrl, itemTitle, itemTime) { const row = document.createElement("tr"); const title = document.createElement("td"); title.className = "title"; const link = document.createElement("a"); link.href = itemUrl; link.innerHTML = itemTitle; // Properly escaped in qutescheme.py const host = document.createElement("span"); host.className = "hostname"; host.innerHTML = link.hostname; title.appendChild(link); title.appendChild(host); const time = document.createElement("td"); time.className = "time"; time.innerHTML = itemTime; row.appendChild(title); row.appendChild(time); return row; } /** * Get JSON from given URL. * * @param {string} url - the url to fetch data from. * @param {function} callback - the function to callback with data. * @returns {void} */ function getJSON(url, callback) { const xhr = new XMLHttpRequest(); xhr.open("GET", url, true); xhr.responseType = "json"; xhr.onload = () => { const status = xhr.status; callback(status, xhr.response); }; xhr.send(); } /** * Receive history data from qute://history/data. * * @param {Number} status - The status of the query. * @param {Array} history - History data. * @returns {void} */ function receiveHistory(status, history) { if (history === null) { return; } if (history.length === 0) { // Reached end of history window.onscroll = null; EOF_MESSAGE.style.display = "block"; LOAD_LINK.style.display = "none"; return; } nextTime = history[history.length - 1].time; nextOffset = 0; for (let i = 0, len = history.length; i < len; i++) { const item = history[i]; // python's time.time returns seconds, but js Date expects ms const currentItemDate = new Date(item.time * 1000); getSessionNode(currentItemDate).appendChild(makeHistoryRow( item.url, item.title, currentItemDate.toLocaleTimeString() )); lastItemDate = currentItemDate; if (item.time === nextTime) { nextOffset++; } } } /** * Load new history. * @return {void} */ function loadHistory() { let url = DATA_URL.concat("?offset=", nextOffset.toString()); if (nextTime === null) { getJSON(url, receiveHistory); } else { url = url.concat("&start_time=", nextTime.toString()); getJSON(url, receiveHistory); } } return loadHistory; })();