<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en"><generator uri="https://jekyllrb.com/" version="4.2.2">Jekyll</generator><link href="https://gmarini.com/feed.xml" rel="self" type="application/atom+xml" /><link href="https://gmarini.com/" rel="alternate" type="text/html" hreflang="en" /><updated>2024-02-17T13:51:58+11:00</updated><id>https://gmarini.com/feed.xml</id><title type="html">Gabriele Marini</title><subtitle>PhD Candidate in Computing and Information System at the University of Melbourne
</subtitle><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><entry><title type="html">My Hydejack Theme Upgrades</title><link href="https://gmarini.com/blog/blog/coding/2022-02-02-hydejack-upgrades/" rel="alternate" type="text/html" title="My Hydejack Theme Upgrades" /><published>2022-02-02T00:00:00+11:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/hydejack-upgrades</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2022-02-02-hydejack-upgrades/"><![CDATA[<!--more-->
<ul class="large-only" id="markdown-toc">
  <li><a href="#css-only-submenu" id="markdown-toc-css-only-submenu">CSS only submenu</a></li>
  <li><a href="#photo-gallery" id="markdown-toc-photo-gallery">Photo Gallery</a></li>
</ul>

<h1 id="css-only-submenu">CSS only submenu</h1>
<p>Create a new file <code class="language-text highlighter-rouge">/#jekyll-theme-hydejack/_includes/body/sub-menu.html</code></p>

<div class="language-liquid highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">menu</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">menu</span><span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="nt">for</span><span class="w"> </span><span class="nv">node</span><span class="w"> </span><span class="nt">in</span><span class="w"> </span><span class="nv">menu</span><span class="w"> </span><span class="p">%}</span>
    <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">url</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">href</span><span class="w"> </span><span class="p">%}</span>
    &lt;li <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">menu</span><span class="p">.</span><span class="nf">size</span><span class="w"> </span><span class="o">&gt;</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">%}</span> class="has_submenu" <span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>&gt;
        &lt;a
        <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nb">forloop.first</span><span class="w"> </span><span class="p">%}</span>id="_drawer--opened"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
        <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">url</span><span class="w"> </span><span class="p">%}</span>href="<span class="p">{%</span><span class="w"> </span><span class="nt">include_cached</span><span class="w"> </span>smart-url<span class="w"> </span><span class="na">url</span><span class="o">=</span>url<span class="w"> </span><span class="p">%}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>        
        class="sidebar-nav-item <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">external</span><span class="w">  </span><span class="p">%}</span>external<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>"
        <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">rel</span><span class="w"> </span><span class="p">%}</span>rel="<span class="p">{{</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">rel</span><span class="w"> </span><span class="p">}}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
    &gt;
        <span class="p">{{</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">name</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="nv">node</span><span class="p">.</span><span class="nv">title</span><span class="w"> </span><span class="p">}}</span>
    &lt;/a&gt;
    <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">node</span><span class="p">.</span><span class="nv">menu</span><span class="w"> </span><span class="p">%}</span>
        &lt;ul class="sub-menu"&gt;
            <span class="p">{%</span><span class="w"> </span><span class="nt">include</span><span class="w"> </span>body/sub-menu.html<span class="w"> </span><span class="na">menu</span><span class="o">=</span>node.menu<span class="w"> </span><span class="p">%}</span>   
        &lt;/ul&gt;     
    <span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
    &lt;/li&gt;
<span class="p">{%</span><span class="w"> </span><span class="nt">endfor</span><span class="w"> </span><span class="p">%}</span>

</code></pre></div></div>

<p>Edit <code class="language-text highlighter-rouge">/#jekyll-theme-hydejack/_includes/body/nav.html</code> and replace the content of this <code class="language-text highlighter-rouge">if</code> statement with this:</p>
<div class="language-liquid highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">site</span><span class="p">.</span><span class="nv">menu</span><span class="w"> </span><span class="p">%}</span>
   <span class="p">{%</span><span class="w"> </span><span class="nt">include</span><span class="w"> </span>body/sub-menu.html<span class="w"> </span><span class="na">menu</span><span class="o">=</span>site.menu<span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="p">%}</span>
  
</code></pre></div></div>

<p>Add a new <code class="language-text highlighter-rouge">sass</code> file <code class="language-text highlighter-rouge">sub-menu.scss</code>:</p>

<div class="language-sass highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// file: '/#jekyll-theme-hydejack/_includes/body/sub-menu.html'</span>
<span class="nc">.sub-menu</span><span class="err">{</span>
    <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="err">;</span>
    <span class="nl">margin</span><span class="p">:</span> <span class="m">0</span><span class="err">;</span>
    <span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="err">;</span>
    <span class="nl">transition</span><span class="p">:</span> <span class="n">max-height</span> <span class="m">0</span><span class="mi">.3s</span> <span class="n">ease-out</span><span class="o">,</span> <span class="n">opacity</span> <span class="m">0</span><span class="mi">.2s</span> <span class="n">linear</span><span class="err">;</span>
    <span class="nl">max-height</span><span class="p">:</span> <span class="m">0rem</span><span class="err">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="err">;</span>
    <span class="nl">margin-left</span><span class="p">:</span> <span class="m">0</span><span class="mi">.5rem</span><span class="err">;</span>
    <span class="nl">list-style</span><span class="p">:</span> <span class="nb">none</span><span class="err">;</span>
    <span class="nl">overflow</span><span class="p">:</span> <span class="nb">hidden</span><span class="err">;</span>
    <span class="nl">opacity</span><span class="p">:</span> <span class="m">0</span><span class="err">;</span>
    <span class="nl">margin-left</span><span class="p">:</span> <span class="m">20%</span><span class="err">;</span>
    <span class="nt">a</span> <span class="err">{</span>
        <span class="nl">font-size</span><span class="p">:</span> <span class="m">90%</span><span class="err">;</span>
        <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">0</span><span class="mi">.2rem</span><span class="err">;</span>
        <span class="nl">font-weight</span><span class="p">:</span> <span class="m">100</span><span class="err">;</span>
        <span class="nl">line-height</span><span class="p">:</span> <span class="m">1</span><span class="mi">.3</span><span class="err">;</span>
        <span class="nl">font-family</span><span class="p">:</span> <span class="s1">'Noto Sans'</span><span class="err">;</span>
        <span class="na">text-decoration-thickness</span><span class="p">:</span> <span class="m">1px</span><span class="err">;</span>
    <span class="err">}</span>
  <span class="err">}</span>
  
  <span class="cm">/* Show the dropdown menu on hover */
</span>  <span class="nc">.has_submenu</span><span class="nd">:hover</span> <span class="o">&gt;</span> <span class="nc">.sub-menu</span><span class="err">{</span>
      <span class="nl">transition</span><span class="p">:</span> <span class="n">max-height</span> <span class="m">0</span><span class="mi">.3s</span> <span class="n">ease-out</span><span class="o">,</span> <span class="n">opacity</span> <span class="m">0</span><span class="mi">.2s</span> <span class="n">linear</span><span class="err">;</span>
      <span class="nl">display</span><span class="p">:</span> <span class="nb">block</span><span class="err">;</span>
      <span class="nl">max-height</span><span class="p">:</span> <span class="m">10rem</span><span class="err">;</span>
      <span class="nl">opacity</span><span class="p">:</span> <span class="m">1</span><span class="err">;</span>
  <span class="err">}</span>

  <span class="err">.</span><span class="na">has_submenu</span><span class="err"> &gt; </span><span class="na">a</span><span class="p">:</span><span class="o">:</span><span class="n">after</span><span class="err">{</span>
    <span class="nl">font-family</span><span class="p">:</span> <span class="s1">'icomoon'</span> <span class="o">!</span><span class="n">important</span><span class="err">;</span>
    <span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="err">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="err">;</span>
    <span class="nl">content</span><span class="p">:</span> <span class="s2">"\ea29"</span><span class="err">;</span>
    <span class="nl">margin-left</span><span class="p">:</span> <span class="m">1</span><span class="mi">.5rem</span><span class="err">;</span>
    <span class="nl">margin-top</span><span class="p">:</span> <span class="m">0</span><span class="mi">.7%</span><span class="err">;</span>
    <span class="nl">font-size</span><span class="p">:</span> <span class="m">90%</span><span class="err">;</span>
    <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="err">;</span>
<span class="err">}</span>

<span class="nc">.sidebar-nav</span> <span class="o">&gt;</span> <span class="nt">ul</span> <span class="o">&gt;</span> <span class="nc">.has_submenu</span> <span class="o">&gt;</span> <span class="nc">.sub-menu</span><span class="err">{</span>
    <span class="nl">margin-left</span><span class="p">:</span> <span class="m">-0</span><span class="mi">.2rem</span><span class="err">;</span>  
    <span class="nl">margin-top</span><span class="p">:</span> <span class="m">-0</span><span class="mi">.1rem</span><span class="err">;</span>
  <span class="err">}</span> 

</code></pre></div></div>

<p>Import the new file adding this at the end of <code class="language-text highlighter-rouge">my-style.scss</code>:</p>
<div class="language-sass highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">@import</span> <span class="s">"sub-menu";</span>
</code></pre></div></div>

<p>Finally, add your submenu (and sub-submenus etc…) to your <code class="language-text highlighter-rouge">_config.yml</code>:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="na">menu</span><span class="pi">:</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>             <span class="s">Blog</span>
    <span class="na">url</span><span class="pi">:</span>               <span class="s">/blog</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>             <span class="s">Photo Gallery</span>
    <span class="na">url</span><span class="pi">:</span>               <span class="s">/photo/</span>
    <span class="na">menu</span><span class="pi">:</span>
      <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>  <span class="s">test1</span>
        <span class="na">url</span><span class="pi">:</span>    <span class="s">/photo/</span>
      <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>  <span class="s">test2</span>
        <span class="na">url</span><span class="pi">:</span>    <span class="s">/photo/</span>
        <span class="na">menu</span><span class="pi">:</span>
        <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>  <span class="s">test21</span>
          <span class="na">url</span><span class="pi">:</span>    <span class="s">/photo/</span>
        <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>  <span class="s">test22</span>
          <span class="na">url</span><span class="pi">:</span>    <span class="s">/photo/</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>             <span class="s">Résumé\CV</span>
    <span class="na">url</span><span class="pi">:</span>               <span class="s">/resume/</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>             <span class="s">Publications</span>
    <span class="na">url</span><span class="pi">:</span>               <span class="s">/resume/#publications</span>
  <span class="pi">-</span> <span class="na">title</span><span class="pi">:</span>             <span class="s">Projects</span>
    <span class="na">url</span><span class="pi">:</span>               <span class="s">/projects/</span>
</code></pre></div></div>

<h1 id="photo-gallery">Photo Gallery</h1>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[]]></summary></entry><entry><title type="html">How to set up your own cloud-based code editor</title><link href="https://gmarini.com/blog/blog/coding/2021-12-21-code-server/" rel="alternate" type="text/html" title="How to set up your own cloud-based code editor" /><published>2021-12-21T00:00:00+11:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/code-server</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2021-12-21-code-server/"><![CDATA[<p>I like my website to be lightweight and static, which is one of the reasons I stuck with Jekyll!
One of the pros is being able to <em>write articles in Markdown</em> which I find quicker, easier and less distracting than Google Docs, Word or even LaTeX.</p>

<p>I also like to have full control over my website and my server, which is why I set up Jekyll in a dedicated server through a cheap hosting service and my own domain from Google Domains.</p>

<p>When self-hosting your website however, adding new posts or even making small changes to your website can be rather time consuming. This is especially true when working from different machines.</p>

<p>I eventually came across <a href="https://coder.com/">Coder</a> and the free cloud-editing service behind it <a href="https://github.com/coder/code-server">code-server</a>.</p>

<!--more-->

<ul class="large-only" id="markdown-toc">
  <li><a href="#getting-your-dedicated-server-up-and-running" id="markdown-toc-getting-your-dedicated-server-up-and-running">Getting your dedicated server up and running</a>    <ul>
      <li><a href="#self-hosting" id="markdown-toc-self-hosting">Self hosting</a></li>
      <li><a href="#domain" id="markdown-toc-domain">Domain</a></li>
    </ul>
  </li>
  <li><a href="#setting-up-your-server" id="markdown-toc-setting-up-your-server">Setting up your server</a>    <ul>
      <li><a href="#setting-up-ssh-access" id="markdown-toc-setting-up-ssh-access">Setting up SSH access</a></li>
    </ul>
  </li>
  <li><a href="#setting-up-nginx-and-code-server" id="markdown-toc-setting-up-nginx-and-code-server">Setting up NGINX and code-server</a></li>
  <li><a href="#configuring-nginx" id="markdown-toc-configuring-nginx">Configuring NGINX</a></li>
  <li><a href="#setting-up-code-server" id="markdown-toc-setting-up-code-server">Setting up code-server</a></li>
  <li><a href="#configuring-code-server" id="markdown-toc-configuring-code-server">Configuring <code class="language-text highlighter-rouge">code-server</code></a></li>
  <li><a href="#exposing-code-server-with-nginx" id="markdown-toc-exposing-code-server-with-nginx">Exposing <code class="language-text highlighter-rouge">code-server</code> with <code class="language-text highlighter-rouge">nginx</code></a>    <ul>
      <li><a href="#opening-the-servers-ports" id="markdown-toc-opening-the-servers-ports">Opening the server’s ports</a></li>
      <li><a href="#forwarding-port-to-subdomain" id="markdown-toc-forwarding-port-to-subdomain">Forwarding port to subdomain</a></li>
      <li><a href="#get-an-ssl-certificate-with-lets-encrypt" id="markdown-toc-get-an-ssl-certificate-with-lets-encrypt">Get an SSL certificate with <code class="language-text highlighter-rouge">Let's Encrypt!</code></a></li>
    </ul>
  </li>
</ul>

<h2 id="getting-your-dedicated-server-up-and-running">Getting your dedicated server up and running</h2>
<h3 id="self-hosting">Self hosting</h3>
<p>After some research I ended up going with <a href="https://www.vultr.com/products/cloud-compute/#pricing">Vultr</a> as a hosting provider.
Being mine a static, lightweight website, I do not really need a powerful server with lots of space.</p>

<table>
  <thead>
    <tr>
      <th>Geekbench Score</th>
      <th>Storage</th>
      <th>CPU</th>
      <th>Memory</th>
      <th>Bandwidth</th>
      <th>Monthly Price *</th>
      <th>Hourly Price *</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td>N/A</td>
      <td>10 GB SSD</td>
      <td>1 CPU</td>
      <td>0.5 GB</td>
      <td>0.50 TBIPv6</td>
      <td>$2.50</td>
      <td>$0.00</td>
    </tr>
    <tr>
      <td>N/A</td>
      <td>10 GB SSD</td>
      <td>1 CPU</td>
      <td>0.5 GB</td>
      <td>0.50 TB</td>
      <td>$3.50</td>
      <td>$0.01</td>
    </tr>
    <tr>
      <td>2413</td>
      <td>25 GB SSD</td>
      <td>1 CPU</td>
      <td>1 GB</td>
      <td>1 TB</td>
      <td>$5.00</td>
      <td>$0.01</td>
    </tr>
    <tr>
      <td>…</td>
      <td>…</td>
      <td>…</td>
      <td>…</td>
      <td>…</td>
      <td>…</td>
      <td>…</td>
    </tr>
  </tbody>
</table>

<p>I opted for the last option with 25gb SSD and 1GB of RAM which is fairly cheap at AU$ 5 per month.</p>

<h3 id="domain">Domain</h3>
<p>In order to make your website secure and trusted you need to set up SSL and HTTPS, and this can only be done with a domain and not with pure IP addresses.</p>

<p>I chose Google Domains in this case as it is fairly cheap and managing DNS records is easy.</p>

<p>Once you set up your domain you can also set up your subdomain for whatever services you might need. This is what my DNS records table look like:</p>

<table>
  <thead>
    <tr>
      <th style="text-align: center">Host name</th>
      <th style="text-align: center">Type</th>
      <th>TTL</th>
      <th>Data</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td style="text-align: center">gmarini.com</td>
      <td style="text-align: center">A</td>
      <td>1 hour</td>
      <td>45.76.124.120</td>
    </tr>
    <tr>
      <td style="text-align: center">*.code.gmarini.com</td>
      <td style="text-align: center">A</td>
      <td>1 hour</td>
      <td>45.76.124.120</td>
    </tr>
    <tr>
      <td style="text-align: center">code.gmarini.com</td>
      <td style="text-align: center">A</td>
      <td>1 hour</td>
      <td>45.76.124.120</td>
    </tr>
    <tr>
      <td style="text-align: center">www.gmarini.com</td>
      <td style="text-align: center">CNAME</td>
      <td>1 hour</td>
      <td>@.</td>
    </tr>
    <tr>
      <td style="text-align: center">www.code.gmarini.com</td>
      <td style="text-align: center">A</td>
      <td>1 hour</td>
      <td>45.76.124.120</td>
    </tr>
  </tbody>
</table>

<p>DNSSEC is disabled and I am using Google Domains servers.</p>

<h2 id="setting-up-your-server">Setting up your server</h2>
<p>Now it’s time to set up your server and environment. The first thing you want to do is to set up SSH.</p>

<h3 id="setting-up-ssh-access">Setting up SSH access</h3>

<p class="message"><strong>HEADS UP</strong>: <strong>NEVER</strong> share your SSH public and/or private keys on the internet! The keys down here have been generated for the purpose of this tutorial and promptly discarded!</p>

<p>This will differ depending on your provider. On Vultr you can go to <a href="https://my.vultr.com/settings/#settingssshkeys">https://my.vultr.com/settings/#settingssshkeys</a>, click on the “+” button and then “Add SSH Key”.
Give it a name to remind you what is the key for, something like <code class="language-text highlighter-rouge">vultr-server-blog</code>.</p>

<p>Now you need to create your SSH key, I found this guide fairly simple and straight to the point <a href="https://www.vultr.com/docs/how-do-i-generate-ssh-keys/">Create an SSH Key with OpenSSH</a>.</p>

<p>In short:</p>
<ul>
  <li>Open <code class="language-text highlighter-rouge">PowerShell</code></li>
  <li>Type in <code class="language-text highlighter-rouge">ssh-keygen -t rsa -b 4096 -C "your_email@example.com"</code></li>
  <li>Confirm the file location</li>
  <li>Enter a passphrase for your key</li>
  <li>Enter the same passphrase again</li>
</ul>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">PS</span><span class="w"> </span><span class="nx">C:\Users\Giaga</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">ssh-keygen</span><span class="w"> </span><span class="nt">-t</span><span class="w"> </span><span class="nx">rsa</span><span class="w"> </span><span class="nt">-b</span><span class="w"> </span><span class="nx">4096</span><span class="w"> </span><span class="nt">-C</span><span class="w"> </span><span class="s2">"your_email@example.com"</span><span class="w">
</span><span class="n">Generating</span><span class="w"> </span><span class="nx">public/private</span><span class="w"> </span><span class="nx">rsa</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="nx">pair.</span><span class="w">
</span><span class="n">Enter</span><span class="w"> </span><span class="nx">file</span><span class="w"> </span><span class="nx">in</span><span class="w"> </span><span class="nx">which</span><span class="w"> </span><span class="nx">to</span><span class="w"> </span><span class="nx">save</span><span class="w"> </span><span class="nx">the</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="p">(</span><span class="n">C:\Users\Giaga/.ssh/id_rsa</span><span class="p">):</span><span class="w">
</span><span class="n">Enter</span><span class="w"> </span><span class="nx">passphrase</span><span class="w"> </span><span class="p">(</span><span class="n">empty</span><span class="w"> </span><span class="nx">for</span><span class="w"> </span><span class="nx">no</span><span class="w"> </span><span class="nx">passphrase</span><span class="p">):</span><span class="w">
</span><span class="n">Enter</span><span class="w"> </span><span class="nx">same</span><span class="w"> </span><span class="nx">passphrase</span><span class="w"> </span><span class="nx">again:</span><span class="w">
</span><span class="n">Your</span><span class="w"> </span><span class="nx">identification</span><span class="w"> </span><span class="nx">has</span><span class="w"> </span><span class="nx">been</span><span class="w"> </span><span class="nx">saved</span><span class="w"> </span><span class="nx">in</span><span class="w"> </span><span class="nx">C:\Users\Giaga/.ssh/id_rsa</span><span class="w">
</span><span class="n">Your</span><span class="w"> </span><span class="nx">public</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="nx">has</span><span class="w"> </span><span class="nx">been</span><span class="w"> </span><span class="nx">saved</span><span class="w"> </span><span class="nx">in</span><span class="w"> </span><span class="nx">C:\Users\Giaga/.ssh/id_rsa.pub</span><span class="w">
</span><span class="n">The</span><span class="w"> </span><span class="nx">key</span><span class="w"> </span><span class="nx">fingerprint</span><span class="w"> </span><span class="nx">is:</span><span class="w">
</span><span class="n">SHA256:gn18jblysbS0XESUadoWM/2pEFJzSYSCEq1lvstPWJQ</span><span class="w"> </span><span class="nx">your_email</span><span class="err">@</span><span class="nx">example.com</span><span class="w">
</span><span class="n">The</span><span class="w"> </span><span class="nx">key</span><span class="s1">'s randomart image is:
+---[RSA 4096]----+
|     .o . .+*B.  |
|     . = o.+X..  |
|      *  Eo+o+ ..|
|     + o. .*o  ..|
|    . o S.B.+ .  |
|       +o= B .   |
|      ..o.B      |
|       o.o       |
|        ..       |
+----[SHA256]-----+
</span></code></pre></div></div>

<p>Now it’s usually a good idea to make a backup of the private key as they cannot be recovered if lost!
Copy the file somewhere safe, possibly NOT on the cloud!</p>

<p>You can now get your public key from the PowerShell by typing:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kr">base</span><span class="p">)</span><span class="w"> </span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\Users\Giaga</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">type</span><span class="w"> </span><span class="nv">$</span><span class="nn">env</span><span class="p">:</span><span class="nv">USERPROFILE</span><span class="nx">\.ssh\id_rsa.pub</span><span class="w">
</span><span class="n">ssh-rsa</span><span class="w"> </span><span class="nx">AAAAB3NzaC1yc2EAAAADAQABAAACAQCsQ/tUy8BcGpoWAMCjHHrucZKJLCuOJpFGFxTxhPDy5xet6khpxpfci/cLgyIUm4DEZSGQpCMszl3MXGJ7QuFDoSBue</span><span class="o">+</span><span class="nx">MebKH9ZC1sK27</span><span class="o">+</span><span class="nx">DFaFJXvDdD3OhFyTgqwrEIGZ7FKr7GNdMpER</span><span class="o">+</span><span class="nx">fItUsAKx3ylwBLU4/dh13jsKE968RtApRkluch8M/L3MfzFBaDWJUPO0E</span><span class="o">+</span><span class="nx">boQv8nOhpZqPU</span><span class="o">+</span><span class="nx">WL2mzM4j/QA5BGssCGxWOm3zV4m4FTJGlJetN</span><span class="o">++</span><span class="nx">5P67qTkMyu3U/UW4Y1ItStwnU8Wz/QvRGppOChJgYoAtWfR6MLIXd2iJ1G6IVwtLokVuFnJZFRnOo4BrdabPmqIPD</span><span class="o">+</span><span class="nx">t3DtBfrsRhXF75IshMrR14r3EYFZKOsX3iJGEWfe3hBmvkoAOmtrV0n70cERcRQdnb8kdgjP4jT60gkt9GMz9UMmAMsMGfhRl3JSMv1bbLoe849xS5T9j/SCMSxQeb6XI6V3mBpFkmIaINs6VwV4/AJGZ9JA/tA175nDrCLsyyo5cGtShNNCYnHSGcFkCc4AWipTTPLdbak3jHetPyK6xn1ExRHKnJd/n65AUFdHNkNj2qj1fSFaWGxOJ706Am6nDWppUVSgWys1J</span><span class="o">+</span><span class="nx">reXvAt7CFO8lPNhkcWxfJsAfcPpHkvF6fBnPScL8HXa9DwMzbfDNkN7aMkF35sY1z/c34w</span><span class="o">==</span><span class="w"> </span><span class="n">your_email</span><span class="err">@</span><span class="nx">example.com</span><span class="w">
</span></code></pre></div></div>

<p>Copy everything from <code class="language-text highlighter-rouge">ssh-rsa</code> to your email included and paste it onto the textbox from the previous point.</p>

<p>That’s it! You can now test your SSH on your PowerShell with</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kr">base</span><span class="p">)</span><span class="w"> </span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\Users\Giaga</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">ssh</span><span class="w"> </span><span class="nt">-i</span><span class="w"> </span><span class="nx">C:\Users\Giaga/.ssh/id_rsa</span><span class="w"> </span><span class="nx">username</span><span class="err">@</span><span class="nx">123.45.678.910</span><span class="w">
</span><span class="n">username</span><span class="err">@</span><span class="nx">123.45.678.910</span><span class="s1">'s password:
[...]
username@vultr-server:~$
</span></code></pre></div></div>

<p>If you had already set up your domain and DNS you can also start an SSH session using your domain:</p>

<div class="language-powershell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">(</span><span class="kr">base</span><span class="p">)</span><span class="w"> </span><span class="n">PS</span><span class="w"> </span><span class="nx">C:\Users\Giaga</span><span class="err">&gt;</span><span class="w"> </span><span class="nx">ssh</span><span class="w"> </span><span class="nt">-i</span><span class="w"> </span><span class="nx">C:\Users\Giaga/.ssh/id_rsa</span><span class="w"> </span><span class="nx">username</span><span class="err">@</span><span class="nx">gmarini.com</span><span class="w">
</span><span class="n">The</span><span class="w"> </span><span class="nx">authenticity</span><span class="w"> </span><span class="nx">of</span><span class="w"> </span><span class="nx">host</span><span class="w"> </span><span class="s1">'gmarini.com (45.76.124.120)'</span><span class="w"> </span><span class="nx">can</span><span class="s1">'t be established.
ED25519 key fingerprint is SHA256:XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX.
This host key is known by the following other names/addresses:
    C:\Users\Giaga/.ssh/known_hosts:4: 45.76.124.120
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes
Warning: Permanently added '</span><span class="nx">gmarini.com</span><span class="s1">' (ED25519) to the list of known hosts.
username@vultr-server:~$
</span></code></pre></div></div>

<h2 id="setting-up-nginx-and-code-server">Setting up NGINX and code-server</h2>
<p>The two most popular choices for web servers are <code class="language-text highlighter-rouge">apache</code> and <code class="language-text highlighter-rouge">nginx</code>. I have tried them both but personally I find <code class="language-text highlighter-rouge">nginx</code> easier to deal with and more intuitive.</p>

<p>Installing <code class="language-text highlighter-rouge">nginx</code> is relatively simple:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>apt update
<span class="nb">sudo </span>apt <span class="nb">install </span>nginx
</code></pre></div></div>

<p>You can then adjust the Firewall rules with <code class="language-text highlighter-rouge">ufw</code>.
First list all available apps:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>username@vultr-server:~<span class="nv">$ </span><span class="nb">sudo </span>ufw app list
<span class="o">[</span><span class="nb">sudo</span><span class="o">]</span> password <span class="k">for </span>username:
Available applications:
  Nginx Full
  Nginx HTTP
  Nginx HTTPS
  OpenSSH
</code></pre></div></div>

<p>Then allow the ones you need:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ufw allow <span class="s1">'Nginx HTTP'</span>
<span class="nb">sudo </span>ufw allow <span class="s1">'Nginx HTTPS'</span>
</code></pre></div></div>

<p>You can always manage the status of <code class="language-text highlighter-rouge">nginx</code> with:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl status nginx
<span class="nb">sudo </span>systemctl start nginx
<span class="nb">sudo </span>systemctl stop nginx
<span class="nb">sudo </span>systemctl restart nginx
<span class="nb">sudo </span>systemctl reload nginx
<span class="nb">sudo </span>systemctl <span class="nb">enable </span>nginx
<span class="nb">sudo </span>systemctl disable nginx
</code></pre></div></div>

<h2 id="configuring-nginx">Configuring NGINX</h2>
<p>Once NGINX is set up you can start serving your own static website by pointing at the folder with the file <code class="language-text highlighter-rouge">index.html</code>.</p>

<p>The way NGINX works is by using configuration files contained in  two main folders <code class="language-text highlighter-rouge">sites-available</code> and <code class="language-text highlighter-rouge">sites-enabled</code>.</p>

<p>The usual process is this:</p>
<ul>
  <li>Create a new configuration file for your wite in the <code class="language-text highlighter-rouge">sites-available</code> folder</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nano /etc/nginx/sites-available/gmarini.com
</code></pre></div></div>

<ul>
  <li>Edit your configuration to point to your website’s root folder. E.g.:</li>
</ul>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
        listen 80;
        listen [::]:80;

        root /var/www/gmarini.com/html;
        index index.html index.htm index.nginx-debian.html;

        server_name gmarini.com www.gmarini.com;

        location / {
                try_files $uri $uri/ =404;
        }
}
</code></pre></div></div>

<ul>
  <li>Create a symlink to the file you just created into the <code class="language-text highlighter-rouge">sites-enabled</code> folder</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo ln</span> <span class="nt">-s</span> /etc/nginx/sites-available/gmarini.com /etc/nginx/sites-enabled/
</code></pre></div></div>

<ul>
  <li>Test your file to make sure there are no errors:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nginx <span class="nt">-t</span>
</code></pre></div></div>

<ul>
  <li>Restart nginx:</li>
</ul>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl restart nginx
</code></pre></div></div>

<h2 id="setting-up-code-server">Setting up code-server</h2>

<p>Now that you know how to configure NGINX is time to set up Visual Studio Code!
While Visual Studio Code is not officially made to be run in a browser, the fact that it’s developed on top of GitHub’s Electron means that it is pretty much a webapp itself. In fact VScode uses Chromium engine for rendering and <code class="language-text highlighter-rouge">node.js</code> to handle local APIs, making it easily portable to the web!</p>

<p>As explained in the intro, a web port of VSCode called <code class="language-text highlighter-rouge">code-server</code> can be found <a href="https://github.com/coder/code-server">here</a>.</p>

<p>So first things first, let’s install <code class="language-text highlighter-rouge">code-server</code> by using the official install script:</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-fsSL</span> https://code-server.dev/install.sh | sh
</code></pre></div></div>

<p>The <a href="https://coder.com/docs/code-server/latest/guide">documentation</a> is quite detailed and well done! For reference, the next two steps are a quick summary of these two bits:</p>
<ol>
  <li><a href="https://coder.com/docs/code-server/latest/guide#expose-code-server">Expose code-server</a></li>
  <li><a href="https://coder.com/docs/code-server/latest/guide#https-and-self-signed-certificates">HTTPS and self-signed certificates</a></li>
</ol>

<h2 id="configuring-code-server">Configuring <code class="language-text highlighter-rouge">code-server</code></h2>
<p>Code-server uses password authentication by default. The password can be find in its config file located at <code class="language-text highlighter-rouge">~/.config/code-server/config.yaml</code>
We can also set the binding address and port.</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nano <span class="sb">`</span>~/.config/code-server/config.yaml<span class="sb">`</span>
</code></pre></div></div>
<p>My config file looks something like this</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">bind-addr</span><span class="pi">:</span> <span class="s">0.0.0.0:8080</span>
<span class="na">auth</span><span class="pi">:</span> <span class="s">password</span>
<span class="na">password</span><span class="pi">:</span> <span class="s">xxxxxxxxxxxxxxxxx</span>
<span class="na">cert</span><span class="pi">:</span> <span class="no">false</span>
</code></pre></div></div>

<h2 id="exposing-code-server-with-nginx">Exposing <code class="language-text highlighter-rouge">code-server</code> with <code class="language-text highlighter-rouge">nginx</code></h2>
<h3 id="opening-the-servers-ports">Opening the server’s ports</h3>
<p>Let’s also make sure that we our Vultr server allows connections through the port we just chose. Most providers will handle Firewall rules through security groups.
You can create a new security group accepting TCP and UDP connections from the port you have chosen (<code class="language-text highlighter-rouge">8080</code> in this case) and from any ip/subent usually denoted as <code class="language-text highlighter-rouge">0.0.0.0/0</code>.
Sometimes you might have to create two different rules, one for TCP and one of UDP connections, with the same port and IP range.</p>

<h3 id="forwarding-port-to-subdomain">Forwarding port to subdomain</h3>
<p>Now that <code class="language-text highlighter-rouge">code-server</code> is set up we can link it with the subdomain we created earlier <code class="language-text highlighter-rouge">code.gmarini.com</code>.</p>

<p>Let’s create a new <code class="language-text highlighter-rouge">nginx</code> configuration file for <code class="language-text highlighter-rouge">code-server</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo touch</span> /etc/nginx/sites-available/code-server.conf
</code></pre></div></div>
<p>The server configuration should look something like this:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
    listen 80;
    listen [::]:80;
    server_name code.gmarini.com www.code.gmarini.com;

        location / {
                proxy_pass http://127.0.0.1:8080/;
                proxy_set_header Host $host;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection upgrade;
                proxy_set_header Accept-Encoding gzip;
        }
}
</code></pre></div></div>

