Every method here is one fossick call plus Vault.add . fossick knows how to get past bot walls, read arXiv and YouTube, and sniff a page’s JSON API; the vault’s job is only to file what comes back with the provenance that explains why it is there.
source
md_title
def md_title(
md:str , fallback:str = ''
)-> str :
*First markdown heading in md, else fallback — scraped
<p>s are often junk.*</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L18" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="clip" class="level3">
<h3>clip</h3>
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb2-1"><a href="#cb2-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> clip(</span>
<span id="cb2-2"><a href="#cb2-2" aria-hidden="true" tabindex="-1"></a> s:<span class="bu">str</span>, n:<span class="bu">int</span><span class="op">=</span><span class="dv">120</span></span>
<span id="cb2-3"><a href="#cb2-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">str</span>:</span></code></pre></div>
<p><em>Collapse whitespace and clip a scraped title to something a breadcrumb can carry.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L63" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.web" class="level3">
<h3>Vault.web</h3>
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb3-1"><a href="#cb3-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> web(</span>
<span id="cb3-2"><a href="#cb3-2" aria-hidden="true" tabindex="-1"></a> query:<span class="bu">str</span>, <span class="co"># what to search for</span></span>
<span id="cb3-3"><a href="#cb3-3" aria-hidden="true" tabindex="-1"></a> n:<span class="bu">int</span><span class="op">=</span><span class="dv">5</span>, <span class="co"># top results to read</span></span>
<span id="cb3-4"><a href="#cb3-4" aria-hidden="true" tabindex="-1"></a> google:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="co"># real Google ranking via a stealth browser (slower)</span></span>
<span id="cb3-5"><a href="#cb3-5" aria-hidden="true" tabindex="-1"></a> chars:<span class="bu">int</span><span class="op">=</span><span class="dv">60000</span>, <span class="co"># max markdown chars kept per source</span></span>
<span id="cb3-6"><a href="#cb3-6" aria-hidden="true" tabindex="-1"></a> verify:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb3-7"><a href="#cb3-7" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span>AttrDict: <span class="co"># forwarded to fossick.research</span></span></code></pre></div>
<p><em>Search the web, read the top <code>n</code> results, and file all of them in the vault.</em></p>
<p>This is the loop the vault exists for: the query that found a page is kept in its metadata, so months later <code>sources()</code> still says <em>why</em> a document is in your corpus. Results already present are skipped rather than duplicated, so re-running an overlapping search is cheap.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L54" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.crawl" class="level3">
<h3>Vault.crawl</h3>
<div class="sourceCode" id="cb4"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb4-1"><a href="#cb4-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> crawl(</span>
<span id="cb4-2"><a href="#cb4-2" aria-hidden="true" tabindex="-1"></a> start_url:<span class="bu">str</span>, max_pages:<span class="bu">int</span><span class="op">=</span><span class="dv">10</span>, sel:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, verify:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb4-3"><a href="#cb4-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span>L:</span></code></pre></div>
<p><em>Crawl a docs site or blog from a start URL and file every page in the vault.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L29" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.url" class="level3">
<h3>Vault.url</h3>
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb5-1"><a href="#cb5-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> url(</span>
<span id="cb5-2"><a href="#cb5-2" aria-hidden="true" tabindex="-1"></a> url:<span class="bu">str</span>, <span class="co"># page to read</span></span>
<span id="cb5-3"><a href="#cb5-3" aria-hidden="true" tabindex="-1"></a> title:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, <span class="co"># defaults to the page's first heading, else its path</span></span>
<span id="cb5-4"><a href="#cb5-4" aria-hidden="true" tabindex="-1"></a> sel:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, <span class="co"># CSS selector to narrow the page before conversion</span></span>
<span id="cb5-5"><a href="#cb5-5" aria-hidden="true" tabindex="-1"></a> kind:<span class="bu">str</span><span class="op">=</span><span class="st">'web'</span>, auto:<span class="bu">bool</span><span class="op">=</span><span class="va">True</span>, <span class="co"># escalate plain -> heavy -> stealthy -> logged-in Chrome past bot walls</span></span>
<span id="cb5-6"><a href="#cb5-6" aria-hidden="true" tabindex="-1"></a> meta:<span class="bu">dict</span><span class="op">=</span><span class="va">None</span>, force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, verify:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb5-7"><a href="#cb5-7" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>: <span class="co"># forwarded to fossick.fetch</span></span></code></pre></div>
<p><em>Fetch one URL, convert it to markdown and file it in the vault.</em></p>
<p><code>auto=True</code> is the default because a bot wall returns HTTP 200 with a challenge page, which would otherwise be indexed as if it were the article.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L138" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.code" class="level3">
<h3>Vault.code</h3>
<div class="sourceCode" id="cb6"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb6-1"><a href="#cb6-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> code(</span>
<span id="cb6-2"><a href="#cb6-2" aria-hidden="true" tabindex="-1"></a> <span class="bu">dir</span>:<span class="bu">str</span>, types:<span class="bu">str</span><span class="op">=</span><span class="st">'.py,.js,.ts,.jsx,.tsx,.java,.go,.cs,.ruby,.php,.swift,.kt,.kts,.rs,.scala,.lua'</span>, <span class="op">**</span>kw</span>
<span id="cb6-3"><a href="#cb6-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span>L:</span></code></pre></div>
<p><em>File a source tree into the vault as <code>kind='code'</code>, so code and prose answer one query.</em> This is deliberately the shallow path: files as documents, headings from the text. For call graphs, PageRank over symbols and <code>where_to_add</code>, use <code>index_code</code> — kosha builds an AST-derived index that this cannot, and <code>federate</code> searches both.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L121" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.grab" class="level3">
<h3>Vault.grab</h3>
<div class="sourceCode" id="cb7"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb7-1"><a href="#cb7-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> grab(</span>
<span id="cb7-2"><a href="#cb7-2" aria-hidden="true" tabindex="-1"></a> target:<span class="bu">str</span>, <span class="co"># a URL, an arXiv id, a YouTube link, a PDF, a local file or a directory</span></span>
<span id="cb7-3"><a href="#cb7-3" aria-hidden="true" tabindex="-1"></a> title:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, sel:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, <span class="co"># CSS selector, for the web cases</span></span>
<span id="cb7-4"><a href="#cb7-4" aria-hidden="true" tabindex="-1"></a> <span class="op">**</span>kw</span>
<span id="cb7-5"><a href="#cb7-5" aria-hidden="true" tabindex="-1"></a>):</span></code></pre></div>
<p><em>File anything, by looking at what it is — the one call a CLI or an agent needs.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L109" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.youtube" class="level3">
<h3>Vault.youtube</h3>
<div class="sourceCode" id="cb8"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb8-1"><a href="#cb8-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> youtube(</span>
<span id="cb8-2"><a href="#cb8-2" aria-hidden="true" tabindex="-1"></a> url:<span class="bu">str</span>, force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span></span>
<span id="cb8-3"><a href="#cb8-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Read a YouTube video’s transcript and metadata into the vault.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L96" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.pdf" class="level3">
<h3>Vault.pdf</h3>
<div class="sourceCode" id="cb9"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb9-1"><a href="#cb9-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> pdf(</span>
<span id="cb9-2"><a href="#cb9-2" aria-hidden="true" tabindex="-1"></a> path_or_url:<span class="bu">str</span>, title:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, verify:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb9-3"><a href="#cb9-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Read a PDF (local path or URL) into the vault, one tree node per heading.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L86" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.arxiv" class="level3">
<h3>Vault.arxiv</h3>
<div class="sourceCode" id="cb10"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb10-1"><a href="#cb10-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> arxiv(</span>
<span id="cb10-2"><a href="#cb10-2" aria-hidden="true" tabindex="-1"></a> id_or_url:<span class="bu">str</span>, save_dir:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, verify:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb10-3"><a href="#cb10-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Read an arXiv paper (metadata + full text) into the vault as <code>kind="arxiv"</code>.</em></p>
</section>
<section id="harvest-read-a-pages-api-not-its-html" class="level3">
<h3>Harvest: read a page’s API, not its HTML</h3>
<p>Listing, product and dashboard pages render from an internal JSON API. Reading that API is faster, paginates cleanly and survives redesigns, where scraping the DOM does none of those. One document per harvest, one <code>##</code> section per record — so <code>build_tree</code> gives every record its own node and breadcrumb, and a catalogue becomes individually retrievable rows sitting next to your notes.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L163" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="records_md" class="level3">
<h3>records_md</h3>
<div class="sourceCode" id="cb11"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb11-1"><a href="#cb11-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> records_md(</span>
<span id="cb11-2"><a href="#cb11-2" aria-hidden="true" tabindex="-1"></a> recs, title_keys:<span class="bu">tuple</span><span class="op">=</span>(<span class="st">'name'</span>, <span class="st">'title'</span>, <span class="st">'displayName'</span>, <span class="st">'productName'</span>, <span class="st">'label'</span>, <span class="st">'sku'</span>, <span class="st">'id'</span>)</span>
<span id="cb11-3"><a href="#cb11-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">str</span>:</span></code></pre></div>
<p><em>Records as markdown, one <code>##</code> section per record, so each becomes its own retrievable node.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L146" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="records" class="level3">
<h3>records</h3>
<div class="sourceCode" id="cb12"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb12-1"><a href="#cb12-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> records(</span>
<span id="cb12-2"><a href="#cb12-2" aria-hidden="true" tabindex="-1"></a> data, min_len:<span class="bu">int</span><span class="op">=</span><span class="dv">2</span></span>
<span id="cb12-3"><a href="#cb12-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">list</span>:</span></code></pre></div>
<p><em>The longest list of dicts inside an arbitrary JSON response.</em> APIs bury their payload at different depths (<code>results</code>, <code>data.products.items</code>, a bare array), so this walks for the longest list of dicts rather than guessing a key name.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L222" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.add_records" class="level3">
<h3>Vault.add_records</h3>
<div class="sourceCode" id="cb13"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb13-1"><a href="#cb13-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> add_records(</span>
<span id="cb13-2"><a href="#cb13-2" aria-hidden="true" tabindex="-1"></a> recs:<span class="bu">list</span>, title:<span class="bu">str</span>, source:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, kind:<span class="bu">str</span><span class="op">=</span><span class="st">'data'</span>, force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, meta:<span class="bu">dict</span><span class="op">=</span><span class="va">None</span></span>
<span id="cb13-3"><a href="#cb13-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>File a list of dicts you already have (any API, any export) as one document, one section each.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L188" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.harvest" class="level3">
<h3>Vault.harvest</h3>
<div class="sourceCode" id="cb14"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb14-1"><a href="#cb14-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> harvest(</span>
<span id="cb14-2"><a href="#cb14-2" aria-hidden="true" tabindex="-1"></a> url:<span class="bu">str</span>, <span class="co"># the page whose API you want</span></span>
<span id="cb14-3"><a href="#cb14-3" aria-hidden="true" tabindex="-1"></a> pattern:<span class="bu">str</span><span class="op">=</span><span class="st">'*'</span>, <span class="co"># which captured request URLs to keep</span></span>
<span id="cb14-4"><a href="#cb14-4" aria-hidden="true" tabindex="-1"></a> title:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, <span class="co"># document title; defaults to the page host + path</span></span>
<span id="cb14-5"><a href="#cb14-5" aria-hidden="true" tabindex="-1"></a> capture:<span class="bu">int</span><span class="op">=</span><span class="va">None</span>, <span class="co"># replay a specific endpoint from the last apis() call</span></span>
<span id="cb14-6"><a href="#cb14-6" aria-hidden="true" tabindex="-1"></a> pages:<span class="bu">int</span><span class="op">=</span><span class="dv">1</span>, <span class="co"># pages to pull; >1 paginates the endpoint</span></span>
<span id="cb14-7"><a href="#cb14-7" aria-hidden="true" tabindex="-1"></a> page_field:<span class="bu">str</span><span class="op">=</span><span class="st">'page'</span>, <span class="co"># query/body key incremented per page</span></span>
<span id="cb14-8"><a href="#cb14-8" aria-hidden="true" tabindex="-1"></a> session:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="co"># capture through the logged-in Chrome</span></span>
<span id="cb14-9"><a href="#cb14-9" aria-hidden="true" tabindex="-1"></a> force:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="op">**</span>kw</span>
<span id="cb14-10"><a href="#cb14-10" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Sniff a page’s JSON API, pull the records, and file them in the vault as <code>kind='data'</code>.</em> Re-harvesting the same source replaces rather than duplicates when <code>force=True</code>; otherwise it is a no-op, which is what makes this safe to put behind a <code>watch</code>.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L172" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.apis" class="level3">
<h3>Vault.apis</h3>
<div class="sourceCode" id="cb15"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb15-1"><a href="#cb15-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> apis(</span>
<span id="cb15-2"><a href="#cb15-2" aria-hidden="true" tabindex="-1"></a> url:<span class="bu">str</span>, <span class="co"># page to watch</span></span>
<span id="cb15-3"><a href="#cb15-3" aria-hidden="true" tabindex="-1"></a> pattern:<span class="bu">str</span><span class="op">=</span><span class="st">'*'</span>, <span class="co"># glob/regex filtering captured request URLs</span></span>
<span id="cb15-4"><a href="#cb15-4" aria-hidden="true" tabindex="-1"></a> session:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, <span class="co"># capture through the logged-in debug Chrome</span></span>
<span id="cb15-5"><a href="#cb15-5" aria-hidden="true" tabindex="-1"></a> preview:<span class="bu">int</span><span class="op">=</span><span class="dv">240</span>, <span class="co"># chars of each response shown</span></span>
<span id="cb15-6"><a href="#cb15-6" aria-hidden="true" tabindex="-1"></a> <span class="op">**</span>kw</span>
<span id="cb15-7"><a href="#cb15-7" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span>L: <span class="co"># forwarded to fossick.find_xhr</span></span></code></pre></div>
<p><em>Discover the JSON endpoints a page calls, so you can read its data instead of its HTML.</em></p>
</section>
<section id="watches-keeping-it-current" class="level3">
<h3>Watches: keeping it current</h3>
<p>A watch is what turns the vault from an archive into something that stays current. <code>action</code> names an acquisition method, so anything you can file once you can file on a schedule; <code>remind</code> writes a note instead of fetching, which is the recurring-reminder case with no network involved. <code>poll()</code> is the tick — cron, a scheduler, or a frontend button.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L296" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.poll" class="level3">
<h3>Vault.poll</h3>
<div class="sourceCode" id="cb16"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb16-1"><a href="#cb16-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> poll(</span>
<span id="cb16-2"><a href="#cb16-2" aria-hidden="true" tabindex="-1"></a> at:<span class="bu">float</span><span class="op">=</span><span class="va">None</span>, limit:<span class="bu">int</span><span class="op">=</span><span class="va">None</span>, <span class="ex">connect</span>:<span class="bu">bool</span><span class="op">=</span><span class="va">True</span></span>
<span id="cb16-3"><a href="#cb16-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Run every watch that is due. This is the tick a scheduler, a cron or a frontend calls.</em></p>
<p>Rebuilds the entity graph once at the end rather than per watch, because <code>connect()</code> reads the whole store and a poll that fired five watches would otherwise pay for it five times.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L278" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.run_watch" class="level3">
<h3>Vault.run_watch</h3>
<div class="sourceCode" id="cb17"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb17-1"><a href="#cb17-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> run_watch(</span>
<span id="cb17-2"><a href="#cb17-2" aria-hidden="true" tabindex="-1"></a> w:<span class="bu">dict</span></span>
<span id="cb17-3"><a href="#cb17-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>:</span></code></pre></div>
<p><em>Fire one watch and record the outcome.</em></p>
<p>A failure is recorded on the row and returned, never raised: one dead URL must not stop a polling loop from servicing every other watch.</p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L273" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.pause" class="level3">
<h3>Vault.pause</h3>
<div class="sourceCode" id="cb18"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb18-1"><a href="#cb18-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> pause(</span>
<span id="cb18-2"><a href="#cb18-2" aria-hidden="true" tabindex="-1"></a> watch_id:<span class="bu">str</span>, enabled:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span></span>
<span id="cb18-3"><a href="#cb18-3" aria-hidden="true" tabindex="-1"></a>):</span></code></pre></div>
<p><em>Disable (or re-enable) a watch without losing it.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L268" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.unwatch" class="level3">
<h3>Vault.unwatch</h3>
<div class="sourceCode" id="cb19"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb19-1"><a href="#cb19-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> unwatch(</span>
<span id="cb19-2"><a href="#cb19-2" aria-hidden="true" tabindex="-1"></a> watch_id:<span class="bu">str</span></span>
<span id="cb19-3"><a href="#cb19-3" aria-hidden="true" tabindex="-1"></a>):</span></code></pre></div>
<p><em>Delete a watch. The documents it already filed stay in the vault.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L261" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.watches" class="level3">
<h3>Vault.watches</h3>
<div class="sourceCode" id="cb20"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb20-1"><a href="#cb20-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> watches(</span>
<span id="cb20-2"><a href="#cb20-2" aria-hidden="true" tabindex="-1"></a> due_only:<span class="bu">bool</span><span class="op">=</span><span class="va">False</span>, at:<span class="bu">float</span><span class="op">=</span><span class="va">None</span></span>
<span id="cb20-3"><a href="#cb20-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span>L:</span></code></pre></div>
<p><em>Every registered watch, soonest first; <code>due_only</code> keeps the ones whose next run has arrived.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L245" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="vault.watch" class="level3">
<h3>Vault.watch</h3>
<div class="sourceCode" id="cb21"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb21-1"><a href="#cb21-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> watch(</span>
<span id="cb21-2"><a href="#cb21-2" aria-hidden="true" tabindex="-1"></a> target:<span class="bu">str</span>, <span class="co"># URL, query, arXiv id, or the text of a reminder</span></span>
<span id="cb21-3"><a href="#cb21-3" aria-hidden="true" tabindex="-1"></a> action:<span class="bu">str</span><span class="op">=</span><span class="st">'url'</span>, <span class="co"># one of ACTIONS — what to do when it fires</span></span>
<span id="cb21-4"><a href="#cb21-4" aria-hidden="true" tabindex="-1"></a> every:<span class="bu">str</span><span class="op">=</span><span class="st">'1d'</span>, <span class="co"># interval: '30m', '6h', '1d', '1w', or seconds</span></span>
<span id="cb21-5"><a href="#cb21-5" aria-hidden="true" tabindex="-1"></a> note:<span class="bu">str</span><span class="op">=</span><span class="va">None</span>, <span class="co"># why you are watching</span></span>
<span id="cb21-6"><a href="#cb21-6" aria-hidden="true" tabindex="-1"></a> start:<span class="bu">float</span><span class="op">=</span><span class="va">None</span>, <span class="co"># first run time (epoch); defaults to now</span></span>
<span id="cb21-7"><a href="#cb21-7" aria-hidden="true" tabindex="-1"></a> <span class="op">**</span>params</span>
<span id="cb21-8"><a href="#cb21-8" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">dict</span>: <span class="co"># forwarded to the action (n=, pattern=, pages=, sel=, ...)</span></span></code></pre></div>
<p><em>Register a recurring job: re-read a page, re-run a search, re-harvest an API, or remind you.</em></p>
<hr />
<p><a href="https://github.com/vedicreader/vishalakshi/blob/main/vishalakshi/acquire.py#L231" target="_blank" style="float:right; font-size:smaller">source</a></p>
</section>
<section id="secs" class="level3">
<h3>secs</h3>
<div class="sourceCode" id="cb22"><pre class="sourceCode python"><code class="sourceCode python"><span id="cb22-1"><a href="#cb22-1" aria-hidden="true" tabindex="-1"></a><span class="kw">def</span> secs(</span>
<span id="cb22-2"><a href="#cb22-2" aria-hidden="true" tabindex="-1"></a> every</span>
<span id="cb22-3"><a href="#cb22-3" aria-hidden="true" tabindex="-1"></a>)<span class="op">-></span><span class="bu">float</span>:</span></code></pre></div>
<p><em>Seconds from <code>'30m'</code>, <code>'6h'</code>, <code>'2 days'</code>, <code>'1w'</code>, or a number of seconds.</em></p>
</section>
<section id="try-it" class="level2">
<h2>Try it</h2>
<div id="ccce65e4" class="cell">
<div class="sourceCode" id="cb23"><pre class="sourceCode python cell-code"><code class="sourceCode python"><span id="cb23-1"><a href="#cb23-1" aria-hidden="true" tabindex="-1"></a>v <span class="op">=</span> Vault(<span class="st">':memory:'</span>)</span>
<span id="cb23-2"><a href="#cb23-2" aria-hidden="true" tabindex="-1"></a>v.add_records([<span class="bu">dict</span>(sku<span class="op">=</span><span class="st">'A1'</span>, name<span class="op">=</span><span class="st">'Free range eggs'</span>, price<span class="op">=</span><span class="fl">4.5</span>),</span>
<span id="cb23-3"><a href="#cb23-3" aria-hidden="true" tabindex="-1"></a> <span class="bu">dict</span>(sku<span class="op">=</span><span class="st">'B2'</span>, name<span class="op">=</span><span class="st">'Oat milk'</span>, price<span class="op">=</span><span class="fl">2.1</span>)], <span class="st">'dairy'</span>)</span>
<span id="cb23-4"><a href="#cb23-4" aria-hidden="true" tabindex="-1"></a>v.find(<span class="st">'eggs'</span>)[<span class="dv">0</span>][<span class="st">'breadcrumb'</span>]</span></code></pre></div>
<div class="cell-output cell-output-stderr">
<pre><code>/Users/71293/code/personal/orgs/vishalakshi/.venv/lib/python3.13/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html
from .autonotebook import tqdm as notebook_tqdm</code></pre>
</div>
<div class="cell-output cell-output-display">
<pre><code>'dairy › Free range eggs'</code></pre>
</div>
</div>
<div id="a52dbdf2" class="cell">
<div class="sourceCode" id="cb26"><pre class="sourceCode python cell-code"><code class="sourceCode python"><span id="cb26-1"><a href="#cb26-1" aria-hidden="true" tabindex="-1"></a>test_eq(<span class="bu">len</span>(records({<span class="st">'data'</span>: {<span class="st">'items'</span>: [{<span class="st">'a'</span>: <span class="dv">1</span>}, {<span class="st">'a'</span>: <span class="dv">2</span>}, {<span class="st">'a'</span>: <span class="dv">3</span>}]}})), <span class="dv">3</span>)</span>
<span id="cb26-2"><a href="#cb26-2" aria-hidden="true" tabindex="-1"></a>test_eq(secs(<span class="st">'6h'</span>), <span class="dv">21600</span>)<span class="op">;</span> test_eq(secs(<span class="st">'1w'</span>), <span class="dv">604800</span>)<span class="op">;</span> test_eq(secs(<span class="dv">90</span>), <span class="dv">90</span>)</span>
<span id="cb26-3"><a href="#cb26-3" aria-hidden="true" tabindex="-1"></a>w <span class="op">=</span> v.watch(<span class="st">'late chunking'</span>, action<span class="op">=</span><span class="st">'web'</span>, every<span class="op">=</span><span class="st">'1d'</span>, n<span class="op">=</span><span class="dv">3</span>)</span>
<span id="cb26-4"><a href="#cb26-4" aria-hidden="true" tabindex="-1"></a>test_eq(w[<span class="st">'params'</span>], <span class="bu">dict</span>(n<span class="op">=</span><span class="dv">3</span>))</span>
<span id="cb26-5"><a href="#cb26-5" aria-hidden="true" tabindex="-1"></a>test_eq(<span class="bu">len</span>(v.watches(due_only<span class="op">=</span><span class="va">True</span>)), <span class="dv">1</span>)</span>
<span id="cb26-6"><a href="#cb26-6" aria-hidden="true" tabindex="-1"></a>v.unwatch(w[<span class="st">'id'</span>])</span>
<span id="cb26-7"><a href="#cb26-7" aria-hidden="true" tabindex="-1"></a>test_eq(<span class="bu">len</span>(v.watches()), <span class="dv">0</span>)</span></code></pre></div>
<div class="cell-output cell-output-stderr">
<pre><code>/var/folders/kg/9vdw4mdd1fs58svgh4k1qhr09x7dqh/T/ipykernel_76490/529824922.py:7: Pandas4Warning: 'w' is deprecated and will be removed in a future version. Please use 'W' instead of 'w'.
return float(every) if isinstance(every, (int, float)) else pd.Timedelta(every).total_seconds()
/var/folders/kg/9vdw4mdd1fs58svgh4k1qhr09x7dqh/T/ipykernel_76490/529824922.py:7: Pandas4Warning: 'd' is deprecated and will be removed in a future version. Please use 'D' instead of 'd'.
return float(every) if isinstance(every, (int, float)) else pd.Timedelta(every).total_seconds()</code></pre>
</div>
</div>
<div id="quarto-navigation-envelope" class="hidden">
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyLXRpdGxl">vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1uYXZiYXItdGl0bGU=">vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1uZXh0">ask</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1wcmV2">core</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9pbmRleC5odG1sdmlzaGFsYWtzaGk=">vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9jb3JlLmh0bWxjb3Jl">core</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9hY3F1aXJlLmh0bWxhY3F1aXJl">acquire</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9hc2suaHRtbGFzaw==">ask</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9jb2RlLmh0bWxjb2Rl">code</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9jbGkuaHRtbGNsaQ==">cli</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWludC1zaWRlYmFyOi9tY3AuaHRtbG1jcA==">mcp</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLWJyZWFkY3J1bWJzLWFjcXVpcmU=">acquire</span></p>
</div>
<div id="quarto-meta-markdown" class="hidden">
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLW1ldGF0aXRsZQ==">acquire – vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLXR3aXR0ZXJjYXJkdGl0bGU=">acquire – vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLW9nY2FyZHRpdGxl">acquire – vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLW1ldGFzaXRlbmFtZQ==">vishalakshi</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLXR3aXR0ZXJjYXJkZGVzYw==">pull the web, papers, video, files, code and JSON APIs into the vault — once, or on a schedule</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLW9nY2FyZGRkZXNj">pull the web, papers, video, files, code and JSON APIs into the vault — once, or on a schedule</span></p>
<p><span class="hidden quarto-markdown-envelope-contents" data-render-id="cXVhcnRvLW1ldGFzaXRlZGVzYw==">one vault for everything you read: web, papers, video, files and code, searchable together and answerable by a local or hosted model</span></p>
</div>
</section>
</main> <!-- /main -->
<script id = "quarto-html-after-body" type="application/javascript">
window.document.addEventListener("DOMContentLoaded", function (event) {
const icon = "";
const anchorJS = new window.AnchorJS();
anchorJS.options = {
placement: 'right',
icon: icon
};
anchorJS.add('.anchored');
const isCodeAnnotation = (el) => {
for (const clz of el.classList) {
if (clz.startsWith('code-annotation-')) {
return true;
}
}
return false;
}
const onCopySuccess = function(e) {
// button target
const button = e.trigger;
// don't keep focus
button.blur();
// flash "checked"
button.classList.add('code-copy-button-checked');
var currentTitle = button.getAttribute("title");
button.setAttribute("title", "Copied!");
let tooltip;
if (window.bootstrap) {
button.setAttribute("data-bs-toggle", "tooltip");
button.setAttribute("data-bs-placement", "left");
button.setAttribute("data-bs-title", "Copied!");
tooltip = new bootstrap.Tooltip(button,
{ trigger: "manual",
customClass: "code-copy-button-tooltip",
offset: [0, -8]});
tooltip.show();
}
setTimeout(function() {
if (tooltip) {
tooltip.hide();
button.removeAttribute("data-bs-title");
button.removeAttribute("data-bs-toggle");
button.removeAttribute("data-bs-placement");
}
button.setAttribute("title", currentTitle);
button.classList.remove('code-copy-button-checked');
}, 1000);
// clear code selection
e.clearSelection();
}
const getTextToCopy = function(trigger) {
const outerScaffold = trigger.parentElement.cloneNode(true);
const codeEl = outerScaffold.querySelector('code');
for (const childEl of codeEl.children) {
if (isCodeAnnotation(childEl)) {
childEl.remove();
}
}
return codeEl.innerText;
}
const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
text: getTextToCopy
});
clipboard.on('success', onCopySuccess);
if (window.document.getElementById('quarto-embedded-source-code-modal')) {
const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
text: getTextToCopy,
container: window.document.getElementById('quarto-embedded-source-code-modal')
});
clipboardModal.on('success', onCopySuccess);
}
var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
var mailtoRegex = new RegExp(/^mailto:/);
var filterRegex = new RegExp("https:\/\/vedicreader\.github\.io\/vishalakshi");
var isInternal = (href) => {
return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
}
// Inspect non-navigation links and adorn them if external
var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
for (var i=0; i<links.length; i++) {
const link = links[i];
if (!isInternal(link.href)) {
// undo the damage that might have been done by quarto-nav.js in the case of
// links that we want to consider external
if (link.dataset.originalHref !== undefined) {
link.href = link.dataset.originalHref;
}
}
}
function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
const config = {
allowHTML: true,
maxWidth: 500,
delay: 100,
arrow: false,
appendTo: function(el) {
return el.parentElement;
},
interactive: true,
interactiveBorder: 10,
theme: 'quarto',
placement: 'bottom-start',
};
if (contentFn) {
config.content = contentFn;
}
if (onTriggerFn) {
config.onTrigger = onTriggerFn;
}
if (onUntriggerFn) {
config.onUntrigger = onUntriggerFn;
}
window.tippy(el, config);
}
const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
for (var i=0; i<noterefs.length; i++) {
const ref = noterefs[i];
tippyHover(ref, function() {
// use id or data attribute instead here
let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
try { href = new URL(href).hash; } catch {}
const id = href.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note) {
return note.innerHTML;
} else {
return "";
}
});
}
const xrefs = window.document.querySelectorAll('a.quarto-xref');
const processXRef = (id, note) => {
// Strip column container classes
const stripColumnClz = (el) => {
el.classList.remove("page-full", "page-columns");
if (el.children) {
for (const child of el.children) {
stripColumnClz(child);
}
}
}
stripColumnClz(note)
if (id === null || id.startsWith('sec-')) {
// Special case sections, only their first couple elements
const container = document.createElement("div");
if (note.children && note.children.length > 2) {
container.appendChild(note.children[0].cloneNode(true));
for (let i = 1; i < note.children.length; i++) {
const child = note.children[i];
if (child.tagName === "P" && child.innerText === "") {
continue;
} else {
container.appendChild(child.cloneNode(true));
break;
}
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(container);
}
return container.innerHTML
} else {
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
return note.innerHTML;
}
} else {
// Remove any anchor links if they are present
const anchorLink = note.querySelector('a.anchorjs-link');
if (anchorLink) {
anchorLink.remove();
}
if (window.Quarto?.typesetMath) {
window.Quarto.typesetMath(note);
}
if (note.classList.contains("callout")) {
return note.outerHTML;
} else {
return note.innerHTML;
}
}
}
for (var i=0; i<xrefs.length; i++) {
const xref = xrefs[i];
tippyHover(xref, undefined, function(instance) {
instance.disable();
let url = xref.getAttribute('href');
let hash = undefined;
if (url.startsWith('#')) {
hash = url;
} else {
try { hash = new URL(url).hash; } catch {}
}
if (hash) {
const id = hash.replace(/^#\/?/, "");
const note = window.document.getElementById(id);
if (note !== null) {
try {
const html = processXRef(id, note.cloneNode(true));
instance.setContent(html);
} finally {
instance.enable();
instance.show();
}
} else {
// See if we can fetch this
fetch(url.split('#')[0])
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.getElementById(id);
if (note !== null) {
const html = processXRef(id, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
} else {
// See if we can fetch a full url (with no hash to target)
// This is a special case and we should probably do some content thinning / targeting
fetch(url)
.then(res => res.text())
.then(html => {
const parser = new DOMParser();
const htmlDoc = parser.parseFromString(html, "text/html");
const note = htmlDoc.querySelector('main.content');
if (note !== null) {
// This should only happen for chapter cross references
// (since there is no id in the URL)
// remove the first header
if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
note.children[0].remove();
}
const html = processXRef(null, note);
instance.setContent(html);
}
}).finally(() => {
instance.enable();
instance.show();
});
}
}, function(instance) {
});
}
let selectedAnnoteEl;
const selectorForAnnotation = ( cell, annotation) => {
let cellAttr = 'data-code-cell="' + cell + '"';
let lineAttr = 'data-code-annotation="' + annotation + '"';
const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
return selector;
}
const selectCodeLines = (annoteEl) => {
const doc = window.document;
const targetCell = annoteEl.getAttribute("data-target-cell");
const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
const lines = annoteSpan.getAttribute("data-code-lines").split(",");
const lineIds = lines.map((line) => {
return targetCell + "-" + line;
})
let top = null;
let height = null;
let parent = null;
if (lineIds.length > 0) {
//compute the position of the single el (top and bottom and make a div)
const el = window.document.getElementById(lineIds[0]);
top = el.offsetTop;
height = el.offsetHeight;
parent = el.parentElement.parentElement;
if (lineIds.length > 1) {
const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
const bottom = lastEl.offsetTop + lastEl.offsetHeight;
height = bottom - top;
}
if (top !== null && height !== null && parent !== null) {
// cook up a div (if necessary) and position it
let div = window.document.getElementById("code-annotation-line-highlight");
if (div === null) {
div = window.document.createElement("div");
div.setAttribute("id", "code-annotation-line-highlight");
div.style.position = 'absolute';
parent.appendChild(div);
}
div.style.top = top - 2 + "px";
div.style.height = height + 4 + "px";
div.style.left = 0;
let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
if (gutterDiv === null) {
gutterDiv = window.document.createElement("div");
gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
gutterDiv.style.position = 'absolute';
const codeCell = window.document.getElementById(targetCell);
const gutter = codeCell.querySelector('.code-annotation-gutter');
gutter.appendChild(gutterDiv);
}
gutterDiv.style.top = top - 2 + "px";
gutterDiv.style.height = height + 4 + "px";
}
selectedAnnoteEl = annoteEl;
}
};
const unselectCodeLines = () => {
const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
elementsIds.forEach((elId) => {
const div = window.document.getElementById(elId);
if (div) {
div.remove();
}
});
selectedAnnoteEl = undefined;
};
// Handle positioning of the toggle
window.addEventListener(
"resize",
throttle(() => {
elRect = undefined;
if (selectedAnnoteEl) {
selectCodeLines(selectedAnnoteEl);
}
}, 10)
);
function throttle(fn, ms) {
let throttle = false;
let timer;
return (...args) => {
if(!throttle) { // first call gets through
fn.apply(this, args);
throttle = true;
} else { // all the others get throttled
if(timer) clearTimeout(timer); // cancel #2
timer = setTimeout(() => {
fn.apply(this, args);
timer = throttle = false;
}, ms);
}
};
}
// Attach click handler to the DT
const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
for (const annoteDlNode of annoteDls) {
annoteDlNode.addEventListener('click', (event) => {
const clickedEl = event.target;
if (clickedEl !== selectedAnnoteEl) {
unselectCodeLines();
const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
if (activeEl) {
activeEl.classList.remove('code-annotation-active');
}
selectCodeLines(clickedEl);
clickedEl.classList.add('code-annotation-active');
} else {
// Unselect the line
unselectCodeLines();
clickedEl.classList.remove('code-annotation-active');
}
});
}
const findCites = (el) => {
const parentEl = el.parentElement;
if (parentEl) {
const cites = parentEl.dataset.cites;
if (cites) {
return {
el,
cites: cites.split(' ')
};
} else {
return findCites(el.parentElement)
}
} else {
return undefined;
}
};
var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
for (var i=0; i<bibliorefs.length; i++) {
const ref = bibliorefs[i];
const citeInfo = findCites(ref);
if (citeInfo) {
tippyHover(citeInfo.el, function() {
var popup = window.document.createElement('div');
citeInfo.cites.forEach(function(cite) {
var citeDiv = window.document.createElement('div');
citeDiv.classList.add('hanging-indent');
citeDiv.classList.add('csl-entry');
var biblioDiv = window.document.getElementById('ref-' + cite);
if (biblioDiv) {
citeDiv.innerHTML = biblioDiv.innerHTML;
}
popup.appendChild(citeDiv);
});
return popup.innerHTML;
});
}
}
});
</script>
</div> <!-- /content -->
</body>
</html>