home

Menu
  • ripgrep search

datasette-search-all/datasette_search_all/templates/search_all.html

{% extends "base.html" %}
{% block title %}Search{% if q %}: {{ q }}{% else %} all tables{% endif %}{% endblock %}
 
{% block extra_head %}
<style type="text/css">
.search-all-loading a {
    padding-left: 0.5em;
}
.search-all-loading:before {
    content: " ";
    opacity: 0.5;
    display: inline-block;
    width: 0.4em;
    height: 0.4em;
    border-radius: 50%;
    border: 4px solid black;
    border-color: black transparent black transparent;
    animation: rotate-360 1.2s linear infinite;
}
@keyframes rotate-360 {
    0% {
        transform: rotate(0deg);
    }
    100% {
        transform: rotate(360deg);
    }
}
</style>
{% endblock %}
 
{% block content %}
<h1>Search all tables</h1>
 
{% if not searchable_tables %}
    <p>There are no tables that have been configured for search.</p>
{% else %}
    <form action="{{ urls.path("/-/search") }}" method="get">
        <p>
            <input type="search" name="q" value="{{ q }}" id="search-all-q">
            <input type="submit" value="Search">
        </p>
    </form>
{% endif %}
<div id="search-all-results" style="margin-top: 1em;">
{% if q and searchable_tables %}
    <ul>
    {% for searchable_table in searchable_tables %}
        <li data-searchable-url="{{ searchable_table.url }}"><a href="{{ searchable_table.url }}?_search={{ q|urlencode }}">Search {{ searchable_table.database }}: {{ searchable_table.table }} for "{{ q }}"</a></li>
    {% endfor %}
    </ul>
{% endif %}
</div>
<div id="search-all-no-results"></div>
 
<script>
var NUM_RESULTS = 5;
var searchable_tables = {{ searchable_tables_json|safe }};
var q = document.getElementById("search-all-q");
var search_results = document.getElementById("search-all-results");
var search_no_results = document.getElementById("search-all-no-results");
 
function isUrl(s) {
  let url;
  try {
    url = new URL(s);
  } catch (_) {
    return false;
  }
  return url.protocol === "http:" || url.protocol === "https:";
}
 
 
function htmlEscape(html) {
  return html.replace(
    /&/g, '&amp;'
  ).replace(
    />/g, '&gt;'
  ).replace(
    /</g, '&lt;'
  ).replace(
    /"/g, '&quot;'
  ).replace(
    /'/g, '&#039;'
  );
}
 
class Safe extends String {}
 
function safe(s) {
  if (!(s instanceof Safe)) {
    return new Safe(s);
  } else {
    return s;
  }
}
 
var autoescape = (fragments, ...inserts) => safe(fragments.map(
  (fragment, i) => {
    var insert = (inserts[i] || '');
    if (!(insert instanceof Safe)) {
      insert = htmlEscape(insert.toString());
    }
    return fragment + insert;
  }
).join(''));
 
function displayCell(cell) {
    // cell is either a value or a {"value:", "label": } object
    let value;
    if (cell && typeof(cell.label) !== "undefined") {
        value = cell.label || cell.value;
    } else {
        value = cell;
    }
    if (isUrl(value)) {
      return safe(`<a href="${htmlEscape(value)}">${htmlEscape(value)}</a>`);
    }
    return value;
}
 
function displayResults(data, base_url) {
    var rows = data.rows;
    var database = data.database;
    var table = data.table;
    var columns = [];
    if (data.rows.length) {
        columns = Object.keys(data.rows[0]);
    }
    var count = data.filtered_table_rows_count || data.count;
    var ths = safe(columns.map(c => autoescape`<th>${c}</th>`).join(""));
    var tr_rows = safe(rows.map(row => autoescape`<tr>${safe(columns.map(column => autoescape`<td>${displayCell(row[column])}</td>`).join(""))}</tr>`).join(""));
    var view_more = '';
    var search_url = `${base_url}?_search=${encodeURIComponent(q.value)}`;
    if (count > NUM_RESULTS) {
        var more_count = count - NUM_RESULTS;
        view_more = autoescape`<p><a href="${search_url}">${Number(more_count).toLocaleString()} more result${more_count == 1 ? '' : 's'}</a></p>`;
    }
    var html, div;
    if (count) {
        html = autoescape`
            <h2><a href="${search_url}">${Number(count).toLocaleString()} result${count == 1 ? '' : 's'}</a> in ${database}: ${table}</h2>
            <div style="overflow: auto">
            <table>
                <tr>${ths}</tr>
                ${tr_rows}
            </table>
            ${view_more}
            </div>
        `;
        div = document.createElement('div');
        div.innerHTML = html;
        search_results.appendChild(div);
    } else {
        search_no_results.innerHTML += autoescape`
            <p>No results in <a href="${base_url}">${database}: ${table}</a></p>
        `;
    }
}
 
if (q.value) {
    searchable_tables.forEach(item => {
        var base_url = item.url;
        var li = document.querySelector(`[data-searchable-url="${base_url}"]`);
        li.classList.add('search-all-loading');
        var json_url = `${item.url_json}?_shape=objects&_labels=on&_extra=count&_extra=database&_extra=table&_size=${NUM_RESULTS}&_search=${encodeURIComponent(q.value)}`;
        fetch(json_url).then(r => r.json()).then((data) => {
            li.style.display = 'none';
            displayResults(data, base_url);
        });
    });
}
</script>
 
 
{% endblock %}
 
Powered by Datasette