<p>Now let’s enable the new configuration</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo ln</span> <span class="nt">-s</span> ../sites-available/code-server /etc/nginx/sites-enabled/code-server.conf
</code></pre></div></div>

<p>And reload <code class="language-text highlighter-rouge">nginx</code></p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>nginx <span class="nt">-t</span> <span class="o">&amp;&amp;</span> <span class="nb">sudo </span>nginx <span class="nt">-s</span> reload
</code></pre></div></div>

<h3 id="get-an-ssl-certificate-with-lets-encrypt">Get an SSL certificate with <code class="language-text highlighter-rouge">Let's Encrypt!</code></h3>

<p>Now that the server is ready and the IP is linked to the domain, we need to set up an SSL certificate for the domain itself. We can self-validate our domain by using <code class="language-text highlighter-rouge">certbot</code>.</p>

<p>First let’s install <code class="language-text highlighter-rouge">certbot</code></p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>add-apt-repository ppa:certbot/certbot
</code></pre></div></div>

<p>Then let’s make sure the right ports are open</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>ufw allow 80
<span class="nb">sudo </span>ufw allow 443
</code></pre></div></div>

<p>Finally, we can certify our domain by providing our email address:</p>

<p>Let’s encrypt</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>certbot <span class="nt">--non-interactive</span> <span class="nt">--redirect</span> <span class="nt">--agree-tos</span> <span class="nt">--nginx</span> <span class="nt">-d</span> code.gmarini.com <span class="nt">-m</span> giaga7@gmail.com
</code></pre></div></div>

<p>Just follow the prompts and you should be ready to go! Certbot will automatically add the certificate to your nginx configuration file <code class="language-text highlighter-rouge">code-server.conf</code> which should look like something like this in the end:</p>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>server {
    server_name code.gmarini.com www.code.gmarini.com;

        location / {
                proxy_pass http://127.0.0.1:8080/;
                proxy_set_header Host $host;
                proxy_set_header Upgrade $http_upgrade;
                proxy_set_header Connection upgrade;
                proxy_set_header Accept-Encoding gzip;
        }

    listen [::]:443 ssl; # managed by Certbot
    listen 443 ssl; # managed by Certbot
    ssl_certificate /etc/letsencrypt/live/code.gmarini.com/fullchain.pem; # managed by Certbot
    ssl_certificate_key /etc/letsencrypt/live/code.gmarini.com/privkey.pem; # managed by Certbot
    include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot
    ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot
}
server {
    if ($host = code.gmarini.com) {
        return 301 https://$host$request_uri;
    } # managed by Certbot


    listen 80;
    listen [::]:80;
    server_name code.gmarini.com www.code.gmarini.com;
    return 404; # managed by Certbot
</code></pre></div></div>

<p>Finally, we can restart <code class="language-text highlighter-rouge">code-server</code> and test it out by navigating to our subdomain!</p>

<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>systemctl restart code-server@<span class="nv">$USER</span>
</code></pre></div></div>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[I like my website to be lightweight and static, which is one of the reasons I stuck with Jekyll! One of the pros is being able to write articles in Markdown which I find quicker, easier and less distracting than Google Docs, Word or even LaTeX. I also like to have full control over my website and my server, which is why I set up Jekyll in a dedicated server through a cheap hosting service and my own domain from Google Domains. When self-hosting your website however, adding new posts or even making small changes to your website can be rather time consuming. This is especially true when working from different machines. I eventually came across Coder and the free cloud-editing service behind it code-server.]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gmarini.com/assets/gabryxx7/img/code-server-img.png" /><media:content medium="image" url="https://gmarini.com/assets/gabryxx7/img/code-server-img.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">I’m Back!</title><link href="https://gmarini.com/blog/blog/life/2021-08-28-im-back/" rel="alternate" type="text/html" title="I’m Back!" /><published>2021-08-28T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/life/im-back</id><content type="html" xml:base="https://gmarini.com/blog/blog/life/2021-08-28-im-back/"><![CDATA[<p>So I disappeared for a while but here I am again! Many things have changed in the past year which is also why I was not very active in writing more content!
Here are some updates regarding my PhD journey, my career and the life in Australia during the COVID pandemic…</p>

<ul class="large-only" id="markdown-toc">
  <li><a href="#phd" id="markdown-toc-phd">PhD</a></li>
  <li><a href="#internship" id="markdown-toc-internship">Internship</a></li>
  <li><a href="#life-in-australia" id="markdown-toc-life-in-australia">Life in Australia</a></li>
  <li><a href="#other-stuff" id="markdown-toc-other-stuff">Other Stuff</a></li>
</ul>

<h2 id="phd">PhD</h2>
<p>Well I haven’t been dealt the best hand when it comes to my PhD. My project was all about tracking people’s location indoor, studying their interactions and how they move around buildings.
Thanks to COVID, buildings were pretty much empty in 2020 and 2021 so most of my work went out of the window.
I’m working on my second big first-author paper and hoping to get another one before the end of the year!</p>

<p>As of now I would like to submit my thesis my Jan/Feb 2022 but who knows! As of 3 months ago we were enjoying life again, travelling everywhere. And now we’ve been on an off lockdown since June, too unpredictable to say!</p>

<h2 id="internship">Internship</h2>
<blockquote>
  <p>Summer Data Science Internship at Nokia Bells Lab, Cambridhge, UK</p>
</blockquote>

<p>Overall the biggest update career-wise is my internship at <strong>Nokia Bells Lab</strong>.
Despite it being a remote internship, it was a very interesting and eye opening experience nevertheless. As I found out during these three months, research in industry is quite a bit different from research in Academia.
For instance one of the things I noticed is the ethics process: In some Universities this process can take anywhere from a few weeks to a few months, and it is extremely important to not go out of the lines defined by your ethics application.
In industry however it would appear that things are a bit more streamlined and open-ended, considerably speeding up the process of getting ethics and starting the data collection.</p>

<p>The initial project proposal was along the line of sensing and social dynamics. The idea was to match the “offline” interaction of a team with its “online” counterpart. In this scenario the offline part would be tracked through an indoor localization system while the online aspect constitutes of Slack messages, channels and interactions.</p>

<p>We then set out to deploy the indoor localization system consisting of RFID-based active badges and Ethernet receivers.
In the few weeks since the beginning on the internship I received a lot of interesting devices, tinkering with all sorts of systems and programming languages. A quick list of what I’ve worked on in the first month or two of the internship:</p>
<ul>
  <li>Developed a Slack Bot to download messages and workspace meta-data automatically (<a href="https://github.com/Gabryxx7/slack_exporter">GitHub Link</a>)
    <ul>
      <li>The exporter is multi-threaded and takes care of load balancing the calls to avoid getting rate limited by the Slack API</li>
    </ul>
  </li>
  <li>Adapted, fixed and implemented a set of NLP tools to automatically analyse the Slack corpus:
    <ul>
      <li>LIWC</li>
      <li>10 Dimensions</li>
      <li>Integrative Complexity</li>
      <li>Sentimnent Analysis through Flair</li>
    </ul>
  </li>
  <li>Implemented the above NLP tools in an encrypted, secure and multi-threaded Python Flask Server service  (<a href="https://github.com/Gabryxx7/nlp-flask-server">GitHub Link</a>)</li>
  <li>Implemented a server to collect RFID data from the active badges</li>
  <li>Configure the Active Badges through Serial Port</li>
  <li>Learnt Data Analysis and Machine Learning on Python, Pandas and MatPlotlib!
    <ul>
      <li>Not sure whether I enjoy it more than R</li>
      <li>I have written some pretty cool functions to export graphics and LaTeX code for density and correlation plots  (<a href="https://github.com/Gabryxx7/python_utils">GitHub Link</a>)</li>
    </ul>
  </li>
  <li>Written a not-so-simple multi-threaded R script to clean up and complete LaTeX references in <code class="language-text highlighter-rouge">.bib</code> files   (<a href="https://github.com/Gabryxx7/bibtex-cleaneR">GitHub Link</a>)</li>
</ul>

<h2 id="life-in-australia">Life in Australia</h2>
<blockquote>
  <p><strong>As of July 14th 2021 I’m officially a Permanent Resident (PR) in Australia!!</strong></p>
</blockquote>

<p>I submitted an EOI (Expression of Interest) for the <a href="https://immi.homeaffairs.gov.au/visas/getting-a-visa/visa-listing/global-talent-visa-858">Global Talent Visa Subclass 858</a> on the 29th of January 2021. I then got the invitation to apply on the 1st of June 2021.
I prepared all the documents proving my “talent” and the ability to earn quite a high salary eventually, plus police clearance and criminal record from Italy. Once I submitted the actual application with all the documents I had to do a Visa medical check.
I booked the first available appointment, which was the 14th of August 2021!! Quite late and I was running the risk of lockdown being in place at that time, further delaying my PR. As a matter of fact, we were in lockdown on that day, and still are at the time of writing :(</p>

<p>I was obviously not going to wait until August and I wrote a little <a href="https://gist.github.com/Gabryxx7/3e7fff8006c5e3d7f2a6ff4031afd34b">Python+Selenium script</a> to automatically check the BUPA medical services website and modify my appointment whenever an appointment closer to the current day would be available.
I ran the script for a few hours on the 28th of June morning and what do you know, it automatically changed my appointment to the day after,t he 29th of June 2021!</p>

<p>Once I did the medical check, I waited 2 weeks and I eventually got my PR! What a relief, at least I’ll be able to live in Australia beyond my student visa expiration date. But most importantly, I’ll be more easily hirable by any company in Australia!</p>

<h2 id="other-stuff">Other Stuff</h2>
<p>I updated the website with a few new features. Most importantly I ended up buying the awesome <a href="https://hydejack.com/">Hydejack Pro</a> theme for Jekyll</p>

<p>Well this part is the one that got affected the most and not just because of the pandemic! I have managed to lose 14kg since the beginning on 2020, went from 80kg to 66kg and I finally learnt the handstand properly (I can still walk for quite a few steps!).</p>

<p>Differently from Europe, the public transport in Australia just sucks. It’s expensive, rather slow, and the stations do not seem to be well spread out. I got a bike as soon as I moved to Melbourne and it has been <strong>essential</strong>.
But I started to feel the need for more adventures, especially in those few weekends that Victoria was not in lockdown! So I got myself a cheap-ish second hand car, a <strong>Toyota Rav4 2011 (Manual)</strong> to drive around Australia and go camping and hiking!</p>
<ul>
  <li>I chucked a mattress and two pillows in it. Got all the camping and cooking gear for when I’m on the road!</li>
  <li>So far I drove ~30 000km already.</li>
  <li>Went to Mungo desert twice in NSW</li>
  <li>Drove all along NSW and Queensland all the way up to Noosa (absolute heaven)</li>
  <li>Drove back to Melbourne alone on a 19h trip from Noosa!</li>
  <li>Drove a few times to the Great Ocean Road, on the snow in Mt. Donna Buang, and of course Phillip Island, Mornington Peninsula and Wilson’s Prom!</li>
</ul>

<p>Since I got a car of my own, I thought it’d be nice to do more water-based activities so I also bought a Stand-Up Paddleboard (SUP)! I brought it all the way to Queensland during my end of the year trip in 2020/2021.
Unfortunately the paddleboard was defective but I got it replaced for free with a newer model (<a href="https://aztronsports.com/products/details.html?id=76">Aztron Terra</a>)!</p>

<p>I then got stuck in Queensland due to borders closure caused by a COVID outbreak. I took the chance to get my PADI open water scuba diving license while in the Gold Coast, Queensland!</p>

<p>Photography wise, I got myself a <a href="https://www.sony.com.au/electronics/camera-lenses/sel70200gm">Sony 70-200m f2.8 G-master</a> lens! I thought I would end up not using it much but oh boy! It’s incredibly sharp and fast! Perfect for portraits :)</p>

<p>I’m not sure whether I have more updates since the past year! I’ll add them whenever I remember them :)</p>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="life" /><summary type="html"><![CDATA[A little update on what's been going on!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gmarini.com/assets/gabryxx7/img/DSC06966.jpg" /><media:content medium="image" url="https://gmarini.com/assets/gabryxx7/img/DSC06966.jpg" xmlns:media="http://search.yahoo.com/mrss/" /></entry><entry><title type="html">Web data scraping with Python, Selenium and BeautifulSoup</title><link href="https://gmarini.com/blog/blog/coding/2020-08-30-web-scraping-selenium/" rel="alternate" type="text/html" title="Web data scraping with Python, Selenium and BeautifulSoup" /><published>2020-08-30T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/web-scraping-selenium</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2020-08-30-web-scraping-selenium/"><![CDATA[<p>Sometimes the data you want or need is not readily avaialble as a nicely formatted <code class="language-text highlighter-rouge">.csv</code> file. Sometimes it’s an <code class="language-text highlighter-rouge">HTML</code> table or even worse a convoluted mess of <code class="language-text highlighter-rouge">li</code> <code class="language-text highlighter-rouge">ul</code> or <code class="language-text highlighter-rouge">ol</code> tags.
We might be lucky and be able to copy-paste the table content to an Excel spreadsheet, but what if the table is split in multiple pages? And what if those pages are dynamically filled with Javascript? We would need some browsing capabilities and not just a static page crawler.</p>

<p>For this example, I’ll scrape data from a <a href="https://cvalues.science.kew.org/search">Plant DNA C-Values Database website</a>. The website has a search function which dynamically loads data in a table displaying the search result.</p>

<!--more-->
<ul class="large-only" id="markdown-toc">
  <li><a href="#static-crawling-with-javascript" id="markdown-toc-static-crawling-with-javascript">Static crawling with Javascript</a></li>
  <li><a href="#dynamic-web-scraping-with-python-and-selenium" id="markdown-toc-dynamic-web-scraping-with-python-and-selenium">Dynamic web scraping with Python and Selenium</a></li>
</ul>
<h1 id="static-crawling-with-javascript">Static crawling with Javascript</h1>
<p>The first and easiest way of getting some data from a table is definitey with Javsacript. It runs on the browser and with the addition of JQuery everything becomes extremely easy.
We could use Javascript on the Chrome developer console.</p>

<p>First and foremost, we need to load JQuery into our Javascript session. Let’s open up the Chrome Developer console with <code class="language-text highlighter-rouge">CTRL + SHIFT + J</code> and type in the following lines</p>
<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">jqry</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">script</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">jqry</span><span class="p">.</span><span class="nx">src</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">https://code.jquery.com/jquery-3.3.1.min.js</span><span class="dl">"</span><span class="p">;</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByTagName</span><span class="p">(</span><span class="dl">'</span><span class="s1">head</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">].</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">jqry</span><span class="p">);</span>
<span class="nx">jQuery</span><span class="p">.</span><span class="nx">noConflict</span><span class="p">();</span>
</code></pre></div></div>

<p>The page might already have JQuery loaded but it might be assigned to a differnt symbol than the usual <code class="language-text highlighter-rouge">$</code>, so <code class="language-text highlighter-rouge">noConflict()</code> is helping us in this regard by not overriding the <code class="language-text highlighter-rouge">$</code>. We can still call jQuery by using the <code class="language-text highlighter-rouge">jQuery()</code> function.</p>

<p>Next up we need a function to convert the table to a csv file. I have taken inspiration by a pretty nice jQuery plgin called <a href="https://github.com/OmbraDiFenice/table2csv"><code class="language-text highlighter-rouge">table2csv</code></a>.
I have added aa <code class="language-text highlighter-rouge">download</code> function to download the file automatically and edited che code to call it and some default options.</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">jQuery</span><span class="p">(</span> <span class="nb">document</span> <span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span> <span class="p">{</span>
    <span class="kd">function</span> <span class="nx">download</span><span class="p">(</span><span class="nx">filename</span><span class="p">,</span> <span class="nx">text</span><span class="p">)</span> <span class="p">{</span>
        <span class="kd">var</span> <span class="nx">element</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">createElement</span><span class="p">(</span><span class="dl">'</span><span class="s1">a</span><span class="dl">'</span><span class="p">);</span>
        <span class="nx">element</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">href</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">data:text/csv;charset=utf8,</span><span class="dl">'</span> <span class="o">+</span> <span class="nb">encodeURIComponent</span><span class="p">(</span><span class="nx">text</span><span class="p">));</span>
        <span class="nx">element</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="dl">'</span><span class="s1">download</span><span class="dl">'</span><span class="p">,</span> <span class="nx">filename</span><span class="p">);</span>

        <span class="nx">element</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">display</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">;</span>
        <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">appendChild</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>

        <span class="nx">element</span><span class="p">.</span><span class="nx">click</span><span class="p">();</span>

        <span class="nb">document</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">removeChild</span><span class="p">(</span><span class="nx">element</span><span class="p">);</span>
    <span class="p">}</span>

    <span class="nx">jQuery</span><span class="p">.</span><span class="nx">fn</span><span class="p">.</span><span class="nx">table2CSV</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(</span><span class="nx">options</span><span class="p">)</span> <span class="p">{</span>
            <span class="kd">var</span> <span class="nx">options</span> <span class="o">=</span> <span class="nx">jQuery</span><span class="p">.</span><span class="nx">extend</span><span class="p">({</span>
                <span class="na">separator</span><span class="p">:</span> <span class="dl">'</span><span class="s1">,</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">header</span><span class="p">:</span> <span class="p">[],</span>
                <span class="na">headerSelector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">th</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">columnSelector</span><span class="p">:</span> <span class="dl">'</span><span class="s1">td</span><span class="dl">'</span><span class="p">,</span>
                <span class="na">delivery</span><span class="p">:</span> <span class="dl">'</span><span class="s1">download</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// popup, value, download</span>
                <span class="na">filename</span><span class="p">:</span> <span class="dl">'</span><span class="s1">table.csv</span><span class="dl">'</span><span class="p">,</span> <span class="c1">// filename to download</span>
                <span class="na">transform_gt_lt</span><span class="p">:</span> <span class="kc">true</span> <span class="c1">// make &amp;gt; and &amp;lt; to &gt; and &lt;</span>
            <span class="p">},</span>
            <span class="nx">options</span><span class="p">);</span>

            <span class="kd">var</span> <span class="nx">csvData</span> <span class="o">=</span> <span class="p">[];</span>
            <span class="kd">var</span> <span class="nx">headerArr</span> <span class="o">=</span> <span class="p">[];</span>
            <span class="kd">var</span> <span class="nx">el</span> <span class="o">=</span> <span class="k">this</span><span class="p">;</span>

            <span class="c1">//header</span>
            <span class="kd">var</span> <span class="nx">numCols</span> <span class="o">=</span> <span class="nx">options</span><span class="p">.</span><span class="nx">header</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span>
            <span class="kd">var</span> <span class="nx">tmpRow</span> <span class="o">=</span> <span class="p">[];</span> <span class="c1">// construct header avalible array</span>

            <span class="k">if</span> <span class="p">(</span><span class="nx">numCols</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">)</span> <span class="p">{</span>
                <span class="k">for</span> <span class="p">(</span><span class="kd">var</span> <span class="nx">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">numCols</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
                    <span class="nx">tmpRow</span><span class="p">[</span><span class="nx">tmpRow</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">formatData</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">header</span><span class="p">[</span><span class="nx">i</span><span class="p">]);</span>
                <span class="p">}</span>
            <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
                <span class="nx">$</span><span class="p">(</span><span class="nx">el</span><span class="p">).</span><span class="nx">filter</span><span class="p">(</span><span class="dl">'</span><span class="s1">:visible</span><span class="dl">'</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">headerSelector</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
                    <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">css</span><span class="p">(</span><span class="dl">'</span><span class="s1">display</span><span class="dl">'</span><span class="p">)</span> <span class="o">!=</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">)</span> <span class="nx">tmpRow</span><span class="p">[</span><span class="nx">tmpRow</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">formatData</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">html</span><span class="p">());</span>
                <span class="p">});</span>
            <span class="p">}</span>

            <span class="nx">row2CSV</span><span class="p">(</span><span class="nx">tmpRow</span><span class="p">);</span>

            <span class="c1">// actual data</span>
            <span class="nx">$</span><span class="p">(</span><span class="nx">el</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="dl">'</span><span class="s1">tr</span><span class="dl">'</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">tmpRow</span> <span class="o">=</span> <span class="p">[];</span>
                <span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">filter</span><span class="p">(</span><span class="dl">'</span><span class="s1">:visible</span><span class="dl">'</span><span class="p">).</span><span class="nx">find</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">columnSelector</span><span class="p">).</span><span class="nx">each</span><span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
                    <span class="k">if</span> <span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">css</span><span class="p">(</span><span class="dl">'</span><span class="s1">display</span><span class="dl">'</span><span class="p">)</span> <span class="o">!=</span> <span class="dl">'</span><span class="s1">none</span><span class="dl">'</span><span class="p">)</span> <span class="nx">tmpRow</span><span class="p">[</span><span class="nx">tmpRow</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">formatData</span><span class="p">(</span><span class="nx">$</span><span class="p">(</span><span class="k">this</span><span class="p">).</span><span class="nx">html</span><span class="p">());</span>
                <span class="p">});</span>
                <span class="nx">row2CSV</span><span class="p">(</span><span class="nx">tmpRow</span><span class="p">);</span>
            <span class="p">});</span>
            <span class="k">if</span> <span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">delivery</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">popup</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">csvData</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
                <span class="k">if</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">transform_gt_lt</span><span class="p">){</span>
                    <span class="nx">mydata</span><span class="o">=</span><span class="nx">sinri_recover_gt_and_lt</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="nx">popup</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
            <span class="p">}</span>
            <span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">delivery</span> <span class="o">==</span> <span class="dl">'</span><span class="s1">download</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">csvData</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
                <span class="k">if</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">transform_gt_lt</span><span class="p">){</span>
                    <span class="nx">mydata</span><span class="o">=</span><span class="nx">sinri_recover_gt_and_lt</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="nx">download</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">filename</span><span class="p">,</span> <span class="nx">mydata</span><span class="p">);</span>
                <span class="c1">//var url='data:text/csv;charset=utf8,' + encodeURIComponent(mydata);</span>
                <span class="c1">//window.open(url);</span>
                <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
            <span class="p">}</span> 
            <span class="k">else</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">mydata</span> <span class="o">=</span> <span class="nx">csvData</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">'</span><span class="se">\n</span><span class="dl">'</span><span class="p">);</span>
                <span class="k">if</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">transform_gt_lt</span><span class="p">){</span>
                    <span class="nx">mydata</span><span class="o">=</span><span class="nx">sinri_recover_gt_and_lt</span><span class="p">(</span><span class="nx">mydata</span><span class="p">);</span>
                <span class="p">}</span>
                <span class="k">return</span> <span class="nx">mydata</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="kd">function</span> <span class="nx">sinri_recover_gt_and_lt</span><span class="p">(</span><span class="nx">input</span><span class="p">){</span>
                <span class="kd">var</span> <span class="nx">regexp</span><span class="o">=</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="sr">/&amp;gt;/g</span><span class="p">);</span>
                <span class="kd">var</span> <span class="nx">input</span><span class="o">=</span><span class="nx">input</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">regexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">&gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="kd">var</span> <span class="nx">regexp</span><span class="o">=</span><span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="sr">/&amp;lt;/g</span><span class="p">);</span>
                <span class="kd">var</span> <span class="nx">input</span><span class="o">=</span><span class="nx">input</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">regexp</span><span class="p">,</span><span class="dl">'</span><span class="s1">&lt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="k">return</span> <span class="nx">input</span><span class="p">;</span>
            <span class="p">}</span>

            <span class="kd">function</span> <span class="nx">row2CSV</span><span class="p">(</span><span class="nx">tmpRow</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">tmp</span> <span class="o">=</span> <span class="nx">tmpRow</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="dl">''</span><span class="p">)</span> <span class="c1">// to remove any blank rows</span>
                <span class="c1">// alert(tmp);</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">tmpRow</span><span class="p">.</span><span class="nx">length</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="nx">tmp</span> <span class="o">!=</span> <span class="dl">''</span><span class="p">)</span> <span class="p">{</span>
                    <span class="kd">var</span> <span class="nx">mystr</span> <span class="o">=</span> <span class="nx">tmpRow</span><span class="p">.</span><span class="nx">join</span><span class="p">(</span><span class="nx">options</span><span class="p">.</span><span class="nx">separator</span><span class="p">);</span>
                    <span class="nx">csvData</span><span class="p">[</span><span class="nx">csvData</span><span class="p">.</span><span class="nx">length</span><span class="p">]</span> <span class="o">=</span> <span class="nx">mystr</span><span class="p">;</span>
                <span class="p">}</span>
            <span class="p">}</span>
            <span class="kd">function</span> <span class="nx">formatData</span><span class="p">(</span><span class="nx">input</span><span class="p">)</span> <span class="p">{</span>
                <span class="c1">// double " according to rfc4180</span>
                <span class="kd">var</span> <span class="nx">regexp</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="sr">/</span><span class="se">[</span><span class="sr">"</span><span class="se">]</span><span class="sr">/g</span><span class="p">);</span>
                <span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="nx">input</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">regexp</span><span class="p">,</span> <span class="dl">'</span><span class="s1">""</span><span class="dl">'</span><span class="p">);</span>
                <span class="c1">//HTML</span>
                <span class="kd">var</span> <span class="nx">regexp</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">RegExp</span><span class="p">(</span><span class="sr">/</span><span class="se">\&lt;[^\&lt;]</span><span class="sr">+</span><span class="se">\&gt;</span><span class="sr">/g</span><span class="p">);</span>
                <span class="kd">var</span> <span class="nx">output</span> <span class="o">=</span> <span class="nx">output</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="nx">regexp</span><span class="p">,</span> <span class="dl">""</span><span class="p">);</span>
                <span class="nx">output</span> <span class="o">=</span> <span class="nx">output</span><span class="p">.</span><span class="nx">replace</span><span class="p">(</span><span class="sr">/&amp;nbsp;/gi</span><span class="p">,</span><span class="dl">'</span><span class="s1"> </span><span class="dl">'</span><span class="p">);</span> <span class="c1">//replace &amp;nbsp;</span>
                <span class="k">if</span> <span class="p">(</span><span class="nx">output</span> <span class="o">==</span> <span class="dl">""</span><span class="p">)</span> <span class="k">return</span> <span class="dl">''</span><span class="p">;</span>
                <span class="k">return</span> <span class="dl">'</span><span class="s1">"</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">output</span><span class="p">.</span><span class="nx">trim</span><span class="p">()</span> <span class="o">+</span> <span class="dl">'</span><span class="s1">"</span><span class="dl">'</span><span class="p">;</span>
            <span class="p">}</span>
            <span class="kd">function</span> <span class="nx">popup</span><span class="p">(</span><span class="nx">data</span><span class="p">)</span> <span class="p">{</span>
                <span class="kd">var</span> <span class="nx">generator</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">''</span><span class="p">,</span> <span class="dl">'</span><span class="s1">csv</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">height=400,width=600</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;html&gt;&lt;head&gt;&lt;title&gt;CSV&lt;/title&gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;/head&gt;&lt;body &gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;textArea cols=70 rows=15 wrap="off" &gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="nx">data</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;/textArea&gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">write</span><span class="p">(</span><span class="dl">'</span><span class="s1">&lt;/body&gt;&lt;/html&gt;</span><span class="dl">'</span><span class="p">);</span>
                <span class="nx">generator</span><span class="p">.</span><span class="nb">document</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
                <span class="k">return</span> <span class="kc">true</span><span class="p">;</span>
            <span class="p">}</span>
        <span class="p">};</span>
<span class="p">});</span>
</code></pre></div></div>
<p>Now for the final touch, just call this line on the result of your search and it will conver the table to a .csv file and automatically download it or you. Pretty cool uh?</p>

<div class="language-javascript highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">jQuery</span><span class="p">(</span> <span class="nb">document</span> <span class="p">).</span><span class="nx">ready</span><span class="p">(</span><span class="kd">function</span><span class="p">(</span><span class="nx">$</span><span class="p">)</span> <span class="p">{</span>
     <span class="nx">jQuery</span><span class="p">(</span><span class="dl">"</span><span class="s2">table.search-results</span><span class="dl">"</span><span class="p">).</span><span class="nx">table2CSV</span><span class="p">();</span>
<span class="p">});</span>
</code></pre></div></div>
<p>There are quite a few limitations with this approach:</p>
<ul>
  <li>Everytime the page reloads we need to re-run the code above</li>
  <li>We need to do this manually for each page of the table</li>
</ul>

<h1 id="dynamic-web-scraping-with-python-and-selenium">Dynamic web scraping with Python and Selenium</h1>
<p>Now that we sort of know where the data is and how to extract it we can think of how to automate this process.
The most common tools for web scraping are <code class="language-text highlighter-rouge">selenium</code> and <code class="language-text highlighter-rouge">beautifulSoup</code> packages for python.</p>

<p>Getting Selenium to work should be a post of its own but for now let’s assume you followed <a href="https://selenium-python.readthedocs.io/installation.html">this guide</a> and that everything is working.
I am using <code class="language-text highlighter-rouge">Anaconda3</code> so I simply ran:</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>conda <span class="nb">install</span> <span class="nt">-c</span> anaconda beautifulsoup4 request selenium
</code></pre></div></div>

<p>I then downloaded the Chrome WebDriver from <a href="https://sites.google.com/a/chromium.org/chromedriver/downloads">here</a> and extracted it to <code class="language-text highlighter-rouge">C:\chromedriver\chromedriver.exe</code>.</p>

<p class="message"><strong>HEADS UP</strong>: Make sure your Chrome driver version matches your installed Chrome version. For instance: I was using Chrome 84 but the chrome driver was for Chrome 85, I just updated my Chrome and it all worked perfectly.</p>

<p>Now that we all set up, let’s start by opening our page:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">selenium</span> <span class="kn">import</span> <span class="n">webdriver</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.common.keys</span> <span class="kn">import</span> <span class="n">Keys</span>
<span class="kn">from</span> <span class="nn">selenium.webdriver.support</span> <span class="kn">import</span> <span class="n">expected_conditions</span> <span class="k">as</span> <span class="n">EC</span>
<span class="kn">from</span> <span class="nn">selenium.common.exceptions</span> <span class="kn">import</span> <span class="n">NoSuchElementException</span>
<span class="kn">from</span> <span class="nn">bs4</span> <span class="kn">import</span> <span class="n">BeautifulSoup</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">pandas</span> <span class="k">as</span> <span class="n">pd</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">import</span> <span class="nn">time</span>
<span class="kn">import</span> <span class="nn">csv</span>


<span class="n">url</span> <span class="o">=</span> <span class="s">"https://cvalues.science.kew.org/search"</span>
<span class="c1"># Create a new Chrome session
</span><span class="n">driver</span> <span class="o">=</span> <span class="n">webdriver</span><span class="p">.</span><span class="n">Chrome</span><span class="p">(</span><span class="s">'C:</span><span class="se">\\</span><span class="s">chromedriver</span><span class="se">\\</span><span class="s">chromedriver.exe'</span><span class="p">)</span> <span class="c1"># I am using Chrome on Windows so I need to specify the path
</span><span class="n">driver</span><span class="p">.</span><span class="n">implicitly_wait</span><span class="p">(</span><span class="mf">0.1</span><span class="p">)</span>
<span class="n">driver</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">)</span> <span class="c1"># Navigate to the page
</span><span class="n">titme</span><span class="p">.</span><span class="n">sleep</span><span class="p">(</span><span class="mi">30</span><span class="p">)</span> <span class="c1"># Give the user some time to see the page at least
</span><span class="n">driver</span><span class="p">.</span><span class="n">close</span><span class="p">()</span>
</code></pre></div></div>

<p>This code is fairly simple: Create a new Chrome session, navigate to the page, wait for 30 seconds and then close it.
But what about <code class="language-text highlighter-rouge">implicitly_wait()</code>? This line sets the amount of time Selenium waits for an element before throwing an Exception. So calling <code class="language-text highlighter-rouge">findElement()</code> will throw an exception after 0.1 seconds if the element is not found. Remembe that this line <em>sets</em> the waiting time. So you only need to call it once to sets the time for the whole session until the browser is closed.</p>

<p>When we open the link we provide you will notice there is no table but only a bunch of paramters we can set to filter our search. Now we could:</p>
<ul>
  <li>Program our script to fill in these parameters and let selenium click the search button</li>
  <li>Let the user fill the search parameters on the web page and then click search</li>
</ul>

<p>The second approach has the pros of saving us a lot of time and headaches especially in case the structure of the form changes. The only thing we should be careful about is to make sure Selenium knows when the search button has been clicked and when to start scraping the data from the table.</p>

<p>The easiest way I found is to just have a loop that constantly checks for the presence of the table of results.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">while</span> <span class="bp">True</span><span class="p">:</span>
    <span class="n">tabResults</span> <span class="o">=</span> <span class="bp">None</span> <span class="c1"># This will contain the table container, it's initially None since we haven't found it yet
</span>    <span class="k">try</span><span class="p">:</span>
        <span class="n">tabResults</span> <span class="o">=</span> <span class="n">driver</span><span class="p">.</span><span class="n">find_element_by_id</span><span class="p">(</span><span class="s">"tabResults"</span><span class="p">)</span> <span class="c1"># Try to find the tabResults which is the table container
</span>    <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
        <span class="k">continue</span> <span class="c1"># If there was an exception then the table was not found so we should skip the next part and re-start the loop
</span>    <span class="p">...</span><span class="c1"># extract the data
</span></code></pre></div></div>
<p>The code is pretty self explanatory: Try to get the table container and if this throws an exception (due to the <code class="language-text highlighter-rouge">implicitly_wait()</code>) restart the loop. If there is not exception and tabResults is not None then do something.</p>

<p>Let’s create some functions to extract the data from the table:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">rowToCsvSel</span><span class="p">(</span><span class="n">columns</span><span class="p">,</span> <span class="n">csvwriter</span><span class="p">):</span>  
    <span class="n">tdRow</span> <span class="o">=</span> <span class="p">[]</span>  
    <span class="k">for</span> <span class="n">td</span> <span class="ow">in</span> <span class="n">columns</span><span class="p">:</span>
        <span class="n">tdRow</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">td</span><span class="p">.</span><span class="n">text</span><span class="p">)</span>
        <span class="n">referenceData</span> <span class="o">=</span> <span class="n">td</span><span class="p">.</span><span class="n">find_elements_by_css_selector</span><span class="p">(</span><span class="s">"a"</span><span class="p">)</span>
        <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">referenceData</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
            <span class="n">tdRow</span><span class="p">.</span><span class="n">append</span><span class="p">(</span><span class="n">referenceData</span><span class="p">[</span><span class="mi">0</span><span class="p">].</span><span class="n">get_attribute</span><span class="p">(</span><span class="s">"data-content"</span><span class="p">))</span>

    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">tdRow</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="n">csvwriter</span><span class="p">.</span><span class="n">writerow</span><span class="p">(</span><span class="n">tdRow</span><span class="p">)</span>
        <span class="k">return</span> <span class="mi">1</span>
    <span class="k">return</span> <span class="mi">0</span>

<span class="k">def</span> <span class="nf">getTableDataSel</span><span class="p">(</span><span class="n">tabResults</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">currentResult</span>
    <span class="k">global</span> <span class="n">looping</span>
    <span class="k">global</span> <span class="n">currentFileIndex</span>
    <span class="k">global</span> <span class="n">csvfile</span>
    <span class="k">global</span> <span class="n">wr</span>
    <span class="n">numbers</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">findall</span><span class="p">(</span><span class="sa">r</span><span class="s">'\d+'</span><span class="p">,</span> <span class="n">tabResults</span><span class="p">.</span><span class="n">find_element_by_css_selector</span><span class="p">(</span><span class="s">"h1"</span><span class="p">).</span><span class="n">text</span><span class="p">)</span>
    <span class="c1"># print(numbers)
</span>    <span class="n">fromRes</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">numbers</span><span class="p">[</span><span class="mi">0</span><span class="p">])</span>
    <span class="n">toRes</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">numbers</span><span class="p">[</span><span class="mi">1</span><span class="p">])</span>
    <span class="n">total</span> <span class="o">=</span> <span class="nb">int</span><span class="p">(</span><span class="n">numbers</span><span class="p">[</span><span class="mi">2</span><span class="p">])</span>  
    <span class="k">if</span> <span class="n">fromRes</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>
        <span class="n">currentResult</span> <span class="o">=</span> <span class="n">fromRes</span>
        <span class="n">looping</span> <span class="o">=</span> <span class="bp">True</span>
        <span class="n">csvfile</span> <span class="o">=</span> <span class="nb">open</span><span class="p">(</span><span class="s">'plants_data{}.csv'</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">currentFileIndex</span><span class="p">),</span> <span class="s">'w'</span><span class="p">,</span> <span class="n">newline</span><span class="o">=</span><span class="s">''</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">'utf-8'</span><span class="p">)</span> <span class="c1"># Getting the handle to the file, I need to specify UTF-8 since the table contains UTF characters
</span>        <span class="n">wr</span> <span class="o">=</span> <span class="n">csv</span><span class="p">.</span><span class="n">writer</span><span class="p">(</span><span class="n">csvfile</span><span class="p">)</span> <span class="c1"># Getting the csv writer with the file associated
</span>    <span class="n">table</span> <span class="o">=</span>  <span class="n">tabResults</span><span class="p">.</span><span class="n">find_element_by_class_name</span><span class="p">(</span><span class="s">"search-results"</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">looping</span><span class="p">:</span>
        <span class="k">for</span> <span class="n">row</span> <span class="ow">in</span> <span class="n">table</span><span class="p">.</span><span class="n">find_elements_by_css_selector</span><span class="p">(</span><span class="s">'tr'</span><span class="p">):</span>
            <span class="n">driver</span><span class="p">.</span><span class="n">implicitly_wait</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span> <span class="c1"># Without this it will just hang since the td elements might not have been loaded
</span>            <span class="k">print</span><span class="p">(</span><span class="s">"Result {} of {}"</span><span class="p">.</span><span class="nb">format</span><span class="p">(</span><span class="n">currentResult</span><span class="p">,</span> <span class="n">total</span><span class="p">))</span>
            <span class="k">if</span> <span class="n">currentResult</span> <span class="o">&lt;=</span> <span class="mi">1</span><span class="p">:</span>                
                <span class="n">rowToCsvSel</span><span class="p">(</span><span class="n">row</span><span class="p">.</span><span class="n">find_elements_by_css_selector</span><span class="p">(</span><span class="s">'th'</span><span class="p">),</span> <span class="n">wr</span><span class="p">)</span>
            <span class="n">currentResult</span> <span class="o">+=</span> <span class="n">rowToCsvSel</span><span class="p">(</span><span class="n">row</span><span class="p">.</span><span class="n">find_elements_by_css_selector</span><span class="p">(</span><span class="s">'td'</span><span class="p">),</span> <span class="n">wr</span><span class="p">)</span>
    <span class="k">if</span> <span class="n">toRes</span> <span class="o">&gt;=</span> <span class="n">total</span><span class="p">:</span>
        <span class="k">if</span> <span class="n">looping</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Data collection completed!"</span><span class="p">)</span>
            <span class="n">currentFileIndex</span> <span class="o">+=</span> <span class="mi">1</span>
        <span class="n">looping</span> <span class="o">=</span> <span class="bp">False</span>
    <span class="k">return</span> <span class="n">currentResult</span>

</code></pre></div></div>

<p>There is a lot to explain here but let’s go step by step:</p>
<ul>
  <li>The global variables are just a way to keep track of what’s happening and</li>
</ul>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[Sometimes the data you want or need is not readily avaialble as a nicely formatted .csv file. Sometimes it’s an HTML table or even worse a convoluted mess of li ul or ol tags. We might be lucky and be able to copy-paste the table content to an Excel spreadsheet, but what if the table is split in multiple pages? And what if those pages are dynamically filled with Javascript? We would need some browsing capabilities and not just a static page crawler. For this example, I’ll scrape data from a Plant DNA C-Values Database website. The website has a search function which dynamically loads data in a table displaying the search result.]]></summary></entry><entry><title type="html">Extracting data with `python` and regular expressions `regEx`</title><link href="https://gmarini.com/blog/blog/coding/2020-08-14-Regexr-git-python/" rel="alternate" type="text/html" title="Extracting data with `python` and regular expressions `regEx`" /><published>2020-08-14T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/Regexr-git-python</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2020-08-14-Regexr-git-python/"><![CDATA[<p>A big portion of my PhD is to deal with patterns. They present themselves in many ways: Movements, images and pixels, sounds etc… But since computers deal mostly with numbers and code, the easiest way is to reduce any pattern to text, or  code.</p>

<h2 id="what-are-regular-expressions">What are regular expressions</h2>
<p>Text, especially code, can easily be parsed by defining patterns and looking for them. Think about it as if you are reading a page of a book or even a website: You look for patterns, be it an email, a phone number, the structure of a sentence.</p>

<p>While the semantic of a sentence is not easy to parse and understand for a computer (see the whole branch of NLP), encoded and marked text can easily be captured by specifying patterns.
Emails for instance are defined by <code class="language-text highlighter-rouge">username@domain</code> and we all know how to read them, we all know that <code class="language-text highlighter-rouge">username</code> is anything that preceeds the <code class="language-text highlighter-rouge">@</code> character, and anything after it is the <code class="language-text highlighter-rouge">domain</code>.</p>

<p>I find this concept of patterns, text and encoding extremely fascinating and stimulating. I might make a post just about them but for now let’s focus on this little project.</p>

<!--more-->

<ul class="large-only" id="markdown-toc">
  <li><a href="#what-are-regular-expressions" id="markdown-toc-what-are-regular-expressions">What are regular expressions</a></li>
  <li><a href="#downloading-github-repositories-data" id="markdown-toc-downloading-github-repositories-data">Downloading GitHub repositories data</a>    <ul>
      <li><a href="#downloading-the-readmemd" id="markdown-toc-downloading-the-readmemd">Downloading the <code class="language-text highlighter-rouge">README.md</code></a></li>
      <li><a href="#downloading-the-images-used-in-the-readmemd" id="markdown-toc-downloading-the-images-used-in-the-readmemd">Downloading the images used in the <code class="language-text highlighter-rouge">README.md</code></a></li>
    </ul>
  </li>
  <li><a href="#how-regular-expressions-work" id="markdown-toc-how-regular-expressions-work">How regular expressions work</a></li>
  <li><a href="#regex-to-match-markdown-image-sources" id="markdown-toc-regex-to-match-markdown-image-sources">RegEx to match markdown image sources</a>    <ul>
      <li><a href="#wrapping-up" id="markdown-toc-wrapping-up">Wrapping up</a></li>
    </ul>
  </li>
</ul>

<h2 id="downloading-github-repositories-data">Downloading GitHub repositories data</h2>
<h3 id="downloading-the-readmemd">Downloading the <code class="language-text highlighter-rouge">README.md</code></h3>
<p>I wanted to port all of my GitHub projects on my website. Since the <code class="language-text highlighter-rouge">README.md</code> files are already markdown, Jekyll will know how to render them. Of course I would also like to get all the images I use in the readmes. As the lazy programmer that I am, I would rather spend 2 hours writing some code that does it for me rather than manually copy-pasting each project’s <code class="language-text highlighter-rouge">README.md</code> file.</p>

<p>The first thing I needed to do is to find out whether GitHub had an API or not. Depending on the answer I could have used a python-based browser, for instance <code class="language-text highlighter-rouge">beautifulsoup</code> or the <code class="language-text highlighter-rouge">request</code> package to send <code class="language-text highlighter-rouge">GET</code> and <code class="language-text highlighter-rouge">POST</code>.</p>

<p>Luckily GitHub does have an API and of course there already was a package wrapped around it, <a href="https://github.com/PyGithub/PyGithub"><code class="language-text highlighter-rouge">PyGithub</code></a>.
So let’s start by importing it and getting my own info from github</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">github</span> <span class="kn">import</span> <span class="n">Github</span>
<span class="kn">import</span> <span class="nn">oyaml</span>

<span class="k">with</span> <span class="nb">open</span><span class="p">((</span><span class="s">"github_credentials.yaml"</span><span class="p">),</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">cf</span><span class="p">:</span>
    <span class="n">cred</span> <span class="o">=</span> <span class="n">oyaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="n">cf</span><span class="p">)</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">Github</span><span class="p">(</span><span class="n">cred</span><span class="p">[</span><span class="s">'username'</span><span class="p">],</span> <span class="n">cred</span><span class="p">[</span><span class="s">'password'</span><span class="p">])</span>

<span class="k">for</span> <span class="n">repo</span> <span class="ow">in</span> <span class="n">g</span><span class="p">.</span><span class="n">get_user</span><span class="p">().</span><span class="n">get_repos</span><span class="p">():</span>
    <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>

</code></pre></div></div>

<p>I made the mistake once of committing a <code class="language-text highlighter-rouge">.py</code> file with my credentials in plain text. This time I am not making the same mistake. My username anf file are in a <code class="language-text highlighter-rouge">yaml</code> file structured like this:</p>

<div class="language-yaml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">username</span><span class="pi">:</span> <span class="s2">"</span><span class="s">gabryxx7"</span>
<span class="na">password</span><span class="pi">:</span> <span class="s2">"</span><span class="s">password"</span> <span class="c1"># You wish...</span>
</code></pre></div></div>

<p>I used a wonderful package called <code class="language-text highlighter-rouge">oyaml</code> (ordered <code class="language-text highlighter-rouge">yaml</code>) which is just a wrapper around <code class="language-text highlighter-rouge">yaml</code>. I can’t remember why I stiched to this one, something to do with the ordering, stumbled upon this issue while working on the <code class="language-text highlighter-rouge">AI_Dating</code> project.
Anyways back to the topic. Once I got my own GitHub details, I am iterating through the list of repositories in my account.
As soon as I run the code I saw an enormous amount of new lines being printed. Well that was not because of my awesome curriculum and activities on GitHub, it was because of the new GitHub Classroom that we are using at the University of Melbourne for the <code class="language-text highlighter-rouge">COMP30019</code> subject.
Every student in the subject can copy one of our workshop’s template repository in our GitHub organisation/classroom. And since me and the other tutors are the owner of the organisation, we are also owners of all the repositories, which means all of those repositories appear in my list.
Besides that, there are lots of forks that I don’t really use. In short, I only want <strong>MY</strong> repositories, the ones that I created. so with a very simple string comparison:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">repo</span> <span class="ow">in</span> <span class="n">g</span><span class="p">.</span><span class="n">get_user</span><span class="p">().</span><span class="n">get_repos</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">"workshop"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">.</span><span class="n">lower</span><span class="p">():</span>
        <span class="k">if</span> <span class="s">'gabryxx7'</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">owner</span><span class="p">.</span><span class="n">login</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">repo</span><span class="p">.</span><span class="n">fork</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
</code></pre></div></div>

<p>Now we are talking! If the name <code class="language-text highlighter-rouge">workshop</code> appears in the title of the repository we’ll just skip it. Same if the owner is not me.
Unfortunately, <code class="language-text highlighter-rouge">PyGithub</code> documentation is not great so it’s hard to know what variables or methods the repository object has.
In these cases I usually just go to the <a href="https://github.com/PyGithub/PyGithub/blob/master/github/Repository.py">source code repository</a> and with a simple <code class="language-text highlighter-rouge">ctrl + f</code> I look for whatever I need. In this case I was looking for:</p>

<ul>
  <li>The name of the repository</li>
  <li>The <code class="language-text highlighter-rouge">README.md</code> file url</li>
  <li>The repository’s date of creation</li>
  <li>The repository caption and description</li>
  <li>The repository’s link</li>
</ul>

<p>So after a few <code class="language-text highlighter-rouge">ctrl+f</code> here and there I found everything I needed to create a Jekyll project out of the GitHub repo. Let’s set up the <code class="language-text highlighter-rouge">yaml</code> header for Jekyll:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">header</span> <span class="o">=</span> <span class="n">oyaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="s">"""
    layout: project
    title: title
    icon: icon-github
    caption: caption
    date: 2020-01-01
    image: ""
    path: ""
    description: &gt;
    Descr
    links:
    - title: Source
        url: https://github.com/Gabryxx7
    """</span><span class="p">)</span>
</code></pre></div></div>
<p>I find it easier to define it this way and then read it with yaml rather than defining the dictionary and adding every <code class="language-text highlighter-rouge">(key,value)</code> pair to it.</p>

<p>So now we are ready to download our <code class="language-text highlighter-rouge">README.md</code> and use them as jekyll projects!</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="n">repo</span> <span class="ow">in</span> <span class="n">g</span><span class="p">.</span><span class="n">get_user</span><span class="p">().</span><span class="n">get_repos</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">"orkshop"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">:</span>
        <span class="k">if</span> <span class="s">'abryxx7'</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">owner</span><span class="p">.</span><span class="n">login</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">repo</span><span class="p">.</span><span class="n">fork</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">get_readme</span><span class="p">().</span><span class="n">download_url</span><span class="p">)</span>        
                <span class="n">abs_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="s">'../../../_projects/github/'</span><span class="p">)</span>
                <span class="n">file_name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span><span class="o">+</span><span class="s">".md"</span>                                        
                <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">):</span>
                    <span class="k">print</span><span class="p">(</span><span class="s">"Readme already exists: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="c1"># Create the folder tree if it does not exist
</span>                    <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="p">):</span>
                        <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">abs_path</span><span class="p">)</span>
                    <span class="c1"># Download the readme/md
</span>                    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">get_readme</span><span class="p">().</span><span class="n">download_url</span><span class="p">,</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
                    <span class="k">if</span> <span class="n">r</span><span class="p">.</span><span class="n">content</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                        <span class="n">readme_text</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span> <span class="c1"># decode the binary content as 'utf-8' to allow for emojis
</span>                        <span class="k">if</span> <span class="n">readme_text</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">readme_text</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">:</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'title'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">name</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'description'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">description</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'caption'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">description</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'date'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">created_at</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'links'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'url'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">html_url</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'image'</span><span class="p">][</span><span class="s">'path'</span><span class="p">]</span> <span class="o">=</span> <span class="n">first_image</span>

                            <span class="n">header_text</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">oyaml</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">header</span><span class="p">,</span> <span class="n">allow_unicode</span><span class="o">=</span><span class="bp">True</span><span class="p">))</span>

                            <span class="nb">open</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">,</span> <span class="s">'w'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="s">"---</span><span class="se">\n</span><span class="s">"</span><span class="o">+</span><span class="n">header_text</span><span class="o">+</span><span class="s">"</span><span class="se">\n</span><span class="s">---</span><span class="se">\n\n</span><span class="s">"</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">readme_text</span><span class="p">))</span>
                            <span class="n">first_image</span> <span class="o">=</span> <span class="bp">None</span>
            <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"No README.md "</span> <span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
            <span class="k">print</span><span class="p">()</span> <span class="c1"># new line separator
</span></code></pre></div></div>

<p>And there you have it! This little for loop will now dowlonad all of your readmes from GitHub and save them into the <code class="language-text highlighter-rouge">_projects</code> folder with a nice <code class="language-text highlighter-rouge">yaml</code> header.</p>

<p>But <strong>WAIT!</strong> the readmes often have images, which are relative to the repository, or they are even external links or links to the raw image file in github. Oh shoot! What’s the point of having this nice project showcase if I then have to manually add the screenshots? And what if I change anything on github, I’d have to do it again manually and it would take ages! Plus I’d have to replace the relative paths in the code as well, besides downloading all the images.</p>

<h3 id="downloading-the-images-used-in-the-readmemd">Downloading the images used in the <code class="language-text highlighter-rouge">README.md</code></h3>
<p>Now it’s the fun part where <code class="language-text highlighter-rouge">regexr</code> come in super handy!
Images in <code class="language-text highlighter-rouge">markdown</code> can be in a few different formats, including <code class="language-text highlighter-rouge">html</code>. This is a few examples of how I used images in my <code class="language-text highlighter-rouge">README.md</code> files.
I have never really paid much attention to how I put images in it, so they are often in the same line, with wrong spacings, with or without alternative text, sometimes relative paths, sometimes a full url, sometimes even from external websites!</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">Blynk1.jpg</span><span class="p">)</span> <span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">/assets/gabryxx7/img/GitHub/ShrekIntruderAlert/Blynk2.jpg</span><span class="p">)</span>

<span class="nt">&lt;img</span>  <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span> <span class="na">src=</span><span class="s">"https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower1.png"</span><span class="nt">/&gt;&lt;br/&gt;</span>

<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower2.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower3.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> 

<span class="nt">&lt;img</span> <span class="na">alt=</span><span class="s">"Gabryxx7 | Instagram"</span> <span class="na">height=</span><span class="s">"100px"</span> <span class="na">src=</span><span class="s">"https://gmarini.com/assets/gabryxx7/img/photos/202006/79841217_555270635156572_8260864494112333405_n.jpg"</span> <span class="nt">/&gt;</span>

<span class="p">![</span><span class="nv">Screenshot</span><span class="p">](</span><span class="sx">tinderai1.png</span><span class="p">)</span>

<span class="p">![](</span><span class="sx">tinderai2.png</span><span class="p">)</span>

<span class="p">![](</span><span class="sx">https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetowerasd.png</span><span class="p">)</span>

</code></pre></div></div>
<p>Now let’s say we want to download all of these images on our computer and then maybe reference them in the same <code class="language-text highlighter-rouge">README.md</code> we downloaded earlier, but with their new path in our computer. Now that sounds like a mess! We need to extract the image filename, understand whether it’s relative to the repository or not, and then even replace its occurrences with the new local path.
Man that’s a lot of different patterns, how to deal with all of this?</p>

<p>Luckily we have our “friends” the regular expressions. It took me around an hour to get the right expression and I saved it here for future references <a href="regexr.com/5a6b1">RegExr</a>.</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IMG_REGEXR</span> <span class="o">=</span> <span class="s">"(?:!\[[^\]]*\]\(([^\)]*)\))+|(?:&lt;img\s.*?src=(?:'|</span><span class="se">\"</span><span class="s">)([^'</span><span class="se">\"</span><span class="s">&gt;]+)(?:'|</span><span class="se">\"</span><span class="s">))"</span>
</code></pre></div></div>
<p>Now that looks like some sort of alien language, does it? Let’s break it down.
Before that, a quick explanation on regexp.</p>

<h2 id="how-regular-expressions-work">How regular expressions work</h2>
<p>Regular Expressions are used to define patterns in text. The idea is to define a pattern and then execute it on your string or document, and it will return you all the “matches” or occurrences of that pattern in your string/document.</p>

<p>They have a few important special features and characters, I’ll go through the most common ones:</p>

<ul>
  <li><code class="language-text highlighter-rouge">.</code>: The dot is a special characters than it matches <strong>ANY</strong> character except for a new line. This means that it will return as a match anything: spaces, commas, letters, digits, tabs etc…</li>
  <li><code class="language-text highlighter-rouge">|</code>: either/or, <code class="language-text highlighter-rouge">a|b</code> matches either an <code class="language-text highlighter-rouge">a</code> or a <code class="language-text highlighter-rouge">b</code> but not both</li>
  <li><code class="language-text highlighter-rouge">\.</code> <code class="language-text highlighter-rouge">\\</code>: Matches the actual character, in this case the actual dot and the actual forward slash respectively, no special meaning.</li>
  <li><code class="language-text highlighter-rouge">\w \d \s</code>: These three match a letter, a digit and a whitespace respectively</li>
  <li><code class="language-text highlighter-rouge">\W \D \S</code>: These three match anything BUT a letter, a digit and a whitespace respectively</li>
  <li><code class="language-text highlighter-rouge">*</code> <code class="language-text highlighter-rouge">+</code> <code class="language-text highlighter-rouge">?</code>: These two tell how many times you want a character to be matches, respectively: 0 or more, 1 or more, 0 or 1</li>
  <li><code class="language-text highlighter-rouge">[]</code>: Matches any one of the characters inside the square brackets. <code class="language-text highlighter-rouge">[ab]</code> matches either a single <code class="language-text highlighter-rouge">a</code> or a single <code class="language-text highlighter-rouge">b</code> separately as separate matches.</li>
  <li><code class="language-text highlighter-rouge">[^]</code> Matches any character except for the one inside. <code class="language-text highlighter-rouge">[^ab]</code> matches anything but a single <code class="language-text highlighter-rouge">a</code> or a single <code class="language-text highlighter-rouge">b</code></li>
  <li><code class="language-text highlighter-rouge">[-]</code>: Matches any character in the range. <code class="language-text highlighter-rouge">[a-g]</code> will match ay character between <code class="language-text highlighter-rouge">a</code> and <code class="language-text highlighter-rouge">g</code> separately, this is the same as <code class="language-text highlighter-rouge">\w</code>. <code class="language-text highlighter-rouge">[0-9]</code> will match any character between <code class="language-text highlighter-rouge">0</code> and <code class="language-text highlighter-rouge">9</code> separately, this is the same as <code class="language-text highlighter-rouge">\d</code></li>
</ul>

<p>So we can now do something simple like <code class="language-text highlighter-rouge">.*</code> which matches anything up to a new line. Running this will just tell us whether there is a match or not in the string. And well, this will always be true, even nothing will return <code class="language-text highlighter-rouge">true</code> because the <code class="language-text highlighter-rouge">*</code> matches 0 or more occurrences of <code class="language-text highlighter-rouge">.</code>.
<code class="language-text highlighter-rouge">gabriele</code> will match the word gabriele for instance. Another example is <code class="language-text highlighter-rouge">[^a]+</code> which matches any character which is not an <code class="language-text highlighter-rouge">a</code> but at least once.</p>

<p>An important features are groups, so that we can define a sequence of character</p>
<ul>
  <li><code class="language-text highlighter-rouge">()</code> Everything in brackets is a “capture group”. Every pattern found inside the capture group can be referenced after running the expression
and it’s very useful to reference certain parts of the pattern you want to extract</li>
  <li><code class="language-text highlighter-rouge">(?:)</code> This is a non-capturing group, same as before but cannot be referenced, well what’s the point then? You might ask. This is useful to define repetitions of a sub-pattern or characters</li>
</ul>

<p>That’s pretty much it for now, let’s break down the regex above:</p>

<h2 id="regex-to-match-markdown-image-sources">RegEx to match markdown image sources</h2>
<p>First of all, the expression is divided in two by the pipe character <code class="language-text highlighter-rouge">|</code>, the first one matches the markdown for images, which is in the form of <code class="language-text highlighter-rouge">![]()</code> while the second one matches the html <code class="language-text highlighter-rouge">&lt;img src=... /&gt;</code>.</p>

<ul>
  <li>Markdown <code class="language-text highlighter-rouge">(?:!\[[^\]]*\]\(([^\)]*)\))+</code>
    <ul>
      <li><code class="language-text highlighter-rouge">(?:</code>: Everything is surrounded by a non-capturing group</li>
      <li><code class="language-text highlighter-rouge">!\[</code>: match an exclamation mark and an open square bracket</li>
      <li><code class="language-text highlighter-rouge">[^\]]*</code>: MAtches anything up to a closed square bracket, there can also be nothing to match (so an empty square brackets <code class="language-text highlighter-rouge">[]</code>)</li>
      <li><code class="language-text highlighter-rouge">\]\(</code>: We are not at the closed square bracket, so let’s match it and look for the open bracket</li>
      <li><code class="language-text highlighter-rouge">([^\)]*)</code>: Now we are inside the round brackets and we need to capture the image url (or path). So this is a capture group and it matches any character up to a close round bracket <code class="language-text highlighter-rouge">[^\)]</code>, it can also be empty thanks to the <code class="language-text highlighter-rouge">*</code>.</li>
      <li><code class="language-text highlighter-rouge">\)</code>: Now we are up to the closed round bracket, so let’s match it</li>
      <li><code class="language-text highlighter-rouge">)</code>: Closure of the non-capturing group</li>
      <li><code class="language-text highlighter-rouge">+</code>: This one is important, it matches the whole pattern we just defined in the non-capturing group, at least once. This is important in case we have two images in the same line such as <code class="language-text highlighter-rouge">![](test.png) ![caption](test2.png)</code>. This will retuurn both <code class="language-text highlighter-rouge">test.png</code> and <code class="language-text highlighter-rouge">test2.png</code></li>
    </ul>
  </li>
  <li>HTML <code class="language-text highlighter-rouge">(?:&lt;img\s.*?src=(?:'|\")([^'\"&gt;]+)(?:'|\"))</code>
    <ul>
      <li><code class="language-text highlighter-rouge">(?:</code>: Everything is surrounded by a non-capturing group</li>
      <li><code class="language-text highlighter-rouge">&lt;img</code>: Matches the open html image tag</li>
      <li><code class="language-text highlighter-rouge">\s.*?</code>: Matches any amount of empty spaces</li>
      <li><code class="language-text highlighter-rouge">src=</code>: Matches the source attribute in the html image tag</li>
      <li><code class="language-text highlighter-rouge">(?:'|\")</code>: This is a non-capturing group and it matches either a <code class="language-text highlighter-rouge">'</code> or a <code class="language-text highlighter-rouge">"</code> quotation mark, we are opening the source string</li>
      <li><code class="language-text highlighter-rouge">([^'\"&gt;]+)</code>: This is a capturing group and it matches anything but  a <code class="language-text highlighter-rouge">'</code> or a <code class="language-text highlighter-rouge">"</code> quotation mark</li>
      <li><code class="language-text highlighter-rouge">(?:'|\")</code>: This is a non-capturing group and it matches either a <code class="language-text highlighter-rouge">'</code> or a <code class="language-text highlighter-rouge">"</code> quotation mark to close the source string</li>
      <li><code class="language-text highlighter-rouge">)</code>: Closure of the non-capturing group</li>
    </ul>
  </li>
</ul>

<h3 id="wrapping-up">Wrapping up</h3>
<p>So there you have it, now we can use the <code class="language-text highlighter-rouge">re</code> python package to match all of these in our <code class="language-text highlighter-rouge">README.md</code> and replace the captured group with the new file’s local path</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">def</span> <span class="nf">download_and_replace</span><span class="p">(</span><span class="n">match</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">first_image</span>
    <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">cap_group</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">cap_group</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
    <span class="n">url</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cap_group</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">if</span> <span class="s">'http'</span> <span class="ow">in</span> <span class="n">cap_group</span><span class="p">:</span>
            <span class="k">if</span> <span class="s">'github.com'</span> <span class="ow">in</span> <span class="n">cap_group</span><span class="p">:</span>                
                <span class="n">url</span> <span class="o">=</span> <span class="n">cap_group</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'github.com'</span><span class="p">,</span> <span class="s">'raw.githubusercontent.com'</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'blob/'</span><span class="p">,</span><span class="s">''</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">url</span> <span class="o">=</span> <span class="n">cap_group</span>
            <span class="n">file_name</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">basename</span><span class="p">(</span><span class="n">urlparse</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="n">path</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">url</span> <span class="o">=</span> <span class="s">"https://raw.githubusercontent.com/"</span> <span class="o">+</span><span class="n">repo</span><span class="p">.</span><span class="n">full_name</span><span class="o">+</span><span class="s">"/master/"</span><span class="o">+</span><span class="n">cap_group</span>
            <span class="n">file_name</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">basename</span><span class="p">(</span><span class="n">cap_group</span><span class="p">)</span>
        <span class="n">path_from_root</span> <span class="o">=</span> <span class="s">'/assets/gabryxx7/img/GitHub/'</span><span class="o">+</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="o">+</span><span class="s">'/'</span>
        <span class="n">abs_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="s">'../../../'</span><span class="o">+</span><span class="n">path_from_root</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">):</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Skipping: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="p">):</span>
                <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">abs_path</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Downloading: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span> <span class="o">+</span><span class="s">"</span><span class="se">\t</span><span class="s">From: "</span> <span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">url</span><span class="p">))</span>
            <span class="n">f_img</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
            <span class="nb">open</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">f_img</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>

        <span class="k">print</span><span class="p">(</span><span class="s">"Replaced string: "</span> <span class="o">+</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">cap_group</span><span class="p">,</span><span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">first_image</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">first_image</span> <span class="o">=</span> <span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span>
        <span class="k">return</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">cap_group</span><span class="p">,</span><span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">cap_group</span>

<span class="p">...</span>

  <span class="n">readme_text</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">sub</span><span class="p">(</span><span class="n">IMG_REGEXR</span><span class="p">,</span> <span class="n">download_and_replace</span><span class="p">,</span> <span class="n">readme_text</span><span class="p">)</span> <span class="c1"># Calling a function for each match which will download the img and replace the path in the markdown file
</span>  
<span class="p">...</span>
</code></pre></div></div>

<p>The function is fairly simple but has a few catches:</p>
<ul>
  <li>First we need to get our captured image path/url:
    <ul>
      <li><code class="language-text highlighter-rouge">match.group(0)</code> represents the <strong>WHOLE</strong> pattern, not just the captured groups</li>
      <li><code class="language-text highlighter-rouge">match.group(1)</code> reference the first capture group (the markdown images)</li>
      <li><code class="language-text highlighter-rouge">match.group(2)</code> reference the second capture group (the html images)</li>
    </ul>
  </li>
  <li>If the input is not empty then we can keep going
    <ul>
      <li>If it’s a link there will be <code class="language-text highlighter-rouge">http</code> in the string</li>
      <li>We cannot download images from github links, so we have to convert those to <code class="language-text highlighter-rouge">raw.githubusercontent.com</code> links</li>
      <li>If it’s not a link then we concatenate the raw github link with the repository full name</li>
    </ul>
  </li>
  <li>Now that we have our link we can send a request and download the binary blob</li>
  <li>Once we downloaded the file we just need to make sure the path exists, and write the content to our disk</li>
  <li>We then return the replacement for the <strong>whole</strong> match, this includes also the non-capturing group. So what we do is return the whole match but we substitute the captured group with the now file’s local path</li>
</ul>

<p>We then return the edited <code class="language-text highlighter-rouge">README.md</code> output to the for loop to then write everything to the new <code class="language-text highlighter-rouge">README.md</code> file with the <code class="language-text highlighter-rouge">yaml</code> header.</p>

<p>The final images’ path it’s now replaced with the local path of the file downloaded on our computer (or server), so lines like these ones:</p>
<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">Blynk1.jpg</span><span class="p">)</span> <span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">Blynk2.jpg</span><span class="p">)</span>
<span class="nt">&lt;img</span>  <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span> <span class="na">src=</span><span class="s">"https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower1.png"</span><span class="nt">/&gt;&lt;br/&gt;</span>
<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower2.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'https://github.com/Gabryxx7/TheTower/blob/master/Textures/thetower3.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> 
</code></pre></div></div>

<p>Will now look something like this:</p>
<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">/assets/Gabryxx7/GitHub/img/ShrekIntruderAlert/Blynk1.jpg</span><span class="p">)</span> <span class="p">![</span><span class="nv">Blynk App</span><span class="p">](</span><span class="sx">/assets/gabryxx7/img/GitHub/ShrekIntruderAlert/Blynk2.jpg</span><span class="p">)</span>
<span class="nt">&lt;img</span>  <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span> <span class="na">src=</span><span class="s">"/assets/Gabryxx7/GitHub/img/thetower1.png"</span><span class="nt">/&gt;&lt;br/&gt;</span>
<span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'/assets/Gabryxx7/GitHub/img/thetower2.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> <span class="nt">&lt;img</span> <span class="na">src=</span><span class="s">'/assets/Gabryxx7/GitHub/img/thetower3.png'</span> <span class="na">alt=</span><span class="s">"The Tower Logo"</span> <span class="na">width=</span><span class="s">"250"</span><span class="nt">/&gt;</span> 
</code></pre></div></div>

<p>Putting it all together, the script ends up looking like this:</p>

<div class="language-python highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kn">from</span> <span class="nn">github</span> <span class="kn">import</span> <span class="n">Github</span>
<span class="kn">import</span> <span class="nn">requests</span>
<span class="kn">import</span> <span class="nn">oyaml</span>
<span class="kn">import</span> <span class="nn">re</span>
<span class="kn">import</span> <span class="nn">os</span>
<span class="kn">from</span> <span class="nn">urllib.parse</span> <span class="kn">import</span> <span class="n">urlparse</span>

<span class="n">IMG_REGEXR</span> <span class="o">=</span> <span class="s">"(?:!\[[^\]]*\]\(([^\)]*)\))+|(?:&lt;img\s.*?src=(?:'|</span><span class="se">\"</span><span class="s">)([^'</span><span class="se">\"</span><span class="s">&gt;]+)(?:'|</span><span class="se">\"</span><span class="s">))"</span>
<span class="n">first_image</span> <span class="o">=</span> <span class="bp">None</span>

<span class="k">def</span> <span class="nf">download_and_replace</span><span class="p">(</span><span class="n">match</span><span class="p">):</span>
    <span class="k">global</span> <span class="n">first_image</span>
    <span class="k">print</span><span class="p">(</span><span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">))</span>
    <span class="k">if</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
        <span class="n">cap_group</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">1</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
    <span class="k">else</span><span class="p">:</span>
        <span class="n">cap_group</span> <span class="o">=</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">2</span><span class="p">).</span><span class="n">strip</span><span class="p">()</span>
    <span class="n">url</span> <span class="o">=</span> <span class="s">""</span>
    <span class="k">if</span> <span class="nb">len</span><span class="p">(</span><span class="n">cap_group</span><span class="p">)</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">:</span>
        <span class="k">if</span> <span class="s">'http'</span> <span class="ow">in</span> <span class="n">cap_group</span><span class="p">:</span>
            <span class="k">if</span> <span class="s">'github.com'</span> <span class="ow">in</span> <span class="n">cap_group</span><span class="p">:</span>                
                <span class="n">url</span> <span class="o">=</span> <span class="n">cap_group</span><span class="p">.</span><span class="n">replace</span><span class="p">(</span><span class="s">'github.com'</span><span class="p">,</span> <span class="s">'raw.githubusercontent.com'</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="s">'blob/'</span><span class="p">,</span><span class="s">''</span><span class="p">)</span>
            <span class="k">else</span><span class="p">:</span>
                <span class="n">url</span> <span class="o">=</span> <span class="n">cap_group</span>
            <span class="n">file_name</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">basename</span><span class="p">(</span><span class="n">urlparse</span><span class="p">(</span><span class="n">url</span><span class="p">).</span><span class="n">path</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="n">url</span> <span class="o">=</span> <span class="s">"https://raw.githubusercontent.com/"</span> <span class="o">+</span><span class="n">repo</span><span class="p">.</span><span class="n">full_name</span><span class="o">+</span><span class="s">"/master/"</span><span class="o">+</span><span class="n">cap_group</span>
            <span class="n">file_name</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">basename</span><span class="p">(</span><span class="n">cap_group</span><span class="p">)</span>
        <span class="n">path_from_root</span> <span class="o">=</span> <span class="s">'/assets/gabryxx7/img/GitHub/'</span><span class="o">+</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="o">+</span><span class="s">'/'</span>
        <span class="n">abs_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="s">'../../../'</span><span class="o">+</span><span class="n">path_from_root</span><span class="p">)</span>

        <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">):</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Skipping: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
        <span class="k">else</span><span class="p">:</span>
            <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="p">):</span>
                <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">abs_path</span><span class="p">)</span>
            <span class="k">print</span><span class="p">(</span><span class="s">"Downloading: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span> <span class="o">+</span><span class="s">"</span><span class="se">\t</span><span class="s">From: "</span> <span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">url</span><span class="p">))</span>
            <span class="n">f_img</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">url</span><span class="p">,</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
            <span class="nb">open</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">,</span> <span class="s">'wb'</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="n">f_img</span><span class="p">.</span><span class="n">content</span><span class="p">)</span>

        <span class="k">print</span><span class="p">(</span><span class="s">"Replaced string: "</span> <span class="o">+</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">cap_group</span><span class="p">,</span><span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span><span class="p">))</span>
        <span class="k">if</span> <span class="n">first_image</span> <span class="ow">is</span> <span class="bp">None</span><span class="p">:</span>
            <span class="n">first_image</span> <span class="o">=</span> <span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span>
        <span class="k">return</span> <span class="n">match</span><span class="p">.</span><span class="n">group</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="n">replace</span><span class="p">(</span><span class="n">cap_group</span><span class="p">,</span><span class="n">path_from_root</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
    <span class="k">return</span> <span class="n">cap_group</span>


<span class="k">with</span> <span class="nb">open</span><span class="p">((</span><span class="s">"github_credentials.yaml"</span><span class="p">),</span> <span class="s">"r"</span><span class="p">)</span> <span class="k">as</span> <span class="n">cf</span><span class="p">:</span>
    <span class="n">cred</span> <span class="o">=</span> <span class="n">oyaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="n">cf</span><span class="p">)</span>
<span class="n">g</span> <span class="o">=</span> <span class="n">Github</span><span class="p">(</span><span class="n">cred</span><span class="p">[</span><span class="s">'username'</span><span class="p">],</span> <span class="n">cred</span><span class="p">[</span><span class="s">'password'</span><span class="p">])</span>

<span class="n">header</span> <span class="o">=</span> <span class="n">oyaml</span><span class="p">.</span><span class="n">safe_load</span><span class="p">(</span><span class="s">"""
layout: project
title: title
icon: icon-github
caption: caption
date: 2020-01-01
image: 
  path: ""
description: &gt;
  Descr
links:
  - title: Source
    url: https://github.com/Gabryxx7/TheTower
"""</span><span class="p">)</span>

<span class="c1"># Then play with your Github objects:
# print(dir(g.get_user().get_repos()[0]))
</span><span class="k">for</span> <span class="n">repo</span> <span class="ow">in</span> <span class="n">g</span><span class="p">.</span><span class="n">get_user</span><span class="p">().</span><span class="n">get_repos</span><span class="p">():</span>
    <span class="k">if</span> <span class="s">"workshop"</span> <span class="ow">not</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">.</span><span class="n">lower</span><span class="p">():</span>
        <span class="k">if</span> <span class="s">'gabryxx7'</span> <span class="ow">in</span> <span class="n">repo</span><span class="p">.</span><span class="n">owner</span><span class="p">.</span><span class="n">login</span><span class="p">.</span><span class="n">lower</span><span class="p">()</span> <span class="ow">and</span> <span class="ow">not</span> <span class="n">repo</span><span class="p">.</span><span class="n">fork</span><span class="p">:</span>
            <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span>
            <span class="k">try</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">get_readme</span><span class="p">().</span><span class="n">download_url</span><span class="p">)</span>
                <span class="n">abs_path</span> <span class="o">=</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">abspath</span><span class="p">(</span><span class="s">'../../../_projects/github/'</span><span class="p">)</span>
                <span class="n">file_name</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">name</span><span class="p">)</span><span class="o">+</span><span class="s">".md"</span>                                        
                <span class="k">if</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">):</span>
                    <span class="k">print</span><span class="p">(</span><span class="s">"Readme already exists: "</span> <span class="o">+</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">)</span>
                <span class="k">else</span><span class="p">:</span>
                    <span class="k">if</span> <span class="ow">not</span> <span class="n">os</span><span class="p">.</span><span class="n">path</span><span class="p">.</span><span class="n">exists</span><span class="p">(</span><span class="n">abs_path</span><span class="p">):</span>
                        <span class="n">os</span><span class="p">.</span><span class="n">makedirs</span><span class="p">(</span><span class="n">abs_path</span><span class="p">)</span>
                    <span class="n">r</span> <span class="o">=</span> <span class="n">requests</span><span class="p">.</span><span class="n">get</span><span class="p">(</span><span class="n">repo</span><span class="p">.</span><span class="n">get_readme</span><span class="p">().</span><span class="n">download_url</span><span class="p">,</span> <span class="n">allow_redirects</span><span class="o">=</span><span class="bp">True</span><span class="p">)</span>
                    <span class="k">if</span> <span class="n">r</span><span class="p">.</span><span class="n">content</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span><span class="p">:</span>
                        <span class="n">readme_text</span> <span class="o">=</span> <span class="n">r</span><span class="p">.</span><span class="n">content</span><span class="p">.</span><span class="n">decode</span><span class="p">(</span><span class="s">'utf-8'</span><span class="p">)</span>
                        <span class="k">if</span> <span class="n">readme_text</span> <span class="ow">is</span> <span class="ow">not</span> <span class="bp">None</span> <span class="ow">and</span> <span class="nb">len</span><span class="p">(</span><span class="n">readme_text</span><span class="p">.</span><span class="n">split</span><span class="p">(</span><span class="s">'</span><span class="se">\n</span><span class="s">'</span><span class="p">))</span> <span class="o">&gt;</span> <span class="mi">5</span><span class="p">:</span>
                            <span class="n">readme_text</span> <span class="o">=</span> <span class="n">re</span><span class="p">.</span><span class="n">sub</span><span class="p">(</span><span class="n">IMG_REGEXR</span><span class="p">,</span> <span class="n">download_and_replace</span><span class="p">,</span> <span class="n">readme_text</span><span class="p">)</span>

                            <span class="n">header</span><span class="p">[</span><span class="s">'title'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">name</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'description'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">description</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'caption'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">description</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'date'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">created_at</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'links'</span><span class="p">][</span><span class="mi">0</span><span class="p">][</span><span class="s">'url'</span><span class="p">]</span> <span class="o">=</span> <span class="n">repo</span><span class="p">.</span><span class="n">html_url</span>
                            <span class="n">header</span><span class="p">[</span><span class="s">'image'</span><span class="p">][</span><span class="s">'path'</span><span class="p">]</span> <span class="o">=</span> <span class="n">first_image</span>

                            <span class="n">header_text</span> <span class="o">=</span> <span class="nb">str</span><span class="p">(</span><span class="n">oyaml</span><span class="p">.</span><span class="n">dump</span><span class="p">(</span><span class="n">header</span><span class="p">,</span> <span class="n">allow_unicode</span><span class="o">=</span><span class="bp">True</span><span class="p">))</span>

                            <span class="nb">open</span><span class="p">(</span><span class="n">abs_path</span><span class="o">+</span><span class="s">'/'</span><span class="o">+</span><span class="n">file_name</span><span class="p">,</span> <span class="s">'w'</span><span class="p">,</span> <span class="n">encoding</span><span class="o">=</span><span class="s">"utf-8"</span><span class="p">).</span><span class="n">write</span><span class="p">(</span><span class="s">"---</span><span class="se">\n</span><span class="s">"</span><span class="o">+</span><span class="n">header_text</span><span class="o">+</span><span class="s">"</span><span class="se">\n</span><span class="s">---</span><span class="se">\n\n</span><span class="s">"</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">readme_text</span><span class="p">))</span>
                            <span class="n">first_image</span> <span class="o">=</span> <span class="bp">None</span>
            <span class="k">except</span> <span class="nb">Exception</span> <span class="k">as</span> <span class="n">e</span><span class="p">:</span>
                <span class="k">print</span><span class="p">(</span><span class="s">"No README.md "</span> <span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">e</span><span class="p">))</span>
            <span class="k">print</span><span class="p">()</span>
</code></pre></div></div>

<p>And there you have it! A little python script to scrape your own github repository data and export their <code class="language-text highlighter-rouge">README.md</code> as jekyll markdown posts, and their images in the correct jekyll <code class="language-text highlighter-rouge">assets</code> folder.</p>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[A big portion of my PhD is to deal with patterns. They present themselves in many ways: Movements, images and pixels, sounds etc… But since computers deal mostly with numbers and code, the easiest way is to reduce any pattern to text, or code. What are regular expressions Text, especially code, can easily be parsed by defining patterns and looking for them. Think about it as if you are reading a page of a book or even a website: You look for patterns, be it an email, a phone number, the structure of a sentence. While the semantic of a sentence is not easy to parse and understand for a computer (see the whole branch of NLP), encoded and marked text can easily be captured by specifying patterns. Emails for instance are defined by username@domain and we all know how to read them, we all know that username is anything that preceeds the @ character, and anything after it is the domain. I find this concept of patterns, text and encoding extremely fascinating and stimulating. I might make a post just about them but for now let’s focus on this little project.]]></summary></entry><entry><title type="html">Knitting RMarkdown to Jekyll post</title><link href="https://gmarini.com/blog/blog/coding/2020-08-13-Knitting-RMD/" rel="alternate" type="text/html" title="Knitting RMarkdown to Jekyll post" /><published>2020-08-13T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/Knitting-RMD</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2020-08-13-Knitting-RMD/"><![CDATA[<p>One thing I would love to do with this blog is to post data anlysis done in R (or Python sometimes). They can be a lot of fun and I would prefer to spend the time writing the R notebook rather than converting it afterward to pure Markdown for Jekyll.
So here is a few tips for an easy R Markdown to Jekyll Markdown port (Thanks to <a href="http://svmiller.com/blog/2019/08/two-helpful-rmarkdown-jekyll-tips/">this guide</a> ).
Another interesting package is <code class="language-text highlighter-rouge">blogdown</code> whith has Jekyll <a href="https://bookdown.org/yihui/blogdown/jekyll.html">support</a>. The most interesting part was their code for generating markdown files and figures in different folders</p>

<!--more-->

<ul class="large-only" id="markdown-toc">
  <li><a href="#setting-up-the-r-markdown-file" id="markdown-toc-setting-up-the-r-markdown-file">Setting up the R Markdown file</a></li>
  <li><a href="#knitting-it" id="markdown-toc-knitting-it">Knitting it!</a></li>
</ul>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">local</span><span class="p">({</span><span class="w">
  </span><span class="c1"># fall back on "/" if baseurl is not specified</span><span class="w">
  </span><span class="n">baseurl</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">blogdown</span><span class="o">:::</span><span class="n">get_config2</span><span class="p">(</span><span class="s2">"baseurl"</span><span class="p">,</span><span class="w"> </span><span class="n">default</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"/"</span><span class="p">)</span><span class="w">
  </span><span class="n">knitr</span><span class="o">::</span><span class="n">opts_knit</span><span class="o">$</span><span class="n">set</span><span class="p">(</span><span class="n">base.url</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">baseurl</span><span class="p">)</span><span class="w">
  </span><span class="n">knitr</span><span class="o">::</span><span class="n">render_jekyll</span><span class="p">()</span><span class="w">  </span><span class="c1"># set output hooks</span><span class="w">

  </span><span class="c1"># input/output filenames as two arguments to Rscript</span><span class="w">
  </span><span class="n">a</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">commandArgs</span><span class="p">(</span><span class="kc">TRUE</span><span class="p">)</span><span class="w">
  </span><span class="n">d</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">gsub</span><span class="p">(</span><span class="s2">"^_|[.][a-zA-Z]+$"</span><span class="p">,</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="p">[</span><span class="m">1</span><span class="p">])</span><span class="w">
  </span><span class="n">knitr</span><span class="o">::</span><span class="n">opts_chunk</span><span class="o">$</span><span class="n">set</span><span class="p">(</span><span class="w">
    </span><span class="n">fig.path</span><span class="w">   </span><span class="o">=</span><span class="w"> </span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"figure/%s/"</span><span class="p">,</span><span class="w"> </span><span class="n">d</span><span class="p">),</span><span class="w">
    </span><span class="n">cache.path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">sprintf</span><span class="p">(</span><span class="s2">"cache/%s/"</span><span class="p">,</span><span class="w"> </span><span class="n">d</span><span class="p">)</span><span class="w">
  </span><span class="p">)</span><span class="w">
  </span><span class="n">knitr</span><span class="o">::</span><span class="n">knit</span><span class="p">(</span><span class="w">
    </span><span class="n">a</span><span class="p">[</span><span class="m">1</span><span class="p">],</span><span class="w"> </span><span class="n">a</span><span class="p">[</span><span class="m">2</span><span class="p">],</span><span class="w"> </span><span class="n">quiet</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w"> </span><span class="n">encoding</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"UTF-8"</span><span class="p">,</span><span class="w">
    </span><span class="n">envir</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nf">globalenv</span><span class="p">()</span><span class="w">
  </span><span class="p">)</span><span class="w">
</span><span class="p">})</span><span class="w">
</span></code></pre></div></div>

<p>So I understood that the knitr options called <code class="language-text highlighter-rouge">base.url</code> is a prefix to <code class="language-text highlighter-rouge">fig.path</code>, while <code class="language-text highlighter-rouge">base.dir</code> is the actual directory where the images will be genereated, <code class="language-text highlighter-rouge">base.url</code> is pretty much an alias.</p>

<p>For instance by setting <code class="language-text highlighter-rouge">base.url='/'</code>, <code class="language-text highlighter-rouge">fig.path='assets/r_figures/'</code> and <code class="language-text highlighter-rouge">base.dir='C:\Users\Gabryxx7\Documents\GitHub\blog\' </code> the final markdown for a figure will look something like this:</p>

<div class="language-markdown highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">![](</span><span class="sx">/assets/r_figures/nice_plot.png</span><span class="p">)</span><span class="c">&lt;!-- --&gt;</span>
</code></pre></div></div>

<p>And <code class="language-text highlighter-rouge">nice_plot.png</code> will actually be located at <code class="language-text highlighter-rouge">C:\Users\Gabryxx7\Documents\GitHub\blog\assets\r_figures\nice_plot.png</code>.</p>

<p>This is awesome since Jekyll will interpret the first <code class="language-text highlighter-rouge">/</code> as the website root folder where the <code class="language-text highlighter-rouge">assets</code> folder actually is!</p>

<p>From the <code class="language-text highlighter-rouge">knitr</code> <a href="https://yihui.org/knitr/objects/">documentation</a>:</p>
<blockquote>
  <p>Knitr’s settings must be set in a chunk before any chunks which rely on those settings to be active. It is recommended to create a knit configuration chunk as the first chunk in a script with cache = FALSE and include = FALSE options set. This chunk must not contain any commands which expect the settings in the configuration chunk to be in effect at the time of execution.</p>
</blockquote>

<p>Another interesting feature is the possibility to set up parameters into the <code class="language-text highlighter-rouge">yaml</code> header and use them withing your code. This makes up for <a href="https://rmarkdown.rstudio.com/developer_parameterized_reports.html">parameterized reports</a></p>

<p>So with all this knowledge, let’s get started.</p>

<h2 id="setting-up-the-r-markdown-file">Setting up the R Markdown file</h2>

<p>When on RStudio the first thing you should do is to create a new R Markdown file. So <code class="language-text highlighter-rouge">File -&gt; New File -&gt; R Markdown</code>.</p>

<p>Once you get your file, you can add the following lines to your heading and as the first chunk of your markdown file:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="nn">---</span>
<span class="na">title</span><span class="pi">:</span> <span class="s2">"</span><span class="s">R</span><span class="nv"> </span><span class="s">Markdown</span><span class="nv"> </span><span class="s">and</span><span class="nv"> </span><span class="s">jekyll"</span>
<span class="na">output</span><span class="pi">:</span>
  <span class="na">md_document</span><span class="pi">:</span>
    <span class="na">variant</span><span class="pi">:</span> <span class="s">gfm</span>
    <span class="na">preserve_yaml</span><span class="pi">:</span> <span class="s">TRUE</span>
<span class="na">params</span><span class="pi">:</span> 
  <span class="na">fig_base_path</span><span class="pi">:</span> <span class="s2">"</span><span class="s">C:</span><span class="se">\\</span><span class="s">Users</span><span class="se">\\</span><span class="s">Gabryxx7</span><span class="se">\\</span><span class="s">Documents</span><span class="se">\\</span><span class="s">GitHub</span><span class="se">\\</span><span class="s">blog</span><span class="se">\\</span><span class="s">assets</span><span class="se">\\</span><span class="s">gabryxx7</span><span class="se">\\</span><span class="s">r_figures"</span>
<span class="na">knit</span><span class="pi">:</span> <span class="s">(function(input, ...) {</span>
  <span class="s">rmarkdown::render(input=input,</span>
    <span class="s">output_file=paste0(Sys.Date(),'-',xfun::sans_ext(basename(input)),'.md'),</span>
    <span class="s">encoding = encoding,</span>
    <span class="s">output_dir="C:\\Users\\Gabryxx7\\Documents\\GitHub\\blog\\blog\\coding\\_posts")</span>
  <span class="s">})</span>
<span class="nn">---</span>

<span class="err">```</span><span class="pi">{</span><span class="nv">r setup</span><span class="pi">,</span> <span class="nv">include=FALSE</span><span class="pi">}</span>
<span class="s">knitr::opts_knit$set( base.dir=params$fig_base_path, base.url="/assets/gabryxx7/r_figures/")</span>
<span class="s">knitr::opts_chunk$set(echo = TRUE, fig.path = "")</span>

<span class="err">```</span></code></pre></figure>

<p class="message"><strong>HEADS UP</strong>: If you get an error saying <code class="language-text highlighter-rouge">Error in setwd(...): cannot change the working directory</code> you probably mispelled the path or pointed at a non existing directory. R Will only create directories specified under <code class="language-text highlighter-rouge">opts_chunk$set</code> but anything else (for instance <code class="language-text highlighter-rouge">fig_base_path</code>) will be expected to exist already.</p>

<p>So connecting back to <code class="language-text highlighter-rouge">blogdown</code>  I found out that you can override the <a href="https://rmarkdown.rstudio.com/docs/reference/render.html"><code class="language-text highlighter-rouge">rmarkdown::render()</code></a> function in R Markdown and R Notebook files.
The render function takes as parameters an input file path and an encoding string (here it’s included in <code class="language-text highlighter-rouge">...</code>).</p>

<p>By overriding the function we can tell it to render the .rmd <code class="language-text highlighter-rouge">input</code> file to a new file with <code class="language-text highlighter-rouge">.md</code> extension and the proper Jekyll’s post markdown formatting. The part <code class="language-text highlighter-rouge">xfun::sans_ext(basename(input))</code> is just getting the filename from the <code class="language-text highlighter-rouge">input</code> file path and removing its extension.
Finally just set the <code class="language-text highlighter-rouge">output_dir</code> to be whatever your Jekyll post folder is (some extra freedom for you! Decoupling is always the key!).</p>

<p>A bit of an annoying thing is that you cannot just use a base path and then concatenate the twos since <code class="language-text highlighter-rouge">rmarkdown::render()</code> will be called before the <code class="language-text highlighter-rouge">YAML</code> header is processed. I mean, <code class="language-text highlighter-rouge">params</code> is literally in the same header, unless <code class="language-text highlighter-rouge">YAML</code> is interpreted which is not, there is no way for <code class="language-text highlighter-rouge">knit</code> to know what other variables are in the yaml header.</p>

<p>For this reason you will have to copy-paste your folder path twice, once as the <code class="language-text highlighter-rouge">output_dir</code> parameter in the <code class="language-text highlighter-rouge">rmarkdown::render()</code> function and another time as a parameter in your <code class="language-text highlighter-rouge">YAML</code> header. But that’s actually not all bad, as I said before now you can output your figures to whatever folder you prefer.
This is especially useful in my case where I work on different machines (develope my blog on Windows/Macbook and depploy on Linux server).</p>

<p>So as you can see here:</p>
<ul>
  <li>The <code class="language-text highlighter-rouge">.md</code> markdown file will be generated in <code class="language-text highlighter-rouge">C:\Users\Gabryxx7\Documents\GitHub\blog\blog\coding\_posts</code></li>
  <li>The figure files will be generated in: <code class="language-text highlighter-rouge">C:\Users\Gabryxx7\Documents\GitHub\blog\assets\gabryxx7\r_figures</code></li>
  <li>The figures in the markdown file will all have <code class="language-text highlighter-rouge">/assets/gabryxx7/r_figures/</code> as base url so that jekyll will know where to get the files starting from the root of your website.</li>
</ul>

<p>We can actually take it a step further and put the figures in each post’s separate folder in the <code class="language-text highlighter-rouge">r_figures</code> base folder:</p>
<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">knitr</span><span class="o">::</span><span class="n">opts_chunk</span><span class="o">$</span><span class="n">set</span><span class="p">(</span><span class="n">echo</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
                      </span><span class="n">strip.white</span><span class="o">=</span><span class="kc">TRUE</span><span class="p">,</span><span class="w">
                      </span><span class="n">message</span><span class="o">=</span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
                      </span><span class="n">warning</span><span class="o">=</span><span class="kc">FALSE</span><span class="p">,</span><span class="w">
                      </span><span class="n">results</span><span class="o">=</span><span class="s2">"markup"</span><span class="p">,</span><span class="w">
                      </span><span class="n">out.format</span><span class="o">=</span><span class="s2">"jekyll"</span><span class="p">,</span><span class="w">
                      </span><span class="n">class.output</span><span class="o">=</span><span class="s2">"plaintext"</span><span class="p">,</span><span class="w">
                      </span><span class="n">out.extra</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">""</span><span class="p">,</span><span class="w">
                      </span><span class="n">fig.path</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">paste0</span><span class="p">(</span><span class="n">Sys.Date</span><span class="p">(),</span><span class="s1">'-'</span><span class="p">,</span><span class="n">xfun</span><span class="o">::</span><span class="n">sans_ext</span><span class="p">(</span><span class="n">basename</span><span class="p">(</span><span class="n">knitr</span><span class="o">::</span><span class="n">current_input</span><span class="p">())),</span><span class="s2">"/"</span><span class="p">))</span><span class="w">
</span></code></pre></div></div>

<p>In this way:</p>
<ul>
  <li>The final path will be <code class="language-text highlighter-rouge">C:\Users\Gabryxx7\Documents\GitHub\blog\assets\gabryxx7\r_figures\2020-08-13-ggantt</code></li>
  <li>The images url will be <code class="language-text highlighter-rouge">/assets/gabryxx7/r_figures/2020-08-13-ggantt/nice_plot.png</code></li>
  <li>Message and warnings won’t be included in the code output. The code output will be wrapped in a code chunk with language <code class="language-text highlighter-rouge">plaintext</code> so that we can capture it with javascript and do something with it</li>
</ul>

<p class="message"><strong>HEADS UP</strong>: Do not forget to add <code class="language-text highlighter-rouge">out.extra = ""</code> otherwise knitr will add images with an ending comment like <code class="language-text highlighter-rouge">![](/assets/gabryxx7/r_figures/2020-08-13-ggantt/test.png)&lt;!-- --&gt;</code> completely messing up the <code class="language-text highlighter-rouge">html</code> page in Jekyll</p>

<h2 id="knitting-it">Knitting it!</h2>
<p>Well there is not much left to do, just press <code class="language-text highlighter-rouge">knit</code> on RStudio with your <code class="language-text highlighter-rouge">.rmd</code> file opened and you’re good to go!</p>

<p>Alternatively you can call the knit function from the console but be careful as this won’t call the <code class="language-text highlighter-rouge">markdown::render()</code> method in the <code class="language-text highlighter-rouge">yaml</code> header so the <code class="language-text highlighter-rouge">.md</code> file will be generated in the working directory unless you specify the output file path:</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">setwd</span><span class="p">(</span><span class="s2">"C:\\Users\\Gabryxx7\\Documents\\GitHub\\whatever\\"</span><span class="p">)</span><span class="w"> </span><span class="c1"># adjust to your preferences.</span><span class="w">
</span><span class="n">knitr</span><span class="o">::</span><span class="n">knit</span><span class="p">(</span><span class="s2">"rmarkdown_file.rmd"</span><span class="p">)</span><span class="w"> </span><span class="c1"># Generate to working directory</span><span class="w">
</span><span class="n">knitr</span><span class="o">::</span><span class="n">knit</span><span class="p">(</span><span class="s2">"ggantt.rmd"</span><span class="p">,</span><span class="w"> </span><span class="n">output</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="s2">"C:\\Users\\Gabryxx7\\Documents\\GitHub\\blog\\blog\\coding\\_posts\\2020-08-13-rmarkdown_file.md"</span><span class="p">)</span><span class="w"> </span><span class="c1"># Generate to Jekyll's post directory</span><span class="w">
</span></code></pre></div></div>

<p>You can also make your own function in case you have multiple files:</p>
<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">knit_to_jekyll</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="k">function</span><span class="p">(</span><span class="n">input</span><span class="p">,</span><span class="w"> </span><span class="n">...</span><span class="p">)</span><span class="w"> </span><span class="p">{</span><span class="w">
  </span><span class="n">rmarkdown</span><span class="o">::</span><span class="n">render</span><span class="p">(</span><span class="n">input</span><span class="o">=</span><span class="n">input</span><span class="p">,</span><span class="w">
    </span><span class="n">output_file</span><span class="o">=</span><span class="n">paste0</span><span class="p">(</span><span class="n">Sys.Date</span><span class="p">(),</span><span class="s1">'-'</span><span class="p">,</span><span class="n">xfun</span><span class="o">::</span><span class="n">sans_ext</span><span class="p">(</span><span class="n">basename</span><span class="p">(</span><span class="n">input</span><span class="p">)),</span><span class="s1">'.md'</span><span class="p">),</span><span class="w">
    </span><span class="n">encoding</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">encoding</span><span class="p">,</span><span class="w">
    </span><span class="n">output_dir</span><span class="o">=</span><span class="s2">"C:\\Users\\Gabryxx7\\Documents\\GitHub\\blog\\blog\\coding\\_posts"</span><span class="p">)</span><span class="w">
  </span><span class="p">}</span><span class="w">
</span><span class="n">knit_to_jekyll</span><span class="p">(</span><span class="s2">"r_notebook_markdown.rmd"</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[One thing I would love to do with this blog is to post data anlysis done in R (or Python sometimes). They can be a lot of fun and I would prefer to spend the time writing the R notebook rather than converting it afterward to pure Markdown for Jekyll. So here is a few tips for an easy R Markdown to Jekyll Markdown port (Thanks to this guide ). Another interesting package is blogdown whith has Jekyll support. The most interesting part was their code for generating markdown files and figures in different folders]]></summary></entry><entry><title type="html">R Markdown</title><link href="https://gmarini.com/blog/blog/coding/2020-08-13-ggantt/" rel="alternate" type="text/html" title="R Markdown" /><published>2020-08-13T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/ggantt</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2020-08-13-ggantt/"><![CDATA[<p>Alright so I am finally doing it! I am describing how I make Gantt
charts in R, and how I want to make it easier to generate them through
<code class="language-text highlighter-rouge">shiny</code> dashboards. This is an R Markdown document. Markdown is a simple
formatting syntax for authoring HTML, PDF, and MS Word documents. For
more details on using R Markdown see <a href="http://rmarkdown.rstudio.com">http://rmarkdown.rstudio.com</a>.</p>

<p>When you click the <strong>Knit</strong> button a document will be generated that
includes both content as well as the output of any embedded R code
chunks within the document. You can embed an R code chunk like this:
<!--more--></p>
<ul class="large-only" id="markdown-toc">
  <li><a href="#title-2" id="markdown-toc-title-2">Title 2</a>    <ul>
      <li><a href="#including-plots" id="markdown-toc-including-plots">Including Plots</a></li>
    </ul>
  </li>
</ul>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">library</span><span class="p">(</span><span class="n">tidyverse</span><span class="p">)</span><span class="w">
</span><span class="n">vlines</span><span class="w"> </span><span class="o">&lt;-</span><span class="w"> </span><span class="n">tribble</span><span class="p">(</span><span class="w">
  </span><span class="o">~</span><span class="n">Date</span><span class="p">,</span><span class="w"> </span><span class="o">~</span><span class="n">LineType</span><span class="p">,</span><span class="w"> </span><span class="o">~</span><span class="n">Size</span><span class="p">,</span><span class="w"> </span><span class="o">~</span><span class="n">Color</span><span class="p">,</span><span class="w">
  </span><span class="s2">"2020-01-01"</span><span class="p">,</span><span class="w"> </span><span class="s2">"solid"</span><span class="p">,</span><span class="w"> </span><span class="m">0.6</span><span class="p">,</span><span class="w"> </span><span class="s2">"grey80"</span><span class="p">,</span><span class="w">
  </span><span class="s2">"2021-01-01"</span><span class="p">,</span><span class="w"> </span><span class="s2">"solid"</span><span class="p">,</span><span class="w"> </span><span class="m">0.6</span><span class="p">,</span><span class="w"> </span><span class="s2">"grey80"</span><span class="p">)</span><span class="w">
</span><span class="n">summary</span><span class="p">(</span><span class="n">cars</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>##      speed           dist       
##  Min.   : 4.0   Min.   :  2.00  
##  1st Qu.:12.0   1st Qu.: 26.00  
##  Median :15.0   Median : 36.00  
##  Mean   :15.4   Mean   : 42.98  
##  3rd Qu.:19.0   3rd Qu.: 56.00  
##  Max.   :25.0   Max.   :120.00
</code></pre></div></div>

<h1 id="title-2">Title 2</h1>

<p>So Imma going to try to add a few extra lines and see what the output is
going to be</p>

<div class="language-r highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">summary</span><span class="p">(</span><span class="n">mtcars</span><span class="p">)</span><span class="w">
</span></code></pre></div></div>

<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>##       mpg             cyl             disp             hp       
##  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
##  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
##  Median :19.20   Median :6.000   Median :196.3   Median :123.0  
##  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
##  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
##  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
##       drat             wt             qsec             vs        
##  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
##  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
##  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
##  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
##  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
##  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
##        am              gear            carb      
##  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
##  Median :0.0000   Median :4.000   Median :2.000  
##  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
##  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :1.0000   Max.   :5.000   Max.   :8.000
</code></pre></div></div>

<div class="language-text highlighter-rouge"><div class="highlight"><pre class="highlight"><code>##       mpg             cyl             disp             hp       
##  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  
##  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  
##  Median :19.20   Median :6.000   Median :196.3   Median :123.0  
##  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  
##  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  
##  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  
##       drat             wt             qsec             vs        
##  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  
##  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  
##  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  
##  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  
##  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  
##  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  
##        am              gear            carb      
##  Min.   :0.0000   Min.   :3.000   Min.   :1.000  
##  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  
##  Median :0.0000   Median :4.000   Median :2.000  
##  Mean   :0.4062   Mean   :3.688   Mean   :2.812  
##  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  
##  Max.   :1.0000   Max.   :5.000   Max.   :8.000
</code></pre></div></div>

<div class="language-console highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="gp">#</span><span class="c">#       mpg             cyl             disp             hp       </span>
<span class="gp">#</span><span class="c">#  Min.   :10.40   Min.   :4.000   Min.   : 71.1   Min.   : 52.0  </span>
<span class="gp">#</span><span class="c">#  1st Qu.:15.43   1st Qu.:4.000   1st Qu.:120.8   1st Qu.: 96.5  </span>
<span class="gp">#</span><span class="c">#  Median :19.20   Median :6.000   Median :196.3   Median :123.0  </span>
<span class="gp">#</span><span class="c">#  Mean   :20.09   Mean   :6.188   Mean   :230.7   Mean   :146.7  </span>
<span class="gp">#</span><span class="c">#  3rd Qu.:22.80   3rd Qu.:8.000   3rd Qu.:326.0   3rd Qu.:180.0  </span>
<span class="gp">#</span><span class="c">#  Max.   :33.90   Max.   :8.000   Max.   :472.0   Max.   :335.0  </span>
<span class="gp">#</span><span class="c">#       drat             wt             qsec             vs        </span>
<span class="gp">#</span><span class="c">#  Min.   :2.760   Min.   :1.513   Min.   :14.50   Min.   :0.0000  </span>
<span class="gp">#</span><span class="c">#  1st Qu.:3.080   1st Qu.:2.581   1st Qu.:16.89   1st Qu.:0.0000  </span>
<span class="gp">#</span><span class="c">#  Median :3.695   Median :3.325   Median :17.71   Median :0.0000  </span>
<span class="gp">#</span><span class="c">#  Mean   :3.597   Mean   :3.217   Mean   :17.85   Mean   :0.4375  </span>
<span class="gp">#</span><span class="c">#  3rd Qu.:3.920   3rd Qu.:3.610   3rd Qu.:18.90   3rd Qu.:1.0000  </span>
<span class="gp">#</span><span class="c">#  Max.   :4.930   Max.   :5.424   Max.   :22.90   Max.   :1.0000  </span>
<span class="gp">#</span><span class="c">#        am              gear            carb      </span>
<span class="gp">#</span><span class="c">#  Min.   :0.0000   Min.   :3.000   Min.   :1.000  </span>
<span class="gp">#</span><span class="c">#  1st Qu.:0.0000   1st Qu.:3.000   1st Qu.:2.000  </span>
<span class="gp">#</span><span class="c">#  Median :0.0000   Median :4.000   Median :2.000  </span>
<span class="gp">#</span><span class="c">#  Mean   :0.4062   Mean   :3.688   Mean   :2.812  </span>
<span class="gp">#</span><span class="c">#  3rd Qu.:1.0000   3rd Qu.:4.000   3rd Qu.:4.000  </span>
<span class="gp">#</span><span class="c">#  Max.   :1.0000   Max.   :5.000   Max.   :8.000</span>
</code></pre></div></div>

<h2 id="including-plots">Including Plots</h2>

<p>You can also embed plots, for example:</p>

<p><img src="/assets/gabryxx7/r_figures/2020-08-13-ggantt/pressure-1.png" /></p>

<p>Note that the <code class="language-text highlighter-rouge">echo = FALSE</code> parameter was added to the code chunk to
prevent printing of the R code that generated the plot.</p>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[Alright so I am finally doing it! I am describing how I make Gantt charts in R, and how I want to make it easier to generate them through shiny dashboards. This is an R Markdown document. Markdown is a simple formatting syntax for authoring HTML, PDF, and MS Word documents. For more details on using R Markdown see http://rmarkdown.rstudio.com. When you click the Knit button a document will be generated that includes both content as well as the output of any embedded R code chunks within the document. You can embed an R code chunk like this:]]></summary></entry><entry><title type="html">How I made the blog’s terminal animation</title><link href="https://gmarini.com/blog/blog/coding/2020-08-10-typing-window/" rel="alternate" type="text/html" title="How I made the blog’s terminal animation" /><published>2020-08-10T00:00:00+10:00</published><updated>2022-05-16T21:12:10+10:00</updated><id>https://gmarini.com/blog/blog/coding/typing-window</id><content type="html" xml:base="https://gmarini.com/blog/blog/coding/2020-08-10-typing-window/"><![CDATA[<script>
var postTyperStarted = false;

var startPostTyper = function(typingWindow){
  postTyperStarted = true;
  var typingWindow = document.getElementsByClassName('typing-window post')[0];  

  if(!typingWindow){
    return;
  };
  
  var app = typingWindow.getElementsByClassName('typing-area')[0];
  var terminalStarter = "> <span style='color: #5cd400;'> gabryxx7@blog:</span><span style='color: #1c39c7;'>~</span>$ ";
  var typewriter = new Typewriter(app, {
    loop: false,
    delay: 'natural',
    cursor: '▌'
  });
  console.log("Starting post typer!");
  typewriter
  .changeDelay(90)
  .pasteString("<br /> " +terminalStarter)
  .pauseFor(1200)
  .typeString("Hey!")
  .pauseFor(1500)
  .pasteString("<br /> " +terminalStarter)
  .pauseFor(1200)
  .typeString("So you're wondering how I did this?")
  .pauseFor(800)
  .pasteString("<br /> " +terminalStarter)
  .pauseFor(1500)
  .typeString("Wonder <strong>NO MORE</strong>")
  .pauseFor(1000)
  .pasteString("<br /> " + terminalStarter)
  .pauseFor(500)
  .typeString("Here is a tutorial!")
  .start();
};
document.getElementById('_pushState').addEventListener('hy-push-state-load', function() {
    if(!postTyperStarted) startPostTyper();
});
document.addEventListener('DOMContentLoaded', function(){ 
  if(!postTyperStarted) startPostTyper();
}, false);
</script>

<p>So I wanted to give my blog a cool vibe while also looking good. I did not want it to be too overwhelming with effects, sliding animations and such.
So I opted for something simple, it’s just text after all! But it’s also quite catchy and fun to watch.</p>

<p>So here is how I have achieved this effect, it’s nothing crazy hard really but I love playing with CSS and Javascript and so I did.</p>

<!--more-->

<ul class="large-only" id="markdown-toc">
  <li><a href="#creating-the-terminal-typewriter" id="markdown-toc-creating-the-terminal-typewriter">Creating the terminal typewriter</a>    <ul>
      <li><a href="#the-basic-html" id="markdown-toc-the-basic-html">The basic HTML</a></li>
      <li><a href="#styling-the-terminal" id="markdown-toc-styling-the-terminal">Styling the terminal</a></li>
      <li><a href="#start-typing" id="markdown-toc-start-typing">Start typing!</a></li>
      <li><a href="#adding-a-status-bar" id="markdown-toc-adding-a-status-bar">Adding a status bar</a></li>
      <li><a href="#extras-looping-list-of-words-emojis" id="markdown-toc-extras-looping-list-of-words-emojis">Extras (looping, list of words, emojis)</a></li>
    </ul>
  </li>
  <li><a href="#adding-a-terminal-typewriter-to-your-page-or-post" id="markdown-toc-adding-a-terminal-typewriter-to-your-page-or-post">Adding a terminal typewriter to your page or post</a>    <ul>
      <li><a href="#adding-content-to-the-blog-page" id="markdown-toc-adding-content-to-the-blog-page">Adding content to the blog page</a></li>
      <li><a href="#allowing-for-custom-featured-images-in-posts" id="markdown-toc-allowing-for-custom-featured-images-in-posts">Allowing for custom featured images in posts</a></li>
      <li><a href="#adding-the-javascript" id="markdown-toc-adding-the-javascript">Adding the javascript</a></li>
      <li><a href="#adding-a-description-to-your-page-terminal" id="markdown-toc-adding-a-description-to-your-page-terminal">Adding a description to your page terminal</a></li>
    </ul>
  </li>
</ul>

<p>I started by looking for Javascript libraries that implemented the typewriter effect. I would rather go for pure javsacript than JQuery, even better if it’s a lightweight specific library that only includes that effect.</p>

<p>So I found <a href="https://github.com/tameemsafi/typewriterjs">Typewriter.js</a>, which does exactly what I needed!</p>

<p>The challenge was of course to make sure it looked nice and smooth!</p>

<h1 id="creating-the-terminal-typewriter">Creating the terminal typewriter</h1>
<h2 id="the-basic-html">The basic HTML</h2>
<p>The <code class="language-text highlighter-rouge">html</code> is really simple, nothing to explain here. I just added a pipe <code class="language-text highlighter-rouge">▌</code> character as default in the typing area so it does not look empty when the user loads the page and Typewriter has not started typing yet.</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html">  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-window'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-area"</span><span class="nt">&gt;</span>▌<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span></code></pre></figure>

<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-window'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-area"</span><span class="nt">&gt;</span>▌<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
</code></pre></div></div>

<p>Now that we have our <code class="language-text highlighter-rouge">html</code> structure we can start styling it and adding the actual typewriter to it!</p>

<h2 id="styling-the-terminal">Styling the terminal</h2>
<p>I wanted to give it a terminal look, but also not a <code class="language-text highlighter-rouge">DOS</code> look, something a bit more fancy and modern. So I started with a simple rounded rectangle with a nice dark gray background:</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.typing-window</span> <span class="p">{</span>
  <span class="nl">transition</span><span class="p">:</span> <span class="n">opacity</span> <span class="m">0.5s</span><span class="p">,</span> <span class="n">max-height</span> <span class="m">0.5s</span><span class="p">;</span>
  <span class="nl">max-height</span><span class="p">:</span> <span class="m">9000px</span><span class="p">;</span>
  <span class="nl">font-size</span><span class="p">:</span> <span class="m">1.6rem</span><span class="p">;</span>
  <span class="nl">line-height</span><span class="p">:</span> <span class="m">1.2</span><span class="p">;</span>
  <span class="nl">font-weight</span><span class="p">:</span> <span class="m">400</span><span class="p">;</span>
  <span class="nl">font-family</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--code-font-family</span><span class="p">);</span>
  <span class="nl">background-color</span><span class="p">:</span> <span class="m">#1f1f21</span><span class="p">;</span>
  <span class="nl">border-radius</span><span class="p">:</span> <span class="m">0.5rem</span><span class="p">;</span>
  <span class="nl">padding</span><span class="p">:</span> <span class="m">0</span><span class="p">;</span>
  <span class="nl">color</span><span class="p">:</span> <span class="m">#ccc</span><span class="p">;</span>
  <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">2rem</span><span class="p">;</span>
  <span class="nl">box-shadow</span><span class="p">:</span> <span class="m">0</span> <span class="m">1px</span> <span class="m">1px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.12</span><span class="p">),</span> <span class="m">0</span> <span class="m">2px</span> <span class="m">2px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.12</span><span class="p">),</span> <span class="m">0</span> <span class="m">4px</span> <span class="m">4px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.12</span><span class="p">),</span> <span class="m">0</span> <span class="m">8px</span> <span class="m">8px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.12</span><span class="p">),</span> <span class="m">0</span> <span class="m">16px</span> <span class="m">16px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.12</span><span class="p">);</span>
  
  <span class="err">strong</span> <span class="err">{</span>
    <span class="nl">font-weight</span><span class="p">:</span> <span class="m">700</span><span class="p">;</span>
  <span class="p">}</span>

  <span class="nc">.typing-area</span><span class="p">{</span>
    <span class="nl">padding</span><span class="p">:</span> <span class="m">1.5rem</span> <span class="m">1.5rem</span> <span class="m">1.5rem</span> <span class="m">1rem</span><span class="p">;</span>
  <span class="p">}</span>
<span class="err">}</span></code></pre></figure>

<div class="typing-window">
  <div class="typing-area">Terminal Example! The font is <strong><a href="https://github.com/tonsky/FiraCode">FiraCode</a></strong> <br />
   &lt;- -&gt; != !== ==&gt; |= ▌</div>
  </div>

<p>(I LOVE this font, it’s amazing and it has some cool ligatures!)
A few CSS tricks I learnt over the years:</p>
<ul>
  <li><code class="language-text highlighter-rouge">transition</code>: It animates any change to the specified property, in this case I am aimating <code class="language-text highlighter-rouge">opacity</code> and <code class="language-text highlighter-rouge">max-height</code> so whenever they change, their value is animated in <code class="language-text highlighter-rouge">0.5s</code></li>
  <li><code class="language-text highlighter-rouge">font-family</code>: I am using the CSS variable that Hydejack uses for the code blocks font family, just to be consistent with the rest of the website</li>
  <li><code class="language-text highlighter-rouge">box-shadow</code>: This is a layered, high quality shadow generated with https://brumm.af/shadows</li>
</ul>

<h2 id="start-typing">Start typing!</h2>
<p>This is the best part of the project!</p>

<p>Now that we have everything set up we can start typing in text into the terminal. Let’s start by retreiving the container where the text will go</p>

<p>Ok so now that we have our container, we need to look for the typing area. It’s also a good idea to only start typing if the container exists, and do nothing otherwise.</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">typerStarted</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">startTyper</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>   
  <span class="nx">typerStarted</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">typingWindow</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="dl">'</span><span class="s1">typing-window</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>  
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">typingWindow</span><span class="p">){</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">};</span>
  <span class="kd">var</span> <span class="nx">app</span> <span class="o">=</span> <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="dl">'</span><span class="s1">typing-area</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
  <span class="kd">var</span> <span class="nx">typewriter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Typewriter</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">loop</span><span class="p">:</span> <span class="kc">true</span><span class="p">,</span>
    <span class="na">delay</span><span class="p">:</span> <span class="dl">'</span><span class="s1">natural</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">cursor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">▌</span><span class="dl">'</span>
  <span class="p">});</span>
  <span class="nx">typewriter</span>
  <span class="p">.</span><span class="nx">pasteString</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hey, I'm alive!)
  .pauseFor(1200)
  .typeString(</span><span class="dl">"</span><span class="o">&lt;</span><span class="nx">br</span><span class="o">/&gt;</span> <span class="nx">Yup</span><span class="p">,</span> <span class="nx">still</span> <span class="nx">alive</span><span class="o">!</span><span class="dl">"</span><span class="s2">)
  .pauseFor(2000)
  .deleteChars(17)
  .typeString(</span><span class="dl">"</span> <span class="nx">Noooo</span> <span class="nx">don</span><span class="dl">'</span><span class="s1">t delete mee!")
  .pauseFor(1000)
  .start();
};</span></code></pre></figure>

<p>We also need to make sure that the typing only starts when the page content is loaded. In the case of Hydejack (the Jekyll theme I am using), we need to account for dynamic page loading so:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="nb">document</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">DOMContentLoaded</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(){</span> 
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">typerStarted</span><span class="p">)</span> <span class="nx">startTyper</span><span class="p">();</span>
<span class="p">},</span> <span class="kc">false</span><span class="p">);</span>
<span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="dl">'</span><span class="s1">_pushState</span><span class="dl">'</span><span class="p">).</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">hy-push-state-load</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">typerStarted</span><span class="p">)</span> <span class="nx">startTyper</span><span class="p">();</span>
<span class="p">});</span></code></pre></figure>

<p>Alright so let’s test it out, shall we?</p>

<div class="typing-window test">
  <div class="typing-area"></div>
  </div>

<script>
var typerStarted = false;
var startTestTyper = function() {   
  typerStarted = true;
  console.log("Starting test typer");
  var typingWindow = document.getElementsByClassName('typing-window test')[0];  
  if(!typingWindow){
    return;
  };
  var app = typingWindow.getElementsByClassName('typing-area')[0];
  var typewriter = new Typewriter(app, {
    loop: true,
    delay: 'natural',
    cursor: '▌'
  });
  typewriter
  .pasteString("Hey, I'm alive!")
  .pauseFor(1200)
  .typeString("<br/> Yup, still alive!")
  .pauseFor(2000)
  .deleteChars(17)
  .typeString(" Noooo don't delete mee!")
  .pauseFor(1000)
  .start();
};

document.addEventListener('DOMContentLoaded', function(){ 
  if(!typerStarted) startTestTyper();
}, false);
document.getElementById('_pushState').addEventListener('hy-push-state-load', function() {
  if(!typerStarted) startTestTyper();
});

</script>

<h2 id="adding-a-status-bar">Adding a status bar</h2>
<p>Let’s try and give it more of a terminal look now by adding a Ubuntu-styled status bar. Let’s add a few <code class="language-text highlighter-rouge">div</code>s for the bar and its buttons</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html">  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-window'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-toolbar'</span><span class="nt">&gt;</span>      
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"toolbar-title"</span><span class="nt">&gt;</span>
          Window Title
        <span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"toolbar-btns-container"</span><span class="nt">&gt;</span>
          <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn min"</span><span class="nt">&gt;</span>—<span class="nt">&lt;/div&gt;</span>
          <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn size"</span><span class="nt">&gt;</span>☐<span class="nt">&lt;/div&gt;</span>
          <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn close disabled"</span><span class="nt">&gt;</span>✕<span class="nt">&lt;/div&gt;</span>
        <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-area"</span><span class="nt">&gt;</span>▌<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span></code></pre></figure>

<p>Let’s style it up a little bit now. We want to use an <code class="language-text highlighter-rouge">inset</code> shadow, and give a different color to the buttons.
The only clickable button should be the closing button so let’s change it’s <code class="language-text highlighter-rouge">:hover:</code> CSS and also add a <code class="language-text highlighter-rouge">disabled</code> state.</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.typing-toolbar</span> <span class="p">{</span>
  <span class="nl">display</span><span class="p">:</span> <span class="n">flex</span><span class="p">;</span>
  <span class="nl">justify-content</span><span class="p">:</span> <span class="n">flex-end</span><span class="p">;</span>
  <span class="nl">height</span><span class="p">:</span> <span class="m">1.6rem</span><span class="p">;</span>
  <span class="nl">padding</span><span class="p">:</span> <span class="m">0.1rem</span> <span class="m">0.1rem</span> <span class="m">0.2rem</span> <span class="m">0.2rem</span><span class="p">;</span>
  <span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--border-color</span><span class="p">);</span>
  <span class="nl">border-radius</span><span class="p">:</span> <span class="m">0.5rem</span> <span class="m">0.5rem</span> <span class="m">0rem</span> <span class="m">0rem</span><span class="p">;</span>
  <span class="nl">box-shadow</span><span class="p">:</span> <span class="nb">inset</span> <span class="m">-6px</span> <span class="m">3px</span> <span class="m">10px</span> <span class="m">0px</span> <span class="n">rgba</span><span class="p">(</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0</span><span class="p">,</span><span class="m">0.11</span><span class="p">);</span>

  <span class="err">.typing-toolbar-btn</span> <span class="err">{</span>
    <span class="nl">transition</span><span class="p">:</span> <span class="n">background-color</span> <span class="m">250ms</span><span class="p">;</span>
    <span class="nl">display</span><span class="p">:</span> <span class="n">inline-block</span><span class="p">;</span>
    <span class="nl">height</span><span class="p">:</span> <span class="m">1.1rem</span><span class="p">;</span>
    <span class="nl">width</span><span class="p">:</span> <span class="m">1.1rem</span><span class="p">;</span>
    <span class="nl">margin-top</span><span class="p">:</span> <span class="m">0.1rem</span><span class="p">;</span>
    <span class="nl">margin-right</span><span class="p">:</span> <span class="m">0.7rem</span><span class="p">;</span>
    <span class="nl">position</span><span class="p">:</span> <span class="nb">relative</span><span class="p">;</span>
    <span class="nl">z-index</span><span class="p">:</span> <span class="m">2</span><span class="p">;</span>
    <span class="nl">border-radius</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
    <span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">0.2rem</span><span class="p">;</span>
    <span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
    <span class="nl">font-size</span><span class="p">:</span> <span class="m">0.8rem</span><span class="p">;</span>
    <span class="nl">padding-top</span><span class="p">:</span> <span class="m">0.1rem</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="m">#848282</span><span class="p">;</span>
    <span class="nl">font-weight</span><span class="p">:</span> <span class="m">700</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nc">.typing-toolbar-btn.close</span> <span class="p">{</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="m">#e01e1e</span><span class="p">;</span>
  <span class="p">}</span>
  <span class="nc">.typing-toolbar-btn.close.disabled</span> <span class="p">{</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="m">#693737</span><span class="p">;</span>
    <span class="nl">color</span><span class="p">:</span> <span class="m">#8e8e8e</span><span class="p">;</span>
  <span class="p">}</span>
  
  <span class="nc">.typing-toolbar-btn.min</span> <span class="p">{</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--body-bg</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nc">.typing-toolbar-btn.size</span> <span class="p">{</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--body-bg</span><span class="p">);</span>
  <span class="p">}</span>
  <span class="nc">.typing-toolbar-btn.close</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.disabled</span><span class="o">)</span><span class="nd">:hover</span><span class="o">,</span>
  <span class="nc">.typing-toolbar-btn.close</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.disabled</span><span class="o">)</span><span class="nd">:focus</span><span class="o">,</span>
  <span class="nc">.typing-toolbar-btn.close</span><span class="nd">:not</span><span class="o">(</span><span class="nc">.disabled</span><span class="o">)</span><span class="nd">:active</span> <span class="p">{</span>
    <span class="nl">transition</span><span class="p">:</span> <span class="n">background-color</span> <span class="m">250ms</span><span class="p">;</span>
    <span class="nl">background-color</span><span class="p">:</span> <span class="m">#f94b29</span><span class="p">;</span>
    <span class="nl">cursor</span><span class="p">:</span> <span class="nb">pointer</span><span class="p">;</span>
  <span class="p">}</span>
<span class="err">}</span></code></pre></figure>

<div class="typing-window test-close_noaction">
  <div class="typing-toolbar">    
        <div class="toolbar-title">
          Window Title
        </div>
        <div class="toolbar-btns-container">
          <div class="typing-toolbar-btn min">—</div>
          <div class="typing-toolbar-btn size">☐</div>
          <div class="typing-toolbar-btn close disabled">✕</div>
        </div>
  </div>
  <div class="typing-area">▌</div>
</div>

<p>Now for the final touch, let’s make it so that the close button actually closes the terminal, but we’ll enable the button after 10 seconds so there is time for some text to be typed in the terminal!</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">startTestTyper</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>   
  <span class="nx">typerStarted</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">typingWindow</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="dl">'</span><span class="s1">typing-window test</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>  
  <span class="k">if</span><span class="p">(</span><span class="o">!</span><span class="nx">typingWindow</span><span class="p">){</span>
    <span class="k">return</span><span class="p">;</span>
  <span class="p">};</span>

  <span class="c1">//Let's retreive the close button, remember that getElementsByClassName returns an array</span>
  <span class="c1">//of elements but we only need the first one</span>
  <span class="kd">var</span> <span class="nx">closeBtn</span> <span class="o">=</span>  <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">getElementsByClassName</span><span class="p">(</span><span class="dl">'</span><span class="s1">typing-toolbar-btn close</span><span class="dl">'</span><span class="p">)[</span><span class="mi">0</span><span class="p">];</span>
  <span class="nx">setTimeout</span> <span class="p">(</span><span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
    <span class="nx">closeBtn</span><span class="p">.</span><span class="nx">classList</span><span class="p">.</span><span class="nx">toggle</span><span class="p">(</span><span class="dl">'</span><span class="s1">disabled</span><span class="dl">'</span><span class="p">);</span>
    <span class="nx">closeBtn</span><span class="p">.</span><span class="nx">onclick</span> <span class="o">=</span> <span class="kd">function</span><span class="p">()</span> <span class="p">{</span>
          <span class="nx">typewriter</span><span class="p">.</span><span class="nx">stop</span><span class="p">();</span>
          <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">opacity</span> <span class="o">=</span> <span class="mf">0.01</span><span class="p">;</span>
      <span class="p">};</span>
  <span class="p">},</span> <span class="mi">10000</span><span class="p">);</span>

  
  <span class="c1">//Let's listen for the end of the CSS transition (remember how opacity and max-height are animated in css?)</span>
  <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">addEventListener</span><span class="p">(</span><span class="dl">'</span><span class="s1">transitionend</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
    <span class="c1">//We don't want this to run for all the children, just this specific target</span>
    <span class="c1">//Events propagate through the DOM tree</span>
    <span class="k">if</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">target</span> <span class="o">!==</span> <span class="nx">event</span><span class="p">.</span><span class="nx">currentTarget</span><span class="p">)</span>
      <span class="k">return</span><span class="p">;</span>
      <span class="c1">// Javascript returns max-height as a string "0px" so we need to check it it's "0px" before removing the element</span>
    <span class="k">if</span> <span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">propertyName</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">max-height</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">maxHeight</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">0px</span><span class="dl">"</span><span class="p">){</span>
      <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">remove</span><span class="p">();</span>
    <span class="p">}</span><span class="k">else</span> <span class="k">if</span><span class="p">(</span><span class="nx">event</span><span class="p">.</span><span class="nx">propertyName</span> <span class="o">===</span> <span class="dl">"</span><span class="s2">opacity</span><span class="dl">"</span> <span class="o">&amp;&amp;</span> <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">opacity</span> <span class="o">&lt;</span> <span class="mf">0.1</span><span class="p">){</span>
      <span class="c1">//We FIRST change the opacity to make it invisible and only THEN we scale it down to slide the other content up</span>
      <span class="nx">typingWindow</span><span class="p">.</span><span class="nx">style</span><span class="p">.</span><span class="nx">maxHeight</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
    <span class="p">};</span>
 <span class="p">});</span>
  <span class="p">...</span>
<span class="p">};</span></code></pre></figure>

<div class="typing-window test-close">
  <div class="typing-toolbar">    
        <div class="toolbar-title">
          Window Title
        </div>
        <div class="toolbar-btns-container">
          <div class="typing-toolbar-btn min">—</div>
          <div class="typing-toolbar-btn size">☐</div>
          <div class="typing-toolbar-btn close disabled">✕</div>
        </div>
  </div>
  <div class="typing-area">▌</div>
</div>

<script>
var closeTyperStarted = false;
var startTestCloseTyper = function() {   
  closeTyperStarted = true;
  var typingWindow = document.getElementsByClassName('typing-window test-close')[0];  
  if(!typingWindow){
    return;
  };
  
  var closeBtn =  typingWindow.getElementsByClassName('typing-toolbar-btn close')[0];
  setTimeout (function() {
    closeBtn.classList.toggle('disabled');
    closeBtn.onclick = function() {
          typewriter.stop();
          typingWindow.style.opacity = 0.01;
      };
  }, 10000);
  
  typingWindow.addEventListener('transitionend', function(event) {
    if(event.target !== event.currentTarget)
      return;
      /* Javascript returns max-height as a string "0px" */
    if (event.propertyName === "max-height" && typingWindow.style.maxHeight === "0px"){
      typingWindow.remove();
    }else if(event.propertyName === "opacity" && typingWindow.style.opacity < 0.1){
      typingWindow.style.maxHeight = 0;
    };
 });

  var app = typingWindow.getElementsByClassName('typing-area')[0];
  var typewriter = new Typewriter(app, {
    loop: true,
    delay: 'natural',
    cursor: '▌'
  });
  typewriter
  .pasteString("Hey, I'm alive!")
  .pauseFor(1200)
  .typeString("<br/> Yup, still alive!")
  .pauseFor(2000)
  .deleteChars(17)
  .typeString(" Noooo don't delete mee!")
  .pauseFor(1000)
  .start();
};

document.addEventListener('DOMContentLoaded', function(){ 
  if(!closeTyperStarted) startTestCloseTyper();
}, false);
document.getElementById('_pushState').addEventListener('hy-push-state-load', function() {
  if(!closeTyperStarted) startTestCloseTyper();
});

</script>

<p class="message"><strong>Heads up</strong>: Reload the page if you don’t see the terminal or if you closed it already!</p>
<p>The code is fairly simple:</p>
<ol>
  <li>Retreive the close button</li>
  <li>Add an event listener to its click</li>
  <li>Add an event listener to the <code class="language-text highlighter-rouge">typingWindow</code> <code class="language-text highlighter-rouge">transitionend</code></li>
  <li>When It gets clicked, set the opacity of the window to 0</li>
  <li>CSS will start the animation, when it’s over it will trigger the event <code class="language-text highlighter-rouge">transitionend</code></li>
  <li>On the event handler we check if the opacity is below a certain threshold, if it is then the element is invisible and we can scale it down by
  setting its <code class="language-text highlighter-rouge">max-height</code> to 0</li>
  <li>When the <code class="language-text highlighter-rouge">transitionend</code> is triggered again we’ll check if <code class="language-text highlighter-rouge">max-height</code> is equal to <code class="language-text highlighter-rouge">0px</code> and if its we can remove the whole <code class="language-text highlighter-rouge">typingWindow</code> from the DOM</li>
</ol>

<h2 id="extras-looping-list-of-words-emojis">Extras (looping, list of words, emojis)</h2>
<p>Alright so here are a few extra tricks I implemented to save some time. I am also writing it down for me to remember.
The way <code class="language-text highlighter-rouge">Typewriter.js</code> works is by adding events to a queue. Calling <code class="language-text highlighter-rouge">pasteString()</code> <code class="language-text highlighter-rouge">deleteChars()</code> or <code class="language-text highlighter-rouge">pauseFor()</code> will add those events to the queue, and they will be executed in order whenever <code class="language-text highlighter-rouge">start()</code> is called. After that, we won’t be able to add any new event to the queue, we’ll only be able to <code class="language-text highlighter-rouge">stop()</code> the typewriter.</p>

<p>I wanted my typewriter to keep changing the last few words in loop so I implemented a little trick without having to rewrite the library:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">function</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">n</span><span class="p">){</span>
  <span class="k">if</span><span class="p">(</span><span class="nx">n</span> <span class="o">&lt;=</span> <span class="mi">9</span><span class="p">){</span>
    <span class="k">return</span> <span class="dl">"</span><span class="s2">0</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">n</span><span class="p">;</span>
  <span class="p">};</span>
  <span class="k">return</span> <span class="nx">n</span><span class="p">;</span>
<span class="p">};</span>

<span class="kd">function</span> <span class="nx">strip</span><span class="p">(</span><span class="nx">html</span><span class="p">){</span>
  <span class="kd">var</span> <span class="nx">doc</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">DOMParser</span><span class="p">().</span><span class="nx">parseFromString</span><span class="p">(</span><span class="nx">html</span><span class="p">,</span> <span class="dl">'</span><span class="s1">text/html</span><span class="dl">'</span><span class="p">);</span>
  <span class="k">return</span> <span class="nx">doc</span><span class="p">.</span><span class="nx">body</span><span class="p">.</span><span class="nx">textContent</span> <span class="o">||</span> <span class="dl">""</span><span class="p">;</span>
<span class="p">};</span>

<span class="kd">var</span> <span class="nx">randomDelay</span> <span class="o">=</span> <span class="kd">function</span><span class="p">(){</span>
  <span class="k">return</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="p">(</span><span class="nx">pause</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">-</span> <span class="nx">pause</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">+</span> <span class="nx">pause</span><span class="p">[</span><span class="mi">0</span><span class="p">]);</span>
<span class="p">};</span>

  <span class="kd">var</span> <span class="nx">totalLoops</span> <span class="o">=</span> <span class="mi">20</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">wordList</span> <span class="o">=</span> <span class="p">[</span><span class="dl">"</span><span class="s2">&lt;em&gt;PhD Student&lt;/em&gt; 📚</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Programmer 💻</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Photographer 📷</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Pizza Lover 🍕</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Gamer 👾</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Swimmer 🏊</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">Traveller 🌏</span><span class="dl">"</span><span class="p">];</span>
  <span class="kd">var</span> <span class="nx">pause</span> <span class="o">=</span> <span class="p">[</span><span class="mi">1000</span><span class="p">,</span> <span class="mi">4500</span><span class="p">];</span>
  <span class="kd">var</span> <span class="nx">terminalStarter</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">&gt; &lt;span style='color: #5cd400;'&gt; gabryxx7@blog:&lt;/span&gt;&lt;span style='color: #1c39c7;'&gt;~&lt;/span&gt;$ </span><span class="dl">"</span><span class="p">;</span>
  <span class="kd">var</span> <span class="nx">current_datetime</span> <span class="o">=</span> <span class="k">new</span> <span class="nb">Date</span><span class="p">();</span>
  <span class="kd">var</span> <span class="nx">formatted_date</span> <span class="o">=</span> <span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getFullYear</span><span class="p">()</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">-</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getMonth</span><span class="p">()</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">-</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getDate</span><span class="p">())</span> <span class="o">+</span> <span class="dl">"</span><span class="s2"> </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getHours</span><span class="p">())</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">:</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getMinutes</span><span class="p">())</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">:</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">appendLeadingZeroes</span><span class="p">(</span><span class="nx">current_datetime</span><span class="p">.</span><span class="nx">getSeconds</span><span class="p">());</span>

  <span class="kd">var</span> <span class="nx">typewriter</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">Typewriter</span><span class="p">(</span><span class="nx">app</span><span class="p">,</span> <span class="p">{</span>
    <span class="na">loop</span><span class="p">:</span> <span class="kc">false</span><span class="p">,</span>
    <span class="na">delay</span><span class="p">:</span> <span class="dl">'</span><span class="s1">natural</span><span class="dl">'</span><span class="p">,</span>
    <span class="na">cursor</span><span class="p">:</span> <span class="dl">'</span><span class="s1">▌</span><span class="dl">'</span>
  <span class="p">});</span>
  <span class="nx">typewriter</span>
  <span class="p">.</span><span class="nx">pasteString</span><span class="p">(</span><span class="nx">terminalStarter</span> <span class="o">+</span><span class="dl">"</span><span class="s2">[</span><span class="dl">"</span><span class="o">+</span><span class="nx">formatted_date</span><span class="o">+</span><span class="dl">"</span><span class="s2">]</span><span class="dl">"</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pauseFor</span><span class="p">(</span><span class="mi">1000</span><span class="p">);</span>
  <span class="p">.</span><span class="nx">changeDelay</span><span class="p">(</span><span class="mi">90</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pasteString</span><span class="p">(</span><span class="dl">"</span><span class="s2">&lt;br /&gt; </span><span class="dl">"</span> <span class="o">+</span><span class="nx">terminalStarter</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pauseFor</span><span class="p">(</span><span class="mi">1200</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">typeString</span><span class="p">(</span><span class="dl">"</span><span class="s2">Hey!</span><span class="dl">"</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pauseFor</span><span class="p">(</span><span class="mi">800</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">typeString</span><span class="p">(</span><span class="dl">"</span><span class="s2"> I'm &lt;strong&gt;Gabriele&lt;/strong&gt;</span><span class="dl">"</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pauseFor</span><span class="p">(</span><span class="mi">1000</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">pasteString</span><span class="p">(</span><span class="dl">"</span><span class="s2">&lt;br /&gt; </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">terminalStarter</span><span class="p">)</span>
  <span class="p">.</span><span class="nx">typeString</span><span class="p">(</span><span class="dl">"</span><span class="s2"> I'm a...</span><span class="dl">"</span><span class="p">);</span>

  <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">k</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="nx">k</span> <span class="o">&lt;</span> <span class="nx">totalLoops</span><span class="p">;</span> <span class="nx">k</span><span class="o">++</span><span class="p">){</span> 
    <span class="k">for</span><span class="p">(</span><span class="kd">var</span> <span class="nx">i</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span> <span class="nx">i</span> <span class="o">&lt;</span> <span class="nx">wordList</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">i</span><span class="o">++</span><span class="p">){</span>
      <span class="nx">typewriter</span>
      <span class="p">.</span><span class="nx">typeString</span><span class="p">(</span><span class="nx">wordList</span><span class="p">[</span><span class="nx">i</span><span class="p">])</span>
      <span class="p">.</span><span class="nx">pauseFor</span><span class="p">(</span><span class="nx">randomDelay</span><span class="p">())</span>
      <span class="p">.</span><span class="nx">deleteChars</span><span class="p">(</span><span class="nx">strip</span><span class="p">(</span><span class="nx">wordList</span><span class="p">[</span><span class="nx">i</span><span class="p">]).</span><span class="nx">length</span><span class="p">);</span>
    <span class="p">};</span>
  <span class="p">};</span>
  <span class="nx">typewriter</span><span class="p">.</span><span class="nx">start</span><span class="p">();</span></code></pre></figure>

<p>So the main idea is to have a list of words that keep being written and deleted, but I did not want to manually enter the char to delete every time.
So by using a list I can delete the length of the previous word and type in the new one:</p>

<ul>
  <li>Create a list of words</li>
  <li>Type the first part of the text (non looped)</li>
  <li>Add a loop which types in and deletes each word from the list (including pauses)</li>
  <li></li>
  <li>Loop the previous loop for how many times you want. If you have 10 words with a delay of 2 seconds between each word, 100 loops will take ~2000 seconds, around half an hour.</li>
</ul>

<p class="message"><strong>TIP:</strong> You can use <code class="language-text highlighter-rouge">html</code> to format your text, but don’t forget that <code class="language-text highlighter-rouge">deleteChars()</code> of Typewriter expects the number of chars without counting html, so a simple <code class="language-text highlighter-rouge">string.length</code> in javascript won’t work. You need to strip the string of the html and that’s where <code class="language-text highlighter-rouge">strip()</code> comes in</p>
<p class="message"><strong>Heads up:</strong> I tried with 100 loops and I noticed it would take a few seconds for the Typewriter to start, slowing down the page loading, so I use 20 loops</p>

<h1 id="adding-a-terminal-typewriter-to-your-page-or-post">Adding a terminal typewriter to your page or post</h1>

<h2 id="adding-content-to-the-blog-page">Adding content to the blog page</h2>

<p>So first thing I did was to allow for the Hydejack’s blog page layout to include content besides the list of post, so I copied the blog layout from hydejack and create a new layout file <code class="language-text highlighter-rouge">_layouts/blog__custom.html</code> based on the <code class="language-text highlighter-rouge">base</code> layout:</p>

<p class="message"><strong>TIP:</strong> You can avoid Jekyll capturing liquid code by wrapping it in <code class="language-text highlighter-rouge">\{\% raw \%\} \{\% endraw \%\}</code></p>

<figure class="highlight"><pre><code class="language-liquid" data-lang="liquid">---
layout: base
---

<span class="p">{{</span><span class="w"> </span><span class="nv">content</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">markdownify</span><span class="w"> </span><span class="p">}}</span>
<span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">plugins</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">site</span><span class="p">.</span><span class="nv">plugins</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="nv">site</span><span class="p">.</span><span class="nv">gems</span><span class="w"> </span><span class="p">%}</span>

<span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">plugins</span><span class="w"> </span><span class="ow">contains</span><span class="w"> </span><span class="s1">'jekyll-paginate'</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">posts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">paginator</span><span class="p">.</span><span class="nv">posts</span><span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">posts</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">site</span><span class="p">.</span><span class="nv">categories</span><span class="p">[</span><span class="nv">page</span><span class="p">.</span><span class="nv">slug</span><span class="p">]</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="nv">site</span><span class="p">.</span><span class="nv">tags</span><span class="p">[</span><span class="nv">page</span><span class="p">.</span><span class="nv">slug</span><span class="p">]</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="nv">site</span><span class="p">.</span><span class="nv">posts</span><span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
  
<span class="p">{%</span><span class="w"> </span><span class="nt">for</span><span class="w"> </span><span class="nv">post</span><span class="w"> </span><span class="nt">in</span><span class="w"> </span><span class="nv">posts</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="nt">include_cached</span><span class="w"> </span>components/post.html<span class="w"> </span><span class="na">post</span><span class="o">=</span>post<span class="w"> </span><span class="na">no_link_title</span><span class="o">=</span>page.no_link_title<span class="w"> </span><span class="na">no_excerpt</span><span class="o">=</span>page.no_excerpt<span class="w"> </span><span class="na">hide_image</span><span class="o">=</span>page.hide_image<span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="nt">endfor</span><span class="w"> </span><span class="p">%}</span>

<span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">plugins</span><span class="w"> </span><span class="ow">contains</span><span class="w"> </span><span class="s1">'jekyll-paginate'</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="nt">include</span><span class="w"> </span>components/pagination.html<span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span></code></pre></figure>

<p>So you can see how I added <code class="language-text highlighter-rouge">{{ content | markdownify }}</code> to the top to add whatever content I’ll write to any page using <code class="language-text highlighter-rouge">blog-custom</code> as a layout.</p>

<p>Now that we can add content to the top of the blog page we can start creating our structure for the typewriter window:</p>

<h2 id="allowing-for-custom-featured-images-in-posts">Allowing for custom featured images in posts</h2>

<p>I made a few changes to the Hydejack theme so that I could:</p>
<ul>
  <li>Display wide images as a post feature image</li>
  <li>Display divs and javascript content as a featured post content (as you can see for this post)</li>
</ul>

<p>For the first point I simply added custom classes for posts, I added a <code class="language-text highlighter-rouge">class</code> property to the post <code class="language-text highlighter-rouge">image</code> (aside from <code class="language-text highlighter-rouge">path</code>).
In order to use this new property I edited the <code class="language-text highlighter-rouge">_incudes/component/hy-img.html</code> as below:</p>

<figure class="highlight"><pre><code class="language-liquid" data-lang="liquid"><span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">img_class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">img</span><span class="p">.</span><span class="nv">class</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="s2">""</span><span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="nt">assign</span><span class="w"> </span><span class="nv">include_class</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">class</span><span class="w"> </span><span class="p">|</span><span class="w"> </span><span class="nf">default</span><span class="p">:</span><span class="s2">""</span><span class="w"> </span><span class="p">%}</span>
<span class="p">{%</span><span class="w"> </span><span class="nt">classes</span><span class="w"> </span>=<span class="w"> </span>include_class<span class="w"> </span>|<span class="w"> </span><span class="na">concat</span><span class="o">:</span><span class="w"> </span>img_class<span class="w"> </span><span class="p">%}</span>
&lt;img
  <span class="p">{{</span><span class="w"> </span><span class="nv">sources</span><span class="w"> </span><span class="p">}}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">alt</span><span class="w"> </span><span class="p">%}</span>alt="<span class="p">{{</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">alt</span><span class="w"> </span><span class="p">}}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">classes</span><span class="w"> </span><span class="o">!=</span><span class="w"> </span><span class="s1">''</span><span class="w"> </span><span class="p">%}</span>class="<span class="p">{{</span><span class="w"> </span><span class="nv">classes</span><span class="w"> </span><span class="p">}}</span>"
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">property</span><span class="w"> </span><span class="p">%}</span>property="<span class="p">{{</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">property</span><span class="w"> </span><span class="p">}}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">width</span><span class="w"> </span><span class="p">%}</span>width="<span class="p">{{</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">width</span><span class="w"> </span><span class="p">}}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">height</span><span class="w"> </span><span class="p">%}</span>height="<span class="p">{{</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">height</span><span class="w"> </span><span class="p">}}</span>"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">width</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="nv">include</span><span class="p">.</span><span class="nv">height</span><span class="w"> </span><span class="p">%}</span>loading="lazy"<span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span>
/&gt;</code></pre></figure>

<p>I simply always add classes to the <code class="language-text highlighter-rouge">img</code> tag which are empty by default, in this way I am sure this will work with the pre-existing configuration. If I always added <code class="language-text highlighter-rouge">include.img.class</code> when it did not exist I could have incurred into issues later, and I definitely wouldn’t want to remove <code class="language-text highlighter-rouge">include.class</code> as it might be used in otehr cases. Furthermore, I did not want to add any <code class="language-text highlighter-rouge">style</code> tag simply because I noticed the <code class="language-text highlighter-rouge">hy-img</code> always end up with <code class="language-text highlighter-rouge">style="opacity: 0;</code> in the final <code class="language-text highlighter-rouge">html</code> page and I am not sure if that’s needed or not so I did not want to overwrite that.</p>

<p>Let’s not forget to add the <code class="language-text highlighter-rouge">CSS</code> for dealing with wide featured images:</p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.content</span> <span class="nc">.aspect-ratio</span> <span class="nt">img</span><span class="nc">.wide-img</span> <span class="p">{</span>
  <span class="nl">margin</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
  <span class="nl">width</span><span class="p">:</span> <span class="m">100%</span><span class="p">;</span>
  <span class="c">/* height: 100%; */</span>
  <span class="c">/* object-fit: scale-down; */</span>
  <span class="nl">object-position</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
  <span class="nl">background-color</span><span class="p">:</span> <span class="n">var</span><span class="p">(</span><span class="n">--gray-bg</span><span class="p">);</span>
  <span class="nl">vertical-align</span><span class="p">:</span> <span class="nb">middle</span><span class="p">;</span>
  <span class="nl">object-fit</span><span class="p">:</span> <span class="n">scale-down</span> <span class="cp">!important</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>

<p>For the second point of displaying divss into post featured images, it was actually fairly simple. Sasme as before I added a new post image property called <code class="language-text highlighter-rouge">html</code>, I then edited the file <code class="language-text highlighter-rouge">_includes/component/post.html</code> so that if <code class="language-text highlighter-rouge">image.html</code> is definied it will use the html instead of the image path. so I changed this :</p>

<figure class="highlight"><pre><code class="language-liquid" data-lang="liquid">&lt;div class="lead aspect-ratio sixteen-nine flip-project-img"&gt;
  <span class="p">{%</span><span class="w"> </span><span class="nt">include_cached</span><span class="w"> </span>components/hy-img.html<span class="w"> </span><span class="na">img</span><span class="o">=</span>post.image<span class="w"> </span><span class="na">alt</span><span class="o">=</span>post.title<span class="w"> </span><span class="na">width</span><span class="o">=</span><span class="mi">864</span><span class="w"> </span><span class="na">height</span><span class="o">=</span><span class="mi">486</span><span class="w"> </span><span class="p">%}</span>
&lt;/div&gt;</code></pre></figure>

<p>To this:</p>

<figure class="highlight"><pre><code class="language-liquid" data-lang="liquid">  <span class="p">{%</span><span class="w"> </span><span class="kr">if</span><span class="w"> </span><span class="nv">post</span><span class="p">.</span><span class="nv">image</span><span class="p">.</span><span class="nv">html</span><span class="w"> </span><span class="p">%}</span>
    &lt;div class="lead aspect-ratio sixteen-nine flip-project-html"&gt;
      <span class="p">{{</span><span class="w"> </span><span class="nv">post</span><span class="p">.</span><span class="nv">image</span><span class="p">.</span><span class="nv">html</span><span class="w"> </span><span class="p">}}</span>
    &lt;/div&gt;
  <span class="p">{%</span><span class="w"> </span><span class="kr">else</span><span class="w"> </span><span class="p">%}</span>
    &lt;div class="lead aspect-ratio sixteen-nine flip-project-img"&gt;
      <span class="p">{%</span><span class="w"> </span><span class="nt">include_cached</span><span class="w"> </span>components/hy-img.html<span class="w"> </span><span class="na">img</span><span class="o">=</span>post.image<span class="w"> </span><span class="na">alt</span><span class="o">=</span>post.title<span class="w"> </span><span class="na">width</span><span class="o">=</span><span class="mi">864</span><span class="w"> </span><span class="na">height</span><span class="o">=</span><span class="mi">486</span><span class="w"> </span><span class="p">%}</span>
    <span class="p">{%</span><span class="w"> </span><span class="kr">endif</span><span class="w"> </span><span class="p">%}</span></code></pre></figure>

<p>Last touch, I added the console effect <code class="language-text highlighter-rouge">html</code> to the post property which now looks something like this:</p>

<figure class="highlight"><pre><code class="language-yaml" data-lang="yaml"><span class="nn">---</span>
<span class="na">layout</span><span class="pi">:</span> <span class="s">post</span>
<span class="na">title</span><span class="pi">:</span> <span class="s">How I made the blog's terminal animation</span>
<span class="na">excerpt_separator</span><span class="pi">:</span> <span class="s">&lt;!--more--&gt;</span>
<span class="c1"># categories: [coding]</span>
<span class="na">image</span><span class="pi">:</span> 
  <span class="na">path</span><span class="pi">:</span> <span class="s">/assets/gabryxx7/img/typing-window.png</span>
  <span class="na">class</span><span class="pi">:</span> <span class="s2">"</span><span class="s">wide-img"</span>
  <span class="na">html</span><span class="pi">:</span> <span class="pi">|-</span>
    <span class="s">&lt;div class='typing-window post' id='typing-window'&gt;</span>
    <span class="s">&lt;div class='typing-toolbar'&gt;    </span>
        <span class="s">&lt;div class="toolbar-title"&gt;</span>
          <span class="s">Window Title</span>
        <span class="s">&lt;/div&gt;</span>
        <span class="s">&lt;div class="toolbar-btns-container"&gt;</span>
          <span class="s">&lt;div class="typing-toolbar-btn min"&gt;—&lt;/div&gt;</span>
          <span class="s">&lt;div class="typing-toolbar-btn size"&gt;☐&lt;/div&gt;</span>
          <span class="s">&lt;div class="typing-toolbar-btn close disabled" id="typing-close-btn"&gt;✕&lt;/div&gt;</span>
        <span class="s">&lt;/div&gt;</span>
    <span class="s">&lt;/div&gt;</span>
    <span class="s">&lt;div class="typing-area"&gt;▌&lt;/div&gt;</span>
    <span class="s">&lt;/div&gt;</span>
<span class="na">description</span><span class="pi">:</span> <span class="pi">&gt;</span>
  <span class="s">I made a gif of me mashing  the keyboard on hackertyper and it got me the idea of using this terminal as a welcoming animation!</span>
<span class="s">---</span></code></pre></figure>

<p>Beware that this only works in the post page or in the post preview, it will NOT work in the post card when showed as related post (you’d have to edit <code class="language-text highlighter-rouge">_includes/component/post-card.html</code> for that). I would not recommend it as it makes everything more complicated and messy with loading randomg javascript all the time. For this reason I would suggest to have an image to display as a fallback option</p>

<h2 id="adding-the-javascript">Adding the javascript</h2>
<p>When it comes to the post terminal I can simply add the javascript shown above in between the <code class="language-text highlighter-rouge">&lt;script&gt;&lt;/script&gt;</code> tags. For the page, I added the javascript to the <code class="language-text highlighter-rouge">_includes/my-scripts.html</code>. The event listeners will try to load the terminal at every page but the function returns if there is no element with classes <code class="language-text highlighter-rouge">typing-window blog</code>.</p>

<p><strong>IMPORTANT</strong>: When deploying jekyll with the command <code class="language-text highlighter-rouge">JEKYLL_ENV=production bundle exec jekyll build</code> (to enable the search function) make sure that your javascript is <strong>PERFECT</strong> as in, add a <code class="language-text highlighter-rouge">;</code> to EVERY statement. Otherwise then it gets minified, something like</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">test</span> <span class="o">=</span> <span class="mi">1</span>
<span class="kd">var</span> <span class="nx">test</span> <span class="o">=</span> <span class="mi">3</span></code></pre></figure>

<p>Becomes:</p>

<figure class="highlight"><pre><code class="language-javascript" data-lang="javascript"><span class="kd">var</span> <span class="nx">test</span> <span class="o">=</span> <span class="mi">1</span><span class="kd">var</span> <span class="nx">test</span> <span class="o">=</span> <span class="mi">3</span></code></pre></figure>

<p>Generating an error and making your whole javascript code crash</p>

<p>An important note, if you end up using IDs instead of classes, multiple terminials on the same page might not work. The reason is that when Javascript calls <code class="language-text highlighter-rouge">getElementById()</code> it always retreives one only element with that id. That’s the purpose of IDs, right? Uniquely identifying elements. If we want it to work for ANY html element with the class <code class="language-text highlighter-rouge">typing-window</code> we need to use <code class="language-text highlighter-rouge">getElementByClass()</code> as shown above.</p>

<h2 id="adding-a-description-to-your-page-terminal">Adding a description to your page terminal</h2>
<p>You will notice I also added a little description to the typing window to link to this post, I simply wrapped the whole window in another div like this:</p>

<figure class="highlight"><pre><code class="language-html" data-lang="html"><span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-window-wrapper"</span><span class="nt">&gt;</span>
  <span class="nt">&lt;p</span> <span class="na">class=</span><span class="s">"note-sm"</span><span class="nt">&gt;</span>
    Do you want to make one yourself for your website? Check this post out:
    <span class="nt">&lt;strong&gt;&lt;a</span> <span class="na">href=</span><span class="s">"/blog/blog/coding/2020-08-10-typing-window/"</span> <span class="na">class=</span><span class="s">"flip-title"</span><span class="nt">&gt;</span>How I made the blog's terminal animation<span class="nt">&lt;/a&gt;&lt;/strong&gt;</span>
  <span class="nt">&lt;/p&gt;</span>
  <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-window blog'</span><span class="nt">&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">'typing-toolbar'</span><span class="nt">&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn min"</span><span class="nt">&gt;</span>—<span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn size"</span><span class="nt">&gt;</span>☐<span class="nt">&lt;/div&gt;</span>
      <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-toolbar-btn close disabled"</span><span class="nt">&gt;</span>✕<span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;/div&gt;</span>
    <span class="nt">&lt;div</span> <span class="na">class=</span><span class="s">"typing-area"</span><span class="nt">&gt;</span>▌<span class="nt">&lt;/div&gt;</span>
  <span class="nt">&lt;/div&gt;</span>
<span class="nt">&lt;/div&gt;</span></code></pre></figure>

<p>I then added some extra <code class="language-text highlighter-rouge">CSS</code> to <code class="language-text highlighter-rouge">_sass/my-style.scss</code></p>

<figure class="highlight"><pre><code class="language-css" data-lang="css"><span class="nc">.typing-window-wrapper</span> <span class="nc">.note-sm</span><span class="p">{</span>
  <span class="nl">position</span><span class="p">:</span> <span class="nb">absolute</span><span class="p">;</span>
  <span class="nl">right</span><span class="p">:</span> <span class="m">23rem</span><span class="p">;</span>
  <span class="nl">width</span><span class="p">:</span> <span class="m">21rem</span><span class="p">;</span>
  <span class="nl">padding</span><span class="p">:</span> <span class="m">0.5rem</span> <span class="m">1rem</span> <span class="m">0</span> <span class="m">1rem</span><span class="p">;</span>
<span class="p">}</span></code></pre></figure>]]></content><author><name>Gabriele Marini</name><email>giaga7@gmail.com</email></author><category term="blog" /><category term="coding" /><summary type="html"><![CDATA[I made a gif of me mashing the keyboard on hackertyper and it got me the idea of using this terminal as a welcoming animation!]]></summary><media:thumbnail xmlns:media="http://search.yahoo.com/mrss/" url="https://gmarini.com/assets/gabryxx7/img/typing-window.png" /><media:content medium="image" url="https://gmarini.com/assets/gabryxx7/img/typing-window.png" xmlns:media="http://search.yahoo.com/mrss/" /></entry></feed>