<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
  <title>Tobias Watzek</title>
  <subtitle>Writing about things that are related to software development.</subtitle>
  <link href="https://watzek.dev/feed/feed.xml" rel="self" />
  <link href="https://watzek.dev/" />
  <updated>2024-12-15T00:00:00Z</updated>
  <id>https://watzek.dev/</id>
  <author>
    <name>Tobias Watzek</name>
  </author>
  <entry>
    <title>Weeknumberology in .NET</title>
    <link href="https://watzek.dev/posts/2024/12/15/weeknumberology-in-.net/" />
    <updated>2024-12-15T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2024/12/15/weeknumberology-in-.net/</id>
    <content type="html">&lt;p&gt;I’m currently writing a little program in C# (.NET 9) that needs to do some calculations with weeks. Specifically ISO weeks which start on Monday and the first week of a year is defined as the first week that contains the first Thursday of a year. I started to write some code that gets me the first day of the first week of the year when I stumbled over the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.globalization.isoweek?view=net-9.0&quot; rel=&quot;noopener&quot;&gt;&lt;code&gt;System.Globalization.ISOWeek&lt;/code&gt;&lt;/a&gt; class. This humble static class contains six methods that cover the common things one may want to do with week numbers.&lt;/p&gt;&lt;p&gt;&lt;code&gt;GetWeekOfYear(DateTime)&lt;/code&gt; returns the week number of a date. For example, &lt;code&gt;GetWeekOfYear(new DateTime(2024, 12, 15))&lt;/code&gt; will return 50.&lt;/p&gt;&lt;p&gt;The method &lt;code&gt;GetWeeksInYear(Int32)&lt;/code&gt; returns the number of weeks in a given year. For 2024 this method returns 52.&lt;/p&gt;&lt;p&gt;&lt;code&gt;GetYear(DateTime)&lt;/code&gt; may surprise you but according to the ISO week numbering system a day in a year may not be in that year if its week isn’t part of the year. Namely, the 1st January 2023 is actually part of the 52 week of 2022. When you pass it to &lt;code&gt;GetYear(DateTime)&lt;/code&gt; you get 2022.&lt;/p&gt;&lt;p&gt;In order to get the start and end date of a year when working with week the methods &lt;code&gt;GetYearStart(Int32)&lt;/code&gt; and &lt;code&gt;GetYearEnd(Int32)&lt;/code&gt; can be used. For instance, the start of the year 2023 is the 2nd January 2023 and the end of 2022 is the 1st January 2023.&lt;/p&gt;&lt;p&gt;Last but not least there is &lt;code&gt;ToDateTime(Int32, Int32, DayOfWeek)&lt;/code&gt; which will calculate the date of a weekday of a week in a year. When called like this &lt;code&gt;ToDateTime(2024, 50, DayOfWeek.Sunday)&lt;/code&gt; the method will return the 15th December 2024.&lt;/p&gt;&lt;p&gt;I was relieved that I found this small but very helpful class. This once again demonstrates the vast collection of things that come with the .NET standard library that make our lives as developers easier.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Time Tracking Like a Nerd</title>
    <link href="https://watzek.dev/posts/2024/03/15/time-tracking-like-a-nerd/" />
    <updated>2024-03-15T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2024/03/15/time-tracking-like-a-nerd/</id>
    <content type="html">&lt;p&gt;Tracking the time I spend on projects is often necessary but always annoying. In my professional life I have worked with various tools to accomplish this daunting task. They ranged from shiny web apps to desktop apps that were so complicated to use that it was easier to keep track of time in a separate Excel file and copy data over to the app every couple of days. If you look online for applications to track time you will find a myriad of results and each one has a ton of features that will probably never be used by the likes of me. I was looking for a solution that allows me to quickly and easily enter data while keeping everything stored locally. Luckily I did not have to look far. I’m already using &lt;a href=&quot;https://hledger.org/index.html&quot; rel=&quot;noopener&quot;&gt;hledger&lt;/a&gt; to keep track of my finances in plain text and recently learned that it has support to read and report on two different plain text time logging formats: &lt;a href=&quot;https://hledger.org/1.32/hledger.html#timeclock&quot; rel=&quot;noopener&quot;&gt;timeclock&lt;/a&gt; and &lt;a href=&quot;https://hledger.org/1.32/hledger.html#timedot&quot; rel=&quot;noopener&quot;&gt;timedot&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Timeclock originates from the Emacs &lt;a href=&quot;https://www.emacswiki.org/emacs/TimeClock&quot; rel=&quot;noopener&quot;&gt;timeclock.el&lt;/a&gt;. It’s a bit harder for humans to read and write but allows logging the start and end of a task with second precision. Each line that starts with an &lt;code&gt;i&lt;/code&gt; marks the start of a task. The next line that isn’t a comment (lines starting with &lt;code&gt;#&lt;/code&gt;, &lt;code&gt;;&lt;/code&gt; or &lt;code&gt;*&lt;/code&gt;) or blank must start with &lt;code&gt;o&lt;/code&gt; which marks the end of the task. After an &lt;code&gt;i&lt;/code&gt; or an &lt;code&gt;o&lt;/code&gt; comes a date and time. The date and time in starting lines has to be followed by an account or project name. Separated by &lt;strong&gt;two&lt;/strong&gt; spaces from the account optional comments and tags can be added.&lt;/p&gt;&lt;pre class=&quot;language-timeclock&quot;&gt;&lt;code class=&quot;language-timeclock&quot;&gt;i 2024-03-15 07:00:00 blogging  writing about time tracking after 2 spaces ; optional comment, tags:
o 2024-03-15 07:30:00
i 2024-03-15 07:30:00 eating  breakfast with coffee
o 2024-03-15 08:00:00&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By running hledger commands with the above sample a number of reports can be generated. For example to get an overview over the hours tracked to each account the &lt;code&gt;balance&lt;/code&gt; command can be used.&lt;/p&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;$ hledger -f sample.timeclock balance
               0.50h  blogging
               0.50h  eating
--------------------
               1.00h&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To get a detailed list of all the logged hours in a specific month the &lt;code&gt;register&lt;/code&gt; command with the &lt;code&gt;--period&lt;/code&gt; option comes in handy.&lt;/p&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;$ hledger -f sample.timeclock register --period 2024-03
2024-03-15 writing about time tracking after 2 spaces  (blogging)  0.50h   0.50h
2024-03-15 breakfast with coffee                       (eating)    0.50h   1.00h&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to get a report of all the hours that were logged to each account per month the &lt;code&gt;register&lt;/code&gt; command in combination with the &lt;code&gt;-M&lt;/code&gt; option can be used.&lt;/p&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;$ hledger -f sample.timeclock register -M
2024-03  blogging    1.00h    1.00h
         eating      0.50h    1.50h&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In contrast to the timeclock format the timedot format is a bit easier to read for humans. It achieves this by removing the exact timestamps that mark the start and end of a task. Instead, it simply adds blocks of time to accounts on a day. There are multiple types of blocks but the one that gives the timedot format its name is the period &lt;code&gt;.&lt;/code&gt; which is interpreted as 15 minutes. To illustrate this look at the following timedot example that tracks the same amount of time as the timeclock example above.&lt;/p&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;2024-03-15
blogging  ....
eating    ..&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;All the same commands that can be used with timeclock files can also be used with timedot files.&lt;/p&gt;&lt;p&gt;I’m still undecided which format I like more. At the moment I lean more towards the timedot format because I seldomly need to know the start and end times of the work I keep track of. In my experience time tracking is a very emotional topic and I don’t know many people that like it. I think that by using a plain text file to keep track of the time I spend on projects I am less distracted from the many features most time tracking applications offer and spend more time on the actual problems I try to solve.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>How I Satisfied My Bookmarking Needs</title>
    <link href="https://watzek.dev/posts/2023/09/17/how-i-satisfied-my-bookmarking-needs/" />
    <updated>2023-09-17T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/09/17/how-i-satisfied-my-bookmarking-needs/</id>
    <content type="html">&lt;p&gt;Some months ago, I was looking for the link to an article I read a while ago. I remembered some keywords and painstakingly sifted through search results of search engines, my browser history, and chats where I might have posted the article in question. All my efforts were in vain. I was furious that I did not save the link when I read the article. At that time, the only bookmarking functionality I used was the one offered by my browser. However, I only use my browser bookmarks to store links to sites I visit regularly and do not use them as a well-organized knowledge base. I decided it was high time that I found a solution for my bookmarking needs.&lt;/p&gt;&lt;p&gt;At first, I looked into the native capabilities of my browser. I use Mozilla Firefox on all of my devices. Bookmarks can easily be stored, tagged with keywords, organized in folders, and synchronized between devices. Although this would probably be enough for my needs, I want a separation between bookmarks of sites I visit regularly, which are more like shortcuts, and my archive of links, a collection of tools, articles, and other intriguing stuff. Because I want to manage these things separately, I decided against using my browsers bookmarks to build my archive.&lt;/p&gt;&lt;p&gt;Whenever I’m looking for a tool, one of the first places I visit is the &lt;a href=&quot;https://github.com/awesome-selfhosted/awesome-selfhosted&quot; rel=&quot;noopener&quot;&gt;Awesome Selfhosted&lt;/a&gt; list. It’s a long list of self-hosted tools. I found what I looked for in the “Bookmarks and Link Sharing” section. The one entry that stood out was &lt;a href=&quot;https://github.com/sissbruecker/linkding&quot; rel=&quot;noopener&quot;&gt;linkding&lt;/a&gt;. It is described as a “Self-hosted bookmark manager that is designed to be minimal, fast, and easy to set up using Docker.”. This description is on point. I won’t get into how I host it because I already wrote about it in &lt;a href=&quot;https://watzek.dev/posts/2023/04/22/how-i-host-my-stuff/&quot; rel=&quot;noopener&quot;&gt;How I Host My Stuff&lt;/a&gt;, it actually is surprisingly easy to host.&lt;/p&gt;&lt;p&gt;An essential aspect of managing my bookmarks is how they are added. It would be a big turnoff when I would have to open my bookmarking application each time I wanted to add a new bookmark and manually add it. Fortunately, linkding offers browser extensions for Firefox and Chrome and provides instructions on adding bookmarks on mobile devices via bookmarklets or shortcuts.&lt;/p&gt;&lt;img src=&quot;https://watzek.dev/images/C3c-x60x04-300.webp&quot; class=&quot;undefined&quot; alt=&quot;Homepage of my linkding instance showing a list of links, the navigation, and a list of tags.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1487&quot; height=&quot;765&quot; srcset=&quot;https://watzek.dev/images/C3c-x60x04-300.webp 300w, https://watzek.dev/images/C3c-x60x04-600.webp 600w, https://watzek.dev/images/C3c-x60x04-800.webp 800w, https://watzek.dev/images/C3c-x60x04-1487.webp 1487w&quot; sizes=&quot;100vw&quot;&gt;&lt;p&gt;Links added to linkding can be tagged with keywords so they can be filtered easily. This makes possible to search for links concerning a specific topic. Additionally, it is possible to add notes to each link, which comes in handy when I want to add some additional information to a link. Another cool feature is the possibility to mark links as unread. I often stumble across interesting tools or articles but might not have the time to read them. By adding them to linkding and marking them as unread, I can quickly find and read them later. Finally, I can close tabs in my browser with things that might be interesting rather than keeping them open for weeks.&lt;/p&gt;&lt;p&gt;After hosting linkding for some time, I must say that it is a solid piece of software. It runs without issues, and all updates have been applied effortlessly. The browser add-on and shortcuts enable me to quickly add new bookmarks to my collection.&lt;/p&gt;&lt;p&gt;In summary, linkding has become a significant part of my digital life. It has many features without feeling bloated and integrates seamlessly into browsers and mobile devices. I would suggest it to anyone looking for a minimal self-hosted bookmarking solution. Admittedly, more recently, I’ve been looking into more sophisticated applications because I would like to effectively manage and connect the entirety of my notes, links, and other information. Stay tuned for more information about this topic in the future.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Solving the Dictionary JSON Serialization Puzzle in .NET</title>
    <link href="https://watzek.dev/posts/2023/09/16/solving-the-dictionary-json-serialization-puzzle-in-.net/" />
    <updated>2023-09-16T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/09/16/solving-the-dictionary-json-serialization-puzzle-in-.net/</id>
    <content type="html">&lt;p&gt;My last post &lt;a href=&quot;https://watzek.dev/posts/2023/08/23/fine-tuning-json-serialization-in-.net/&quot; rel=&quot;noopener&quot;&gt;Fine-Tuning JSON Serialization in .NET&lt;/a&gt; introduced JSON converters that can be used to serialize and deserialize types to and from JSON in .NET. The methods I described in that post work for most use cases. Unfortunately, I ran into some issues when working with dictionaries. In this post, I want to explain these problems and show how they can be solved.&lt;/p&gt;&lt;p&gt;To demonstrate how to serialize dictionaries to JSON, look at the following example, which only uses primitive types as keys and values that can be serialized and deserialized out of the box by the &lt;code&gt;JsonSerializer&lt;/code&gt; provided by &lt;code&gt;System.Text.Json&lt;/code&gt;.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Generic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WriteIndented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;d&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As expected, this code works, and the resulting JSON looks like this:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;foo&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;However, using a custom type as the key of a dictionary that should be serialized to JSON will result in a runtime exception. It will even fail when a &lt;code&gt;JsonConverter&amp;lt;T&amp;gt;&lt;/code&gt;, implemented as described in my previous post, is used. Let’s try to serialize a dictionary that uses a custom &lt;code&gt;Currency&lt;/code&gt; type as its key with a custom JSON converter.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Generic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Serialization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; eur &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; eur&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    WriteIndented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Converters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;d&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteStringValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Running this example will result in a &lt;code&gt;System.NotSupportedException&lt;/code&gt; with the message “The type ‘Currency’ is not a supported dictionary key using converter of type ‘CurrencyJsonConverter’.”. So it seems that the &lt;code&gt;CurrencyJsonConverter&lt;/code&gt; is used, but the implementation of the &lt;code&gt;Write&lt;/code&gt; method is not used to serialize the dictionary key. A look at the stack trace of the exception, provides insight into where the exception occurred and how the problem might be resolved.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;at System.Text.Json.ThrowHelper.ThrowNotSupportedException_DictionaryKeyTypeNotSupported(
  Type keyType,
  JsonConverter converter)
at System.Text.Json.Serialization.JsonConverter`1.WriteAsPropertyName(
  Utf8JsonWriter writer,
  T value,
  JsonSerializerOptions options)
...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The last call of the stack trace is just a helper method that throws the exception, but the call before to the &lt;code&gt;JsonConverter.WriteAsPropertyName&lt;/code&gt; method looks interesting. A peak at the &lt;a href=&quot;https://learn.microsoft.com/en-us/dotnet/api/system.text.json.serialization.jsonconverter-1.writeaspropertyname?view=net-7.0&quot; rel=&quot;noopener&quot;&gt;documentation&lt;/a&gt; reveals that this method “Writes a dictionary key as a JSON property name.”. Furthermore, the method is marked &lt;code&gt;virtual&lt;/code&gt; so it can be overridden in the &lt;code&gt;CurrencyJsonConverter&lt;/code&gt;.&lt;/p&gt;&lt;pre class=&quot;language-diff-csharp&quot;&gt;&lt;code class=&quot;language-diff-csharp&quot;&gt;&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteStringValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted language-csharp&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WriteAsPropertyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WritePropertyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It’s crucial to notice that, other than the &lt;code&gt;Write&lt;/code&gt; method, the new &lt;code&gt;WriteAsPropertyName&lt;/code&gt; method uses the &lt;code&gt;WritePropertyName&lt;/code&gt; method of the &lt;code&gt;Utf8JsonWriter&lt;/code&gt; instead of &lt;code&gt;WriteStringValue&lt;/code&gt;. Running the example from above with the updated JSON converter works and produces the following JSON:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This is great, but what if some JSON should be deserialized to a dictionary as demonstrated below?&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Collections&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Generic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Serialization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&quot;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;42&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt;&quot;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Converters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; d &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Dictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;json&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Similar to the serialization example, the deserialization also throws a &lt;code&gt;System.NotSupportedException&lt;/code&gt; with the message “The type ‘Currency’ is not a supported dictionary key using converter of type ‘CurrencyJsonConverter’.”. Something is still missing from the &lt;code&gt;CurrencyJsonConverter&lt;/code&gt;. A look at the stack trace reveals a call to a &lt;code&gt;JsonConverter.ReadAsPropertyName&lt;/code&gt; method. This method deserializes a JSON property name to a dictionary key and can be overridden, just like the &lt;code&gt;JsonConverter.WriteAsPropertyName&lt;/code&gt; method.&lt;/p&gt;&lt;pre class=&quot;language-diff-csharp&quot;&gt;&lt;code class=&quot;language-diff-csharp&quot;&gt;&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteStringValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;WriteAsPropertyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WritePropertyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token inserted-sign inserted language-csharp&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ReadAsPropertyName&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;            &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-csharp&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In the override of the &lt;code&gt;ReadAsPropertyName&lt;/code&gt; method, the already implemented &lt;code&gt;Read&lt;/code&gt; method of the &lt;code&gt;CurrencyJsonConverter&lt;/code&gt; can be used. With the updated &lt;code&gt;CurrencyJsonConverter&lt;/code&gt;, the previously introduced deserialization example runs without issues.&lt;/p&gt;&lt;p&gt;I didn’t expect any problems when serializing and deserializing dictionaries to JSON, and was surprised to encounter them, although I was using JSON converters. After some debugging and digging into the documentation, finding the solution wasn’t as cumbersome as I initially anticipated. Simply implementing the methods &lt;code&gt;WriteAsPropertyName&lt;/code&gt;and &lt;code&gt;ReadAsPropertyName&lt;/code&gt; on a custom JSON converter makes it possible to use custom types as keys for dictionaries that are serialized to or deserialized from JSON. At last, I hope my findings are valuable for people struggling with the same issues.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Fine-Tuning JSON Serialization in .NET</title>
    <link href="https://watzek.dev/posts/2023/08/23/fine-tuning-json-serialization-in-.net/" />
    <updated>2023-08-23T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/08/23/fine-tuning-json-serialization-in-.net/</id>
    <content type="html">&lt;p&gt;Working with JSON in .NET is pretty easy with the types provided by &lt;code&gt;System.Text.Json&lt;/code&gt;. Nevertheless, it is sometimes necessary to implement custom converters to serialize or deserialize some structures. To illustrate this, take the following example where I declare some simple types that are used to represent money.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt; Amount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; Currency&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In order to serialize an instance of &lt;code&gt;Money&lt;/code&gt; to JSON, the &lt;code&gt;JsonSerializer.Serialize&lt;/code&gt; method from the &lt;code&gt;System.Text.Json&lt;/code&gt; namespace can be used.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; oneEur &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; WriteIndented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;oneEur&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The JSON looks like this:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Amount&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;Value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Currency&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;Value&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The produced JSON is nested two levels deep because of the &lt;code&gt;Amount&lt;/code&gt; and &lt;code&gt;Currency&lt;/code&gt; records. Neither is this efficient nor is it especially pretty. I would like the JSON to look like this:&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Amount&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Currency&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To accomplish this, I need to implement one JSON converter for &lt;code&gt;Amount&lt;/code&gt; and another one for &lt;code&gt;Currency&lt;/code&gt;. A JSON converter can be created by inheriting from the &lt;code&gt;JsonConverter&amp;lt;T&amp;gt;&lt;/code&gt; class and implementing the &lt;code&gt;Write&lt;/code&gt; and &lt;code&gt;Read&lt;/code&gt; method. The converters for &lt;code&gt;Amount&lt;/code&gt; and &lt;code&gt;Currency&lt;/code&gt; can be seen in the next example.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Serialization&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AmountJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Amount&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetDecimal&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Amount&lt;/span&gt; amount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteNumberValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Currency&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;reader&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Currency&lt;/span&gt; currency&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
            writer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteStringValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The converters can be used by adding them to the &lt;code&gt;JsonSerializerOptions&lt;/code&gt; that are passed to the &lt;code&gt;JsonSerializer.Serialize&lt;/code&gt; method.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;System&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Text&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Json&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; oneEur &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; options &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    WriteIndented &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Converters &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;AmountJsonConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;CurrencyJsonConverter&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; json &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;oneEur&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now the produced JSON is no longer unnecessary nested.&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Amount&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;Currency&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;EUR&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Another way to reach the same goal would be to implement a JSON converter for &lt;code&gt;Money&lt;/code&gt; instead of implementing converters for &lt;code&gt;Amount&lt;/code&gt; and &lt;code&gt;Currency&lt;/code&gt;. In this converter, an intermediate type can be used for serialization and deserialization. This is especially useful when extensive customizations are necessary. A converter for &lt;code&gt;Money&lt;/code&gt; could look like this:&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;MoneyJsonConverter&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;JsonConverter&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;Money&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;sealed&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;record&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;JsonMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;decimal&lt;/span&gt;&lt;/span&gt; Amount&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; Currency&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;Money&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Read&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonReader&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Type&lt;/span&gt; typeToConvert&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; jm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token generic-method&quot;&gt;&lt;span class=&quot;token function&quot;&gt;Deserialize&lt;/span&gt;&lt;span class=&quot;token generic class-name&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;JsonMoney&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;ref&lt;/span&gt; reader&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Money&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Amount&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Currency&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;jm&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;void&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Write&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Utf8JsonWriter&lt;/span&gt; writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;Money&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;JsonSerializerOptions&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; jm &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;JsonMoney&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;money&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Amount&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; money&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Currency&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        JsonSerializer&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Serialize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;writer&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; jm&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; options&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Custom JSON converters are a powerful tool to finely control the serialization and deserialization of complex data structures. I often use them when I have to conform to a specific JSON format while still being able to use expressive data structures in the code.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>How I Back Up My Stuff</title>
    <link href="https://watzek.dev/posts/2023/04/23/how-i-back-up-my-stuff/" />
    <updated>2023-04-23T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/04/23/how-i-back-up-my-stuff/</id>
    <content type="html">&lt;p&gt;In my &lt;a href=&quot;https://watzek.dev/posts/2023/04/22/how-i-host-my-stuff/&quot; rel=&quot;noopener&quot;&gt;last article&lt;/a&gt;, I wrote about how I host my stuff. A topic that goes hand in hand with hosting is backing up data. There are many strategies when it comes to backups. Depending on the type of data that should be backed up, they range from naive to doomsday prepper. For my simple applications, I apply a strategy that is more on the naive side because it will not hurt immensely if I should lose the data.&lt;/p&gt;&lt;p&gt;I started by choosing a place where I wanted to store my backups&lt;sup class=&quot;footnote-ref&quot;&gt;&lt;a href=&quot;https://watzek.dev/posts/2023/04/23/how-i-back-up-my-stuff/#fn1&quot; id=&quot;fnref1&quot;&gt;[1]&lt;/a&gt;&lt;/sup&gt;. My applications run on virtual private servers that I rent. Those servers run in a data center in Germany. Each of those servers has snapshot backup enabled, which makes it possible to reset the server to a previous snapshot for some days. This is the only backup I have enabled for servers that just serve statically generated sites. If something were to happen to those servers, I can just deploy the site to another server and nothing will be lost. For servers that host more dynamic sites that have a database or store uploaded files, I decided to use a tool called &lt;a href=&quot;https://restic.net/&quot; rel=&quot;noopener&quot;&gt;restic&lt;/a&gt; and back up my data via SFTP to rented storage hosted in Finland. This makes it less likely that my data will be lost if something happens to the data center in Germany.&lt;/p&gt;&lt;p&gt;Restic is a tool written in Go that can securely back up data to different storage types. It is pretty simple to set up and even supports cleaning up old backups via policies. I have it scheduled on my servers via Cron and configured to keep seven daily, four weekly, and three monthly backups. This tool makes it easy to back up all user-generated content of my applications.&lt;/p&gt;&lt;p&gt;Backups always seemed like a very complicated and exhausting topic to me, but after I researched a bit, it wasn’t as tricky as I made it out to be. My final setup utilizing server snapshots, restic, and hosted storage is simple but effective. Of course, if you have to securely back up critical data, it may be best to give this topic a tad more thought.&lt;/p&gt;&lt;hr class=&quot;footnotes-sep&quot;&gt;&lt;section class=&quot;footnotes&quot;&gt;&lt;ol class=&quot;footnotes-list&quot;&gt;&lt;li id=&quot;fn1&quot; class=&quot;footnote-item&quot;&gt;&lt;p&gt;Something that comes to mind here is the 3-2-1 rule for backups. Three copies of data, two on different media, one is kept off-site. &lt;a href=&quot;https://watzek.dev/posts/2023/04/23/how-i-back-up-my-stuff/#fnref1&quot; class=&quot;footnote-backref&quot;&gt;↩︎&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/section&gt;</content>
  </entry>
  <entry>
    <title>How I Host My Stuff</title>
    <link href="https://watzek.dev/posts/2023/04/22/how-i-host-my-stuff/" />
    <updated>2023-04-22T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/04/22/how-i-host-my-stuff/</id>
    <content type="html">&lt;p&gt;This blog isn’t the only website or webapp that I operate from my living room. I operate several small sites and tools that run on rented virtual private servers. Until recently, I ran those applications directly on the machine and configured everything manually. On the one hand, this can be a lot of fun but on the other hand, sometimes it also causes great pain. Triggered by the need to host new applications, which were a bit more complex, I started to tinker with my setup. First, I gave &lt;a href=&quot;https://coolify.io/&quot; rel=&quot;noopener&quot;&gt;Coolify&lt;/a&gt; a shot which is a cool project, but it did too much magic for my taste. Then I went down another rabbit hole and looked into simple Kubernetes variants like &lt;a href=&quot;https://k3s.io/&quot; rel=&quot;noopener&quot;&gt;k3s&lt;/a&gt;, fortunately, this adventure didn’t last long. My goal was to find a way to run containerized versions of applications while still keeping everything as simple as possible. I found the solution in &lt;a href=&quot;https://docs.docker.com/compose/&quot; rel=&quot;noopener&quot;&gt;Docker Compose&lt;/a&gt; in combination with &lt;a href=&quot;https://traefik.io/traefik/&quot; rel=&quot;noopener&quot;&gt;Traefik Proxy&lt;/a&gt;. Docker Compose is a neat way of defining and running applications that can consist of one or more containers, volumes, and networks. Traefik Proxy is a reverse proxy that can be hosted via Docker, dynamically load configuration from Docker containers, and automatically manage &lt;a href=&quot;https://letsencrypt.org/&quot; rel=&quot;noopener&quot;&gt;Let’s Encrypt&lt;/a&gt; certificates for applications. In the following, I want to show you how I use this combination to host my stuff.&lt;/p&gt;&lt;p&gt;To start everything off, I created a new Git repository that holds all my infrastructure configuration files. Never again would I edit a file somewhere on a remote machine and then lose all changes because I didn’t open it as root. Having all configurations in Git makes it easy to roll back changes and investigate what I did to bring down my server.&lt;/p&gt;&lt;p&gt;Now that I had a repository, I added some files, namely compose files. Each application has a &lt;code&gt;docker-compose.yaml&lt;/code&gt; file that instructs Docker Compose on how to run the application. My initial compose file for &lt;a href=&quot;https://github.com/sissbruecker/linkding&quot; rel=&quot;noopener&quot;&gt;linkding&lt;/a&gt;, a self hosted bookmark service can be seen in the following example.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;linkding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sissbruecker/linkding:1.17.2&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;links&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;9090:9090&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;links-data:/etc/linkding/data&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unless&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stopped
&lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;links-data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here you can see that the file starts with information about the Docker Compose version, then comes a section that defines all services that should be run, and at the end, I define a single volume that is used by the service to persist data. This compose file can be started with the Docker Compose CLI by running &lt;code&gt;docker-compose up&lt;/code&gt;. After doing this, the service can be accessed on port 9090. That’s a great start, but I needed the services to be accessible from the internet, and of course, they have to be secured with a TLS certificate.&lt;/p&gt;&lt;p&gt;That sounds like a job for a reverse proxy, and my reverse proxy of choice happens to be Traefik Proxy. It can be hosted via Docker, and dynamically loads its configuration from Docker. This means that I can spin up containers, and when they have some labels the proxy will automatically route traffic to them and even manage certificates. The next example shows the &lt;code&gt;docker-compose.yaml&lt;/code&gt; for Traefik Proxy.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;

&lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;traefik&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik:v2.9.10&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;command&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--providers.docker=true&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--providers.docker.exposedbydefault=false&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--providers.docker.network=traefik_traefik&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--entrypoints.web.address=:80&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--entrypoints.web.http.redirections.entrypoint.to=websecure&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--entrypoints.web.http.redirections.entrypoint.scheme=https&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--entrypoints.web.http.redirections.entrypoint.permanent=true&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--entrypoints.websecure.address=:443&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--certificatesresolvers.letsencrypt.acme.tlschallenge=true&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--certificatesresolvers.letsencrypt.acme.email=REDACTED&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;--certificatesresolvers.letsencrypt.acme.storage=/letsencrypt/acme.json&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;443:443&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;80:80&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik-letsencrypt:/letsencrypt&quot;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;/var/run/docker.sock:/var/run/docker.sock:ro&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik&quot;&lt;/span&gt;
    &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unless&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stopped
&lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;traefik-letsencrypt&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;traefik&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The basic structure of this compose file is similar to the one I use for linkding. As you can see, I expose the default ports for HTTP 80 and HTTPS 443 traffic. To automatically detect configuration from other containers Traefik needs access to the &lt;code&gt;/var/run/docker.sock&lt;/code&gt; socket which is solved by mounting it as a volume. Another thing I did was to put all applications that should be routed by Traefik into the same “traefik” network (called “traefik_traefik” when used by other applications). In the command section of the &lt;code&gt;traefik&lt;/code&gt; service configuration, you can see the various command line arguments I pass to Traefik’s static configuration. These arguments instruct Traefik to load dynamic configuration from all Docker containers in the “traefik_traefik” network, redirect all traffic from the “web” endpoint on port 80 to the “websecure” endpoint on port 443, and lastly, I define a certificate resolver “letsencrypt” that can be used to fetch Let’s Encrypt certificates for applications.&lt;/p&gt;&lt;p&gt;Next, I wanted to route traffic to my linkding container. This can be achieved by simply placing some labels on the container. These labels are used to enable Traefik, define rules used to route traffic, set the entry points used by the container, and finally, the last label sets the certificate resolver that should be used. Additionally, the container has to be in the “traefik_traefik” network. The ports section is no longer needed. The changes I made to the compose file are shown in the following example.&lt;/p&gt;&lt;pre class=&quot;language-diff-yaml&quot;&gt;&lt;code class=&quot;language-diff-yaml&quot;&gt;&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token key atrule&quot;&gt;linkding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sissbruecker/linkding:1.17.2&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;links&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token deleted-sign deleted language-yaml&quot;&gt;&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;ports&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix deleted&quot;&gt;-&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;9090:9090&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-yaml&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;labels&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik.enable=true&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik.http.routers.links.rule=Host(`example.org`)&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik.http.routers.links.entrypoints=websecure&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik.http.routers.links.tls.certresolver=letsencrypt&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;links-data:/etc/linkding/data&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-yaml&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;traefik_traefik&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unless&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stopped
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;volumes&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token key atrule&quot;&gt;links-data&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-yaml&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;networks&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;   &lt;span class=&quot;token key atrule&quot;&gt;traefik_traefik&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;external&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;After configuring some applications in the same way and running them on my small server, I ran into a problem with my server’s resources. Sometimes some of the applications took up all of the available memory and the host system had to take them down. Fortunately, there is a simple way to avoid this by setting some limits for the services defined in the compose files. These limit the memory and CPU power a service is able to use, and I highly recommend setting them. In this last example, you can see how I configured the limits for the linkding service.&lt;/p&gt;&lt;pre class=&quot;language-diff-yaml&quot;&gt;&lt;code class=&quot;language-diff-yaml&quot;&gt;&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;version&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;3&quot;&lt;/span&gt;
&lt;/span&gt;
&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;services&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;   &lt;span class=&quot;token key atrule&quot;&gt;linkding&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;image&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;sissbruecker/linkding:1.17.2&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;container_name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;links&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;restart&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; unless&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt;stopped
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-yaml&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;     &lt;span class=&quot;token key atrule&quot;&gt;deploy&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;       &lt;span class=&quot;token key atrule&quot;&gt;resources&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;         &lt;span class=&quot;token key atrule&quot;&gt;limits&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;           &lt;span class=&quot;token key atrule&quot;&gt;cpus&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;0.25&quot;&lt;/span&gt;
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt;           &lt;span class=&quot;token key atrule&quot;&gt;memory&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;200M&quot;&lt;/span&gt;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-yaml&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; &lt;span class=&quot;token comment&quot;&gt;# ...&lt;/span&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Hosting my applications as containers works really well, and with all their configuration in a Git repository, setting everything up is fast and easy. As with all servers, it’s important to consider security every step of the way so please research best practices and stay safe while self-hosting.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Beautiful Architecture Diagrams</title>
    <link href="https://watzek.dev/posts/2023/03/30/beautiful-architecture-diagrams/" />
    <updated>2023-03-30T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/03/30/beautiful-architecture-diagrams/</id>
    <content type="html">&lt;p&gt;Anybody who works with me knows that I’m a big fan of visualizing software with diagrams. In my experience, having a diagram of an application’s structure or processes performed by the application makes it a lot easier to talk about. High-level diagrams can be especially helpful when communicating with non-technical people. A model I really like to use for architecture diagrams is &lt;a href=&quot;https://c4model.com/&quot; rel=&quot;noopener&quot;&gt;the C4 model by Simon Brown&lt;/a&gt;. This model takes a hierarchical approach and introduces four core diagram types: system context, container, component, and code. These diagrams visualize a software system from the highest to the lowest level.&lt;/p&gt;&lt;p&gt;Until recently I always used the well-known &lt;a href=&quot;https://www.diagrams.net&quot; rel=&quot;noopener&quot;&gt;diagrams.net&lt;/a&gt; (formerly &lt;a href=&quot;http://draw.io&quot; rel=&quot;noopener&quot;&gt;draw.io&lt;/a&gt;) with this &lt;a href=&quot;https://github.com/tobiashochguertel/c4-draw.io&quot; rel=&quot;noopener&quot;&gt;C4 plugin&lt;/a&gt; to draw such diagrams. As I already wrote in &lt;a href=&quot;https://watzek.dev/posts/2022/07/31/drawing-diagrams-with-mermaids/&quot; rel=&quot;noopener&quot;&gt;this article about Mermaid&lt;/a&gt; I prefer to create diagrams using plain text tools. Mermaid provides an &lt;a href=&quot;https://mermaid.js.org/syntax/c4c.html&quot; rel=&quot;noopener&quot;&gt;experimental C4 diagram type&lt;/a&gt; which looks promising, but is not ready for professional use. While exploring the C4 model website for the hundredth time, something caught my eye. There on the bottom was a logo and the text “The example diagrams were created with Structurizr”. Looking at the &lt;a href=&quot;https://structurizr.com/&quot; rel=&quot;noopener&quot;&gt;Structurizr&lt;/a&gt; site, I got excited because this was the tool I was looking for.&lt;/p&gt;&lt;p&gt;Structurizr introduces a sane syntax for C4 diagrams that feels right for me. There are customization options, but the generated diagrams look good enough out of the box.&lt;/p&gt;&lt;p&gt;To demonstrate the syntax, I created a small set of diagrams that describe this blog. Because of this blog’s simplicity, I only added a system context and a container diagram. In the following code snippet, the source code for the diagrams is shown. You can actually copy the code and paste it to the &lt;a href=&quot;https://structurizr.com/dsl&quot; rel=&quot;noopener&quot;&gt;Structurizr DSL&lt;/a&gt; page to play around with it.&lt;/p&gt;&lt;pre class=&quot;language-txt&quot;&gt;&lt;code class=&quot;language-txt&quot;&gt;workspace {

    model {
        reader = person &quot;Reader&quot; &quot;Surves the web in the search for interesting content.&quot;
        author = person &quot;Author&quot; &quot;Writes articles.&quot;
        blog = softwareSystem &quot;Blog&quot; &quot;A website that serves articles and pages.&quot; {
            webserver = container &quot;Web Server&quot; &quot;Serves websites over the internet.&quot; &quot;Caddy&quot; {
                reader -&gt; this &quot;visits&quot;
            }
            folder = container &quot;Folder&quot; &quot;Contains HTML files and other assets.&quot; {
                webserver -&gt; this &quot;serves&quot;
            }
            ssg = container &quot;Static Site Generator&quot; &quot;Takes input files and generates a static website.&quot; &quot;Eleventy&quot; {
                this -&gt; folder &quot;generates website&quot;
                author -&gt; this &quot;writes source files&quot;
            }
        }
    }

    views {
        systemContext blog {
            include *
            autolayout lr
        }

        container blog {
            include *
            autolayout lr
        }

        theme default
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Structurizr diagrams are composed of a &lt;code&gt;model&lt;/code&gt; and a &lt;code&gt;views&lt;/code&gt; section that are contained in a &lt;code&gt;workspace&lt;/code&gt;. In the first section all systems, persons, containers, and components of the diagrams and their relationships, are defined. The second section defines which views should be generated from the model and how they should look. The above code generates the following two diagrams.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/RrA1nEoI5a-300.webp 300w, https://watzek.dev/images/RrA1nEoI5a-600.webp 600w, https://watzek.dev/images/RrA1nEoI5a-800.webp 800w, https://watzek.dev/images/RrA1nEoI5a-1550.webp 1550w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/RrA1nEoI5a-1550.svg&quot; class=&quot;undefined&quot; alt=&quot;C4 system context diagram of this blog&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1550&quot; height=&quot;1500&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;In the above diagram, you can see the blog displayed as a software system and two persons that interact with the blog. The persons represent the readers and the author of the blog.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/Q3v9sEogzY-300.webp 300w, https://watzek.dev/images/Q3v9sEogzY-600.webp 600w, https://watzek.dev/images/Q3v9sEogzY-800.webp 800w, https://watzek.dev/images/Q3v9sEogzY-2420.webp 2420w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/Q3v9sEogzY-2420.svg&quot; class=&quot;undefined&quot; alt=&quot;C4 container diagram of this blog&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;2420&quot; height=&quot;1558&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Going down one layer in the diagram, the containers that make up the blog are described. It is composed of a web server, a folder in the file system, and a static site generator that generates HTML files from source files.&lt;/p&gt;&lt;p&gt;If you have to draw some architecture diagrams, I can only recommend that you give Structurizr a try. I’m really impressed by the clear and expressive language, the tooling, and the generated diagrams.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Sounds like Vue</title>
    <link href="https://watzek.dev/posts/2023/02/26/sounds-like-vue/" />
    <updated>2023-02-26T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/02/26/sounds-like-vue/</id>
    <content type="html">&lt;p&gt;Recently I built a custom audio player for one of my websites. I quickly created the first version using &lt;a href=&quot;https://alpinejs.dev/&quot; rel=&quot;noopener&quot;&gt;Alpine.js&lt;/a&gt;. But after looking at the kilobytes shipped to the user, I decided to try to build a more lightweight version with &lt;a href=&quot;https://vuejs.org&quot; rel=&quot;noopener&quot;&gt;Vue.js&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;Last, I used Vue in a customer project three or four years ago. Since then, Vue version 3 was released which introduced a new API called the composition API. This was the first time I was using the new API. There were some initial learnings, but I quickly got used to the new way of building Vue components. Honestly, Vue is a lot of fun to work with.&lt;/p&gt;&lt;p&gt;Although Vue can be used without any build process, the real fun starts with single-file components. A single-file component is a file with the &lt;code&gt;.vue&lt;/code&gt; extension. Such a file contains the logic, template, and styles of a single component. No special syntax has to be learned as it is mostly just JavaScript in a script tag, HTML in a template tag, and CSS in a style tag. The following figure shows what a simple “Hello, World!” component looks like.&lt;/p&gt;&lt;figure&gt;&lt;div class=&quot;lg:grid lg:grid-cols-2 lg:gap-2&quot;&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; name &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Tobias&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Hello, {{ name }}!&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;style&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;scoped&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token style&quot;&gt;&lt;span class=&quot;token language-css&quot;&gt;
  &lt;span class=&quot;token selector&quot;&gt;div&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-size&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; 2rem&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;font-weight&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; bold&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;style&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;p-2 mt-2 mb-4 border border-blue-800&quot;&gt;&lt;div id=&quot;hello-world&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption class=&quot;text-sm&quot;&gt;&lt;p&gt;Source and rendered output of the “Hello, World!” Vue component.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;Although this component is pretty simple many things are going on. First, you might spot the &lt;code&gt;setup&lt;/code&gt; attribute on the script tag. This tells Vue that this component uses the composition API and that some boilerplate for this API can be prepared by Vue. Inside the template tag, the variables and functions defined in the script can be used. In this component, simple text interpolation using the double curly braces is used to print the value of the &lt;code&gt;name&lt;/code&gt; variable. At the end of the single-file component comes the style tag which has a special &lt;code&gt;scoped&lt;/code&gt; attribute. This attribute instructs Vue to only apply the given styles to the current component. Therefore, I can just write a CSS rule targeting &lt;code&gt;div&lt;/code&gt; without having to worry about breaking the styles of everything else on the page.&lt;/p&gt;&lt;p&gt;This is already pretty nice but to implement an audio player some interactivity is necessary. So let’s take a look at how Vue deals with that.&lt;/p&gt;&lt;figure&gt;&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; reactive &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reactive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;stepSize&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;gauge&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;countDecrease&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;countIncrease&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onDecrease&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gauge &lt;span class=&quot;token operator&quot;&gt;-=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stepSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;countDecrease &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onIncrease&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;gauge &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;stepSize&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;countIncrease &lt;span class=&quot;token operator&quot;&gt;+=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Gauge: {{ state.gauge }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Increase Count: {{ state.countIncrease }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Decrease Count: {{ state.countDecrease }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;label&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;for&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;interactive-step-size&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Step size&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;label&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;id&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;interactive-step-size&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;number&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;step&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;1&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;99&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.stepSize&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token attr-name&quot;&gt;@input&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.stepSize = parseInt($event.target.value, 10)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onDecrease&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Decrease&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onIncrease&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;Increase&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;
        state.stepSize = 1;
        state.gauge = 0;
        state.countDecrease = 0;
        state.countIncrease = 0;
      &lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      Reset
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;p-2 mt-2 mb-4 border border-blue-800&quot;&gt;&lt;div id=&quot;interactive-component&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption class=&quot;text-sm&quot;&gt;&lt;p&gt;Source and rendered output of an interactive Vue component.&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;The component above consists of an input, three buttons, and three values. The decrease and the increase button decrease or increase the gauge by the number of steps configured via the step-size input. The third button can be used to reset the state of the component. The values that are displayed by the component are the current value of the gauge, the number of times the increase button was clicked, and the number of times the decrease button was clicked.&lt;/p&gt;&lt;p&gt;First, let’s take a look at the template tag. There you can see that text interpolation is used to display the three values. More interesting are the input and the three buttons. Some attributes start with an &lt;code&gt;@&lt;/code&gt; sign. These are event handlers. If you compare them you can see that it is possible to either pass a function that should be called each time the event occurs, for example &lt;code&gt;onDecrease&lt;/code&gt;, or to handle the event inline and use the &lt;code&gt;$event&lt;/code&gt; variable to access the event. Double curly braces cannot be used to dynamically bind the values of attributes. Instead, to dynamically bind a value to an attribute the attribute has to be prefixed with &lt;code&gt;:&lt;/code&gt;. This is what happens to the &lt;code&gt;value&lt;/code&gt; attribute of the input. It is bound to the &lt;code&gt;state.stepSize&lt;/code&gt; variable.&lt;/p&gt;&lt;p&gt;If you take a look at the script tag you can see that the &lt;code&gt;reactive&lt;/code&gt; function is imported from Vue. This function can be used to create reactive objects in Vue. By using this function Vue can track what happens to the object and update the UI if it needs to. The rest of the script only contains the two event handlers to increase or decrease the gauge. With this example, all basics of Vue should be clear and we can get into how I built the audio player, if you want to know more about Vue just head over to its &lt;a href=&quot;https://vuejs.org&quot; rel=&quot;noopener&quot;&gt;great documentation&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;At the core of my custom audio player lies an HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/audio&quot; rel=&quot;noopener&quot;&gt;audio&lt;/a&gt; element. Without any customization, this element looks different in each browser. Just like you can see and try below.&lt;/p&gt;&lt;figure&gt;&lt;div class=&quot;flex justify-center&quot;&gt;&lt;audio controls=&quot;&quot; src=&quot;https://watzek.dev/audio/posts/sounds-like-vue/heroic_age.mp3&quot; preload=&quot;metadata&quot;&gt;&lt;/audio&gt;&lt;/div&gt;&lt;figcaption class=&quot;text-sm&quot;&gt;&lt;p&gt;HTML audio element playing:&lt;br&gt;“Heroic Age” Kevin MacLeod (&lt;a href=&quot;https://incompetech.com&quot; rel=&quot;noopener&quot;&gt;incompetech.com&lt;/a&gt;)&lt;br&gt;Licensed under &lt;a href=&quot;https://creativecommons.org/licenses/by/4.0/&quot; rel=&quot;noopener&quot;&gt;Creative Commons: By Attribution 4.0 License&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;I wanted my audio player to have the following features:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;play/pause&lt;/li&gt;&lt;li&gt;go back 10 seconds&lt;/li&gt;&lt;li&gt;go forward 30 seconds&lt;/li&gt;&lt;li&gt;show progress&lt;/li&gt;&lt;li&gt;allow position seeking&lt;/li&gt;&lt;li&gt;display the current position&lt;/li&gt;&lt;li&gt;display the total duration&lt;/li&gt;&lt;li&gt;provide accessible labels&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To implement the first three features only the basics of Vue and some knowledge of the HTML audio element are required. There needs to be some kind of state that holds information about the current playing state, position, and total duration. Then some buttons with event handlers are needed to toggle play and pause, go back 10 seconds, and go forward 30 seconds. These can be implemented via the &lt;code&gt;reactive&lt;/code&gt; function and some &lt;code&gt;@&lt;/code&gt; event handlers as shown earlier. But somehow the audio element needs to be controlled and the total duration of the audio has to be known to correctly calculate the position when going forward. To do this two Vue functions are used.&lt;/p&gt;&lt;p&gt;First, we need the &lt;code&gt;ref&lt;/code&gt; function which can either be used similarly to the &lt;code&gt;reactive&lt;/code&gt; function or to hold a reference to a real DOM element, which has to be marked with the &lt;code&gt;ref&lt;/code&gt; attribute, in a variable. With a &lt;code&gt;ref&lt;/code&gt; variable that holds an audio element its JavaScript functions and properties can be accessed, for example, &lt;code&gt;play()&lt;/code&gt;, &lt;code&gt;pause()&lt;/code&gt;, &lt;code&gt;duration&lt;/code&gt;, or &lt;code&gt;currentTime&lt;/code&gt;. So now the element can be controlled by accessing the value of the &lt;code&gt;ref&lt;/code&gt; variable that contains it. Additionally, I added event handlers to the &lt;code&gt;play&lt;/code&gt; and &lt;code&gt;pause&lt;/code&gt; events of the audio element to update the state of the component in case an external input starts or stops audio, for example media controls of the operating system.&lt;/p&gt;&lt;p&gt;Second, to set the total duration of the audio an event listener that listens to the &lt;code&gt;loadedmetadata&lt;/code&gt; event can be registered. This event listener can store the duration in the reactive state. This will work sometimes but more often this event is not dispatched because the metadata of the audio element was already fetched before Vue registers its handlers. To still get the duration from the element a Vue lifecycle hook called &lt;code&gt;onMounted&lt;/code&gt; can be used which is called after Vue created and inserted the components’ DOM tree. In this hook, the audio element can be accessed and its duration can be stored.&lt;/p&gt;&lt;p&gt;Without further ado here is the very simple audio player in all its glory.&lt;/p&gt;&lt;figure&gt;&lt;div&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reactive&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; onMounted &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reactive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;playing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; audioElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;togglePlayPause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;playing&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;play&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;goBack&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;max&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;goForward&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token function&quot;&gt;setPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;min&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;+&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token function&quot;&gt;onMounted&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;readyState &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;duration&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
      &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;audio&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;audioElement&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/audio.mp3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;preload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;metadata&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;@loadedmetadata&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.duration = $event.target.duration&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;@pause&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.playing = false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;@play&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.playing = true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token attr-name&quot;&gt;@timeupdate&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.position = $event.target.currentTime&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;audio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;goBack()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;-10 Seconds&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;togglePlayPause()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        {{ state.playing ? &quot;Pause&quot; : &quot;Play&quot; }}
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
      &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;button&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;@click&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;goForward()&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;+30 Seconds&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;button&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;p-2 mt-2 mb-4 border border-blue-800&quot;&gt;&lt;div id=&quot;simple-audio-player&quot; data-src=&quot;/audio/posts/sounds-like-vue/heroic_age.mp3&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;figcaption class=&quot;text-sm&quot;&gt;&lt;p&gt;Simple audio player playing:&lt;br&gt;“Heroic Age” Kevin MacLeod (&lt;a href=&quot;https://incompetech.com&quot; rel=&quot;noopener&quot;&gt;incompetech.com&lt;/a&gt;)&lt;br&gt;Licensed under &lt;a href=&quot;https://creativecommons.org/licenses/by/4.0/&quot; rel=&quot;noopener&quot;&gt;Creative Commons: By Attribution 4.0 License&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;&lt;p&gt;After getting the basic functionality working it was time to look into how I could implement the progress and position-seeking. I used an HTML &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range&quot; rel=&quot;noopener&quot;&gt;input of type range&lt;/a&gt; as the base for this functionality. The biggest challenge was styling this 🤬 control, which I will not discuss here. Just know that if you want to style such an input you are going to write many different styles for different browsers, which, as everybody knows, is always fun.&lt;/p&gt;&lt;p&gt;Back to the matter at hand. To implement seeking and keeping track of the progress I introduced a new property called &lt;code&gt;seekingPosition&lt;/code&gt; to the reactive state of the component. This property is &lt;code&gt;undefined&lt;/code&gt; until the range input is touched. While the range input is in use the &lt;code&gt;seekingPosition&lt;/code&gt; holds the value of the input. When the range input is no longer used the &lt;code&gt;seekingPosition&lt;/code&gt; is set back to &lt;code&gt;undefined&lt;/code&gt; and the audio plays from the last value that was assigned to the &lt;code&gt;seekingPosition&lt;/code&gt;. This means that the component can be in two different modes: playing mode or seeking mode. In playing mode the range input shows the current position. In seeking mode the range input shows the position the user is currently at with the handle. When switching to seeking mode the audio must continue to play until the handle is let go and a change event is dispatched by the input.&lt;/p&gt;&lt;p&gt;Most of the functionality from the simple player can be used as is. There are just some changes required to incorporate the &lt;code&gt;seekingPosition&lt;/code&gt;. The &lt;code&gt;setPosition&lt;/code&gt; function now resets the &lt;code&gt;seekingPosition&lt;/code&gt; to &lt;code&gt;undefined&lt;/code&gt; in case the position was set via the change event of the range input. Instead of an inline listener to the &lt;code&gt;timeupdate&lt;/code&gt; event of the audio element that sets the &lt;code&gt;position&lt;/code&gt; a function is introduced which does not update the &lt;code&gt;position&lt;/code&gt; if the &lt;code&gt;seekingPosition&lt;/code&gt; has a value. The &lt;code&gt;seekingPosition&lt;/code&gt; is set via an inline event handler which reacts to the input event of the range input. Another Vue feature called a computed property is used for this part of the player. A computed property gets calculated based on other reactive variables and can be created with the &lt;code&gt;computed&lt;/code&gt; function. In this case, the &lt;code&gt;currentPosition&lt;/code&gt; is a computed property that is either set to the &lt;code&gt;position&lt;/code&gt; or to the &lt;code&gt;seekingPosition&lt;/code&gt; depending on the current mode of the component. This computed property is used to bind the value of the range input and to display the value beside the input formatted as hours, minutes, and seconds.&lt;/p&gt;&lt;p&gt;The following code snippet shows how the seeking and playing mode work. Some irrelevant parts have been removed from the snippet so please do not try to execute this.&lt;/p&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;script&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;setup&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token script&quot;&gt;&lt;span class=&quot;token language-javascript&quot;&gt;
  &lt;span class=&quot;token keyword&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; ref&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; reactive&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; computed &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;vue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; state &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;reactive&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;playing&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;seekingPosition&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token literal-property property&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; audioElement &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; currentPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;computed&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
    Math&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;floor&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
      state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;seekingPosition &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;
        &lt;span class=&quot;token operator&quot;&gt;?&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position
        &lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;seekingPosition
    &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;setPosition&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    audioElement&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;seekingPosition &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;function&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;onTimeupdate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token parameter&quot;&gt;e&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;seekingPosition &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;undefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    state&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;position &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;target&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;currentTime&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;script&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;

&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;audio&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;ref&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;audioElement&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;/path/to/audio.mp3&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;preload&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;metadata&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@loadedmetadata&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.duration = $event.target.duration&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@pause&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.playing = false&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@play&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.playing = true&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@timeupdate&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;onTimeupdate&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;audio&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;input&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;range&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;aria-label&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Position&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;:max&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Math.floor(state.duration)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;:value&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;currentPosition&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;:aria-valuetext&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;formatSecondToValuetext(currentPosition)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@input&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;state.seekingPosition = $event.target.value&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token attr-name&quot;&gt;@change&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;setPosition($event.target.value)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;:datetime&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;formatSecondsToDuration(currentPosition)&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;{{ formatSecondsToDisplay(currentPosition) }}&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;time&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;template&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Last but not least I want to mention some techniques I used to try to make the audio player as accessible as possible. To make the player accessible for people that use assistive technologies like screen readers I: set labels for all controls, used the &lt;code&gt;aria-valuetext&lt;/code&gt; attribute on the range input to provide a more meaningful textual representation of its value, declared a region around the player with the label “Audio Player”, and used &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/HTML/Element/time&quot; rel=&quot;noopener&quot;&gt;HTML time elements&lt;/a&gt; to represent the duration and position with valid duration strings as &lt;code&gt;datetime&lt;/code&gt; attribute values. Just remember that audio is inherently inaccessible for certain groups ant it should be accompanied by a textual representation whenever possible similar to images &lt;code&gt;alt&lt;/code&gt; text.&lt;/p&gt;&lt;p&gt;Finally, the following audio player is the result of this endeavor. You can find its source in &lt;a href=&quot;https://gist.github.com/tobiaswatzek/9c7b53c685d317a0763e16bfb866b369&quot; rel=&quot;noopener&quot;&gt;this GitHub Gist&lt;/a&gt; which you can use however you want. I hope you learned something from this article and will try out Vue if you haven’t already.&lt;/p&gt;&lt;figure&gt;&lt;div id=&quot;audio-player&quot; data-src=&quot;/audio/posts/sounds-like-vue/heroic_age.mp3&quot;&gt;&lt;/div&gt;&lt;figcaption class=&quot;text-sm&quot;&gt;&lt;p&gt;Audio player playing:&lt;br&gt;“Heroic Age” Kevin MacLeod (&lt;a href=&quot;https://incompetech.com&quot; rel=&quot;noopener&quot;&gt;incompetech.com&lt;/a&gt;)&lt;br&gt;Licensed under &lt;a href=&quot;https://creativecommons.org/licenses/by/4.0/&quot; rel=&quot;noopener&quot;&gt;Creative Commons: By Attribution 4.0 License&lt;/a&gt;&lt;/p&gt;&lt;/figcaption&gt;&lt;/figure&gt;</content>
  </entry>
  <entry>
    <title>Fun with YAML</title>
    <link href="https://watzek.dev/posts/2023/01/31/fun-with-yaml/" />
    <updated>2023-01-31T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/01/31/fun-with-yaml/</id>
    <content type="html">&lt;p&gt;Today the release pipeline at work broke. No harm was done, but finding the cause of the problem was difficult. While I analyzed the issue, once again, I learned something surprising, nay horrifying, about YAML.&lt;/p&gt;&lt;p&gt;Our application is deployed to a &lt;a href=&quot;https://kubernetes.io/&quot; rel=&quot;noopener&quot;&gt;Kubernetes&lt;/a&gt; cluster via &lt;a href=&quot;https://helm.sh/&quot; rel=&quot;noopener&quot;&gt;Helm&lt;/a&gt;. Helm uses so-called Helm charts to package applications and their Kubernetes configuration together. These packages can then be used to install or upgrade applications on Kubernetes clusters. Charts are composed of YAML files that will be processed by a template engine during the deployment. One of our use cases for this feature is the replacement of placeholder tokens with environment-specific values.&lt;/p&gt;&lt;p&gt;Now to the helpful error message that was logged when the error occurred during the deployment. I formatted it for better readability and removed some irrelevant details.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;Error: UPGRADE FAILED: YAML parse error on chart/templates/deployment.yaml:
error converting YAML to JSON:
yaml: invalid map key:
map[interface {}]interface {}{
  &quot;required &#92;&quot;A .Values.someValue value is required.&#92;&quot; .Values.appSettings.someValue&quot;:
  interface {}(nil)
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Only two lines were added to the &lt;code&gt;deployment.yaml&lt;/code&gt; file mentioned in the error message. These two lines contained a placeholder that should be replaced by &lt;code&gt;.Values.someValue&lt;/code&gt; that is passed to the Helm upgrade command. After I completely ignored the error message, I checked the usual suspects. First, I inspected if &lt;code&gt;.Values.someValue&lt;/code&gt; contained a value. Second, I ensured that the &lt;code&gt;deployment.yaml&lt;/code&gt; file is correctly indented. Neither of these theories checked out, so I finally read the error message and realized that the message was new to me. Unfortunately, I only understood that something went wrong during the parsing of the &lt;code&gt;deployment.yaml&lt;/code&gt; file and that Helm is written in Go (&lt;code&gt;interface {}&lt;/code&gt; ❤️).&lt;/p&gt;&lt;p&gt;So let’s take a look at the two lines that were added.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;token key atrule&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;SomeName&quot;&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; required &quot;A .Values.someValue value is required.&quot; .Values.someValue &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It’s actually pretty easy to spot the issue in these two lines. Especially if I tell you that placeholders in Helm chart template files are supposed to start with two opening curly braces &lt;code&gt;{{&lt;/code&gt; and end with two closing curly braces &lt;code&gt;}}&lt;/code&gt;. So the issue here is the space between the curly braces &lt;code&gt;{ {&lt;/code&gt; and &lt;code&gt;} }&lt;/code&gt;. After the space was removed, the release worked again.&lt;/p&gt;&lt;p&gt;I could have stopped there, but one thing that left me puzzled was the error message. It isn’t simply saying something like: “Hey, you added some weird braces in a YAML file which isn’t valid YAML, duh!” but it actually tells me something like: “Hey, I tried to put your stuff into a map type but failed, and by the way, I use Go!”. So I assumed the YAML must be at least partially valid if the parser does not stop at the first curly brace it encounters.&lt;/p&gt;&lt;p&gt;It’s YAML specification reading time, yay. Specifically, I looked at the current YAML specification, which at the time of writing is version 1.2.2 and can be found at &lt;a href=&quot;https://yaml.org/spec/1.2.2/&quot; rel=&quot;noopener&quot;&gt;yaml.org&lt;/a&gt;. This is a pretty long document spanning ten chapters. So I did the first thing that came to my mind — activate the browsers search and type in “curly” — Bingo! I found exactly one match for the word, which led me to &lt;a href=&quot;https://yaml.org/spec/1.2.2/#flow-style-productions&quot; rel=&quot;noopener&quot;&gt;Chapter 7. Flow Style Production&lt;/a&gt; in the specification. If you want to know in detail what YAML’s flow styles are, just read the specification. I will only tell you that the following sample is 100% valid YAML and 97.8% valid JSON. Only the last value of the structure is not valid JSON.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&quot;is&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&quot;really&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;valid&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&quot;YAML?&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean important&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&quot;btw&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;this&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;is&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;also&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;YAML&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token key atrule&quot;&gt;&quot;oh&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; no
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This explains why the parser didn’t fail when it encountered curly braces in the YAML but instead tried to do its best with what it got. After looking into this issue in detail, the error message also seems less cryptic. At its core, the error message says “invalid map key”. This means that a map, sometimes called a dictionary or object, is created, and the given key is invalid. The key is the construct inside the outer curly braces, which once again is a map. But a map cannot be used as the key of a map. Therefore, the error is raised.&lt;/p&gt;&lt;p&gt;It’s not over yet. The snippet above can be translated differently depending on the YAML specification that is implemented by the parser. Specifically, the last value, &lt;code&gt;no&lt;/code&gt;, is problematic. In YAML version 1.2.2, this would be interpreted as the string &lt;code&gt;&amp;quot;no&amp;quot;&lt;/code&gt; because you do not always need quotes around strings in YAML. But in earlier versions, &lt;code&gt;no&lt;/code&gt; will be translated to the boolean &lt;code&gt;false&lt;/code&gt; (see &lt;a href=&quot;https://yaml.org/type/bool.html&quot; rel=&quot;noopener&quot;&gt;YAML v1.1 bool draft&lt;/a&gt;). Just search for the Norway problem online, in case you want to know more about this hilarious topic.&lt;/p&gt;&lt;p&gt;Once again, I know more about YAML than I did before, which will probably make it easier to avoid and detect bugs in the future. Generally, I think YAML has its place, but it is way more complex than JSON, making mistakes more common. The German phrase &lt;span lang=&quot;de&quot;&gt;“So ein Jammer!”&lt;/span&gt; can roughly be translated to “What a pity!” and I often have to say &lt;span lang=&quot;de&quot;&gt;“So ein YAML!”&lt;/span&gt; whenever I have to touch YAML.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>DIY Visual Studio Code Autocomplete</title>
    <link href="https://watzek.dev/posts/2023/01/04/diy-visual-studio-code-autocomplete/" />
    <updated>2023-01-04T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2023/01/04/diy-visual-studio-code-autocomplete/</id>
    <content type="html">&lt;p&gt;Several months ago, I received a little consulting job that required me to create a structured report.Naturally, I wrote a custom application called &lt;a href=&quot;https://github.com/tobiaswatzek/dokumentatorin&quot; rel=&quot;noopener&quot;&gt;dokumentatorin&lt;/a&gt; to convert YAML files to other formats. The transformed files can be processed further by tools like &lt;a href=&quot;https://pandoc.org&quot; rel=&quot;noopener&quot;&gt;Pandoc&lt;/a&gt; to generate nice-looking documents. Although the generated reports were guaranteed to look beautiful, I still had to create dozens of YAML files and fill them with data. My editor of choice for such a task is &lt;a href=&quot;https://code.visualstudio.com&quot; rel=&quot;noopener&quot;&gt;Visual Studio Code&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;So there I was, creating the first YAML file and adding about ten fields to it. Some of the fields contained simple strings, while others expected a certain format. After finishing the third file, I decided that I could not possibly continue like this. I am used to all of my code editors supporting me whenever I edit structured data or code. I wanted to have the same experience for these YAML files. Otherwise, I could have just used some office text editor like a sane person.&lt;/p&gt;&lt;p&gt;My tool that processes the YAML files could already validate the files using a &lt;a href=&quot;https://json-schema.org/&quot; rel=&quot;noopener&quot;&gt;JSON Schema&lt;/a&gt;. So why not reuse the schema that I already built? In general, a JSON Schema is used to describe and validate the structure of a JSON document while also using the JSON data format itself. The following is an example JSON Schema for a person with a name, an age, and a favorite ice cream flavor. Each field has a type and some annotations used to validate and describe the data and mark which fields are required.&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;object&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;A person.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;additionalProperties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;properties&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;First and last name of the person.&quot;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;integer&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Age of the person.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;minimum&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;iceCreamFlavor&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;type&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;string&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Favourite ice cream flavor of the person.&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token property&quot;&gt;&quot;enum&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;vanilla&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;chocolate&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;strawberry&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;blue&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;required&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;name&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;age&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;iceCreamFlavor&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I spent some time searching around and found out that with the help of the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=redhat.vscode-yaml&quot; rel=&quot;noopener&quot;&gt;YAML extension&lt;/a&gt; for Visual Studio Code and some settings, I could build my own autocomplete and validation using my JSON Schema. To demonstrate this imagine a directory structured like this:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;.vscode/&lt;/code&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;settings.json&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;data/&lt;/code&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;john.yaml&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;schema.json&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I want the JSON Schema defined above and stored in the &lt;code&gt;schema.json&lt;/code&gt; file to provide autocompletion and validation for all YAML files in the &lt;code&gt;data&lt;/code&gt; directory. To accomplish this, I have to add the following setting to the &lt;code&gt;.vscode/settings.json&lt;/code&gt; file.&lt;/p&gt;&lt;pre class=&quot;language-json&quot;&gt;&lt;code class=&quot;language-json&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;&quot;yaml.schemas&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token property&quot;&gt;&quot;schema.json&quot;&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;data/*.yaml&quot;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now I can press &lt;code&gt;CTRL&lt;/code&gt; + &lt;code&gt;Space&lt;/code&gt; in the &lt;code&gt;john.yaml&lt;/code&gt; file and get suggestions for the complete structure defined in the JSON Schema.&lt;/p&gt;&lt;img src=&quot;https://watzek.dev/images/1hl8IvGiHV-300.webp&quot; class=&quot;undefined&quot; alt=&quot;Screenshot of Visual Studio Code displaying the person YAML and an autocompletion window for the values of the iceCreamFlavor field.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1157&quot; height=&quot;370&quot; srcset=&quot;https://watzek.dev/images/1hl8IvGiHV-300.webp 300w, https://watzek.dev/images/1hl8IvGiHV-600.webp 600w, https://watzek.dev/images/1hl8IvGiHV-800.webp 800w, https://watzek.dev/images/1hl8IvGiHV-1157.webp 1157w&quot; sizes=&quot;100vw&quot;&gt;&lt;p&gt;In my opinion, this feature is powerful and can be used in many situations. It certainly made my job a lot easier and prevented some mistakes.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Drawing Diagrams with Mermaids</title>
    <link href="https://watzek.dev/posts/2022/07/31/drawing-diagrams-with-mermaids/" />
    <updated>2022-07-31T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2022/07/31/drawing-diagrams-with-mermaids/</id>
    <content type="html">&lt;p&gt;An image says more than a thousand words. This old saying is very true when it comes to software development. It is way easier to discuss a topic with multiple people when you can draw an image of the thing you are talking about. Otherwise, everybody will probably have a slightly different understanding of the topic. There are many tools out there that make it easy to quickly draw some boxes and arrows like &lt;a href=&quot;https://app.diagrams.net/&quot; rel=&quot;noopener&quot;&gt;draw.io&lt;/a&gt;, &lt;a href=&quot;https://excalidraw.com/&quot; rel=&quot;noopener&quot;&gt;Excalidraw&lt;/a&gt;, or &lt;a href=&quot;https://miro.com/&quot; rel=&quot;noopener&quot;&gt;Miro&lt;/a&gt;. I have used all of those tools and I can recommend them but one thing all of them have in common is that they are graphical tools that require the user to think about distracting stuff like layout or colors when thinking about a problem. As a programmer, my brain is most comfortable with thinking in plain text and several tools out there make it possible to write text that gets turned into beautiful diagrams. In this post, I want to tell you about &lt;a href=&quot;https://mermaid-js.github.io&quot; rel=&quot;noopener&quot;&gt;Mermaid&lt;/a&gt; which works well for most of my use-cases but there exist quite a few similar tools like &lt;a href=&quot;https://graphviz.org/&quot; rel=&quot;noopener&quot;&gt;Graphviz&lt;/a&gt; or &lt;a href=&quot;https://plantuml.com/&quot; rel=&quot;noopener&quot;&gt;PlantUML&lt;/a&gt; which are also quite interesting.&lt;/p&gt;&lt;p&gt;Mermaid describes itself as a “JavaScript based diagramming and charting tool that renders Markdown-inspired text definitions to create and modify diagrams dynamically”. It does an extraordinary job. The syntax of the different diagram types is intuitive and easy to remember. There are many different diagram types like flowcharts, sequence diagrams, class diagrams, state diagrams, and many more. For quick designs, there exists a &lt;a href=&quot;https://mermaid-js.github.io/mermaid-live-editor/&quot; rel=&quot;noopener&quot;&gt;live editor&lt;/a&gt; with share and export functionality. Even platforms like GitHub or Azure DevOps offer support for embedded Mermaid diagrams in Markdown. This makes Mermaid a great choice to document software. I use the Mermaid live editor to quickly sketch out parts of a system and then discuss them with colleagues. These diagrams can then be used to implement the design and also end up in the documentation. Due to the plain text source of those diagrams, it is also possible to quickly change the documentation without having to redraw everything.&lt;/p&gt;&lt;p&gt;As I mentioned at the beginning an image says more than a thousand words so let me show you some diagrams and their source code so you get a better feeling for Mermaid.&lt;/p&gt;&lt;p&gt;The following is a diagram that creates a flowchart for “make it work, make it right, make it fast”. In my opinion, the source code can be understood without looking into the documentation of Mermaid.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;graph TD
    A(Develop feature)
    work{Does it work?}
    makeWork[make it work]

    right{Is it right?}
    makeRight[make it right]

    fast{Is it fast enough?}
    makeFast[make it fast]

    done(feature done)

    A --&gt; work
    work -- no --&gt; makeWork
    work -- yes --&gt; right
    makeWork --&gt; work
    right -- no --&gt; makeRight
    makeRight --&gt; right
    right -- yes --&gt; fast
    fast -- no --&gt; makeFast
    makeFast --&gt; fast
    fast -- yes --&gt; done&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://watzek.dev/images/B10scSFMUQ-300.webp&quot; class=&quot;undefined&quot; alt=&quot;Flowchart of make it work, make it right, make it fast generated by Mermaid&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;361&quot; height=&quot;808&quot; srcset=&quot;https://watzek.dev/images/B10scSFMUQ-300.webp 300w, https://watzek.dev/images/B10scSFMUQ-361.webp 361w&quot; sizes=&quot;100vw&quot;&gt;&lt;p&gt;The next diagram is a sequence diagram of a pizza order. This type of diagram demonstrates that not a lot of code is needed to draw a quite complex diagram that would be hard to draw in a graphical tool.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;sequenceDiagram
    Me-&gt;&gt;Pizza-Place: I&#39;d like to order a pizza
    Pizza-Place-&gt;&gt;+Me: Order Confirmation
    Pizza-Place-)+Cook: Make a new pizza
    Cook--)-Pizza-Place: Pizza
    Pizza-Place-)+Delivery Person: Deliver this pizza
    Delivery Person-&gt;&gt;Me: Pizza
    Delivery Person--)-Pizza-Place: Pizza delivered&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://watzek.dev/images/6OeUFbs0uT-300.webp&quot; class=&quot;undefined&quot; alt=&quot;Sequence diagram of a pizza order generated by Mermaid&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;890&quot; height=&quot;533&quot; srcset=&quot;https://watzek.dev/images/6OeUFbs0uT-300.webp 300w, https://watzek.dev/images/6OeUFbs0uT-600.webp 600w, https://watzek.dev/images/6OeUFbs0uT-800.webp 800w, https://watzek.dev/images/6OeUFbs0uT-890.webp 890w&quot; sizes=&quot;100vw&quot;&gt;&lt;p&gt;As you can see in the examples Mermaid is a very powerful tool. It allows everyone to quickly draw good-looking diagrams that are easy to maintain and understand. I suggest that you consider it the next time you need a diagram. Here is a final example of a pie chart.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;pie title Which tools were used to draw diagrams in this post?
    &quot;Mermaid&quot; : 100&lt;/code&gt;&lt;/pre&gt;&lt;img src=&quot;https://watzek.dev/images/QMmQhXNN7U-300.webp&quot; class=&quot;undefined&quot; alt=&quot;Pie chart: Which tools were used to draw diagrams in this post? - 100% Mermaid&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;566&quot; height=&quot;387&quot; srcset=&quot;https://watzek.dev/images/QMmQhXNN7U-300.webp 300w, https://watzek.dev/images/QMmQhXNN7U-566.webp 566w&quot; sizes=&quot;100vw&quot;&gt;</content>
  </entry>
  <entry>
    <title>Faster Frontend Builds</title>
    <link href="https://watzek.dev/posts/2022/07/21/faster-frontend-builds/" />
    <updated>2022-07-21T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2022/07/21/faster-frontend-builds/</id>
    <content type="html">&lt;p&gt;At work, we are developing a single-page application with &lt;a href=&quot;https://reactjs.org/&quot; rel=&quot;noopener&quot;&gt;React&lt;/a&gt;. It used the &lt;a href=&quot;https://create-react-app.dev/&quot; rel=&quot;noopener&quot;&gt;Create React App&lt;/a&gt; build tools that, albeit they were not the fastest, did their job. I was bothered by this as it often slowed me down when developing locally but I also had some issues with our build pipeline. Just imagine that you write a fix for a problem and you have to wait about ten minutes for your pull request build and after that, you have to wait the same amount of time for the release build. For an impatient millennial this is unacceptable. I had to change something.&lt;/p&gt;&lt;p&gt;It is common knowledge that in the JavaScript world every problem can be solved by the inclusion of yet another package. So I did exactly that. Instead of using Create React App I updated the project to use …drumroll please… &lt;a href=&quot;https://vitejs.dev/&quot; rel=&quot;noopener&quot;&gt;Vite&lt;/a&gt;. Locally this made things fast, very fast. Starting the local development server now takes about three seconds instead of thirty. This was already a huge win for us. On the build server, this change did not provide any noticeable speed improvements. That is because, at the moment of writing, Vite uses different mechanisms for development and optimized production builds.&lt;/p&gt;&lt;p&gt;After I was satisfied with the improvements to development builds I started to look at how the &lt;a href=&quot;https://dev.azure.com&quot; rel=&quot;noopener&quot;&gt;Azure DevOps&lt;/a&gt; build pipeline that I wrote half a year ago could be improved. This pipeline runs for each pull request and the release build. Its output is a Docker image that includes a web server that serves the built single-page application. Inside the pipeline, a multi-stage Docker build was run. This build used a Dockerfile which first built the application and ran tests inside of a Node.js base image and then copied the built application from the first stage to the final stage that was based on the image of a web server. The major reason for building and testing the application during the Docker build was to ensure that the build is independent of dependencies installed on the build server. This can be a valid reason but in our case, all steps of the build run on a hosted agent which is a container that gets created for the build job and is destroyed after the job finishes. So we do not have to worry about someone installing updates on the shared build server and with that breaking all the builds that relied on a previous version of some dependency. For the frontend build, I decided it was perfectly safe to drop the build inside of Docker, build everything on the agent itself, and as a final step create a Docker image with the finished application. This change alone chopped off one and a half minutes of build time.&lt;/p&gt;&lt;p&gt;Finally, I could rest and enjoy the fruits of my labor, or could I? There was still one thing that bothered me. At the beginning of each automated build, about thirty seconds were spent downloading NPM packages. These packages seldom change and would therefore be a perfect candidate for some sort of caching. This is where the &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/devops/pipelines/tasks/utility/cache?view=azure-devops&quot; rel=&quot;noopener&quot;&gt;Cache task&lt;/a&gt; comes in. This simple task can be used to cache dependencies for a given cache key which can also be the contents of a file. By using the &lt;code&gt;package.lock.json&lt;/code&gt; file as a cache key all NPM packages are cached until the file changes. By applying this final improvement most of the builds only spend a tiny amount of time restoring the package cache instead of downloading the whole internet over and over again.&lt;/p&gt;&lt;p&gt;It’s over it’s done. I could now finally move my attention to the features I should have been building the whole time, but now I can develop them without having to wait for eons (aka seconds) until my development environment spins up and my code is released to production. Unless by coincidence some new JavaScript engine named like a baked good comes along that makes everything run faster I am finished with performance improvements of my build.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Favicon Quantization</title>
    <link href="https://watzek.dev/posts/2022/04/28/favicon-quantization/" />
    <updated>2022-04-28T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2022/04/28/favicon-quantization/</id>
    <content type="html">&lt;p&gt;Recently I stumbled upon the tool &lt;a href=&quot;https://ign.schrodinger-hat.it/&quot; rel=&quot;noopener&quot;&gt;Schrödinger Hat&lt;/a&gt; which can be used to transform images to only use the colors of specific color palettes. What does that mean? Look at the following image that I took during my trip to Bruges.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/urA7hIaEv3-300.webp 300w, https://watzek.dev/images/urA7hIaEv3-500.webp 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/urA7hIaEv3-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Houses along one of the canals in Bruges&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;375&quot; srcset=&quot;https://watzek.dev/images/urA7hIaEv3-300.jpeg 300w, https://watzek.dev/images/urA7hIaEv3-500.jpeg 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;After I processed it with the tool using the Tokyo Night palette the old houses of Bruges look more like a place that can be found in the &lt;a href=&quot;https://forgottenrealms.fandom.com/wiki/Feywild&quot; rel=&quot;noopener&quot;&gt;Feywild&lt;/a&gt; than a place in Belgium.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/h5zIXULVBz-300.webp 300w, https://watzek.dev/images/h5zIXULVBz-500.webp 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/h5zIXULVBz-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Houses along one of the canals in Bruges processed using the Tokyo Night palette&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;375&quot; srcset=&quot;https://watzek.dev/images/h5zIXULVBz-300.jpeg 300w, https://watzek.dev/images/h5zIXULVBz-500.jpeg 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Seeing those images I got an idea for a new favicon. I wanted to retire my previous favicon for quite some time now so I started experimenting. After looking at the website of the tool I found out that it is based on the Python library &lt;a href=&quot;https://github.com/Schrodinger-Hat/ImageGoNord&quot; rel=&quot;noopener&quot;&gt;ImageGoNord&lt;/a&gt;. It was time to warm up my rusty Python skills and fire up a &lt;a href=&quot;https://jupyter.org/&quot; rel=&quot;noopener&quot;&gt;Jupyter&lt;/a&gt; notebook.&lt;/p&gt;&lt;p&gt;Looking at the library and experimenting with some images I found out that one of the methods that the library uses is color quantization. This image processing method can be used to reduce the number of colors in an image. Usually, the goal is to use fewer colors than the original image uses while still maintaining the original look of the image. One of the reasons to do this is to reduce the size of an image. If you want to know more about this process I suggest you take a look at the &lt;a href=&quot;https://en.wikipedia.org/wiki/Color_quantization&quot; rel=&quot;noopener&quot;&gt;color quantization article&lt;/a&gt; on Wikipedia.&lt;/p&gt;&lt;p&gt;Unfortunately, I was not happy with the results I got from the Python library so I looked into some alternatives in the .NET/C# space with which I am more familiar. Luckily the great &lt;a href=&quot;https://github.com/SixLabors/ImageSharp&quot; rel=&quot;noopener&quot;&gt;ImageSharp&lt;/a&gt; library which I already had the pleasure of working with in the past has quite an impressive repertoire of image processing options. One of them being color quantization. I played around for some time and finally reached a result with which I was happy.&lt;/p&gt;&lt;p&gt;In the following code block you can see the whole program I used to process the image. First, the image called &lt;code&gt;me.jpg&lt;/code&gt; gets loaded. Then a palette using the primary colors of the website is created and used to initialize a &lt;code&gt;PaletteQuantizer&lt;/code&gt;. Using the &lt;code&gt;Mutate&lt;/code&gt; function a chain of modifications is applied to the image. In this chain the image is converted to black and white, then the contrast is increased and finally, the quantization is applied. The only step left is to save the created image. As a side note, I want to mention that I did all this using a &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-dotnettools.dotnet-interactive-vscode&quot; rel=&quot;noopener&quot;&gt;.NET Interactive Notebook&lt;/a&gt; which was a pleasant experience.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;SixLabors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ImageSharp&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token namespace&quot;&gt;SixLabors&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;ImageSharp&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Processing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;using&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; image &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; Image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Load&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;me.jpg&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; palette &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Grey&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Rgb24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x2E&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x34&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x40&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Green&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Rgb24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0x48&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xbb&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x78&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// Pink&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Color&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Rgb24&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;0xf6&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0x87&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0xb3&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; quantizer &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;PaletteQuantizer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;palette&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Mutate&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;x &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; x&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;BlackWhite&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Contrast&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1.5f&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Quantize&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;quantizer&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

image&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Save&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;favicon.png&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now for the big reveal here are the source photo I used for my favicon and the processed version of the image using the code above.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/q7Vv14sONK-300.webp 300w, https://watzek.dev/images/q7Vv14sONK-400.webp 400w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/q7Vv14sONK-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Me in front of the ocean before color quantization.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;400&quot; height=&quot;400&quot; srcset=&quot;https://watzek.dev/images/q7Vv14sONK-300.jpeg 300w, https://watzek.dev/images/q7Vv14sONK-400.jpeg 400w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/pTMiAUBcS1-300.webp 300w, https://watzek.dev/images/pTMiAUBcS1-400.webp 400w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/pTMiAUBcS1-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Me in front of the ocean after color quantization using a palette of pink, green and grey.&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;400&quot; height=&quot;400&quot; srcset=&quot;https://watzek.dev/images/pTMiAUBcS1-300.jpeg 300w, https://watzek.dev/images/pTMiAUBcS1-400.jpeg 400w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;As you can see, all it takes is one library and a handful of lines of C# code to apply some major modifications to an image. The next time I have to resize some pictures I will think twice if I open &lt;a href=&quot;https://www.gimp.org/&quot; rel=&quot;noopener&quot;&gt;GIMP&lt;/a&gt; or instead write a quick C# program to do the job.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Rider Plugin Recommendations</title>
    <link href="https://watzek.dev/posts/2022/04/26/rider-plugin-recommendations/" />
    <updated>2022-04-26T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2022/04/26/rider-plugin-recommendations/</id>
    <content type="html">&lt;p&gt;At work, I mostly write C# code using the great &lt;a href=&quot;https://www.jetbrains.com/rider/&quot; rel=&quot;noopener&quot;&gt;JetBrains Rider&lt;/a&gt; &lt;abbr title=&quot;Integrated Development Environment&quot;&gt;IDE&lt;/abbr&gt;. To add some features to the already great feature set I use some plugins that I will share in this article.&lt;/p&gt;&lt;h2 id=&quot;cognitive-complexity&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2022/04/26/rider-plugin-recommendations/#cognitive-complexity&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Cognitive Complexity&lt;/h2&gt;&lt;p&gt;The &lt;a href=&quot;https://plugins.jetbrains.com/plugin/12024-cognitivecomplexity&quot; rel=&quot;noopener&quot;&gt;Cognitive Complexity&lt;/a&gt; plugin tells you when your methods start to get out of hand. It calculates a score for each method that is based on the paper &lt;a href=&quot;https://www.sonarsource.com/docs/CognitiveComplexity.pdf&quot; rel=&quot;noopener&quot;&gt;G. Ann Campbell in Cognitive Complexity - A new way of measuring understandability&lt;/a&gt;. This score is composed primarily of the nesting of statements and the control flow structures used in the method. The plugin gives you a nice badge above each method that indicates how complicated it seems according to its score. The score is updated while the code is written so you get instant feedback.&lt;/p&gt;&lt;p&gt;I like this plugin because it offers an objective rating of the complicatedness of a method which can be discussed with other developers. Like with every metric this one should not be used without thinking. Just because a method is rated “simple enough” by the plugin it does not mean that is not actually “very complex” and vice versa.&lt;/p&gt;&lt;h2 id=&quot;string-manipulation&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2022/04/26/rider-plugin-recommendations/#string-manipulation&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; String Manipulation&lt;/h2&gt;&lt;p&gt;All your &lt;a href=&quot;https://plugins.jetbrains.com/plugin/2162-string-manipulation&quot; rel=&quot;noopener&quot;&gt;String Manipulation&lt;/a&gt; needs will be satisfied by this one. It can do so many things: converting casing like PascalCase to snake_case, escaping text, encoding text, sorting lines, trimming, aligning multiple carets, converting dates, or even inserting a sequence of numbers. I do not need this often but when I do this plugin never disappoints me.&lt;/p&gt;&lt;h2 id=&quot;nyan-progress-bar&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2022/04/26/rider-plugin-recommendations/#nyan-progress-bar&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Nyan Progress Bar&lt;/h2&gt;&lt;p&gt;Progress bars are boring and I have to admit that Rider does show them occasionally. To make the experience more entertaining I highly recommend installing the &lt;a href=&quot;https://plugins.jetbrains.com/plugin/8575-nyan-progress-bar&quot; rel=&quot;noopener&quot;&gt;Nyan Progress Bar&lt;/a&gt; 🐈 and if you can’t get enough just visit &lt;a href=&quot;https://www.nyan.cat&quot; rel=&quot;noopener&quot;&gt;nyan.cat&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Type Alias</title>
    <link href="https://watzek.dev/posts/2021/09/24/type-alias/" />
    <updated>2021-09-24T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2021/09/24/type-alias/</id>
    <content type="html">&lt;p&gt;While working on projects sometimes the need to add a thing that behaves like a primitive type but only allows a defined set of values. Many people would just use an enum and get on with their lives. I’m not one of those people therefore I built myself something I call a &lt;code&gt;TypeAlias&lt;/code&gt; which I use in many projects.&lt;/p&gt;&lt;p&gt;Imagine you have the assignment to store identifiers for different types of animals (Dolphin, Hippopotamus, and Tiger) and also have some code that behaves differently depending on the animal type. Of course, you do not simply want to use strings or magic numbers to do this.&lt;/p&gt;&lt;p&gt;First, let me demonstrate how this could be solved with an enum and tell you why this might not be the best solution.&lt;/p&gt;&lt;p&gt;The following code is the definition of the &lt;code&gt;AnimalTypeEnum&lt;/code&gt; enum.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;enum&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnimalTypeEnum&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    Dolphin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Hippopotamus &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    Tiger &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;3&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;So far so good. One requirement may be to store or transmit a textual representation of the &lt;code&gt;AnimalTypeEnum&lt;/code&gt; instead of the numeric value of the enum member. This could be done by a piece of code similar to the following.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; tigerString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AnimalTypeEnum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Tiger&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now for the inverse, the following can be used.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Enum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TryParse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dolphin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnimalTypeEnum&lt;/span&gt; a&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// it worked&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Now for some fun stuff. What happens if you work with the numerical values of the enum and you want to convert this number back to the enum?&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; animalType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AnimalTypeEnum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;animalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints &quot;Dolphin&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The example above would work but what would happen if there was once the enum member &lt;code&gt;AnimalTypeEnum.Dodo&lt;/code&gt; with the value &lt;code&gt;4&lt;/code&gt;. Because of its extinction, it was removed from the code but some entries that are using the value still exist in the database.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; animalType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AnimalTypeEnum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;animalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints &quot;4&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This behavior certainly is not pretty. There is no warning of any kind. The correct way is to always check if the provided value is even defined before casting it. This is demonstrated in the next snippet.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; v &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Enum&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;IsDefined&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;typeof&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token type-expression class-name&quot;&gt;AnimalTypeEnum&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; animalType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;AnimalTypeEnum&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; v&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;animalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// no output&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When using enums this validation should be made in every function that accepts enum values to protect the program from invalid enum values. A similar case of this happens when an uninitialized variable of the enum type is used. Per default, such a variable has the value &lt;code&gt;0&lt;/code&gt;. Therefore it is recommended to define an enum member with the value &lt;code&gt;0&lt;/code&gt; that is defined as nothing or unknown. Otherwise, the following case is possible.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;Thing&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;AnimalTypeEnum&lt;/span&gt; AnimalType &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; thing &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;Thing&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
Console&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;WriteLine&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;thing&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;AnimalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;// prints &quot;0&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It may seem petty to not use enums because of the issues mentioned above but when I write software I like to make impossible states impossible and enums provide way too many ways in which they could be misused.&lt;/p&gt;&lt;p&gt;This is where the following construct comes in to save the day. The following snipped contains an abstract class that can be inherited from to build strongly typed aliases for types like strings or integers.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;summary&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// A generic class that can be used&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// to create a strongly typed alias of a type.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;/summary&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;typeparam name=&quot;T&quot;&gt;&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// Type of the value.&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;/typeparam&gt;&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;abstract&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;IEquatable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
      &lt;span class=&quot;token class-name&quot;&gt;IEquatable&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;where&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;notnull&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;summary&gt;&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// Value of the instance.&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;/summary&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;T&lt;/span&gt; Value &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;TypeAlias&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        Value &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;implicit&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; typeAlias&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; typeAlias &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;default&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; typeAlias&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;inheritdoc /&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;??&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Empty&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;


    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;inheritdoc /&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; EqualityComparer&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Default&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;inheritdoc /&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;object&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ReferenceEquals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;false&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;TypeAlias&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;obj&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;inheritdoc /&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;other&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token comment&quot;&gt;/// &amp;lt;inheritdoc /&gt;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;override&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;int&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt;
     EqualityComparer&lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Default&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;GetHashCode&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objA &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objA &lt;span class=&quot;token keyword&quot;&gt;is&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; objB &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Equals&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objA &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; objB &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;bool&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;operator&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;T&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;T&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; objB&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;!&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;objB &lt;span class=&quot;token operator&quot;&gt;==&lt;/span&gt; objA&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The &lt;code&gt;TypeAlias&amp;lt;T&amp;gt;&lt;/code&gt; has a &lt;code&gt;Value&lt;/code&gt; property which stores the value of the instance. It implements &lt;code&gt;IEquatable&amp;lt;TypeAlias&amp;lt;T&amp;gt;&amp;gt;&lt;/code&gt; in a way that uses its value to determine equality with other instances. Additionally &lt;code&gt;IEquatable&amp;lt;T&amp;gt;&lt;/code&gt; is implemented so it can be compared with values of type &lt;code&gt;T&lt;/code&gt;. This means that for example the equality between a &lt;code&gt;TypeAlias&amp;lt;string&amp;gt;&lt;/code&gt; and a &lt;code&gt;string&lt;/code&gt; can be determined. The corresponding operators &lt;code&gt;==&lt;/code&gt; and &lt;code&gt;!=&lt;/code&gt; are also overloaded. Last but not least the implicit cast operator is implemented for &lt;code&gt;T?&lt;/code&gt; which makes it possible to for example cast a &lt;code&gt;TypeAlias&amp;lt;int&amp;gt;&lt;/code&gt; to an &lt;code&gt;int&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;So how would I implement the &lt;code&gt;AnimalType&lt;/code&gt; enum from above as a &lt;code&gt;TypeAlias&lt;/code&gt; and perform similar operations as above? The implementation of the &lt;code&gt;TypeAlias&lt;/code&gt; is provided in the next snippet.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;AnimalType&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token type-list&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;TypeAlias&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;AnimalType&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        &lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;base&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;AnimalType&lt;/span&gt; Dolphin &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Dolphin&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;AnimalType&lt;/span&gt; Hippopotamus &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Hippopotamus&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;AnimalType&lt;/span&gt; Tiger &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;Tiger&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;IImmutableSet&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;AnimalType&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; PossibleValues &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;token constructor-invocation class-name&quot;&gt;HashSet&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;AnimalType&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; Dolphin&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Hippopotamus&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; Tiger &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
            &lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToImmutableHashSet&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token return-type class-name&quot;&gt;AnimalType&lt;span class=&quot;token punctuation&quot;&gt;?&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;FromString&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        StringToAnimalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;TryGetValue&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;value&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;out&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt;&lt;/span&gt; animalType&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; animalType&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

    &lt;span class=&quot;token keyword&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;readonly&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;IImmutableDictionary&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; AnimalType&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; StringToAnimalType &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        PossibleValues&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;ToImmutableDictionary&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;e &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Value&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; e &lt;span class=&quot;token operator&quot;&gt;=&gt;&lt;/span&gt; e&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Using the new &lt;code&gt;AnimalType&lt;/code&gt; as string can be achieved by calling the &lt;code&gt;ToString()&lt;/code&gt; method similar to the enum example above or by using the implicit conversion as demonstrated below.&lt;/p&gt;&lt;pre class=&quot;language-csharp&quot;&gt;&lt;code class=&quot;language-csharp&quot;&gt;&lt;span class=&quot;token class-name&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;string&lt;/span&gt;&lt;/span&gt; dolphin &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; AnimalType&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Dolphin&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is no need to validate if a value is defined or not when using a variable of type &lt;code&gt;AnimalType&lt;/code&gt; because it is not possible to produce an instance of the &lt;code&gt;AnimalType&lt;/code&gt; that contains an invalid value. To create an instance of &lt;code&gt;AnimalType&lt;/code&gt; from a string the &lt;code&gt;FromString&lt;/code&gt; method can be used that returns &lt;code&gt;null&lt;/code&gt; when the value is invalid.&lt;/p&gt;&lt;p&gt;Not only does using the &lt;code&gt;TypeAlias&lt;/code&gt; class enable the creation of safer code it also works nicely together with EntityFramework Core value conversions to convert between raw values and &lt;code&gt;TypeAlias&lt;/code&gt; implementations. This means that all application code can use the safe &lt;code&gt;TypeAlias&lt;/code&gt; and values of primitive types and validations of those values only have to be present at the edges of the application.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Handcrafting Enums Sucks</title>
    <link href="https://watzek.dev/posts/2020/10/22/handcrafting-enums-sucks/" />
    <updated>2020-10-22T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/10/22/handcrafting-enums-sucks/</id>
    <content type="html">&lt;p&gt;Go is one of my go-to languages (pun intended) when I want to quickly build something or try something out. Recently I built a small HTTP API that returned an entity as JSON. This entity had a member that was an enum type. Go doesn’t have enums like some other languages but it allows the imitation of enums by using a type alias, constant groups, and the keyword &lt;code&gt;iota&lt;/code&gt;.&lt;/p&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Pet &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    cat Pet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;iota&lt;/span&gt;
    dog
    pig
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The values of the &lt;code&gt;Pet&lt;/code&gt; type above will start at &lt;code&gt;0&lt;/code&gt; for the &lt;code&gt;cat&lt;/code&gt; and end with &lt;code&gt;2&lt;/code&gt; for the &lt;code&gt;pig&lt;/code&gt;. That’s the way how the &lt;code&gt;iota&lt;/code&gt; keyword works. Now I defined the enum but when I serialize one of its values to JSON it is transformed into a number. An alternative would be to define the enum by using a string alias and assigning string values.&lt;/p&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; PetString &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    cat PetString &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;
    dog &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;
    pig &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By using the strings to define the values of the enum a conversion to JSON would return string values. But now I’ve lost the cool numbering features of &lt;code&gt;iota&lt;/code&gt; and everything feels a bit repetitive.&lt;/p&gt;&lt;p&gt;Being the responsible software engineer I am I went on to find an alternative. Naturally the great &lt;code&gt;go generate&lt;/code&gt; command came to my rescue. This command is bundled with the Go tools and can be used to execute commands that are specified inside of Go source files in special comments. Such a comment can look like this &lt;code&gt;//go:generate touch foo&lt;/code&gt;. This would execute the &lt;code&gt;touch&lt;/code&gt; command on a file called &lt;code&gt;foo&lt;/code&gt; and therefore update the files timestamp or create the file if it didn’t exist. The funny thing is that you can actually run any Go source file by calling &lt;code&gt;go run mycode.go&lt;/code&gt; in a Go generate comment. That means that you can write source code that generates other source code.&lt;/p&gt;&lt;p&gt;Now I knew what I wanted to do. I wanted to write a code generator that generates my enums and all the custom JSON conversion logic from and to strings. To generate my code I wrote a Go file that uses the &lt;a href=&quot;https://github.com/dave/jennifer&quot; rel=&quot;noopener&quot;&gt;jennifer&lt;/a&gt; package to generate Go code in a fluent fashion. After passing the values of my &lt;code&gt;Pet&lt;/code&gt; enum to my generator the following code is generated. I removed the package, import, and JSON serialization code from the code for brevity.&lt;/p&gt;&lt;pre class=&quot;language-go&quot;&gt;&lt;code class=&quot;language-go&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;type&lt;/span&gt; Pet &lt;span class=&quot;token builtin&quot;&gt;int&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;const&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;
    cat Pet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token boolean&quot;&gt;iota&lt;/span&gt;
    dog
    pig
&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s Pet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;String&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; toStringPet&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;s&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toStringPet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;Pet&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    cat&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    dog&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    pig&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;var&lt;/span&gt; toIdPet &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;map&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;string&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;Pet&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;cat&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; cat&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;dog&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; dog&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;token string&quot;&gt;&quot;pig&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; pig&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s Pet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;MarshalJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... marshaling logic&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;func&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;s &lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;Pet&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;UnmarshalJSON&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;b &lt;span class=&quot;token punctuation&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;]&lt;/span&gt;&lt;span class=&quot;token builtin&quot;&gt;byte&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token builtin&quot;&gt;error&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token comment&quot;&gt;// ... unmarshaling logic&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The complete code for my generator can be found on &lt;a href=&quot;https://github.com/tobiaswatzek/code-from-articles/blob/main/enums/enums.go&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;. After the enums are generated I can reference the enums in my code and serialize them to JSON as strings. Another benefit of using a code generator is that I could actually generate the enums by calling another API or reading in a file that provides the values. All in all, I am very happy with my solution and think that it may come in quite handy in some situations.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Privacy for My Azure SQL Database</title>
    <link href="https://watzek.dev/posts/2020/10/11/privacy-for-my-azure-sql-database/" />
    <updated>2020-10-11T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/10/11/privacy-for-my-azure-sql-database/</id>
    <content type="html">&lt;p&gt;I’m a bit of an &lt;a href=&quot;https://azure.microsoft.com&quot; rel=&quot;noopener&quot;&gt;Microsoft Azure&lt;/a&gt; novice. Recent tasks lead to me learning some of the possibilities offered by Microsofts cloud computing service. Diving into this world feels like exploring a deep dungeon filled with great treasures but also with great dangers. One who ventures into this world has to learn new terminologies, access long-forgotten networking knowledge, and have a keen mind to avoid paying for things that aren’t required.&lt;/p&gt;&lt;p&gt;First, let me tell you what I want to run in the cloud. I have an &lt;a href=&quot;http://ASP.NET&quot; rel=&quot;noopener&quot;&gt;ASP.NET&lt;/a&gt; web application that uses a Microsoft SQL database as data storage. The classic approach to this setup would be to either run the application and the database server on one machine or to separate the application and the database by using two machines. It doesn’t matter if the machines are physical or virtual as long as they are running Microsoft Windows. As intriguing as it seems to be running and administrating two virtual machines in Azure it didn’t seem like this is the cloud way to go.&lt;/p&gt;&lt;p&gt;After some research, I found the two services I wanted to use. My application should run inside of an &lt;a href=&quot;https://azure.microsoft.com/en-us/services/app-service/&quot; rel=&quot;noopener&quot;&gt;Azure App Service&lt;/a&gt; which is more or less a container that only runs my application and makes it accessible to the web. To avoid having to manage a Microsoft SQL Server I opted in to use an &lt;a href=&quot;https://azure.microsoft.com/en-us/services/sql-database/&quot; rel=&quot;noopener&quot;&gt;Azure SQL Database&lt;/a&gt;. This database is mostly compatible with Microsoft SQL Server and completely managed.&lt;/p&gt;&lt;p&gt;The initial setup in which I tried to just make it work was fairly simple. I created an app service and a SQL database via the Azure web interface. After that, I published my application to the app service with the help of Visual Studio. Configuring the app service to use the Azure SQL database worked by configuring a connection string in the settings of the service. I opened the web app in the browser and everything worked fine.&lt;/p&gt;&lt;p&gt;Now that everything worked I wanted to secure the setup. In the current state, the database could be accessed from everywhere by everyone who has the credentials. But I only want my application to be able to access the database. In a classical scenario, I would put the database and the application together in a private network so they could communicate without using the public internet. As expected Azure provides a service called &lt;a href=&quot;https://azure.microsoft.com/en-us/services/virtual-network/&quot; rel=&quot;noopener&quot;&gt;Virtual Network&lt;/a&gt; which provides exactly what I need. It allows the creation of private networks that can be used with other Azure services.&lt;/p&gt;&lt;p&gt;So I created a Virtual Network in the web interface. Navigated to the database server firewall settings disabled all public access and added the newly created network. Then I navigated to the App Service and added the virtual network in the networking tab. I opened the application in the browser and was greeted with an error. The application could not reach the database. After some cursing and applying the old &lt;a href=&quot;https://youtu.be/5UT8RkSmN4k&quot; rel=&quot;noopener&quot;&gt;“Have you tried turning it off and on again?”&lt;/a&gt; fix nothing changed.&lt;/p&gt;&lt;p&gt;Beaten down I did the one thing only a desperate person does: I read the documentation. There is a page about &lt;a href=&quot;https://docs.microsoft.com/en-us/azure/app-service/web-sites-integrate-with-vnet&quot; rel=&quot;noopener&quot;&gt;integrating app services with virtual networks&lt;/a&gt; which mentions a configuration key called &lt;code&gt;WEBSITE_VNET_ROUTE_ALL&lt;/code&gt;. I skimped over it a couple of times but then I finally understood my problem. Azure SQL Databases get a hostname that is resolved to a &lt;strong&gt;public&lt;/strong&gt; IP. An app service with a virtual network by default only sends &lt;a href=&quot;https://tools.ietf.org/html/rfc1918&quot; rel=&quot;noopener&quot;&gt;RFC1918&lt;/a&gt; traffic through the virtual network. Meaning that it only sends traffic that is intended for an IP in one of the private IP ranges. All other traffic is routed over the public internet. This means that requests to the database came from a public IP and not from within the virtual network. The firewall didn’t allow those requests and the access failed. After I enabled the &lt;code&gt;WEBSITE_VNET_ROUTE_ALL&lt;/code&gt; configuration key for the app service by setting it to &lt;code&gt;1&lt;/code&gt; I could access the database again. I also tried to access the database from my local machine which didn’t work. I finally reached my goal.&lt;/p&gt;&lt;p&gt;Cloud services like Microsoft Azure make it very easy to get an application up and running fast. But to keep your programs and data safe it is important to read the documentation carefully and to make yourself comfortable with the platform by trying things out.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Light It Up</title>
    <link href="https://watzek.dev/posts/2020/07/31/light-it-up/" />
    <updated>2020-07-31T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/07/31/light-it-up/</id>
    <content type="html">&lt;p&gt;My team and I at work are currently working on a website that is built in a very classic way with a content management system in the backend that renders pages directly on the server. There is not a lot of action going on in the user interface as it mostly displays text and image content. But one or two of our tasks required us to implement some dynamic elements for the site. Dynamic elements in this context mean elements on the page that instantly react to user input. For example, a select box which filters a list of items when a user selects a value. After some consideration, we decided to build custom Web Components for those elements. Web Components describe a collection of browser technologies that make it possible to define so-called custom elements. A custom element can be used like any other HTML element (e.g. &lt;code&gt;p&lt;/code&gt;, &lt;code&gt;a&lt;/code&gt;, &lt;code&gt;input&lt;/code&gt;,…).&lt;/p&gt;&lt;p&gt;Although it is possible to build Web Components only with the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/Web_Components&quot; rel=&quot;noopener&quot;&gt;APIs provided by browsers&lt;/a&gt; our choice was it to use the &lt;a href=&quot;https://lit-element.polymer-project.org/&quot; rel=&quot;noopener&quot;&gt;LitElement&lt;/a&gt; library as it provides a declarative way to define Web Components paired with excellent TypeScript support. To make it easier to build Web Components the library provides some decorators, lifecycle events, templating functions, and styling techniques.&lt;/p&gt;&lt;p&gt;To demonstrate some functionalities of the library in this article I decided to write a small component which is called &lt;code&gt;light-it-up&lt;/code&gt;. It shows a pulsating round light in a box. It has the properties &lt;code&gt;boxColor&lt;/code&gt; and &lt;code&gt;lightColor&lt;/code&gt; which control which colors are used. Below is a code block that shows how the component is used in this article and the rendered component itself.&lt;/p&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;light-it-up&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;boxColor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#03071e&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;lightColor&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;#dc2f02&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;light-it-up&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;div class=&quot;flex justify-center pb-4&quot;&gt;&lt;span class=&quot;sr-only&quot;&gt;In this div an example Web Component is rendered which shows a pulsating circular light.&lt;/span&gt;&lt;light-it-up boxcolor=&quot;#03071e&quot; lightcolor=&quot;#dc2f02&quot;&gt;&lt;/light-it-up&gt;&lt;/div&gt;&lt;p&gt;Now to the fun part. The code of the whole component:&lt;/p&gt;&lt;pre class=&quot;language-ts&quot;&gt;&lt;code class=&quot;language-ts&quot;&gt;&lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;customElement&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;light-it-up&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token keyword&quot;&gt;export&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LightItUp&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;extends&lt;/span&gt; &lt;span class=&quot;token class-name&quot;&gt;LitElement&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;property&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; boxColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#000&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token decorator&quot;&gt;&lt;span class=&quot;token at operator&quot;&gt;@&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;property&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; type&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; String &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; lightColor &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;#3F88C5&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;

  &lt;span class=&quot;token keyword&quot;&gt;public&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;get&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;styles&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; CSSResult &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; css&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;
      .box {
        // ...
      }
      .light {
        // ...
      }
    &lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;

  &lt;span class=&quot;token comment&quot;&gt;// Implement `render` to define a template for your element.&lt;/span&gt;
  &lt;span class=&quot;token keyword&quot;&gt;protected&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;render&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; TemplateResult &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&quot;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;return&lt;/span&gt; html&lt;span class=&quot;token template-string&quot;&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&amp;lt;div
      class=&quot;box&quot;
      style=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;styleMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; backgroundColor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;boxColor &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;
    &gt;
      &amp;lt;div
        class=&quot;light&quot;
        style=&quot;&lt;/span&gt;&lt;span class=&quot;token interpolation&quot;&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;${&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;styleMap&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt; backgroundColor&lt;span class=&quot;token operator&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;lightColor &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token interpolation-punctuation punctuation&quot;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token string&quot;&gt;&quot;
      &gt;&amp;lt;/div&gt;
    &amp;lt;/div&gt;&lt;/span&gt;&lt;span class=&quot;token template-punctuation string&quot;&gt;`&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
  &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;By adding &lt;code&gt;@customElement(&amp;quot;light-it-up&amp;quot;)&lt;/code&gt; to the class it is registered as a custom element with the name &lt;code&gt;light-it-up&lt;/code&gt;. The &lt;code&gt;@property&lt;/code&gt; decorators make it possible to bind values from attributes to the members &lt;code&gt;boxColor&lt;/code&gt; and &lt;code&gt;lightColor&lt;/code&gt;. Another benefit that &lt;code&gt;@property&lt;/code&gt; provides is the re-rendering of the component when their values change. The &lt;code&gt;static get styles()&lt;/code&gt; getter returns a tagged template string that is used as a stylesheet for the component. And finally, there is the &lt;code&gt;render()&lt;/code&gt; method that gets called each time the component is rendered to the browser. It uses a tagged template string &lt;code&gt;html`...`&lt;/code&gt; which gets processed by the great &lt;a href=&quot;https://lit-html.polymer-project.org/&quot; rel=&quot;noopener&quot;&gt;lit-html&lt;/a&gt; templating library.&lt;/p&gt;&lt;p&gt;This is just a simple example as to how Web Components and libraries like LitElement can be used. Of course, there are pretty complex components that can be built without the need of including a big JavaScript UI framework into your project. The complete code for the &lt;code&gt;light-it-up&lt;/code&gt; component can be found in the following &lt;a href=&quot;https://github.com/tobiaswatzek/light-it-up&quot; rel=&quot;noopener&quot;&gt;repository&lt;/a&gt;.&lt;/p&gt;&lt;script src=&quot;https://watzek.dev/scripts/posts/light-it-up/light-it-up.js&quot; type=&quot;module&quot;&gt;&lt;/script&gt;</content>
  </entry>
  <entry>
    <title>All Shapes Are Beautiful</title>
    <link href="https://watzek.dev/posts/2020/06/07/all-shapes-are-beautiful/" />
    <updated>2020-06-07T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/06/07/all-shapes-are-beautiful/</id>
    <content type="html">&lt;style&gt;.circle {
        -webkit-clip-path: circle(50% at 50% 50%);
        clip-path: circle(50% at 50% 50%);
    }
    .diamond {
        -webkit-clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
        clip-path: polygon(50% 0, 100% 50%, 50% 100%, 0 50%);
    }
    .ellipse {
        -webkit-clip-path: ellipse(25% 40% at 50% 50%);
        clip-path: ellipse(25% 40% at 50% 50%);
    }
    .cat {
        -webkit-clip-path: url(&#39;#cat-clip-path&#39;);
        clip-path: url(&#39;#cat-clip-path&#39;);
    }&lt;/style&gt;&lt;div class=&quot;info&quot;&gt;This post contains some CSS style demonstrations which may only work in recent versions of modern browsers. A feed reader, Lynx, or Internet Explorer will probably not display this article in all its beauty.&lt;/div&gt;&lt;p&gt;What if I told you, that the web doesn’t have to consist only of boxes. This is something I just recently discovered after I had to build the header of a website in the shape of a trapezoid. At first, I wanted to use images to accomplish this but the site should be able to scale for multiple resolutions which made this approach quite difficult. After some research, I found out about the CSS &lt;code&gt;clip-path&lt;/code&gt; property. This property makes it possible to define basic shapes like polygons, ellipses, or circles. When a &lt;code&gt;clip-path&lt;/code&gt; is applied to an element only the defined region of the element is visible. Most modern browsers support &lt;code&gt;clip-path&lt;/code&gt;. To see which browsers should work you can take a look at the following &lt;a href=&quot;https://caniuse.com/#feat=css-clip-path&quot; rel=&quot;noopener&quot;&gt;“Can I use…” table&lt;/a&gt;. Below you should see three elements that are shaped like a circle, a diamond, and an ellipse.&lt;/p&gt;&lt;div class=&quot;flex flex-row justify-center p-2&quot;&gt;&lt;div class=&quot;w-12 h-12 m-2 bg-blue-800 circle&quot;&gt;&lt;/div&gt;&lt;div class=&quot;w-12 h-12 m-2 bg-blue-800 diamond&quot;&gt;&lt;/div&gt;&lt;div class=&quot;w-12 h-12 m-2 bg-blue-800 ellipse&quot;&gt;&lt;/div&gt;&lt;/div&gt;&lt;p&gt;These are not images but actual &lt;code&gt;div&lt;/code&gt; elements. I apply one of the following classes to each of the elements to clip them into the right shape.&lt;/p&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.circle&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;circle&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;50% at 50% 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.diamond&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;polygon&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;50% 0&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 100% 50%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 50% 100%&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; 0 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
&lt;span class=&quot;token selector&quot;&gt;.ellipse&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ellipse&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;25% 40% at 50% 50%&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is quite nice to be able to transform elements into simple shapes but the real power of clip-path is that it can use SVG to define what should be clipped away. In the image below you can see my cat. An SVG clip-path of a brushstroke is applied to it which gives the image a creative border.&lt;/p&gt;&lt;div class=&quot;max-w-full&quot;&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/yfdUnSc0qy-300.webp 300w, https://watzek.dev/images/yfdUnSc0qy-500.webp 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/yfdUnSc0qy-300.jpeg&quot; class=&quot;cat&quot; alt=&quot;&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;500&quot; height=&quot;333&quot; srcset=&quot;https://watzek.dev/images/yfdUnSc0qy-300.jpeg 300w, https://watzek.dev/images/yfdUnSc0qy-500.jpeg 500w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/div&gt;&lt;p&gt;The code to use an SVG as a clip-path is very similar to the simple shapes above. A SVG &lt;code&gt;clipPath&lt;/code&gt; element is referenced by id via the &lt;code&gt;url(&#39;#cat-clip-path&#39;)&lt;/code&gt; function. The referenced SVG is included in the source code of this post.&lt;/p&gt;&lt;pre class=&quot;language-css&quot;&gt;&lt;code class=&quot;language-css&quot;&gt;&lt;span class=&quot;token selector&quot;&gt;.cat&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
  &lt;span class=&quot;token property&quot;&gt;clip-path&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token url&quot;&gt;&lt;span class=&quot;token function&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token string url&quot;&gt;&quot;#cat-clip-path&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To find out more about CSS clip-path I recommend that you look at the guides provided by &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/clip-path&quot; rel=&quot;noopener&quot;&gt;MDN&lt;/a&gt;. A nice tool that can help to build clip paths is &lt;a href=&quot;https://bennettfeely.com/clippy/&quot; rel=&quot;noopener&quot;&gt;Clippy&lt;/a&gt;. I hope this article offered an interesting introduction to the CSS &lt;code&gt;clip-path&lt;/code&gt; property and why it is pretty awesome.&lt;/p&gt;&lt;svg class=&quot;w-0 h-0&quot;&gt;&lt;defs&gt;&lt;clipPath id=&quot;cat-clip-path&quot; clipPathUnits=&quot;objectBoundingBox&quot;&gt;&lt;path d=&quot;m 0.26087206,0.99471956 c 0,-0.002904 -2.4e-4,-0.004724 -5.36e-4,-0.004044 -2.94e-4,6.8e-4 -9.02e-4,-0.002052 -0.001352,-0.00607 -4.74e-4,-0.00424 -0.001204,-0.007308 -0.00174,-0.007308 -7.3e-4,0 -7.9e-4,-6.08e-4 -2.8e-4,-0.002896 4.92e-4,-0.002224 4.76e-4,-0.003104 -7.2e-5,-0.003792 -5.6e-4,-7.06e-4 -5.36e-4,-0.0017 1.16e-4,-0.004666 6.3e-4,-0.00287 6.64e-4,-0.003974 1.38e-4,-0.004636 -3.8e-4,-4.76e-4 -6.9e-4,-0.001774 -6.9e-4,-0.00288 0,-0.00111 2.46e-4,-0.001448 5.46e-4,-7.54e-4 3.12e-4,7.22e-4 4.04e-4,-1.06e-4 2.16e-4,-0.001942 -1.8e-4,-0.001762 -4.6e-5,-0.003204 3.02e-4,-0.003204 4.6e-4,0 4.7e-4,-0.00112 4e-5,-0.004128 -4.76e-4,-0.003316 -4.2e-4,-0.004128 2.78e-4,-0.004128 7.02e-4,0 7.36e-4,-5.32e-4 1.78e-4,-0.002768 -7.14e-4,-0.00286 -0.002104,-3.0199e-4 -0.00192,0.00354 4.6e-5,9.94e-4 -2.08e-4,0.002208 -5.64e-4,0.0027 -4.24e-4,5.86e-4 -5.3599e-4,-6.72e-4 -3.26e-4,-0.003678 1.76e-4,-0.002512 9.4e-5,-0.004076 -1.7999e-4,-0.003472 -2.78e-4,6.04e-4 -0.001,4.14e-4 -0.001606,-4.2e-4 -8.76e-4,-0.001206 -6.2e-4,-0.00183 0.00124,-0.00302 0.001464,-9.38e-4 0.002348,-0.0026 0.002348,-0.004416 0,-0.001604 4.82e-4,-0.002916 0.001072,-0.002916 5.9e-4,0 0.001414,-0.001814 0.001828,-0.00403 7.46e-4,-0.003976 0.003558,-0.0094 0.004468,-0.00862 2.6e-4,2.2e-4 0.001874,-9.2399e-4 0.00359,-0.002544 0.001716,-0.00162 0.004672,-0.003598 0.006572,-0.0044 0.001898,-7.9999e-4 0.00359,-0.00229 0.00376,-0.003314 1.68e-4,-0.001024 8.5e-4,-0.001882 0.001516,-0.001908 6.6399e-4,-2.6e-5 0.0027,-0.00279 0.00452,-0.006146 0.001824,-0.003354 0.003734,-0.00612 0.004248,-0.006146 5.14e-4,-2.6e-5 0.001086,-9.7399e-4 0.001274,-0.00211 1.88e-4,-0.001136 6e-5,-0.002064 -2.8e-4,-0.002064 -7.66e-4,0 -4.9e-4,-0.009232 2.8e-4,-0.009432 3.04e-4,-8e-5 0.00164,-0.00131 0.002974,-0.002734 0.00133,-0.001426 0.003026,-0.002022 0.003766,-0.001328 7.4e-4,6.9599e-4 0.002136,3.5399e-4 0.0031,-7.58e-4 9.66e-4,-0.00111 0.00314,-0.003036 0.004834,-0.004274 0.00465,-0.003402 0.007134,-0.006246 0.007134,-0.008164 0,-0.00199 -0.02832,-6.02e-4 -0.035334,0.001732 -0.003694,0.001228 -0.005104,8.92e-4 -0.006624,-0.00158 -0.001064,-0.001728 -0.002058,-0.003276 -0.002208,-0.00344 -1.52e-4,-1.64e-4 -0.001002,-0.00142 -0.00189,-0.00279 -8.88e-4,-0.00137 -0.002006,-0.00192 -0.002484,-0.001224 -4.8e-4,7e-4 -0.007828,0.001602 -0.01633,0.00201 -0.008502,4.08e-4 -0.016576,0.001666 -0.017942,0.002796 -0.001366,0.00113 -0.00546,0.003134 -0.0091,0.004452 -0.00364,0.001318 -0.00709,0.003348 -0.007672,0.00451 -5.8e-4,0.001162 -0.00227,0.001678 -0.003752,0.001146 -0.001484,-5.34e-4 -0.00283,-1.6e-4 -0.002994,8.32e-4 -1.6399e-4,9.9e-4 -5.2999e-4,0.001266 -8.1199e-4,6.12e-4 -2.82e-4,-6.52e-4 -5.14e-4,-2.04e-4 -5.14e-4,9.9999e-4 0,0.0012 -1.86e-4,0.002 -4.14e-4,0.001776 -9.2e-4,-9.1e-4 -0.00566,0.002846 -0.00566,0.004482 0,0.002014 -0.00417,0.005364 -0.005746,0.004616 -5.8e-4,-2.76e-4 -0.00152,0.001372 -0.002092,0.00366 -5.7e-4,0.00229 -0.001462,0.004274 -0.001982,0.004408 -5.2e-4,1.34e-4 -0.002436,4.44e-4 -0.004258,6.88e-4 -0.00182,2.46e-4 -0.004306,0.001518 -0.00552,0.002828 -0.001214,0.001312 -0.003146,0.002906 -0.004294,0.003544 -0.001148,6.4e-4 -0.00195,0.001976 -0.001786,0.002972 5.14e-4,0.003108 -7.14e-4,0.003924 -0.010306,0.00684 -5.08e-4,1.54e-4 -8.8e-4,0.00116 -8.28e-4,0.002236 5.6e-5,0.001076 -3.98e-4,0.002502 -0.001004,0.00317 -6.08e-4,6.6799e-4 -0.001732,0.00281 -0.0025,0.00476 -0.001,0.00255 -0.003092,0.003918 -0.007452,0.004874 -0.003332,7.3e-4 -0.006634,0.00123 -0.007336,0.001112 -7e-4,-1.2e-4 -0.00204,0.00126 -0.00297,0.00306 -9.64e-4,0.001864 -0.001844,0.00246 -0.002038,0.001386 -4.74e-4,-0.00264 -0.00242,-0.005176 -0.001992,-0.0026 4.48e-4,0.002714 -9e-5,0.002576 -0.00156,-4.04e-4 -0.002966,-0.006012 -0.017012,-0.001442 -0.018476,0.006012 -4.54e-4,0.002308 -5.04e-4,0.003866 -1.4e-4,0.00432 3.16e-4,3.92e-4 5.72e-4,0.001536 5.72e-4,0.00254 0,0.001006 -3.46e-4,0.001328 -7.72e-4,7.2e-4 -4.24e-4,-6.1e-4 -9.36e-4,-1.16e-4 -0.001136,0.001096 -5.36e-4,0.00324 0.002772,0.00277 0.00456,-6.46e-4 8.2e-4,-0.00157 0.002608,-0.003536 0.003974,-0.004372 0.001366,-8.3599e-4 0.00292,-0.00182 0.00345,-0.00219 5.32e-4,-3.68e-4 9.66e-4,5.46e-4 9.66e-4,0.002034 0,0.001912 6.86e-4,0.002502 0.002346,0.002016 0.001814,-5.3e-4 0.002126,-2.06e-4 0.00137,0.001432 -5.3999e-4,0.001166 -8.5399e-4,0.002872 -7.0199e-4,0.003792 3.28e-4,0.001982 -0.00276,0.003976 -0.008734,0.00564 -0.003356,9.3599e-4 -0.004452,5.66e-4 -0.004824,-0.001628 -3.48e-4,-0.00206 -4.82e-4,-0.002132 -4.88e-4,-2.6e-4 -1.2e-5,0.0038 -0.003332,0.003186 -0.00452,-8.36e-4 -5.56e-4,-0.00188 -0.00101,-0.002538 -0.00101,-0.001464 0,0.001184 -0.001792,0.001394 -0.004554,5.34e-4 a 0.32421974,0.32421907 0 0 0 -0.008696,-0.002556 c -0.002278,-6.2599e-4 -0.004956,-0.002218 -0.005954,-0.003538 -9.98e-4,-0.00132 -0.002432,-0.001956 -0.003188,-0.001416 -7.56e-4,5.4e-4 -0.00153,3.4e-5 -0.001722,-0.001124 -2.14e-4,-0.001296 -7e-5,-0.001464 3.74e-4,-4.38e-4 4.5e-4,0.00104 5.9e-4,8.6e-4 3.68e-4,-4.74e-4 -4.16e-4,-0.002516 -0.003048,-0.004794 -0.006164,-0.005334 -0.00184,-3.2e-4 -0.00222,-0.001126 -0.002268,-0.00482 -5.4e-5,-0.003964 3.26e-4,-0.0046 0.003588,-0.006014 0.002006,-8.7e-4 0.00427,-0.00138 0.005028,-0.001136 l 0.00138,4.48e-4 -0.001464,-0.002978 c -0.00254,-0.005162 -0.005018,-0.00734 -0.009852,-0.008664 -0.002582,-7.08e-4 -0.006308,-0.002804 -0.008282,-0.004658 -0.00454,-0.004264 -0.02029198,-0.00459 -0.02097998,-4.34e-4 -2.34e-4,0.00143 -7.2e-4,0.001924 -0.001078,0.0011 -9.2e-4,-0.002132 -0.01107999,-0.00285 -0.01073599,-7.6e-4 3.24e-4,0.001956 2.4e-4,0.00199 -0.00698,0.002808 -0.0051,5.8e-4 -0.00725,0.001284 -0.01488199,0.00488 -0.00348,0.001638 -0.005516,7e-4 -0.007504,-0.003464 -8.34e-4,-0.001742 -6.54e-4,-0.002734 0.001352,-0.007468 0.00162,-0.00382 0.003596,-0.006066 0.00658,-0.007476 0.00509,-0.002406 0.00995,-0.00544 0.01344999,-0.008394 0.002094,-0.001768 0.00251,-0.00286 0.002292,-0.005996 -1.58e-4,-0.002252 -5.88e-4,-0.003378 -0.001042,-0.002726 -4.26e-4,6.1e-4 -7.74e-4,8.2e-5 -7.74e-4,-0.001174 0,-0.001258 4.34e-4,-0.0023 9.66e-4,-0.002318 8.26e-4,-2.6e-5 8.46e-4,-3.2e-4 1.38e-4,-0.002032 -5.24e-4,-0.001266 -5.8e-4,-0.002012 -1.56e-4,-0.002032 3.72e-4,-2e-5 5.3e-4,-0.001426 3.54e-4,-0.00314 -2.2e-4,-0.00216 3.3e-4,-0.004184 0.001812,-0.006646 l 0.00213,-0.00354 -0.005394,-0.001246 c -0.002966,-6.8399e-4 -0.00688,-0.00115 -0.008694,-0.001034 -0.001816,1.14e-4 -0.003302,9.2e-5 -0.003302,-5e-5 0,-1.4e-4 4.96e-4,-0.0024 0.001104,-0.005018 6.08e-4,-0.002618 0.001104,-0.004132 0.001104,-0.003364 0,7.68e-4 5.6e-4,2.6e-5 0.001244,-0.00165 6.84e-4,-0.001674 0.003084,-0.004114 0.005336,-0.00542 0.00225,-0.001306 0.005076,-0.003672 0.00628,-0.005258 0.0012,-0.001586 0.003086,-0.002918 0.004186,-0.002958 0.003074,-1.12e-4 0.01081999,-0.007642 0.00858,-0.00834 -2.8e-4,-8.8e-5 -0.001002,-5.86e-4 -0.00161,-0.001108 -6.08e-4,-5.24e-4 -0.001408,-0.0012 -0.00178,-0.001504 -0.00126,-0.001034 -0.001342,-0.003044 -1.76e-4,-0.004428 6.4e-4,-7.6e-4 0.001812,-0.00363 0.002606,-0.00638 7.94e-4,-0.00275 0.001998,-0.005 0.002676,-0.005 6.76e-4,0 0.001514,-0.001278 0.00186,-0.002838 5.2e-4,-0.002332 6.4e-5,-0.00308 -0.002554,-0.004194 -0.001752,-7.46e-4 -0.00393,-0.001806 -0.00484,-0.002356 -0.002168,-0.001308 -0.01079199,-0.00112 -0.01138399,2.5e-4 -8e-4,0.001846 -0.007112,0.002718 -0.007112,9.7999e-4 0,-9.0999e-4 3.74e-4,-0.002814 8.28e-4,-0.004228 4.56e-4,-0.001412 8.3e-4,-0.00354 8.3e-4,-0.004724 0,-0.001186 -1.64e-4,-0.001544 -3.64e-4,-7.98e-4 -2e-4,7.48e-4 -6.96e-4,3.24e-4 -0.001104,-9.4e-4 -4.1e-4,-0.001274 -7.4e-4,-0.00142 -7.4e-4,-3.2599e-4 0,0.001084 -3.74e-4,0.001972 -8.3e-4,0.001972 -0.001326,0 -9.6e-4,-0.00221 8.6e-4,-0.005226 0.00205,-0.00339 0.001756,-0.004812 -0.001548,-0.007502 -0.003012,-0.002454 -0.003588,-0.0063 -0.001426,-0.009554 0.002236,-0.00337 0.01273999,-0.007744 0.01874599,-0.007804 0.002814,-3e-5 0.005776,-6.72e-4 0.00658,-0.001428 l 0.001466,-0.001374 -0.001574,-0.00481 c -8.6399e-4,-0.002646 -0.001992,-0.004846 -0.002506,-0.004886 -0.001308,-1.04e-4 -0.001706,-0.003388 -0.001004,-0.00829 7.4e-4,-0.00518 0.005544,-0.01449 0.007472,-0.01449 8.14e-4,0 0.001284,-8.9e-4 0.00109,-0.002064 -1.86e-4,-0.001136 -7.96e-4,-0.002064 -0.001352,-0.002064 -9.36e-4,0 -0.001976,-0.005154 -0.001772,-0.008772 2.08e-4,-0.00368 -0.00855,4.34e-4 -0.009284,0.00436 -5.6e-4,0.003 -0.01465799,0.010834 -0.01604199,0.008918 l -0.00132,-0.001826 0.001284,-0.002568 c 7.06e-4,-0.001414 0.005292,-0.005068 0.010192,-0.008122 0.008966,-0.00559 0.01114999,-0.009536 0.005274,-0.009536 -0.001806,0 -0.003466,-0.00118 -0.004006,-0.002844 -7.68e-4,-0.002364 -0.00183,-0.00243 -0.006302,-3.88e-4 -0.008224,0.003756 -0.01300399,0.006336 -0.01412599,0.007624 -7.42e-4,8.5199e-4 -9.66e-4,2.4e-4 -8.28e-4,-0.002268 2e-4,-0.00366 0.00191,-0.00795 0.004588,-0.011526 0.002304,-0.003078 8.72e-4,-0.005792 -0.002076,-0.00393 -0.001278,8.06e-4 -0.00344,0.00212 -0.004808,0.00292 l -0.00248399,0.001456 0.00206999,-0.00742 c 0.00114,-0.00408 0.002072,-0.007748 0.002072,-0.008154 0,-4.06e-4 -0.00154,-0.00139 -0.003424,-0.002186 C 0.00235227,0.66694355 -2.9728712e-5,0.66227555 2.7122073e-7,0.65549955 1.6271208e-5,0.65192654 0.00358827,0.64713256 0.00691226,0.64622056 c 0.001368,-3.72e-4 0.004474,-0.002154 0.006904,-0.00396 0.002428,-0.001802 0.006156,-0.003696 0.00828,-0.004206 0.002128,-5.1199e-4 0.005358,-0.001908 0.00718,-0.003104 0.01131199,-0.007426 0.01385799,-0.009254 0.01434398,-0.0103 3e-4,-6.42e-4 -5.6999e-4,-0.002182 -0.001932,-0.003422 -0.00137,-0.001248 -0.002476,-0.003466 -0.002476,-0.004966 0,-0.003474 0.005564,-0.01102 0.01061399,-0.014394 0.002456,-0.00164 0.004274,-0.004082 0.00487,-0.00654 5.32e-4,-0.002202 0.00238,-0.004978 0.004208,-0.006322 0.004148,-0.003052 0.004036,-0.005946 -4.7e-4,-0.012174 -9.68e-4,-0.001334 -0.001646,-0.0035 -0.00151,-0.004814 3.12e-4,-0.002988 0.002872,-0.007548 0.004238,-0.007548 5.64e-4,0 8.72e-4,-9.2799e-4 6.84e-4,-0.002064 -1.86e-4,-0.001136 -0.00219,-0.002064 -0.004448,-0.002064 -0.003724,0 -0.005218,-0.00211 -0.00342,-0.004826 0.00161,-0.002426 -0.006448,-0.001112 -0.010428,0.0017 -0.004954,0.003502 -0.007652,0.002036 -0.007652,-0.00416 0,-0.002856 0.001442,-0.006608 0.00542,-0.014094 0.003746,-0.007054 0.005194,-0.010744 0.004692,-0.011958 -9.54e-4,-0.0023 -1.2e-5,-0.004656 0.00369,-0.009228 0.005228,-0.006454 0.009374,-0.0172739 0.00883,-0.0230359 -3.72e-4,-0.003936 0.001664,-0.006084 0.01007999,-0.010626 0.004184,-0.002258 0.008374,-0.005534 0.00931,-0.007282 9.38e-4,-0.001748 0.00292,-0.003724 0.00441,-0.00439 0.0041,-0.00184 0.01194999,-0.009264 0.01317999,-0.012466 5.98e-4,-0.001562 0.00184,-0.002906 0.00276,-0.002986 9.2e-4,-8e-5 0.002168,-9.4399e-4 0.002774,-0.00192 8.8e-4,-0.001412 6.54e-4,-0.001836 -0.001104,-0.002078 -0.004706,-6.4799e-4 -0.007202,-0.00238 -0.007826,-0.005436 -4.26e-4,-0.002086 -0.001092,-0.002726 -0.001932,-0.001862 -0.001104,0.001134 -0.001148,0.001 -3.08e-4,-9.6799e-4 5.38e-4,-0.00126 0.001408,-0.002022 0.001934,-0.0017 5.24e-4,3.24e-4 0.00129,-1.94e-4 0.001698,-0.001152 4.4e-4,-0.001028 0.0013,-6.26e-4 0.002104,9.8599e-4 0.00106,0.002122 0.001592,0.002186 0.002406,2.86e-4 6.6e-4,-0.001544 0.001168,-0.001696 0.00138,-4.18e-4 1.84e-4,0.001112 0.001748,0.002056 0.003476,0.002096 0.001728,4e-5 0.003762,0.0014 0.00452,0.003022 0.001656,0.003536 0.00221,0.003794 0.00221,0.001032 0,-0.001134 -3.88e-4,-0.002064 -8.6e-4,-0.002064 -4.74e-4,0 -7.26e-4,-8.22e-4 -5.6e-4,-0.00183 1.68e-4,-0.001006 -2.18e-4,-0.002338 -8.52e-4,-0.00296 -6.36e-4,-6.2e-4 -0.00105,-0.002352 -9.18e-4,-0.003846 2.44e-4,-0.002756 0.002718,-0.004372 0.010366,-0.006772 0.006912,-0.002166 0.008834,-0.003334 0.008834,-0.00536 0,-0.001066 -3.72e-4,-0.001938 -8.28e-4,-0.001938 -4.6e-4,0 -8.28e-4,-0.001834 -8.28e-4,-0.004128 0,-0.003026 3.68e-4,-0.004128 0.00138,-0.004128 0.001588,0 0.00186,-0.00318 3.94e-4,-0.004612 -5.44e-4,-5.2999e-4 -0.00334,-4.6799e-4 -0.006212,1.38e-4 -0.008368,0.001764 -0.011714,0.001178 -0.013466,-0.002364 -8.8799e-4,-0.0018 -0.001776,-0.002904 -0.00197,-0.002454 -1.94e-4,4.5e-4 -2.5e-4,-0.001798 -1.26e-4,-0.004994 2.74e-4,-0.006972 -5.02e-4,-0.00806 -0.00617,-0.008644 -0.003394,-3.48e-4 -0.003656,-6.6e-4 -0.002478,-0.002946 0.00194,-0.00376 0.009056,-0.009152 0.01329999,-0.01008 0.002166,-4.76e-4 0.004216,-0.002172 0.004952,-0.0041 0.001568,-0.004108 0.002514,-0.00416 0.003574,-1.94e-4 4.76e-4,0.001782 0.001806,0.003076 0.003134,0.003052 0.004092,-8e-5 0.01936,-0.005884 0.021432,-0.00815 0.001936,-0.002116 0.001994,-0.002458 0.001586,-0.009226 -2.5799e-4,-0.004266 -2e-4,-0.007872 1.48e-4,-0.009168 3.94e-4,-0.001472 3.76e-4,-0.003012 -5.6e-5,-0.004956 -0.001318,-0.00594 3.6e-4,-0.007172 0.008416,-0.00617 0.00626,7.8e-4 0.008214,3.52e-4 0.009484,-0.00207 0.001344,-0.002562 0.007404,-0.006096 0.011538,-0.00673 6.06e-4,-9.2e-5 0.002346,-0.001 0.003864,-0.002014 l 0.00276,-0.001846 -0.002484,-0.0015 c -0.001366,-8.26e-4 -0.005466,-0.001028 -0.00911,-4.5e-4 -0.003644,5.8e-4 -0.00886,9.2999e-4 -0.011594,7.7999e-4 -0.002732,-1.52e-4 -0.008074,0.001236 -0.01187,0.003086 -0.00751,0.00366 -0.015254,0.00622 -0.018588,0.006144 -0.001162,-2.8e-5 -0.00227,7.62e-4 -0.00246,0.001754 -1.92e-4,9.92e-4 -0.001124,0.00192 -0.002072,0.002066 -9.48e-4,1.46e-4 -0.001722,0.001032 -0.001722,0.001972 0,9.4e-4 -5.6e-4,0.00112 -0.001242,4.04e-4 -9.7399e-4,-0.001024 -0.001064,-8.36e-4 -4.1599e-4,8.6e-4 7.1399e-4,0.00187 6.9599e-4,0.002172 -1.38e-4,0.0022 -5.3e-4,1.8e-5 -9.66e-4,0.001068 -9.66e-4,0.002332 0,0.001414 -5.24e-4,0.001016 -0.00136,-0.001032 -0.001512,-0.0037 -0.002174,-0.004282 -0.001714,-0.001504 1.66e-4,0.001006 -1.92e-4,0.002314 -7.96e-4,0.002904 -6.28e-4,6.14e-4 -0.001098,5.2e-5 -0.001098,-0.001314 0,-0.00138 -8.72e-4,-0.002386 -0.00207,-0.002386 -0.001316,0 -0.002024,9.04e-4 -0.00194,0.00248 8.8e-5,0.001708 -5.08e-4,0.00218 -0.00191,0.001506 -0.002298,-0.0011 -0.004566,-0.005908 -0.004578,-0.00971 -6e-6,-0.001626 -2e-4,-0.001424 -5.44e-4,5.62e-4 -4.14e-4,0.002404 -5.36e-4,0.001796 -5.44e-4,-0.002714 -10e-6,-0.007446 9e-4,-0.013798 0.001976,-0.013798 4.64e-4,0 6.88e-4,-9.28e-4 5e-4,-0.002064 -1.86e-4,-0.001136 1.08e-4,-0.002136 6.6e-4,-0.002222 0.001818,-2.88e-4 0.002972,-0.00154 0.004204,-0.004552 0.001102,-0.002702 0.003694,-0.001662 0.003694,0.001482 0,5.68e-4 -3.32e-4,5.56e-4 -7.4e-4,-3e-5 -4.06e-4,-5.84e-4 -0.00109,5.52e-4 -0.001518,0.002526 -6.7e-4,0.00309 -6.7e-4,0.00334 6e-6,0.00182 6.04e-4,-0.00136 6.94e-4,-8.94e-4 3.96e-4,0.002018 -2.32e-4,0.002264 -1.04e-4,0.00446 3.2e-4,0.005468 0.001056,0.002504 0.001946,0.002034 9.84e-4,-5.2199e-4 -6.22e-4,-0.00166 -3.02e-4,-0.001932 0.001292,-0.0011 0.001174,6.1e-4 0.002572,5e-5 0.00313,-0.001254 7.64e-4,-0.001784 0.001206,-0.001746 0.00182,1.52e-4 6.5e-4,0.002024 8.9e-4,0.0018 0.001234,-0.001156 2.36e-4,-0.002016 3.9e-4,-0.00436 3.4e-4,-0.005212 -4.6e-5,-8.52e-4 2.66e-4,-0.001548 6.96e-4,-0.001548 4.3e-4,0 8.76e-4,-0.001064 9.92e-4,-0.00236 1.16e-4,-0.0013 9.4e-4,-0.002652 0.00183,-0.003008 8.92e-4,-3.56e-4 0.001898,-0.002064 0.002236,-0.0038 3.4e-4,-0.001734 8.2e-4,-0.002472 0.001066,-0.001638 2.46e-4,8.3399e-4 2.9e-4,-1.1e-4 10e-5,-0.002096 -4.26e-4,-0.004446 6.3e-4,-0.004862 0.001084,-4.26e-4 2.9e-4,0.00282 4.46e-4,0.00278 0.001364,-3.2e-4 9.78e-4,-0.003312 0.001036,-0.003288 0.001044,4.26e-4 8e-6,0.003294 9.4e-5,0.00343 5.44e-4,8.36e-4 7.98e-4,-0.004616 6.84e-4,-0.00562 -7.94e-4,-0.007008 -9.38e-4,-8.8e-4 -0.00132,-0.002582 -0.00126,-0.005608 10e-5,-0.00482 0.00202,-0.008426 0.004238,-0.00794 0.002064,4.56e-4 0.003888,-0.0022 0.003888,-0.005664 0,-0.00223 5.48e-4,-0.002884 0.00207,-0.002468 0.002226,6.08e-4 0.00597,-0.001716 0.007316,-0.00454 6.68e-4,-0.001402 6.16e-4,-0.001768 -2.76e-4,-0.001898 -6.08e-4,-8.6e-5 -0.002346,-5.96e-4 -0.003864,-0.001128 -0.004696,-0.001644 -0.01094,-0.001592 -0.013192,1.1e-4 -0.001548,0.00117 -0.002042,9.72e-4 -0.001766,-6.9999e-4 2.38e-4,-0.001438 8.8e-5,-0.0019 -3.9e-4,-0.001214 -4.56e-4,6.5e-4 -0.001188,-0.00124 -0.00177,-0.004564 -7.12e-4,-0.00406 -8.06e-4,-0.00591 -3.28e-4,-0.006504 4.46e-4,-5.58e-4 3.4e-4,-0.001788 -3.3e-4,-0.00375 -9.08e-4,-0.00266 -8.5e-4,-0.002824 6.6e-4,-0.001842 9.12e-4,5.9199e-4 0.001346,5.2799e-4 9.66e-4,-1.4e-4 -0.001398,-0.002466 -6.28e-4,-0.005 0.00152,-0.005 0.001392,0 0.00221,9.5e-4 0.002216,0.00258 6e-6,0.001896 1.34e-4,0.00184 4.82e-4,-2.22e-4 3e-4,-0.00176 6.04e-4,-0.00202 8.22e-4,-6.9999e-4 3.72e-4,0.002242 0.001074,0.002068 0.0028,-6.94e-4 5.92e-4,-9.48e-4 0.001892,-0.001784 0.00289,-0.00186 0.002288,-1.74e-4 0.00303,-0.0053 7.68e-4,-0.0053 -8.96e-4,0 -0.001718,-0.00134 -0.001898,-0.003094 -2.14e-4,-0.00209 2.8e-5,-0.003096 7.42e-4,-0.003096 5.82e-4,0 0.001258,-0.00116 0.0015,-0.00258 6.34e-4,-0.0037 0.002884,-0.008084 0.003464,-0.006746 2.7e-4,6.26e-4 6.46e-4,1.92e-4 8.38e-4,-9.5999e-4 1.9e-4,-0.001156 0.001032,-0.0021 0.00187,-0.0021 8.3799e-4,0 0.002872,-0.001508 0.00452,-0.003352 0.002604,-0.00291 0.00284,-0.00358 0.001776,-0.00507 -6.74e-4,-9.44e-4 -0.005284,-0.001592 -0.010244,-0.001438 -0.00496,1.56e-4 -0.009344,-4.7e-4 -0.00974,-0.001386 -4.12e-4,-9.4999e-4 -8.84e-4,-6.8399e-4 -0.0011,6.18e-4 -4.44e-4,0.002684 -0.017606,0.00472 -0.018032,0.002138 -1.5e-4,-9.08e-4 -6.6e-4,-0.001096 -0.00113,-4.2e-4 -5.8e-4,8.32e-4 -6.56e-4,0.001692 -2.36e-4,0.00266 3.4e-4,7.9e-4 -1.36e-4,0.001434 -0.00106,0.001434 -9.2e-4,0 -0.001676,-8.88e-4 -0.001676,-0.001972 0,-0.001086 -3.7999e-4,-8e-4 -8.3999e-4,6.38e-4 -4.64e-4,0.001436 -0.00102,0.00195 -0.001236,0.001144 -2.16e-4,-8.08e-4 2.26e-4,-0.003226 9.8e-4,-0.005372 l 0.001372,-0.003906 -0.00138,9.92e-4 c -0.00121,8.7e-4 -0.001244,7.5e-4 -2.76e-4,-9.7e-4 0.001592,-0.002828 0.004208,-0.002534 0.005742,6.44e-4 0.001496,0.003098 0.003366,0.002076 0.003366,-0.001836 0,-0.001802 0.00109,-0.002612 0.00359,-0.002666 0.00225,-4.6e-5 0.003588,7.7e-4 0.003588,0.002192 0,0.001248 3.1e-4,7.46e-4 6.9e-4,-0.001114 5e-4,-0.002442 0.001994,-0.00375 0.005382,-0.004706 0.00258,-7.2999e-4 0.00626,-0.002636 0.00818,-0.004238 0.001914,-0.0016 0.004276,-0.003 0.005244,-0.00311 9.68e-4,-1.08e-4 0.003,-0.001466 0.00452,-0.003016 0.002588,-0.002642 0.002296,-0.002748 -0.004694,-0.001692 -0.0041,6.2e-4 -0.007824,0.00114 -0.00828,0.00116 -4.56e-4,1.8e-5 -0.00617,0.00221 -0.0127,0.004872 -0.006526,0.00266 -0.013644,0.005122 -0.015814,0.005468 -0.002172,3.48e-4 -0.003834,0.001328 -0.003694,0.00218 1.4e-4,8.5e-4 -8.2e-4,0.001552 -0.002126,0.001556 -0.00131,0 -0.004616,0.001354 -0.007348,0.003 -0.004686,0.00282 -0.01570999,0.005726 -0.01421399,0.003748 3.82e-4,-5.04e-4 5.54e-4,-0.001766 3.82e-4,-0.002804 -1.72e-4,-0.00104 3.86e-4,-0.001934 0.00124,-0.001988 8.52e-4,-5.2e-5 0.00242,-0.00186 0.003482,-0.004016 0.001062,-0.002154 0.00243,-0.004782 0.003036,-0.00584 0.001996,-0.00348 0.03455797,-0.0236199 0.04223397,-0.0261239 0.002278,-7.42e-4 0.00886,-0.00319 0.01463,-0.00544 0.00577,-0.002248 0.013384,-0.004988 0.016922,-0.00609 0.00354,-0.001102 0.007628,-0.003348 0.009088,-0.004992 0.00192,-0.00216 0.002762,-0.00235 0.003038,-6.86e-4 3.48e-4,0.002108 0.006156,0.001532 0.013188,-0.001312 0.001062,-4.3e-4 0.004416,-0.00136 0.007452,-0.002072 0.005448,-0.001272 0.01375,-0.005554 0.018396,-0.009488 0.001312,-0.00111 0.00262,-0.001482 0.002902,-8.26e-4 2.84e-4,6.58e-4 0.004132,-8.52e-4 0.008548,-0.003354 0.006536,-0.0037 0.008266,-0.004078 0.009294,-0.00202 0.001034,0.002068 0.00236,0.00172 0.007286,-0.00191 0.008202,-0.006044 0.018906,-0.0139 0.02424,-0.017794 0.00243,-0.001774 0.00596,-0.00454 0.007844,-0.006152 0.001884,-0.00161 0.004244,-0.002922 0.005244,-0.002918 0.001,0 0.002936,-0.001326 0.004304,-0.00296 0.002302,-0.002756 0.007016,-0.005816 0.0196,-0.01271597 0.00646,-0.003546 0.007764,-0.004364 0.014352,-0.00902 a 0.25755979,0.25755926 0 0 1 0.011042,-0.007308 l 0.007178,-0.004432 c 0.006446,-0.003982 0.007672,-0.003908 0.008954,5.4e-4 6.92e-4,0.0024 0.00126,0.00542 0.00126,0.00671 0,0.001288 1.84e-4,0.002232 4.12e-4,0.0021 2.28e-4,-1.34e-4 9.8199e-4,-1.34e-4 0.001676,0 0.001056,2.02e-4 0.001172,8.7399e-4 7.1e-4,0.0041 -6.76e-4,0.00472 -1.36e-4,0.007268 8.76e-4,0.004128 0.00114,-0.003532 8.8e-4,-3.54e-4 -4.3e-4,0.005278 -6.88e-4,0.002954 -0.002066,0.0056 -0.003336,0.006404 -0.001854,0.00117 -0.002066,0.001816 -0.001472,0.00449 8.9e-4,0.004014 1.38e-4,0.004264 0.035516,-0.01173997 0.002428,-0.0011 0.006776,-0.002444 0.00966,-0.00299 0.002886,-5.44e-4 0.008102,-0.001966 0.011594,-0.003162 0.003492,-0.001194 0.008088,-0.00254 0.010214,-0.00299 0.002126,-4.5e-4 0.004848,-0.00178 0.006048,-0.002956 0.0012,-0.001176 0.005964,-0.002196 0.010586,-0.002266 0.00462,-7e-5 0.008732,-5.4e-4 0.009134,-0.001046 9.94e-4,-0.00125 0.01393,-0.003784 0.01701,-0.003332 0.00136,2e-4 0.002854,0.001648 0.00332,0.00322 0.00104,0.00352 0.003688,0.003872 0.016012,0.002132 0.005664,-8e-4 0.010866,-0.00262 0.012574,-0.0044 0.00223,-0.00232 0.005926,-0.002954 0.015734,-0.0027 0.007052,1.8e-4 0.018784,-3.74e-4 0.026072,-0.00123 0.014768,-0.001736 0.01398,-0.001756 0.02926,7.26e-4 0.009894,0.001604 0.015602,0.001672 0.024292,2.82e-4 0.001518,-2.4e-4 0.002326,-8.66e-4 0.001794,-0.001384 -5.32e-4,-5.1999e-4 -9.66e-4,-0.001916 -9.66e-4,-0.003102 0,-0.001424 0.00155,-0.001748 0.004554,-9.5599e-4 0.002506,6.6199e-4 0.01002,0.002144 0.0167,0.003294 0.00668,0.001152 0.015252,0.003046 0.019048,0.00421 0.015248,0.00468 0.021064,0.006328 0.028984,0.00822 0.019806,0.004726 0.030248,0.00661 0.04113,0.00742 0.00501,3.72e-4 0.01122,0.00131 0.013802,0.002084 0.00258,7.74e-4 0.006432,0.001322 0.008558,0.00122 0.002124,-1e-4 0.006348,7.3e-4 0.009384,0.001846 0.003038,0.001116 0.012872,0.00244 0.021854,0.00294 0.008982,4.9999e-4 0.01743,0.001814 0.01877,0.00292 0.00134,0.001106 0.00439,0.002516 0.006776,0.00313 0.002386,6.14e-4 0.00512,0.001596 0.006074,0.00218 0.003264,0.001996 0.00836,0.003354 0.015814,0.004212 0.006562,7.56e-4 0.01294,0.00257 0.02236,0.00636 0.001516,6.12e-4 0.003628,0.00137 0.004692,0.001688 0.00106,3.16e-4 0.003794,0.001172 0.006072,0.0019 0.002278,7.28e-4 0.006128,0.001894 0.008556,0.002588 0.00243,6.94e-4 0.00454,0.001548 0.004694,0.0019 1.52e-4,3.48e-4 0.001688,0.001196 0.003416,0.001882 0.001726,6.86e-4 0.003504,0.00177 0.003948,0.002408 4.44e-4,6.3999e-4 0.003006,0.001436 0.005694,0.001776 0.00738,9.28e-4 0.007368,9.22e-4 0.007368,0.00426 0,0.0017 2.64e-4,0.003094 5.86e-4,0.003094 3.2e-4,0 4.04e-4,-0.001086 1.86e-4,-0.002414 -2.72e-4,-0.001646 -9.4e-5,-0.001976 5.64e-4,-0.001032 5.3e-4,7.6e-4 0.001512,0.001382 0.00218,0.001382 8.24e-4,0 0.001246,0.001342 0.001302,0.004128 10e-5,0.004936 -2e-4,0.005172 -0.001308,0.001032 -0.001154,-0.00432 -0.002448,-0.003996 -0.00206,5.18e-4 1.92e-4,0.00222 1.32e-4,0.002724 -1.54e-4,0.001304 -3.2e-4,-0.001572 -0.001874,-0.001866 -0.004884,-9.26e-4 -0.00518,0.00162 -0.009092,0.001174 -0.016286,-0.001856 -0.002884,-0.001214 -0.006612,-0.002708 -0.00828,-0.003318 -0.001672,-6.0999e-4 -0.003328,-0.001464 -0.003682,-0.001898 -3.54e-4,-4.32e-4 -7.9e-4,6.2e-4 -9.66e-4,0.002344 -2.38e-4,0.002318 -7.6e-4,0.002768 -0.00202,0.001734 -0.00198,-0.001624 -0.00325,-0.00224 -0.008516,-0.004136 -0.002124,-7.66e-4 -0.004012,-0.001734 -0.004194,-0.00215 -3.88e-4,-8.9e-4 -0.007772,-0.001036 -0.017888,-3.54e-4 -0.00642,4.32e-4 -0.010946,-7.5e-4 -0.014322,-0.00374 -0.001904,-0.001684 -0.015216,-0.002412 -0.015216,-8.32e-4 0,8.12e-4 0.001678,0.002148 0.003728,0.002968 0.00205,8.2e-4 0.004222,0.00186 0.00483,0.002306 0.00426,0.00314 0.015668,0.006662 0.024844,0.007668 0.002126,2.32e-4 0.004128,0.001052 0.004448,0.00182 5.34e-4,0.00128 0.01744,0.006206 0.018346,0.00535 2.16e-4,-2.06e-4 0.002256,2.78e-4 0.004534,0.001072 0.00352,0.001228 0.012512,0.003698 0.014768,0.004058 8.16e-4,1.3e-4 9.12e-4,0.002794 1.64e-4,0.004524 -5.38e-4,0.00124 -0.0047,0.001528 -0.008466,5.82e-4 -0.00115,-2.88e-4 -0.002292,-0.001746 -0.00254,-0.00324 -3.34e-4,-0.002022 -0.00128,-0.00236 -0.003706,-0.00132 -0.00179,7.6999e-4 -0.003422,0.00177 -0.003626,0.00222 -2.04e-4,4.52e-4 -7.46e-4,-5.86e-4 -0.001208,-0.002308 -4.6e-4,-0.001722 -9.68e-4,-0.002336 -0.001128,-0.001364 -3.1199e-4,0.001886 -0.011456,0.001082 -0.014686,-0.00106 -0.00106,-7.0399e-4 -0.002936,-0.001472 -0.004162,-0.001706 -0.001226,-2.34e-4 -0.00278,-0.001524 -0.00345,-0.002866 -9.76e-4,-0.001952 -0.00122,-0.00189 -0.00122,3.14e-4 0,0.001622 -7.4e-4,0.00298 -0.001794,0.003298 -0.004366,0.001316 -0.010476,0.001192 -0.01532,-3.14e-4 -0.003486,-0.00108 -0.004974,-9.46e-4 -0.004434,4.04e-4 0.002176,0.005448 -0.001906,0.008136 -0.008264,0.00544 -0.005638,-0.00239 -0.006646,-0.002516 -0.00908,-0.001134 -0.00126,7.14e-4 -0.00302,5.6e-4 -0.00392,-3.38e-4 -0.002364,-0.002368 -0.014652,-0.006564 -0.020126,-0.00687 -0.00258,-1.44e-4 -0.006308,-8.9799e-4 -0.00828,-0.001674 -0.001974,-7.76e-4 -0.00595,-0.001886 -0.008834,-0.00247 -0.002884,-5.82e-4 -0.009716,-0.002424 -0.015182,-0.004094 -0.005466,-0.00167 -0.013596,-0.00366 -0.018066,-0.00442 -0.007286,-0.001244 -0.008166,-0.001032 -0.00848,0.002048 -3.02e-4,0.002932 -0.00126,0.003426 -0.006504,0.00335 -0.00338,-4.8e-5 -0.007266,6.66e-4 -0.008632,0.001586 l -0.002486,0.001676 0.00414,0.0023 c 0.00228,0.001266 0.005012,0.002278 0.006074,0.002246 0.001064,-3.4e-5 0.00541,0.00138 0.00966,0.00314 0.004252,0.001758 0.012328,0.004234 0.017944,0.0055 0.005618,0.001268 0.010766,0.00275 0.011442,0.003292 6.76e-4,5.44e-4 0.006266,0.001894 0.012422,0.003 0.011064,0.001984 0.018818,0.003606 0.02472,0.005168 0.00167,4.4e-4 0.0074,0.001742 0.012732,0.002892 0.005332,0.00115 0.010052,0.002504 0.010488,0.00301 4.38e-4,5.06e-4 0.001292,0.001622 0.0019,0.00248 6.06e-4,8.6e-4 0.00206,0.00182 0.003226,0.002134 0.001168,3.16e-4 0.00241,0.002046 0.002762,0.003846 5.08e-4,0.0026 0.001948,0.003384 0.006988,0.003808 0.00349,2.92e-4 0.006594,0.001104 0.0069,0.001802 3.02e-4,6.98e-4 0.002414,0.001814 0.004692,0.00248 0.010886,0.003186 0.021768,0.005772 0.026776,0.006364 0.003036,3.6e-4 0.009494,0.002276 0.014354,0.00426 0.004858,0.00198 0.011068,0.003912 0.0138,0.004288 0.018916,0.002608 0.03402,0.005892 0.04196,0.009122 0.002732,0.001114 0.007522,0.00262 0.010644,0.00335 0.00312,7.3e-4 0.005808,0.002122 0.005968,0.003092 1.6e-4,9.72e-4 -5.3e-4,0.00184 -0.001536,0.001932 -0.001004,9.4e-5 -0.002528,3.6e-4 -0.003386,5.92e-4 -8.56e-4,2.32e-4 -0.001666,-2.3e-4 -0.001798,-0.00103 -1.32e-4,-7.98e-4 -0.001158,-5.04e-4 -0.00228,6.54e-4 -0.00114,0.001176 -0.003636,0.001504 -0.005654,7.4399e-4 -0.001988,-7.4999e-4 -0.01256,-0.001726 -0.02349,-0.002172 -0.01093,-4.44e-4 -0.023974,-0.001406 -0.028984,-0.00214 -0.00893,-0.001306 -0.01146,-5.08e-4 -0.005602,0.00177 0.001624,6.3e-4 0.003074,0.001598 0.00322,0.002146 3.94e-4,0.001472 -0.001624,0.005114 -0.0033,0.005952 -0.00195,9.78e-4 -0.002566,0.003592 -8.98e-4,0.003816 7.34e-4,10e-5 0.004564,0.001046 0.008512,0.002104 0.003948,0.001058 0.011712,0.002472 0.017252,0.003144 0.00911,0.0011 0.010076,0.001566 0.010076,0.004836 0,0.00454 0.002222,0.005614 0.015618,0.007544 0.0054,7.8e-4 0.00995,0.0022 0.010108,0.003158 1.58e-4,9.58e-4 0.001376,0.00174 0.002706,0.00174 0.004536,0 0.003264,0.0057 -0.002306,0.010344 -0.001574,0.001312 -0.003684,0.002212 -0.004694,0.002004 -0.006724,-0.001394 -0.00496,0.00267 0.002584,0.005952 0.003036,0.00132 0.00726,0.002852 0.009386,0.0034 0.002124,5.52e-4 0.005976,0.001928 0.008556,0.00306 0.00258,0.00113 0.0078,0.002668 0.011594,0.003418 0.004674,9.2e-4 0.00708,0.002234 0.007454,0.004066 3.06e-4,0.001498 0.002274,0.003304 0.004416,0.004048 0.002126,7.4e-4 0.004114,0.002478 0.004416,0.003864 3.04e-4,0.001386 0.002368,0.003148 0.004584,0.003912 0.00222,7.68e-4 0.00459,0.002758 0.00527,0.004426 7.8e-4,0.00191 0.003168,0.003366 0.006458,0.00394 0.007094,0.001234 0.01265,0.00356 0.01296,0.005422 3.22e-4,0.00196 -0.004512,0.004758 -0.005254,0.00304 -3.04e-4,-7.02e-4 -5.54e-4,-3.98e-4 -5.54e-4,6.78e-4 0,0.001076 8.7e-4,0.002566 0.001932,0.003312 0.0021,0.001472 0.002496,0.00404 9.66e-4,0.006254 -0.001804,0.002616 7.2e-4,0.006756 0.006398,0.010492 0.004766,0.00314 0.00778,0.007018 0.005582,0.00719 -0.002064,1.6e-4 0.001124,0.0038 0.00444,0.00507 0.00197,7.52e-4 0.003868,0.002264 0.00422,0.00336 3.54e-4,0.001094 0.002214,0.002936 0.004136,0.004094 0.005336,0.003212 0.005278,0.005794 -1.66e-4,0.007394 -0.002034,5.96e-4 -0.003894,0.002368 -0.004452,0.00424 -7.84e-4,0.002624 -0.001744,0.00294 -0.005244,0.001716 -0.00712,-0.002486 -0.00853,-0.002 -0.009598,0.003294 -5.22e-4,0.002592 -8.42e-4,0.00536 -7.12e-4,0.006156 1.32e-4,7.94e-4 0.001914,0.002094 0.00396,0.002888 0.002046,7.96e-4 0.00397,0.002174 0.004272,0.003066 3.04e-4,8.9e-4 0.001732,0.002276 0.003176,0.00308 0.003958,0.002202 0.003146,0.00512 -0.001428,0.00512 -0.002226,0 -0.004236,6.96e-4 -0.004466,0.001546 -2.3e-4,8.52e-4 -0.002546,0.001548 -0.005146,0.001548 -0.003288,0 -0.0048,7.86e-4 -0.004964,0.00258 -1.62e-4,0.00177 4.28e-4,0.002584 0.00188,0.002592 0.001164,4e-6 0.002984,8.64e-4 0.004048,0.001906 0.001062,0.00104 0.004034,0.00248 0.006606,0.0032 0.00257,7.18e-4 0.004952,0.002174 0.005294,0.003236 3.42e-4,0.001062 7.68e-4,0.00139 9.44e-4,7.3e-4 1.78e-4,-6.62e-4 9e-4,-4.32e-4 0.001606,5.12e-4 9.92e-4,0.001324 4.04e-4,0.002014 -0.00258,0.003028 -0.00352,0.0012 -0.007538,0.004212 -0.009662,0.007248 -0.001348,0.001928 7.48e-4,0.004046 0.00704,0.007112 0.003112,0.001516 0.005658,0.00331 0.005658,0.003982 0,6.74e-4 0.001614,8.4e-4 0.003588,3.7e-4 0.002884,-6.8599e-4 0.003588,-2.6e-4 0.003588,0.002162 0,0.003742 -0.001746,0.00986 -0.002802,0.00982 -6.5e-4,-2.8e-5 -6.42e-4,-3.8e-4 4.2e-5,-0.002034 7.04e-4,-0.001702 6.8e-4,-0.002006 -1.56e-4,-0.002032 -5.4e-4,-2e-5 -0.00113,0.00141 -0.001312,0.003172 -2.8e-4,0.002728 -0.001278,0.003042 -0.006746,0.002122 -0.00685,-0.001152 -0.008534,2.14e-4 -0.007272,0.0059 0.002114,0.009524 0.010734,0.0197359 0.01666,0.0197359 0.00358,0 0.00545,0.002504 0.00545,0.007296 0,0.0024 -8.82e-4,0.003014 -0.004278,0.002976 -0.002352,-2.6e-5 -0.005272,-0.001028 -0.006486,-0.002224 -0.001216,-0.001196 -0.006938,-0.002594 -0.012718,-0.00311 -0.01022,-9.08e-4 -0.01051,-8.2e-4 -0.010592,0.003216 -8e-5,0.00397 5.94e-4,0.006828 0.00178,0.007544 3e-4,1.84e-4 0.002684,0.001838 0.00529,0.003674 0.008534,0.006012 0.01611,0.009674 0.023866,0.011534 0.007184,0.001724 0.009634,0.004088 0.008892,0.00858 -1.7e-4,0.001026 -5.46e-4,0.001316 -8.36e-4,6.44e-4 -2.9e-4,-6.7e-4 -4.6e-4,1.2e-5 -3.76e-4,0.001516 8.4e-5,0.001512 -2.78e-4,0.002606 -8.08e-4,0.00245 -5.28e-4,-1.56e-4 -8.4e-4,4.4e-4 -6.96e-4,0.00132 1.46e-4,8.84e-4 -5.78e-4,0.002198 -0.001608,0.00292 -0.002228,0.001564 -0.002756,0.005284 -0.001616,0.011376 6.08e-4,0.003246 6.74e-4,0.00518 2.4e-4,0.007134 -7.12e-4,0.003206 -2.48e-4,0.0054 0.001144,0.0054 5.56e-4,0 0.001012,3.8799e-4 0.001012,8.5999e-4 0,4.76e-4 1.64e-4,0.002472 3.66e-4,0.004436 3.36e-4,0.00328 -3.4e-5,0.00346 -0.004554,0.0022 -0.002708,-7.5199e-4 -0.005854,-0.00192 -0.006992,-0.002594 -0.001648,-9.76e-4 -0.00207,-5.7e-4 -0.00207,0.001992 0,0.001772 -4e-4,0.004058 -8.9e-4,0.005082 -0.001264,0.002644 -5.06e-4,0.014538 0.00108,0.016928 0.001132,0.00171 0.001136,0.001808 8.6e-5,0.00207 -0.001104,2.76e-4 -0.001104,2.76e-4 0,0.00205 0.001076,0.001732 -4.28e-4,0.00247 -0.006074,0.002986 -0.002354,2.16e-4 -0.00414,0.002994 -0.00414,0.006442 0,0.001844 -5.72e-4,0.002968 -0.00152,0.002986 -8.34e-4,1.8e-5 -0.00208,5.9399e-4 -0.002772,0.001286 -0.001176,0.00118 -0.001194,0.001428 -2.76e-4,0.004034 5.4e-4,0.001528 0.00166,0.002806 0.00249,0.002842 8.3e-4,4e-5 0.003148,0.003318 0.005152,0.00729 0.00202,0.004008 0.004446,0.007226 0.005448,0.007226 0.003346,0 0.008564,0.002628 0.008308,0.004182 -1.4e-4,8.5e-4 1.66e-4,0.001416 6.84e-4,0.00126 5.16e-4,-1.56e-4 0.001026,0.00132 0.001134,0.003274 2.52e-4,0.004542 -0.003008,0.012372 -0.004688,0.011266 -6.74e-4,-4.44e-4 -0.001314,2.08e-4 -0.001424,0.001446 -2.6e-4,0.00292 -0.006704,0.003746 -0.00928,0.00119 -0.001096,-0.001086 -0.006748,-0.002226 -0.01256,-0.002534 -0.00983,-5.2e-4 -0.012728,5.34e-4 -0.009054,0.003292 a 0.11369591,0.11369567 0 0 1 0.004004,0.00324 c 0.001366,0.001156 0.00354,0.00214 0.00483,0.002192 0.00129,4.8e-5 0.002346,0.001018 0.002346,0.002154 0,0.001134 3.8999e-4,0.002064 8.6599e-4,0.002064 4.74e-4,0 0.001374,0.00102 0.001996,0.002268 0.00112,0.002242 0.00112,0.002294 -1.74e-4,0.004704 -0.002264,0.004222 -0.001562,0.008204 0.002244,0.01273 0.001952,0.00232 0.003666,0.005572 0.00381,0.007224 1.4e-4,0.001652 4.78e-4,0.003542 7.46e-4,0.0042 8.44e-4,0.002072 -1.66e-4,0.00844 -0.001122,0.00707 -4.84e-4,-6.94e-4 -7.26e-4,-6.9e-4 -5.4e-4,8e-6 1.86e-4,6.9599e-4 -2.88e-4,0.003608 -0.001052,0.006468 -0.001008,0.003766 -0.002046,0.0053 -0.003756,0.005546 -0.0013,1.88e-4 -0.00226,9.5e-4 -0.00214,0.001692 1.24e-4,7.4e-4 -0.002154,8.9399e-4 -0.00506,3.38e-4 -0.004224,-8.08e-4 -0.005468,-3.84e-4 -0.006208,0.002118 -0.00103,0.003474 -0.006452,0.004906 -0.00928,0.00245 -0.00108,-9.3799e-4 -0.001868,-6.4799e-4 -0.002102,7.72e-4 -2.06e-4,0.00125 -9.6e-4,0.002272 -0.00168,0.002272 -0.00166,0 -0.003048,0.00198 -0.003048,0.00435 0,0.001014 3.6e-4,0.001844 8e-4,0.001844 4.4e-4,0 0.001012,0.001408 0.00127,0.00313 2.92e-4,0.00194 0.00245,0.00457 0.005686,0.006924 0.00638,0.00464 0.011076,0.011734 0.010632,0.01606 -1.72e-4,0.00166 -6.64e-4,0.002514 -0.001096,0.001894 -4.32e-4,-6.2e-4 -6.48e-4,-2.88e-4 -4.78e-4,7.3999e-4 1.7e-4,0.001024 -3.42e-4,0.004758 -0.00114,0.008294 -0.001064,0.004734 -0.00174,0.006084 -0.002556,0.005114 -6.14e-4,-7.28e-4 -9.72e-4,-4.78e-4 -8e-4,5.6399e-4 a 0.001794,0.00179399 0 0 1 -0.001608,0.002084 c -0.001056,1.1e-4 -0.003664,0.0018 -0.005796,0.003754 l -0.003874,0.00355 0.001232,0.004908 c 6.8e-4,0.0027 0.001464,0.004908 0.001744,0.004908 2.8e-4,0 0.001364,0.001512 0.002406,0.00336 0.001816,0.003224 0.00186,0.00356 0.001056,0.00815 -5.3e-4,0.003016 -6.34e-4,0.00526 -2.86e-4,0.00607 3.06e-4,7.06e-4 3.96e-4,0.002246 2.02e-4,0.003424 -2.28e-4,0.00138 -5.26e-4,0.001148 -8.38e-4,-6.6e-4 -3.12e-4,-0.001816 -6.28e-4,-0.00206 -8.96e-4,-6.94e-4 -2.28e-4,0.00116 -0.00116,0.00345 -0.00207,0.005094 l -0.001656,0.002986 0.001746,7e-4 c 0.001892,7.6e-4 0.002904,0.006058 0.001532,0.008026 -4.62e-4,6.6399e-4 -9.8e-4,6.9399e-4 -0.001148,6.799e-5 -1.68e-4,-6.2799e-4 -0.001132,5.96e-4 -0.002142,0.002718 -0.00101,0.002122 -0.002782,0.00452 -0.003936,0.00533 -0.001154,8.1e-4 -0.002432,0.002722 -0.00284,0.004252 -9.7e-4,0.00362 -0.00135,0.00354 -0.00648,-0.001364 -0.004602,-0.004404 -0.0059,-0.003992 -0.00779,0.002462 -0.001064,0.00364 -0.001494,0.00382 -0.00438,0.001828 -0.003784,-0.002616 -0.010912,-0.00295 -0.010506,-4.94e-4 1.56e-4,9.44e-4 0.002096,0.002872 0.004312,0.004284 0.005644,0.0036 0.00628,0.004372 0.005352,0.006512 -0.001606,0.003712 0.005586,0.012474 0.012616,0.01537 0.0019,7.8e-4 0.003572,0.00215 0.00372,0.00304 1.46e-4,8.9e-4 -2.54e-4,0.004094 -8.92e-4,0.00712 -0.001068,0.005084 -0.00109,0.005784 -2.68e-4,0.00918 4.9e-4,0.00202 8.9e-4,0.00418 8.9e-4,0.004802 0,0.002552 -0.00935,-0.00112 -0.012422,-0.004878 -0.00442,-0.005402 -0.013368,-0.009346 -0.014194,-0.006256 -4.38e-4,0.001636 -4.42e-4,0.00304 -1.4e-5,0.004966 5.76e-4,0.0026 0.00146,0.003714 0.008824,0.01109 0.002296,0.0023 0.00345,0.004496 0.00345,0.006566 0,0.002614 -6.4999e-4,0.002888 -0.004096,0.00172 -0.002624,-8.88e-4 -0.004222,-6.2e-4 -0.004448,7.48e-4 -1.94e-4,0.001174 -5e-5,0.002136 3.24e-4,0.002136 3.72e-4,0 5.48e-4,4.8e-4 3.9e-4,0.001068 -3.7e-4,0.001384 -0.008188,-0.004752 -0.008966,-0.007036 -6.12e-4,-0.001796 -0.008674,-0.00644 -0.010394,-0.005984 -5.3e-4,1.4e-4 -9.66e-4,0.001418 -9.66e-4,0.002838 0,0.00142 -1.28e-4,0.003836 -2.84e-4,0.005366 -2.72e-4,0.002644 0.00647,0.007988 0.018088,0.01434 0.001746,9.52e-4 0.004074,0.00314 0.005174,0.00486 0.0011,0.001718 0.00278,0.00313 0.003728,0.003136 0.0025,1.8e-5 0.006092,0.004104 0.005512,0.006274 -2.7e-4,0.001004 -0.003632,0.002092 -0.007472,0.002414 -0.00384,3.24e-4 -0.00694,7.46e-4 -0.00689,9.4e-4 5e-5,1.92e-4 0.00132,0.002152 0.00282,0.004356 0.002612,0.003838 0.002674,0.004112 0.00148,0.0065 -9.34e-4,0.001872 -0.001934,0.001944 -0.004,2.88e-4 -0.00336,-0.002694 -0.0038,-0.00273 -0.0038,-3.06e-4 0,0.001044 7.4e-4,0.00251 0.001648,0.003254 0.00302,0.00248 0.002366,0.005166 -0.001256,0.005166 -0.002106,0 -0.004238,0.00131 -0.005274,0.00324 -0.001046,0.001954 -0.00222,0.002664 -0.002958,0.00179 -0.001574,-0.001868 -0.027496,-0.00258 -0.027192,-7.48e-4 6.4e-4,0.003886 -0.0066,0.003866 -0.01278,-3.8e-5 -0.012516,-0.007898 -0.023164,-0.012388 -0.030032,-0.012662 -0.003796,-1.52e-4 -0.00951,-0.001552 -0.0127,-0.003112 -0.003186,-0.00156 -0.008866,-0.003794 -0.01262,-0.004966 -0.003754,-0.00117 -0.006984,-0.002484 -0.007178,-0.00292 -1.94e-4,-4.38e-4 -0.002214,-0.001388 -0.004492,-0.002112 -0.002278,-7.26e-4 -0.00501,-0.001712 -0.006074,-0.002192 -0.002212,-9.9999e-4 -0.012284,-0.0031 -0.019598,-0.004086 -0.00529,-7.12e-4 -0.01333,-0.002612 -0.01877,-0.004432 -0.001822,-6.1e-4 -0.008904,-0.001506 -0.015736,-0.00199 -0.006832,-4.8399e-4 -0.014906,-0.00164 -0.01794,-0.002572 -0.00304,-9.3e-4 -0.011436,-0.0016 -0.018664,-0.001486 -0.01093,1.7e-4 -0.013228,7.1599e-4 -0.013664,0.003248 -4.44e-4,0.00258 -5.98e-4,0.002606 -0.00102,1.6e-4 -3.3e-4,-0.001916 -0.00104,-0.00247 -0.002126,-0.00166 -9e-4,6.74e-4 -0.001904,2.08e-4 -0.002236,-0.001038 -3.32e-4,-0.001242 -0.001424,-0.002454 -0.002428,-0.002694 -0.001,-2.4e-4 -0.00232,-9.86e-4 -0.002926,-0.001658 -0.003308,-0.003662 -0.006756,-0.004728 -0.008144,-0.002516 -0.001116,0.001774 -0.003898,-3.22e-4 -0.003514,-0.002648 1.42e-4,-8.56e-4 -0.00252,-0.001808 -0.005912,-0.002112 -0.009138,-8.22e-4 -0.010086,-0.001198 -0.01112,-0.004406 -8.4e-4,-0.00261 -9.24e-4,-0.00249 -7.58e-4,0.001084 1.28e-4,0.002722 -8e-5,0.00378 -6.4e-4,0.003284 -4.56e-4,-4.04e-4 -0.001388,-7.52e-4 -0.00207,-7.74e-4 -6.84e-4,-2.4e-5 -0.001244,-7.54e-4 -0.001244,-0.001626 0,-8.7199e-4 -4e-4,-0.00216 -8.88e-4,-0.002862 -6.46e-4,-9.2799e-4 -8e-4,-3.94e-4 -5.6e-4,0.00195 2.3e-4,0.002224 1.2e-4,0.00272 -3.52e-4,0.001602 -3.76e-4,-8.94e-4 -0.003044,-0.001916 -0.005928,-0.002272 -0.006066,-7.5e-4 -0.005164,-7.4e-4 -0.033678,-3.64e-4 -0.011994,1.58e-4 -0.023342,-3.84e-4 -0.02522,-0.001204 -0.001878,-8.1999e-4 -0.00383,-8.8999e-4 -0.00434,-1.6e-4 -9.4e-4,0.00135 -0.018976,0.004012 -0.024518,0.00362 -0.004332,-3.06e-4 -0.009248,0.009218 -0.006514,0.012624 4.08e-4,5.08e-4 1.26e-4,0.00197 -6.9e-4,0.003578 -7.5799e-4,0.001496 -0.001516,0.003504 -0.001686,0.00446 -6.08e-4,0.003464 -0.005296,0.005398 -0.006628,0.002734 -8.4e-4,-0.00168 -0.00306,-0.00228 -0.006556,-0.00178 -0.00291,4.2e-4 -0.012462,0.001252 -0.02123,0.001852 -0.008768,5.9999e-4 -0.016546,0.001654 -0.017284,0.002346 -7.36e-4,6.92e-4 -0.00387,0.0014 -0.006964,0.001574 -0.003094,1.7399e-4 -0.00612,0.00136 -0.006728,0.002636 -0.001086,0.002284 -0.006206,0.006032 -0.007064,0.00517 -0.001056,-0.00106 -0.003018,-7.92e-4 -0.004806,6.54e-4 -0.001062,8.6e-4 -0.00243,0.001408 -0.003036,0.001218 -6.08e-4,-1.9e-4 -0.0016,-5.1e-4 -0.002208,-7.1e-4 -6.08e-4,-2e-4 -0.003466,7.26e-4 -0.00635,0.00206 -0.002884,0.001332 -0.00632,0.002404 -0.007632,0.00238 -0.001312,-2e-5 -0.00268,8.3599e-4 -0.003036,0.00191 -3.58e-4,0.001076 -0.001456,0.0026 -0.002444,0.003388 -0.001468,0.001174 -0.001794,8e-4 -0.001794,-0.00206 0,-0.003092 -4.56e-4,-0.003324 -0.004002,-0.00203 -0.002202,8.04e-4 -0.00798,0.001674 -0.012836,0.001936 -0.00486,2.6e-4 -0.010172,0.001566 -0.011808,0.0029 -0.001636,0.001334 -0.003094,0.002426 -0.00324,0.002426 -1.5e-4,0 4e-5,0.001392 4.1799e-4,0.003096 3.78e-4,0.001702 0.001526,0.003096 0.002552,0.003096 0.001024,0 0.001864,3.88e-4 0.001864,8.62e-4 0,4.76e-4 1.5e-4,0.002334 3.36e-4,0.00413 4.6e-4,0.004476 -0.001096,0.004256 -0.002322,-3.3e-4 -6.96e-4,-0.0026 -0.001026,-0.00288 -0.001192,-0.00102 -1.26e-4,0.001416 -4.56e-4,0.00205 -7.34e-4,0.001408 -2.78e-4,-6.4e-4 -5.04e-4,-4.14e-4 -5.04e-4,5.04e-4 0,9.2e-4 -5.5999e-4,0.001784 -0.001242,0.001924 -6.84e-4,1.4e-4 -0.004472,0.001304 -0.00842,0.002588 -0.003948,0.001286 -0.011898,0.002708 -0.017666,0.003164 -0.00577,4.54e-4 -0.011112,0.001336 -0.01187,0.001958 -0.002572,0.00211 -0.016714,0.007 -0.024292,0.0084 -0.01404,0.002598 -0.024688,0.010106 -0.026882,0.0189619 -6.88e-4,0.00277 -0.001898,0.005008 -0.002766,0.005112 -0.007144,8.68e-4 -0.017066,0.00365 -0.02142,0.006006 -0.00182,9.86e-4 -0.005018,0.002362 -0.0071,0.00306 -0.002086,6.94e-4 -0.00504,0.002148 -0.006564,0.00323 -0.001526,0.00108 -0.002862,0.001966 -0.002974,0.001966 -1.1e-4,0 -2e-4,-0.002378 -2.0012e-4,-0.005281 z m 0,-0.0187799 c 0,-0.002258 -2.44e-4,-0.002426 -0.001084,-7.48e-4 -6.46e-4,0.001296 -9.54e-4,0.003424 -7.64e-4,0.005286 2.48e-4,0.002402 4.96e-4,0.002574 0.001084,7.48e-4 4.2e-4,-0.001304 7.64e-4,-0.003682 7.64e-4,-0.005286 z m 0.0930259,-0.0307 c 0.00216,-0.002896 0.002106,-0.00294 -0.002484,-0.001932 -0.01051,0.002304 -0.013802,0.00347 -0.013802,0.004894 0,0.002692 0.013962,1.52e-4 0.016286,-0.002962 z M 0.09745619,0.92031178 a 8.2199934e-4,8.2199765e-4 0 0 0 -8.28e-4,-8.1e-4 c -4.56e-4,0 -8.28e-4,9e-4 -8.28e-4,0.002 0,0.0011 3.72e-4,0.001464 8.28e-4,8.1e-4 4.56e-4,-6.54e-4 8.28e-4,-0.001552 8.28e-4,-0.002 z m 0.04002597,-0.007002 c 1.92e-4,-0.001166 -2.6e-4,-0.002064 -0.00104,-0.002064 -7.6e-4,0 -0.001532,9.28e-4 -0.00172,0.002064 -1.94e-4,0.001166 2.6e-4,0.002064 0.00104,0.002064 7.58e-4,0 0.001532,-9.3e-4 0.00172,-0.002064 z m -0.026224,-0.010562 c 0,-0.00213 -6.6e-4,-0.00233 -0.001888,-5.7e-4 -0.001384,0.001986 -0.001012,0.004466 5.08e-4,0.00338 7.6e-4,-5.42e-4 0.00138,-0.001808 0.00138,-0.00281 z m 0.31238775,-3.36e-4 c 0.001884,-0.001962 0.002142,-0.002716 0.001214,-0.00354 -0.001474,-0.001304 -0.01904,0.001456 -0.028452,0.00447 -0.002558,8.2e-4 0.017198,0.00282 0.021256,0.002152 0.001974,-3.24e-4 0.004666,-0.001712 0.00598,-0.003082 z m 0.019138,-0.006352 c 6.06e-4,-5.3799e-4 -5.12e-4,-8.9999e-4 -0.002486,-8.0999e-4 -0.00471,2.2e-4 -0.006574,0.002118 -0.003588,0.00365 0.001214,6.22e-4 0.00283,4.6e-4 0.003588,-3.66e-4 7.6e-4,-8.24e-4 0.001878,-0.001938 0.002486,-0.002474 z m -0.35232171,6.06e-4 c 0.001136,-1.24e-4 0.00113,-2.32e-4 -1.48e-4,-0.00214 -0.001504,-0.002248 -0.002796,-0.001128 -0.002796,0.002426 0,0.001296 3.92e-4,0.001788 8.74e-4,0.001098 4.8e-4,-6.9e-4 0.001412,-0.001312 0.00207,-0.001384 z m 0.36061371,-0.002324 c 1.48e-4,-5.52e-4 -8.34e-4,-0.00116 -0.002184,-0.00135 -0.001348,-1.92e-4 -0.002624,6.92e-4 -0.002832,0.00196 -2.68e-4,0.001622 3.8e-4,0.002024 0.002182,0.00135 0.001412,-5.2599e-4 0.002688,-0.001408 0.002834,-0.00196 z m 0.43171565,-0.0243799 c 2.12e-4,-0.001278 -6.7e-4,-0.00197 -0.002314,-0.00182 -0.002356,2.18e-4 -0.002468,4.2e-4 -0.001,0.00182 0.002418,0.0023 0.002934,0.0023 0.003314,0 z M 0.03186224,0.83965401 c -4e-4,-5.96e-4 -8.7599e-4,-5.22e-4 -0.00106,1.64e-4 -1.82e-4,6.8599e-4 1.44e-4,0.001174 7.2599e-4,0.001084 6.44e-4,-10e-5 7.74e-4,-5.88e-4 3.3401e-4,-0.001248 z m 0.00652,-0.002718 c 0.00111,-0.001328 0.001164,-0.001664 2.78e-4,-0.001712 -6.08e-4,-3.4e-5 -0.001602,7.36e-4 -0.00221,0.001712 -0.001406,0.00226 4.6e-5,0.00226 0.001934,0 z m 0.00741,8.12e-4 c 4.8e-4,-6.9e-4 8.72e-4,-0.002924 8.72e-4,-0.004964 0,-0.003162 -3.46e-4,-0.003548 -0.002346,-0.002614 -0.002536,0.001184 -0.004192,0.004702 -0.002214,0.004702 6.5e-4,0 0.001336,9.3e-4 0.001524,0.002064 3.92e-4,0.002376 9.32e-4,0.00258 0.002164,8.12e-4 z m 0.89689928,-0.0359019 c 0,-0.001136 -4.34e-4,-0.00205 -9.64e-4,-0.002032 -8.26e-4,2.6e-5 -8.46e-4,3.22e-4 -1.4e-4,0.002032 0.001042,0.002518 0.001106,0.002518 0.001106,0 z m -0.8832233,-0.0839758 c -4e-4,-5.96e-4 -8.76e-4,-5.22e-4 -0.00106,1.6399e-4 -1.82e-4,6.86e-4 1.44e-4,0.001174 7.26e-4,0.001084 6.44e-4,-10e-5 7.74e-4,-5.88e-4 3.34e-4,-0.001248 z m -0.00452,-0.00602 c 0,-0.003366 -0.001536,-0.003744 -0.003554,-8.7799e-4 -0.002196,0.003122 -0.001674,0.00532 0.00107,0.004504 0.001666,-4.96e-4 0.002484,-0.00169 0.002484,-0.003628 z m 0.01025399,0.001488 c -5.52e-4,-0.003336 -0.002526,-0.004186 -0.002526,-0.001088 0,0.001738 5.68e-4,0.002902 0.001414,0.002902 7.76e-4,0 0.001278,-8.1599e-4 0.001112,-0.001814 z m -0.00418,-0.002314 c 6.14e-4,-0.001488 2.96e-4,-0.002008 -0.001244,-0.002032 a 0.002036,0.00203599 0 0 0 -0.00207,0.002032 c 0,0.00263 0.002224,0.00263 0.003312,0 z m 0.92031727,-0.2415033 c 0,-0.001136 -2.5e-4,-0.002064 -5.54e-4,-0.002064 -3.02e-4,0 -5.52e-4,9.28e-4 -5.52e-4,0.002064 0,0.001136 2.5e-4,0.002064 5.52e-4,0.002064 3.04e-4,0 5.52e-4,-9.28e-4 5.52e-4,-0.002064 z m 0.004,-0.0385199 c -3.78e-4,-5.74e-4 -0.001,-5.74e-4 -0.00138,0 -3.8e-4,5.7e-4 -6.8e-5,0.00104 6.92e-4,0.00104 7.6e-4,0 0.00107,-4.6999e-4 6.9e-4,-0.00104 z m -0.00828,-0.0248599 c -6.82e-4,-4.94e-4 -0.0018,-4.94e-4 -0.002484,0 -6.84e-4,4.9e-4 -1.24e-4,8.94e-4 0.001242,8.94e-4 0.001366,0 0.001926,-4.04e-4 0.001242,-8.96e-4 z m -0.025534,-0.0166639 c 0,-8.84e-4 -0.00205,-0.002708 -0.004554,-0.004054 -0.002506,-0.001348 -0.005152,-0.003092 -0.005884,-0.00388 -8.24e-4,-8.88e-4 -0.0012,-6.56e-4 -9.9e-4,6.12e-4 1.86e-4,0.001124 0.002522,0.003224 0.005192,0.004668 0.002672,0.001444 0.005104,0.00292 0.005408,0.00328 l 6.9e-4,8.18e-4 c 7.6e-5,9e-5 1.4e-4,-5.6e-4 1.4e-4,-0.001444 z m -0.80520933,-0.019036 c 9.86e-4,-0.001584 9.44e-4,-0.00179 -3.82e-4,-0.00192 -8.18e-4,-8e-5 -0.00164,7.8399e-4 -0.001828,0.00192 -4.06e-4,0.002464 6.76e-4,0.002464 0.00221,0 z m 0.80134536,-0.012094 c 0,-9.76e-4 -0.002494,-0.002832 -0.00554,-0.004128 -0.006864,-0.00292 -0.008098,-0.002978 -0.007396,-3.54e-4 2.96e-4,0.0011 0.003264,0.00283 0.006598,0.00384 0.003334,0.00101 0.006124,0.001968 0.0062,0.002126 7.6e-5,1.6e-4 1.38e-4,-5.1e-4 1.38e-4,-0.001484 z M 0.11534218,0.33362946 c 1.56e-4,-9.46e-4 -7.8e-5,-0.00172 -5.2e-4,-0.00172 -4.42e-4,0 -8.04e-4,7.74e-4 -8.04e-4,0.00172 0,9.4599e-4 2.34e-4,0.00172 5.2e-4,0.00172 2.86e-4,0 6.48e-4,-7.74e-4 8.04e-4,-0.00172 z m 0.0738619,-0.003818 c -3.98e-4,-5.96e-4 -8.74e-4,-5.22e-4 -0.001058,1.64e-4 -1.82e-4,6.8599e-4 1.44e-4,0.001174 7.26e-4,0.001084 6.42e-4,-1e-4 7.74e-4,-5.8799e-4 3.32e-4,-0.001248 z m -0.0856759,-0.002718 c 0,-0.001134 -4.34e-4,-0.00205 -9.66e-4,-0.002032 -8.26e-4,2.6e-5 -8.46e-4,3.22e-4 -1.38e-4,0.002032 0.001042,0.002518 0.001104,0.002518 0.001104,0 z m 0.00221,-0.003218 c 0,-4.9999e-4 -2.4999e-4,-9.0999e-4 -5.5399e-4,-9.0999e-4 -3.02e-4,0 -5.52e-4,9.8399e-4 -5.52e-4,0.002186 0,0.001202 2.5e-4,0.001612 5.52e-4,9.1e-4 3.04e-4,-7e-4 5.5399e-4,-0.001686 5.5399e-4,-0.002186 z m 0.031468,-0.0143719 c 7.6e-4,-7.94e-4 0.00312,-0.002218 0.005244,-0.003164 0.002126,-9.4799e-4 0.004734,-0.002706 0.005798,-0.003906 0.001062,-0.0012 0.001932,-0.002104 0.001932,-0.002008 0,7.9e-4 0.00378,-0.003626 0.00566,-0.00661 0.002846,-0.004528 0.003026,-0.008004 4.12e-4,-0.008004 -0.001348,0 -0.001932,-9.3399e-4 -0.001932,-0.003096 0,-0.003174 0.002472,-0.00443 0.006572,-0.00334 0.001788,4.76e-4 0.006886,-0.00308 0.008052,-0.005618 8.72e-4,-0.001894 -0.001914,-0.00403 -0.005548,-0.004254 -0.002364,-1.46e-4 -0.00438,0.001308 -0.006868,0.004954 -0.001936,0.00284 -0.004034,0.005176 -0.00466,0.005192 -9.04e-4,2.6e-5 -9.74e-4,4.2e-4 -3.34e-4,0.001928 4.4e-4,0.001044 0.001,0.00144 0.00124,8.7999e-4 2.44e-4,-5.6e-4 4.42e-4,-1.4e-4 4.42e-4,9.34e-4 0,0.001848 -0.001208,0.003152 -0.005426,0.005858 -0.00101,6.48e-4 -0.00197,0.002486 -0.002134,0.004084 -2.44e-4,0.002374 -4.56e-4,0.002424 -0.001164,2.7e-4 -7.28e-4,-0.002204 -8.16e-4,-0.002104 -5.42e-4,6.2e-4 2.36e-4,0.00238 6e-6,0.00316 -8.64e-4,0.002908 -6.5199e-4,-1.9e-4 -0.002012,4.98e-4 -0.00302,0.001528 -0.00127,0.0013 -0.002074,0.00112 -0.00262,-5.8e-4 -4.46e-4,-0.00138 -7.92e-4,-0.001562 -7.92e-4,-4.16e-4 0,0.002276 -0.004206,0.00879 -0.005678,0.00879 -5.1999e-4,0 -9.4799e-4,0.001268 -9.4799e-4,0.00282 0,0.002102 7.3999e-4,0.002676 0.0029,0.002248 0.001594,-3.16e-4 0.00352,-0.001224 0.004278,-0.002018 z m 0.02271,-0.0748578 c -3.98e-4,-4.96e-4 -9.86e-4,8e-5 -0.001306,0.00128 -4.1e-4,0.00153 -1.96e-4,0.0018 7.24e-4,8.9999e-4 7.46e-4,-7.2799e-4 9.96e-4,-0.001664 5.82e-4,-0.00218 z m 0.66683947,-0.00818 c 0,-8.4999e-4 -7.56e-4,-0.001744 -0.001678,-0.001986 -9.58e-4,-2.5e-4 -0.001534,4.12e-4 -0.001346,0.001548 3.86e-4,0.00233 0.003024,0.002712 0.003022,4.4e-4 z m -0.66525947,-0.013002 0.00138,-1.6e-4 -0.001286,-0.001928 c -0.001456,-0.00218 -0.003958,-5e-4 -0.003958,0.002656 0,0.00115 5.56e-4,0.001524 0.001242,8.36e-4 6.84e-4,-6.86e-4 0.001864,-0.001316 0.002624,-0.001404 z m 0.66084347,4.46e-4 c 0,-9.46e-4 -3.74e-4,-0.00172 -8.28e-4,-0.00172 -4.56e-4,0 -8.28e-4,7.74e-4 -8.28e-4,0.00172 0,9.46e-4 3.72e-4,0.00172 8.28e-4,0.00172 4.54e-4,0 8.28e-4,-7.7399e-4 8.28e-4,-0.00172 z m 0.002312,3.1e-4 c -4e-4,-5.96e-4 -8.76e-4,-5.22e-4 -0.00106,1.64e-4 -1.82e-4,6.86e-4 1.44e-4,0.001174 7.26e-4,0.001084 6.42e-4,-1e-4 7.74e-4,-5.8799e-4 3.34e-4,-0.001248 z m -0.65366548,-0.004128 c -4e-4,-5.96e-4 -8.74e-4,-5.22e-4 -0.001058,1.64e-4 -1.84e-4,6.84e-4 1.42e-4,0.001172 7.24e-4,0.001084 6.44e-4,-10e-5 7.74e-4,-5.88e-4 3.34e-4,-0.001248 z m -0.0619359,-0.002718 c 0,-0.001136 -2.48e-4,-0.002064 -5.52e-4,-0.002064 -3.04e-4,0 -5.52e-4,9.28e-4 -5.52e-4,0.002064 0,0.001136 2.48e-4,0.002064 5.52e-4,0.002064 3.04e-4,0 5.52e-4,-9.28e-4 5.52e-4,-0.002064 z m 0.014354,-0.005412 c 0,-0.002314 -0.001038,-2.86e-4 -0.001334,0.002608 -2.9e-4,0.002832 -2.1e-4,0.00296 4.92e-4,7.7999e-4 4.64e-4,-0.001436 8.4e-4,-0.00296 8.4e-4,-0.003388 z m 0.00552,2.52e-4 c 0,-0.001702 -9.8e-5,-0.003096 -2.16e-4,-0.003096 -1.2e-4,0 -3.6e-4,0.001394 -5.36e-4,0.003096 -1.74e-4,0.001702 -7.6e-5,0.003096 2.18e-4,0.003096 2.94e-4,0 5.34e-4,-0.001394 5.34e-4,-0.003096 z m 0.72277932,-0.0705597 c -4e-4,-5.9599e-4 -8.76e-4,-5.1999e-4 -0.00106,1.64e-4 -1.82e-4,6.86e-4 1.44e-4,0.001174 7.26e-4,0.001084 6.42e-4,-10e-5 7.74e-4,-5.88e-4 3.34e-4,-0.001246 z M 0.34338399,0.08986215 c 1.76e-4,-0.001056 9.2e-5,-0.002444 -1.84e-4,-0.003084 -2.78e-4,-6.4e-4 -6.46e-4,2.26e-4 -8.2e-4,0.00192 -3.4399e-4,0.003346 4.86e-4,0.004306 0.001004,0.001164 z M 0.0813362,0.97574962 c -3.18e-4,-0.00142 -3.6e-4,-0.002608 -9.6e-5,-0.002644 2.64e-4,-3.4e-5 1.6e-5,-0.001072 -5.4999e-4,-0.002304 -0.001458,-0.003164 -1.68e-4,-0.005878 0.002688,-0.005652 0.00149,1.18e-4 0.00185,6.1e-4 0.001104,0.001516 -0.001052,0.00128 -0.001836,0.006534 -9.66e-4,0.006476 0.00162,-1.12e-4 0.002792,-0.00281 0.00254,-0.005852 -2.48e-4,-0.002972 -1.8e-4,-0.002946 5.12e-4,2.02e-4 9.42e-4,0.00428 0.005144,0.005068 0.005508,0.001034 1.84e-4,-0.00205 4.08e-4,-0.002036 0.00109,6.8e-5 0.001074,0.003314 0.001708,0.01137 7.9599e-4,0.010112 -3.56e-4,-4.9e-4 -5.66e-4,-0.002144 -4.68e-4,-0.003672 1.26e-4,-0.001944 -1.28e-4,-0.00174 -8.4199e-4,6.76e-4 -8.16e-4,0.00276 -0.002,0.003374 -0.00588,0.00304 -0.003086,-2.68e-4 -0.00507,-0.00136 -0.005436,-0.003 z m -0.00758,-0.00455 c -7.34e-4,-0.0017 -3.8e-4,-0.006288 4.86e-4,-0.006288 6.3e-4,0 0.001182,0.00583 6.66e-4,0.007022 -2.28e-4,5.3e-4 -7.46e-4,2e-4 -0.00115,-7.34e-4 z m 0.01168199,-0.0166079 c -6.1e-4,-0.003656 -5.46e-4,-0.004164 5.58e-4,-0.004442 6.86e-4,-1.74e-4 0.00162,-4.66e-4 0.002074,-6.52e-4 0.001474,-6e-4 0.00175,0.001286 7.8e-4,0.0053 -0.001228,0.005068 -0.002544,0.00499 -0.003412,-2.06e-4 z m 0.33443373,-0.01536 c 0,-5e-4 2.48e-4,-0.001484 5.52e-4,-0.002186 3.04e-4,-7e-4 5.52e-4,-2.92e-4 5.52e-4,9.1199e-4 0,0.0012 -2.48e-4,0.002186 -5.52e-4,0.002186 -3.04e-4,0 -5.52e-4,-4.1e-4 -5.52e-4,-9.1199e-4 z m 0.006478,-0.00141 c 3.74e-4,-0.00141 8.2e-4,-0.002044 9.9e-4,-0.001408 1.7e-4,6.34e-4 -1.38e-4,0.00179 -6.8e-4,0.002564 -7.88e-4,0.00112 -8.52e-4,8.84e-4 -3.1e-4,-0.001156 z m -0.29631975,-0.005682 c 0,-9.98e-4 3.8e-4,-0.003 8.4999e-4,-0.004454 6.62e-4,-0.002054 0.001,-0.002092 0.001514,-1.68e-4 5.14e-4,0.001926 4.8e-4,0.00291 -1.6e-4,0.004422 -0.001042,0.00246 -0.002206,0.002566 -0.002206,2e-4 z m 0.62329949,-0.0188299 c 0,-0.001136 3.58e-4,-0.002064 7.94e-4,-0.002064 4.4e-4,0 9.5e-4,9.28e-4 0.001138,0.002064 1.88e-4,0.001134 -1.7e-4,0.002064 -7.96e-4,0.002064 -6.26e-4,0 -0.001136,-9.3e-4 -0.001136,-0.002064 z m -0.009386,-0.002194 c 0,-0.002348 0.001482,-0.002588 0.00186,-3e-4 1.5e-4,9e-4 -2.08e-4,0.001772 -7.94e-4,0.001936 -5.86e-4,1.66e-4 -0.001066,-5.7e-4 -0.001066,-0.001636 z m -0.54750356,-0.00333 c -4.96e-4,-0.00484 2.6e-4,-0.00555 0.00217,-0.002034 9.94e-4,0.001834 0.00156,0.003474 0.001258,0.003646 -0.002328,0.001308 -0.003168,9.1199e-4 -0.003428,-0.001612 z m 0.47738962,-0.004798 c 0,-0.001134 1.08e-4,-0.002064 2.42e-4,-0.002064 1.34e-4,0 3.98e-4,9.3e-4 5.86e-4,0.002064 1.88e-4,0.001136 7.8e-5,0.002064 -2.44e-4,0.002064 -3.2e-4,0 -5.84e-4,-9.28e-4 -5.84e-4,-0.002064 z m -0.00796,-0.001786 c -2.3799e-4,-0.00144 -2.8399e-4,-0.003174 -9.999e-5,-0.003856 4.9199e-4,-0.001846 0.001032,8.7e-4 7.6399e-4,0.003864 -1.86e-4,0.002098 -3.16e-4,0.002096 -6.64e-4,-1.2e-5 z m 0.002024,4.2e-4 c -0.00113,-0.001706 -7.8e-4,-0.00453 4.14e-4,-0.00336 6.08e-4,5.94e-4 0.001104,0.0019 0.001104,0.002904 0,0.001968 -4.3e-4,0.002096 -0.001518,4.56e-4 z m 0.26467779,-0.01033 c -6.36e-4,-0.002376 -4.02e-4,-0.004816 4.6e-4,-0.004816 4.56e-4,0 8.28e-4,0.001392 8.28e-4,0.003096 0,0.003222 -6.54e-4,0.004094 -0.001288,0.00172 z m -0.68515545,-0.012644 c -1.48e-4,-8.9999e-4 2.16e-4,-0.001772 8.08e-4,-0.00194 5.94e-4,-1.66e-4 9.34e-4,5.7e-4 7.58e-4,0.001636 -3.92e-4,0.002366 -0.0012,0.002522 -0.001566,3.04e-4 z m 0.004978,-0.003546 c 1.72e-4,-0.001692 7.24e-4,-0.003148 0.001222,-0.003234 5e-4,-8.8e-5 0.002026,-5.7799e-4 0.003392,-0.00109 0.001792,-6.7e-4 0.002252,-3.46e-4 0.001656,0.00116 -6.84e-4,0.001724 -6.92e-4,0.002094 -4.6e-5,0.00212 4.3e-4,2e-5 6.28e-4,6.12e-4 4.38e-4,0.00132 -1.9e-4,7.06e-4 -0.001836,0.001626 -0.00366,0.002042 -0.002764,6.3e-4 -0.003266,2.44e-4 -0.003002,-0.00232 z m -0.22530582,-0.004996 c -4.08e-4,-0.00247 6.32e-4,-0.00492 0.001366,-0.00322 6.18e-4,0.001428 2.12e-4,0.00514 -5.6e-4,0.00514 -2.68e-4,0 -6.3e-4,-8.6399e-4 -8.06e-4,-0.00192 z m 0.005222,-0.0536898 c 0,-0.001068 2.46e-4,-0.00251 5.46e-4,-0.003202 3.12e-4,-7.24e-4 4.06e-4,1.04e-4 2.16e-4,0.00194 -3.66e-4,0.003578 -7.6e-4,0.004232 -7.6e-4,0.001262 z m 0.004048,-0.001062 c 0,-0.001654 2.12e-4,-0.00252 4.7e-4,-0.001922 2.58e-4,5.96e-4 3.26e-4,0.001948 1.52e-4,0.003006 -4.48e-4,0.0027 -6.2e-4,0.002396 -6.2e-4,-0.001084 z M 0.99845347,0.54589485 c -4.56e-4,-0.0011 -5.8e-4,-0.002002 -2.76e-4,-0.002002 3.04e-4,0 9.24e-4,9e-4 0.00138,0.002 4.5603e-4,0.0011 5.8003e-4,0.002 2.76e-4,0.002 -3.04e-4,0 -9.24e-4,-9e-4 -0.00138,-0.002 z M 0.0913802,0.39270729 c 0,-8.94e-4 4.98e-4,-0.001626 0.001106,-0.001626 6.06e-4,0 0.001104,8.72e-4 0.001104,0.001936 0,0.001064 -4.9799e-4,0.001796 -0.001104,0.001624 -6.08e-4,-1.7e-4 -0.001106,-0.00104 -0.001106,-0.001934 z m 0.02311998,-0.0303139 -0.001294,-0.00392 0.003304,-0.00164 c 0.001816,-9e-4 0.004608,-0.001692 0.0062,-0.001756 0.001594,-6.6e-5 0.0029,-7.4999e-4 0.0029,-0.001522 0,-0.001608 0.003288,0.001594 0.0038,0.0037 1.88e-4,7.66e-4 -4.26e-4,8.5e-4 -0.001362,1.84e-4 -0.001048,-7.44e-4 -0.002322,1.6e-5 -0.003312,0.001972 -8.86e-4,0.00175 -0.002914,0.00327 -0.004508,0.00338 -0.001594,1.1e-4 -0.0029,9.48e-4 -0.0029,0.00186 0,0.00296 -0.0015,0.00176 -0.002828,-0.00226 z m -0.00696,6.08e-4 c 1.84e-4,-6.86e-4 6.6e-4,-7.6e-4 0.00106,-1.64e-4 4.4e-4,6.6e-4 3.08e-4,0.001148 -3.34e-4,0.001248 -5.82e-4,8.8e-5 -9.08e-4,-4e-4 -7.26e-4,-0.001084 z M 0.09749819,0.33448945 c -2.4e-5,-0.002176 1.06e-4,-0.003394 2.9e-4,-0.002708 1.82e-4,6.86e-4 2.02e-4,0.002466 4.2e-5,0.003956 -1.76e-4,0.001646 -3.06e-4,0.001156 -3.32e-4,-0.001248 z m 0.01507799,-0.017348 c 1.54e-4,-9.3399e-4 -2.28e-4,-0.003324 -8.5e-4,-0.005314 -0.001112,-0.003558 -0.001076,-0.003712 0.002286,-0.009656 0.004048,-0.00716 0.00442,-0.00732 0.00442,-0.001912 0,0.002544 -3.6999e-4,0.00414 -9.6599e-4,0.00416 -7.82e-4,2.6e-5 -8.18e-4,3.82e-4 -1.84e-4,0.00188 4.3e-4,0.001016 0.001236,0.00152 0.001794,0.00112 5.58e-4,-4.02e-4 0.001014,2.16e-4 0.001014,0.001374 0,0.001156 -8.08e-4,0.001938 -0.001794,0.001736 -9.8799e-4,-2.02e-4 -0.002292,-2.6e-4 -0.0029,-1.26e-4 -9.2e-4,2e-4 -9.66e-4,5.74e-4 -2.76e-4,0.00224 7.08e-4,0.001712 6.88e-4,0.002006 -1.38e-4,0.002034 -5.3e-4,1.8e-5 -9.66e-4,9.6e-4 -9.66e-4,0.002096 0,0.001136 -3.86e-4,0.002064 -8.6e-4,0.002064 -4.74e-4,0 -7.34e-4,-7.64e-4 -5.8001e-4,-0.001696 z m 0.00706,-0.016656 c -5.56e-4,-0.001284 -5.34e-4,-0.002144 1e-4,-0.00412 8.96e-4,-0.002772 0.001608,-3.52e-4 9.92e-4,0.003368 -2.2e-4,0.001326 -7e-4,0.001658 -0.001094,7.52e-4 z m -0.019146,-0.0869197 c -1.88e-4,-0.001136 3.2e-5,-0.002064 4.88e-4,-0.002064 4.54e-4,0 9.8e-4,9.2799e-4 0.001168,0.002064 1.88e-4,0.001136 -3e-5,0.002064 -4.86e-4,0.002064 -4.56e-4,0 -9.82e-4,-9.2799e-4 -0.00117,-0.002064 z m 0.011308,-0.00924 c 7.94e-4,-0.003582 0.001114,-0.003956 0.001114,-0.0013 0,0.001012 -4.08e-4,0.002428 -9.06e-4,0.003142 -7.58e-4,0.001088 -7.92e-4,7.86e-4 -2.08e-4,-0.001844 z m 0.78025737,-0.0333119 c 6.46e-4,-0.00242 0.00418,-0.004316 0.004894,-0.002626 4.1e-4,9.72e-4 5e-4,0.001874 1.94e-4,0.002004 -0.00297,0.001274 -0.00534,0.001564 -0.005088,6.22e-4 z m -0.003352,-0.002546 c -4.6e-4,-0.001088 -1.16e-4,-0.001688 9.66e-4,-0.001688 9.2199e-4,0 0.001678,7.74e-4 0.001678,0.00172 0,0.002196 -0.001714,0.002176 -0.002644,-3.4e-5 z m -0.014194,-0.002636 c -0.002364,-0.001978 -0.0081,-0.003396 -0.01882,-0.004654 -0.003674,-4.3e-4 -0.007208,-0.001846 -0.007858,-0.003144 -7.06e-4,-0.001414 -0.00318,-0.002132 -0.00616,-0.001792 -0.00274,3.14e-4 -0.00498,3.6e-4 -0.00498,1e-4 0,-7.1599e-4 0.003212,-0.006766 0.003594,-0.006766 1.84e-4,0 1.8e-4,9.3e-4 -4e-6,0.002064 -1.9999e-4,0.001204 3.6e-4,0.002064 0.001346,0.002064 9.3e-4,0 0.00169,-9.28e-4 0.00169,-0.002064 0,-0.001134 2.62e-4,-0.002064 5.84e-4,-0.002064 3.22e-4,0 4e-4,0.00116 1.7e-4,0.00258 -3.06e-4,0.001908 -2.06e-4,0.002036 3.8e-4,4.8999e-4 4.4e-4,-0.00115 0.001294,-0.001984 0.001902,-0.001852 6.08e-4,1.28e-4 0.002424,3.74e-4 0.004036,5.4e-4 0.001612,1.7e-4 0.002814,0.001004 0.002674,0.001854 -1.4e-4,8.52e-4 0.001272,0.001412 0.00314,0.001246 0.00187,-1.68e-4 0.003834,5.64e-4 0.004366,0.00162 6.44e-4,0.001286 9.66e-4,0.001174 9.66e-4,-3.3399e-4 0,-0.001242 3e-4,-0.001564 6.68e-4,-7.16e-4 3.66e-4,8.48e-4 0.002788,0.001796 0.005382,0.002104 0.002594,3.1e-4 0.004716,6.74e-4 0.004716,8.08e-4 0,1.36e-4 0.001242,5.46e-4 0.00276,9.14e-4 0.00152,3.68e-4 0.002822,8.7399e-4 0.0029,0.001126 1.28e-4,4.3e-4 6.98e-4,0.001054 0.001654,0.00182 4.4e-4,3.52e-4 -0.002152,0.006348 -0.00262,0.006068 -1.54e-4,-9.2e-5 -0.001272,-9.96e-4 -0.002486,-0.002012 z m -0.02236,-0.01032 c 0,-9.92e-4 -4.82e-4,-0.001806 -0.001072,-0.001806 -5.9e-4,0 -0.001206,8.14e-4 -0.00137,0.001806 -1.64e-4,9.9399e-4 3.2e-4,0.001806 0.001072,0.001806 7.54e-4,0 0.00137,-8.12e-4 0.00137,-0.001806 z m 0.02953,-0.005968 c -1.9e-4,-0.001154 -5.78e-4,-0.001564 -8.6e-4,-9.0999e-4 -2.82e-4,6.5399e-4 -5.14e-4,2.6e-4 -5.14e-4,-8.76e-4 0,-0.003996 8.9e-4,-0.00406 0.00155,-1.1e-4 3.68e-4,0.002198 5.56e-4,0.003994 4.2e-4,0.003994 -1.36e-4,0 -4.04e-4,-9.44e-4 -5.96e-4,-0.002096 z m -0.011818,-0.00805 c -5.8e-4,-0.002176 -0.001172,-0.003526 -0.001314,-0.003 -1.4e-4,5.28e-4 -0.001466,0.001268 -0.002944,0.00165 -0.00256,6.6e-4 -0.005738,-0.003594 -0.004932,-0.006604 6.44e-4,-0.00241 0.004458,-0.001846 0.00529,7.82e-4 8.52e-4,0.002692 0.002406,0.00278 0.003102,1.78e-4 5.86e-4,-0.00219 0.003402,-0.00171 0.003792,6.4599e-4 5.3e-4,0.003208 -2.58e-4,0.010302 -0.001144,0.010302 -4.36e-4,0 -0.001268,-0.00178 -0.00185,-0.003956 z m 0.0057,0.001572 c -2.36e-4,-0.00142 -1.54e-4,-0.001824 2.04e-4,-9.98e-4 3.3e-4,7.62e-4 7.56e-4,4.4e-4 9.46e-4,-7.1199e-4 5.14e-4,-0.003114 0.0012,-0.00254 0.0012,9.9799e-4 0,0.003636 -0.00178,0.004176 -0.002352,7.12e-4 z M 0.23010209,0.12769005 c 1.8399e-4,-6.86e-4 6.5999e-4,-7.6e-4 0.00106,-1.62e-4 4.4e-4,6.5799e-4 3.08e-4,0.001148 -3.34e-4,0.001246 -5.82e-4,9e-5 -9.0799e-4,-3.98e-4 -7.2399e-4,-0.001084 z M 0.341474,0.06907621 c 0,-0.001134 1.1e-4,-0.002064 2.44e-4,-0.002064 1.34e-4,0 3.96e-4,9.3e-4 5.8399e-4,0.002064 1.88e-4,0.001136 8e-5,0.002064 -2.4399e-4,0.002064 -3.2e-4,0 -5.84e-4,-9.28e-4 -5.84e-4,-0.002064 z m 0.012874,2.1e-4 c -8.92e-4,-0.002448 -6.8e-4,-0.005966 3.02e-4,-0.005008 6.2599e-4,6.12e-4 0.001028,-2.3e-4 0.001086,-0.002274 l 1.84e-4,-0.006344 c 5e-5,-0.001704 5.48e-4,-0.003212 0.001104,-0.003354 5.58e-4,-1.42e-4 0.0012,-4.5e-4 0.001426,-6.84e-4 2.28e-4,-2.36e-4 0.00116,-0.001176 0.00207,-0.002092 0.002026,-0.002036 0.003126,-0.005076 0.002722,-0.00752 -1.66e-4,-0.001008 1.88e-4,-0.001832 7.88e-4,-0.001832 6e-4,0 0.001218,0.00116 0.001372,0.00258 1.94e-4,0.001794 4.2e-4,0.001906 7.36e-4,3.66e-4 8.1e-4,-0.003936 0.0025,-0.005164 0.003392,-0.002464 7.16e-4,0.002166 7.84e-4,0.002112 3.92e-4,-3.16e-4 -3.6e-4,-0.002226 -2.4e-4,-0.0026 4.9e-4,-0.001548 5.32e-4,7.6e-4 0.0013,0.001382 0.00171,0.001382 4.1e-4,0 8.96e-4,0.001764 0.00108,0.003918 2.12e-4,0.002516 6.86e-4,0.003568 0.001324,0.002944 0.00141,-0.001378 0.001264,-0.00648 -1.58e-4,-0.005504 -0.00112,7.72e-4 -0.001122,7.44e-4 -4.6e-5,-0.00117 6.08e-4,-0.00108 0.001946,-0.002026 0.002974,-0.002106 0.001028,-8e-5 0.003388,-0.001056 0.005244,-0.00217 0.004814,-0.002886 0.016912,-0.007942 0.022424,-0.009372 0.00258,-6.7e-4 0.00581,-0.001586 0.007176,-0.002036 0.00288,-9.48e-4 0.008562,-0.002458 0.01601,-0.004252 0.002886,-6.9599e-4 0.00686,-0.001776 0.008834,-0.0024 0.004744,-0.001502 0.012212,-0.001614 0.01325,-2e-4 4.56e-4,6.22e-4 0.001884,0.001844 0.003174,0.002714 0.001292,8.72e-4 0.002348,0.00227 0.002348,0.003104 0,8.36e-4 -6.16e-4,0.003816 -0.001366,0.006622 -0.00111,0.004154 -0.00188,0.005066 -0.004142,0.0049 -0.001652,-1.22e-4 -0.002704,6.44e-4 -0.002592,0.001888 10e-5,0.001148 0.0021,0.002166 0.004436,0.00226 0.003672,1.46e-4 0.006116,0.002816 0.005282,0.005766 -3.72e-4,0.00132 -0.007692,0.002626 -0.00769,0.001372 0,-6.14e-4 -0.001676,-8.26e-4 -0.003728,-4.7e-4 -0.00205,3.54e-4 -0.005152,-2e-4 -0.006894,-0.001232 -0.001742,-0.001032 -0.003286,-0.001436 -0.00343,-8.9799e-4 -1.44e-4,5.3999e-4 -0.001326,0.001176 -0.002628,0.001416 -0.001304,2.4e-4 -0.003056,0.001152 -0.003894,0.00203 -9.08e-4,9.4799e-4 -0.00181,7.1399e-4 -0.002226,-5.8e-4 -3.86e-4,-0.001198 -0.003104,-0.00239 -0.00604,-0.002654 -0.003634,-3.24e-4 -0.005224,-0.001186 -0.004982,-0.0027 1.96e-4,-0.00122 -6.6e-5,-9.3999e-4 -5.82e-4,6.2e-4 -6.44e-4,0.001956 -0.001638,0.002424 -0.003184,0.0015 -0.001474,-8.8e-4 -0.002126,-6.16e-4 -0.001896,7.7199e-4 3.92e-4,0.002364 3.2e-4,0.00241 -0.004684,0.002912 -0.001974,1.98e-4 -0.004148,0.00109 -0.004832,0.001984 -6.82e-4,8.92e-4 -0.00124,8.64e-4 -0.00124,-6.4e-5 0,-9.28e-4 -6.22e-4,-0.001688 -0.00138,-0.001688 -7.6e-4,0 -0.00138,6.9599e-4 -0.00138,0.001548 0,8.52e-4 -3.9e-4,0.001548 -8.62e-4,0.001548 -4.74e-4,0 -7.32e-4,-7.84e-4 -5.72e-4,-0.001744 3.82e-4,-0.002318 -0.00529,0.001086 -0.0057,0.003422 -7.54e-4,0.004274 -0.011086,0.008606 -0.011086,0.004648 0,-0.001208 -2.64e-4,-0.002198 -5.86e-4,-0.002198 -3.2e-4,0 -4.4e-4,8.6599e-4 -2.66e-4,0.001922 5e-4,0.00302 -7.7e-4,0.00488 -0.001592,0.002332 -5.52e-4,-0.00171 -8.1e-4,-0.001606 -0.001018,4.1e-4 -1.8e-4,0.00174 -0.001086,0.002536 -0.002548,0.00223 -0.00125,-2.6e-4 -0.002272,3.9399e-4 -0.002272,0.001454 0,0.003412 -0.002836,0.001172 -0.003388,-0.002678 -2.92e-4,-0.002046 -7.72e-4,-0.003166 -0.001066,-0.002488 -2.92e-4,6.7799e-4 -4.06e-4,0.002 -2.5e-4,0.002938 1.54e-4,9.3799e-4 -8.2e-5,0.002226 -5.26e-4,0.002862 -4.44e-4,6.38e-4 -9.56e-4,2.56e-4 -0.00114,-8.48e-4 -2e-4,-0.001224 -9.08e-4,-8.02e-4 -0.00181,0.001078 -0.0017,0.003538 -0.00698,0.007296 -0.007654,0.005446 z m -0.007906,-0.009376 c 0,-0.003626 3.32e-4,-0.00416 8.06e-4,-0.001296 1.74e-4,0.001056 6.4e-5,0.002504 -2.44e-4,0.003218 -3.08e-4,7.12e-4 -5.6e-4,-1.5e-4 -5.6e-4,-0.001922 z m 0.00208,-0.003566 c 3.22e-4,-0.0012 9.1e-4,-0.001774 0.001308,-0.001278 4.14e-4,5.16e-4 1.64e-4,0.00145 -5.82e-4,0.00218 -9.2e-4,9e-4 -0.001134,6.3e-4 -7.24e-4,-9e-4 z m 0.19018385,-0.005238 c -6.84e-4,-4.92e-4 -0.001242,-0.001602 -0.001242,-0.00247 0,-0.002526 0.0024,-0.001152 0.002636,0.00151 1.2e-4,0.00133 1.32e-4,0.002292 3.2e-5,0.002136 -10e-5,-1.54e-4 -7.44e-4,-6.84e-4 -0.001426,-0.001176 z m -0.011456,-0.004734 c 0.001518,-0.00244 0.00239,-0.00244 0.00138,0 -4.56e-4,0.0011 -0.0012,0.00195 -0.001656,0.001886 -5.46e-4,-7.4e-5 -4.52e-4,-7.1799e-4 2.76e-4,-0.001886 z m 0.003344,1.92e-4 c -1.7e-4,-0.00103 1.88e-4,-0.001386 7.96e-4,-7.92e-4 0.001288,0.00126 0.001452,0.002664 3.08e-4,0.002664 -4.36e-4,0 -9.34e-4,-8.4399e-4 -0.001104,-0.001872 z m -0.0710719,-0.004672 c -8.7e-4,-0.003266 -4.76e-4,-0.003492 0.006348,-0.003646 0.004,-9e-5 0.00783,-5.64e-4 0.008506,-0.001052 6.76e-4,-4.9e-4 0.00136,-1.1e-4 0.001518,8.4199e-4 a 0.001576,0.001576 0 0 0 0.001974,0.001294 c 0.002512,-6.5e-4 0.002524,0.002356 1.2e-5,0.00325 -0.001186,4.24e-4 -0.00204,5.6e-5 -0.001896,-8.16e-4 4.54e-4,-0.00274 -3.96e-4,-0.001724 -0.001262,0.00151 -9.4e-4,0.003512 -0.003272,0.004144 -0.003786,0.001028 -2.28e-4,-0.001376 -7.06e-4,-0.001344 -0.001424,9.4e-5 -0.001224,0.002446 -0.002374,7.84e-4 -0.001812,-0.002616 2.46e-4,-0.001486 1.1e-4,-0.001628 -4.04e-4,-4.2e-4 -4.28e-4,0.001 -0.002142,0.002314 -0.003812,0.002916 -0.00239,8.64e-4 -0.003234,3.56e-4 -0.003964,-0.002384 z m 0.044264,0.002414 c 0,-0.001134 1.1e-4,-0.002064 2.44e-4,-0.002064 1.3399e-4,0 3.9599e-4,9.3e-4 5.8399e-4,0.002064 1.88e-4,0.001136 8e-5,0.002064 -2.44e-4,0.002064 -3.2e-4,0 -5.8399e-4,-9.28e-4 -5.8399e-4,-0.002064 z m -0.022084,-0.004128 c -4.96e-4,-0.001202 -5.64e-4,-0.002014 -1.7e-4,-0.002032 3.6e-4,-2e-5 8.12e-4,8.96e-4 0.001,0.002032 4.22e-4,0.002564 2.32e-4,0.002564 -8.3e-4,0 z m 0.21563583,-2.8e-4 c 2.86e-4,-0.00173 -0.00182,-0.003762 -0.002244,-0.00217 -5.94e-4,0.00222 -0.003048,0.00264 -0.003048,5.2e-4 0,-0.001308 -0.00157,-0.001624 -0.00428,-8.6e-4 -0.00332,9.4e-4 -0.00471,4.56e-4 -0.00621,-0.002158 -0.001062,-0.001852 -0.001514,-0.002944 -0.001,-0.002422 5.1e-4,5.2e-4 0.001008,1.4e-4 0.001102,-8.48e-4 9.6e-5,-9.86e-4 5.48e-4,-0.002094 0.001002,-0.002464 4.56e-4,-3.68e-4 0.001012,-0.0016 0.001234,-0.002734 2.24e-4,-0.001136 4.1e-4,-2.999e-5 4.14e-4,0.002458 8e-6,0.004274 9.2e-5,0.00436 0.001528,0.001556 0.00136,-0.00266 0.001596,-0.002594 0.00228,6.38e-4 7.28e-4,0.003428 7.5e-4,0.003428 4.34e-4,0 -2.58e-4,-0.002788 -3e-5,-0.003612 0.001,-0.003612 7.36e-4,0 0.00159,0.001626 0.0019,0.003612 3.12e-4,0.002004 5.7e-4,0.002464 5.8e-4,0.001032 1.8e-5,-0.002702 0.001672,-0.003724 0.001672,-0.001032 0,8.52e-4 0.00188,0.001494 0.004174,0.001428 0.002296,-6.4e-5 0.004298,6.32e-4 0.00445,0.001548 1.5e-4,9.18e-4 8.06e-4,0.001668 0.001456,0.001668 9.46e-4,0 0.001044,5.16e-4 4.92e-4,0.00258 -6.9e-4,0.002584 -0.007356,0.0038 -0.006936,0.001268 z m 0.009062,-6.4e-5 c 0,-9.46e-4 1.86e-4,-0.001782 4.14e-4,-0.00186 2.28e-4,-7.6e-5 0.00121,-5.28e-4 0.00218,-0.001002 0.002186,-0.001068 0.002962,0.002472 8.6e-4,0.00392 -0.002076,0.00143 -0.003454,0.001008 -0.003454,-0.001058 z m -0.03975,-0.00688 c 0,-0.001136 -9.2999e-4,-0.002064 -0.00207,-0.002064 -0.00114,0 -0.002252,9.28e-4 -0.002476,0.002064 -2.7e-4,0.001376 -4.08e-4,0.001376 -4.16e-4,0 -1.2e-5,-0.002586 -0.00332,-0.003198 -0.00332,-6.14e-4 0,0.00104 -2.24e-4,0.001368 -5e-4,7.3e-4 -5.26e-4,-0.001214 -0.005382,-0.003166 -0.01054,-0.004234 -0.00167,-3.46e-4 -0.003038,-8.36e-4 -0.003038,-0.001092 0,-0.001384 -0.007264,-0.001404 -0.007728,-2.2e-5 -8.3e-4,0.002468 -0.00635,0.004188 -0.00635,0.00198 0,-0.00105 -3.72e-4,-0.001908 -8.28e-4,-0.001908 -4.54e-4,0 -8.28e-4,8.96e-4 -8.28e-4,0.001992 0,0.001244 -0.003888,0.001624 -0.01035,0.001014 -0.005694,-5.36e-4 -0.010952,-9.6599e-4 -0.011684,-9.5399e-4 -7.32e-4,2e-5 -0.00216,-8.34e-4 -0.003174,-0.00188 -0.001108,-0.001146 -0.001844,-0.001152 -0.001844,-2e-5 0,0.001876 -0.00193,0.001572 -0.002454,-3.86e-4 -1.5e-4,-5.66e-4 -0.002664,-6.98e-4 -0.005584,-2.94e-4 -0.003518,4.88e-4 -0.00557,-8e-5 -0.006088,-0.001686 -6.08e-4,-0.001886 -7.8e-4,-0.001794 -7.88e-4,4.12e-4 -6e-6,0.00179 -1.58e-4,0.00204 -4.14e-4,6.76e-4 -2.44e-4,-0.001294 -0.003384,-0.00226 -0.00786,-0.002412 -0.004098,-1.4e-4 -0.01652,-7.9e-4 -0.027602,-0.00144 -0.011084,-6.48e-4 -0.025306,-0.001302 -0.031608,-0.001454 -0.0063,-1.5e-4 -0.011456,-8.56e-4 -0.011456,-0.001566 0,-7.0999e-4 -0.001368,-0.001292 -0.003038,-0.001292 -0.001668,0 -0.003034,6.96e-4 -0.003034,0.001548 0,8.52e-4 -2.64e-4,0.001548 -5.84e-4,0.001548 -3.22e-4,0 -4.32e-4,-9.28e-4 -2.44e-4,-0.002064 1.88e-4,-0.001136 -4.6e-5,-0.002064 -5.2e-4,-0.002064 -4.74e-4,0 -8.6e-4,-9.28e-4 -8.6e-4,-0.002062 0,-0.001136 -2.64e-4,-0.002066 -5.86e-4,-0.002066 -3.2e-4,0 -4.3e-4,9.3e-4 -2.42e-4,0.002066 2e-4,0.00122 -4.08e-4,0.00205 -0.001486,0.002032 -0.001382,-2.6e-5 -0.001626,-5.2e-4 -0.001,-0.002032 6.12e-4,-0.00148 6.4e-4,-0.00201 10e-5,-0.002034 -5.62e-4,-2.6e-5 -5.4e-4,-7.4199e-4 10e-5,-0.003128 4.54e-4,-0.00170199 0.00134,-0.00309599 0.00197,-0.00309599 6.28e-4,0 0.001144,-9.92e-4 0.001144,-0.00220199 0,-0.001308 4.76e-4,-0.001638 0.001172,-8.12e-4 7.9e-4,9.38e-4 0.00105,6.46e-4 7.96e-4,-8.92e-4 -2.08e-4,-0.001258 -2.3e-4,-0.002285994 -5.2e-5,-0.002285994 1.8e-4,0 5.16e-4,0.001909994 7.48e-4,0.004245994 l 4.2e-4,0.00424398 0.001456,-0.00427998 c 9.9799e-4,-0.002936 0.001576,-0.003552 0.00184,-0.00196 2.7e-4,0.001644 5.18e-4,0.001504 8.46e-4,-4.8e-4 3.76e-4,-0.002277993 0.004164,-0.0027879928 0.02022,-0.00272599281 0.010866,4.39999996e-5 0.024228,6.739996866e-4 0.029694,0.00140399281 0.005466,7.3e-4 0.014534,0.001864 0.02015,0.002522 0.020284,0.00237799 0.02551,0.00323999 0.025964,0.00429199 0.001016,0.00234599 0.005466,0.00442799 0.00614,0.00287199 4.14e-4,-9.6e-4 8.98e-4,-6.18e-4 0.001146,8.12e-4 3.2e-4,0.001844 5.66e-4,0.001686 9.72e-4,-6.26e-4 3e-4,-0.001702 5.48e-4,-0.00216599 5.52e-4,-0.001032 6e-6,0.001304 0.00342,0.001972 0.009256,0.001816 0.009222,-2.5e-4 0.02498,0.003082 0.02498,0.00528 2e-6,6.4e-4 0.001928,0.001056 0.00428,9.3e-4 0.004032,-2.2e-4 0.01332,0.002606 0.014968,0.004552 6.08e-4,7.1799e-4 6.2e-4,0.00149 6e-5,0.004008 -5.64e-4,0.002538 -5.5e-4,0.00295 7.6e-5,0.002212 4.22e-4,-5e-4 0.00146,-0.001476 0.00231,-0.002172 9.48e-4,-7.7999e-4 0.001418,-0.002232 0.00122,-0.003786 -1.74e-4,-0.001384 -3e-5,-0.001064 3.26e-4,7.1199e-4 5.2e-4,0.002612 0.001228,0.002934 0.003708,0.001676 0.001744,-8.84e-4 0.003484,-7.2e-4 0.004034,3.8e-4 5.32e-4,0.001066 0.001666,0.002064 0.002522,0.00222 0.001432,2.58e-4 0.00145,3.54e-4 2.22e-4,0.00122 -0.00103,7.24e-4 -0.001154,0.001354 -5.52e-4,0.00278 8.54e-4,0.00202 1.36e-4,0.002274 -0.00764,0.002696 -0.002712,1.46e-4 -0.004554,-5.88e-4 -0.004554,-0.001818 z M 0.50619986,0.01439037 c -1.74e-4,-0.001696 -5.4e-4,-0.00257 -8.12e-4,-0.00194 -7.8e-4,0.0018 -5.76e-4,0.005024 3.18e-4,0.005024 4.92e-4,0 6.86e-4,-0.00121 4.94e-4,-0.003084 z m -0.0322,0.007056 c 5.68e-4,-0.003968 5.34e-4,-0.00442 -2.46e-4,-0.0033 -7.8e-4,0.00112 -8.16e-4,6.68e-4 -2.54e-4,-0.003256 l 6.52e-4,-0.004554 5.5e-4,0.0046 c 6.3e-4,0.00528 0.00177,0.006048 0.002236,0.001506 1.74e-4,-0.001702 1.2e-4,-0.003096 -1.26e-4,-0.003096 -2.42e-4,0 -4.42e-4,-0.001404 -4.42e-4,-0.00312 0,-0.00253999 5e-4,-0.00287599 0.002668,-0.00179999 0.001824,9.0399e-4 0.002668,5.86e-4 0.002668,-0.001006 0,-0.001294 8.74e-4,-0.00232999 0.001966,-0.00232999 0.001288,0 0.001836,7.8199e-4 0.00159,0.00226999 -2.68e-4,0.001614 -4e-5,0.0016 7.82e-4,-4.8e-5 9.62e-4,-0.00192399 0.001104,-0.00178599 8.36e-4,8.26e-4 -1.78e-4,0.00172999 -5.44e-4,0.00314399 -8.16e-4,0.00314399 -2.7e-4,0 -4.92e-4,9.3e-4 -4.92e-4,0.002064 0,0.004304 9.98e-4,0.001816 0.001656,-0.00412798 7.68e-4,-0.00693199 7.88e-4,-0.00696999 0.002172,-0.00419999 6.98e-4,0.00139599 9.14e-4,0.00357799 7.22e-4,0.00730998 -1.5e-4,0.002924 -5.4e-5,0.005048 2.12e-4,0.004716 2.64e-4,-3.3e-4 6.06e-4,3.06e-4 7.6e-4,0.001416 1.6e-4,0.001194 -0.00115,0.00216 -0.003214,0.002368 -0.00319,3.2e-4 -0.00354,-7.6e-5 -0.004092,-0.004598 -5.94e-4,-0.004876 -0.002634,-0.00701398 -0.002634,-0.00276 0,0.0012 2.46e-4,0.001618 5.46e-4,9.24e-4 3.08e-4,-7.12e-4 4.08e-4,8e-5 2.3e-4,0.001812 -1.84e-4,0.001796 -6.78e-4,0.002552 -0.001188,0.00182 -5.68e-4,-8.14e-4 -7.36e-4,-4.28e-4 -4.84e-4,0.001102 4.44e-4,0.002686 -0.00139,0.00467 -0.00352,0.003806 -7.6e-4,-3.06e-4 -0.001834,2.66e-4 -0.002388,0.001276 -9.04e-4,0.001644 -9.4e-4,0.001356 -3.5e-4,-0.002764 z m 0.015104,-0.007756 c 1.56e-4,-9.4599e-4 -6.8e-5,-0.00172 -4.96e-4,-0.00172 -4.28e-4,0 -6.52e-4,7.74e-4 -4.96e-4,0.00172 1.58e-4,9.46e-4 3.8e-4,0.00172 4.96e-4,0.00172 1.16e-4,0 3.4e-4,-7.74e-4 4.96e-4,-0.00172 z m -0.02254,0.008148 c -1.68e-4,-0.001006 1.9199e-4,-0.002314 7.9799e-4,-0.002906 7.32e-4,-7.16e-4 9.86e-4,-0.002172 7.62e-4,-0.004362 -1.86e-4,-0.001808 -1.04e-4,-0.003288 1.8e-4,-0.003288 7.6e-4,0 6.36e-4,0.006744 -1.76e-4,0.00978 -8.24e-4,0.00308 -0.001156,0.003246 -0.001564,7.7602e-4 z m 0.003384,-0.00574 c 1.56e-4,-0.001512 3.54e-4,-0.002752 4.4e-4,-0.002752 8.8e-5,0 2.86e-4,0.00124 4.4e-4,0.002752 1.56e-4,0.001514 -4e-5,0.002754 -4.4e-4,0.002754 -3.96e-4,0 -5.94e-4,-0.00124 -4.4e-4,-0.002754 z m 0.00213,-0.00578 c 5.9e-4,-0.00166799 0.001616,-0.00305999 0.00228,-0.00309799 0.001318,-7.2e-5 0.002158,0.00305999 0.001332,0.00496599 -2.82e-4,6.5199e-4 -6.1e-4,1.2e-4 -7.26e-4,-0.001186 -1.6e-4,-0.00178399 -5.04e-4,-0.00178799 -0.00139,-2e-5 -0.001698,0.003396 -0.002764,0.002918 -0.001496,-6.7e-4 z m -0.007336,-2.82e-4 c 1.84e-4,-6.86e-4 6.6e-4,-7.5999e-4 0.001058,-1.64e-4 4.4e-4,6.6e-4 3.1e-4,0.001148 -3.34e-4,0.001248 -5.8e-4,9e-5 -9.08e-4,-4e-4 -7.24e-4,-0.001084 z m 0.02911,-8.1799e-4 c -7.08e-4,-0.00171 -6.88e-4,-0.002006 1.4e-4,-0.002032 5.3e-4,-2e-5 9.64e-4,8.96e-4 9.64e-4,0.002032 0,0.00251799 -6e-5,0.00251799 -0.001104,0 z m -0.002964,-0.003144 c -2.64e-4,-0.00256399 -1.22e-4,-0.00273999 7.6799e-4,-9.5999e-4 0.00128,0.00255999 0.0014,0.00410399 3.24e-4,0.00410399 -4.2399e-4,0 -9.1399e-4,-0.001414 -0.001092,-0.003144 z&quot;&gt;&lt;/path&gt;&lt;/clipPath&gt;&lt;/defs&gt;&lt;/svg&gt;</content>
  </entry>
  <entry>
    <title>Counting Goats</title>
    <link href="https://watzek.dev/posts/2020/06/02/counting-goats/" />
    <updated>2020-06-02T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/06/02/counting-goats/</id>
    <content type="html">&lt;p&gt;I would describe myself as a privacy-conscious person. Therefore it wasn’t an easy choice for me if I should implement some kind of analytics on my website. But in the end, my curiosity won and I decided to add some kind of tracking/analytics to my site. The solution had to be as privacy-aware as possible while still providing useful data. So I didn’t even consider tools like Google Analytics which have a very bad reputation regarding privacy.&lt;/p&gt;&lt;p&gt;After some research I found three possible candidates &lt;a href=&quot;https://usefathom.com/&quot; rel=&quot;noopener&quot;&gt;Fathom Analytics&lt;/a&gt;, &lt;a href=&quot;https://goaccess.io/&quot; rel=&quot;noopener&quot;&gt;GoAccess&lt;/a&gt;, and &lt;a href=&quot;https://www.goatcounter.com/&quot; rel=&quot;noopener&quot;&gt;GoatCounter&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I rejected Fathom Analytics early on as it is only available via a paid subscription which would not be a viable option for my homepage. There is also a lite version available that can be hosted but it requires cookies to work which also was a no-go for me.&lt;/p&gt;&lt;p&gt;GoAccess takes a different approach to aggregate visitor information than most solutions. Instead of a client-side script, it analyzes the server access logs for the page in real-time. The results can either be viewed in the terminal or the browser. And I have to say that the web view is pretty sexy but see for yourself in this &lt;a href=&quot;https://rt.goaccess.io/&quot; rel=&quot;noopener&quot;&gt;live demo&lt;/a&gt; or the screenshot below. GoAccess provides many functionalities and presents the data very nicely. But server access logs have to be enabled and GoAccess has to have access to them. Because of its complexity and because it didn’t quite feel like the right tool for the job I rejected GoAccess after playing around with it for some time.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/MA1J7WqxjG-300.webp 300w, https://watzek.dev/images/MA1J7WqxjG-600.webp 600w, https://watzek.dev/images/MA1J7WqxjG-800.webp 800w, https://watzek.dev/images/MA1J7WqxjG-854.webp 854w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/MA1J7WqxjG-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Screenshot of the GoAccess web interface&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;854&quot; height=&quot;472&quot; srcset=&quot;https://watzek.dev/images/MA1J7WqxjG-300.jpeg 300w, https://watzek.dev/images/MA1J7WqxjG-600.jpeg 600w, https://watzek.dev/images/MA1J7WqxjG-800.jpeg 800w, https://watzek.dev/images/MA1J7WqxjG-854.jpeg 854w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;This left only GoatCounter as a possible analytics tool. It is built and operated by Martin Tournoij aka &lt;a href=&quot;https://www.arp242.net/&quot; rel=&quot;noopener&quot;&gt;arp242&lt;/a&gt; and the source code is available on &lt;a href=&quot;https://github.com/zgoat/goatcounter&quot; rel=&quot;noopener&quot;&gt;GitHub&lt;/a&gt;. On the landing page, it advertises features like privacy-awareness, small size, accessibility, and data ownership. All of those features lead me to chose this solution. Besides the usage is free for non-commercial projects which also made it attractive for me. The user interface is pretty simple but it provides clear information about all the data that I care about. A screenshot can be seen below. Getting the whole thing to work took surprisingly little time. I only had to create an account and insert a small script tag at the bottom of my page and suddenly the page visits were visible in the backend.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/_WEhsKGVxM-300.webp 300w, https://watzek.dev/images/_WEhsKGVxM-600.webp 600w, https://watzek.dev/images/_WEhsKGVxM-800.webp 800w, https://watzek.dev/images/_WEhsKGVxM-823.webp 823w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/_WEhsKGVxM-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Screenshot of the GoatCounter web interface&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;823&quot; height=&quot;480&quot; srcset=&quot;https://watzek.dev/images/_WEhsKGVxM-300.jpeg 300w, https://watzek.dev/images/_WEhsKGVxM-600.jpeg 600w, https://watzek.dev/images/_WEhsKGVxM-800.jpeg 800w, https://watzek.dev/images/_WEhsKGVxM-823.jpeg 823w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;As you can see there are plenty of privacy-focused web analytics tools out there. It is just pretty difficult to choose one of them. Time will show if I made the right decision by choosing GoatCounter but I have a very good feeling that it was the right choice.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>MIDI for the Home Office</title>
    <link href="https://watzek.dev/posts/2020/03/22/midi-for-the-home-office/" />
    <updated>2020-03-22T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/03/22/midi-for-the-home-office/</id>
    <content type="html">&lt;p&gt;I’m one of the many employees who work from home because of the currently ongoing COVID-19 pandemic. This is not my normal work environment and I had to make some adjustments to my workspace at home to make it work. One of the most fun and probably one of the most unnecessary ones was it to make a USB MIDI controller act as an input device for frequently used functions or hotkeys on my Windows work computer.&lt;/p&gt;&lt;p&gt;Fortunately, I had an old USB MIDI controller lying around. It is an AKAI LPD8 but any MIDI controller should work. This controller has eight pads and eight control dials. Additionally, the controller can switch between four programs which can change the signals of each input. Therefore it would be possible to configure 64 different functions. I currently use four pads and one dial.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/fBdzTbjdjp-300.webp 300w, https://watzek.dev/images/fBdzTbjdjp-600.webp 600w, https://watzek.dev/images/fBdzTbjdjp-800.webp 800w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/fBdzTbjdjp-300.jpeg&quot; class=&quot;undefined&quot; alt=&quot;MIDI controller AKAI LPD8&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;800&quot; height=&quot;307&quot; srcset=&quot;https://watzek.dev/images/fBdzTbjdjp-300.jpeg 300w, https://watzek.dev/images/fBdzTbjdjp-600.jpeg 600w, https://watzek.dev/images/fBdzTbjdjp-800.jpeg 800w&quot; sizes=&quot;100vw&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;I had the idea to use this controller after my first day working from home. Three things annoyed me: controlling my music, activating push to talk in the work voice chat, and controlling the volume of my music player. At first, I just installed the software for the controller and tried to find out if this would be sufficient. But the software can only be used to change the MIDI signals (e.g. which pad is mapped to which note) that the controller emits. This didn’t stop me and I started to look into alternatives. Then I found &lt;a href=&quot;https://www.autohotkey.com/&quot; rel=&quot;noopener&quot;&gt;AutoHotKey&lt;/a&gt; which is best described by this sentence on their website:&lt;/p&gt;&lt;blockquote&gt;&lt;p&gt;The ultimate automation scripting language for Windows.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;It makes it possible to react to keypresses and execute complex actions. For example, it is possible to send key combinations, build simple GUIs, or execute programs. But there seemed to be one drawback. AutoHotKey normally does not understand MIDI signals. A bit more digging and I stumbled upon &lt;a href=&quot;https://github.com/micahstubbs/midi4ahk&quot; rel=&quot;noopener&quot;&gt;midi4ahk&lt;/a&gt;. This project provides a DLL and AutoHotKey scripts that can be linked in custom scripts to react to MIDI events.&lt;/p&gt;&lt;p&gt;Let’s take a quick look at how a MIDI message is structured. It consists of a status byte that describes the type of the message and sometimes the channel on which the message was sent. After the status byte are two additional bytes which are the parameters of the type. The three messages which were most important to me were “Note On”, “Note Off”, and “Control Change”. A “Note On” message has the status byte 1001 0000&lt;sub&gt;2&lt;/sub&gt; or a bit shorter as hexadecimal number 90&lt;sub&gt;16&lt;/sub&gt;. The first half of the status byte indicates the “Note On” type and the second half is the channel number. The parameters are the musical note (0-127) and the velocity (0-127) or how hard the button was pressed. The first bit of both bytes is always 0 therefore only 128 values are possible. The “Note Off” message is the same except for the status byte which is 80&lt;sub&gt;16&lt;/sub&gt;. “Control Change” messages have the status byte B0&lt;sub&gt;16&lt;/sub&gt;. Similar to the note messages the first half of the byte indicates that the message is of the type “Control Change” and the second half is the channel number. This message type has as parameters the controller number (0-127) and the controller value (0-127).&lt;/p&gt;&lt;p&gt;Now I could finally start with the implementation. The library &lt;code&gt;midi4ahk&lt;/code&gt; provides a function called &lt;code&gt;listenNote&lt;/code&gt; which can be used to execute another function when a specific note is played. I wanted to use pad 8 of my controller to play or pause my music. The software of the MIDI controller showed me that this pad produces the note with the number 41 (F&lt;sub&gt;2&lt;/sub&gt;). So I wrote the following script:&lt;/p&gt;&lt;pre class=&quot;language-autohotkey&quot;&gt;&lt;code class=&quot;language-autohotkey&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; 1. param note number&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 2. param function name&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 3. param MIDI channel (0 == all)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;listenNote&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;41&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;playPauseMusic&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;; 1. param note number&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 2. param velocity (0-127)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;playPauseMusic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token command selector&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;Media_Play_Pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As expected it didn’t work when I tried it out. After some MIDI debugging with the excellent &lt;a href=&quot;https://www.morson.jp/pocketmidi-webpage/&quot; rel=&quot;noopener&quot;&gt;Pocket MIDI&lt;/a&gt; tool I found out the following. My MIDI controller first sends a “Note On” message with the velocity set according to how strong I press the pad. When I release the pad it sends a “Note Off” message with the velocity set to 127. My script does not differentiate between “Note On” and “Note Off” events. Hence it pauses the music for a short moment when receiving the “Note On” message and shortly after it starts the music again when the “Note Off” message is received. To overcome this and only react to a “Note On” event I used a small hack. I only send the play or pause signal when the velocity is above zero but below 127. Because “Note Off” events always have a velocity of 127 this works as long as I don’t smash the pad with full force. The following code snippet shows the final implementation of the &lt;code&gt;playPauseMusic&lt;/code&gt; function.&lt;/p&gt;&lt;pre class=&quot;language-autohotkey&quot;&gt;&lt;code class=&quot;language-autohotkey&quot;&gt;&lt;span class=&quot;token function&quot;&gt;playPauseMusic&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;note&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; vel&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;vel &lt;span class=&quot;token operator&quot;&gt;&gt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt; and vel &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;token command selector&quot;&gt;Send&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;token symbol&quot;&gt;Media_Play_Pause&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;And to demonstrate that it works here is a short video of me pressing the pad on the MIDI controller and my music player reacting to it by pausing the playback.&lt;/p&gt;&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;&lt;source src=&quot;https://watzek.dev/videos/hotkeys/pause.webm&quot; type=&quot;video/webm&quot;&gt;&lt;source src=&quot;https://watzek.dev/videos/hotkeys/pause.mp4&quot; type=&quot;video/mp4&quot;&gt;Sorry, the video is not supported by your browser.&lt;/video&gt;&lt;p&gt;After implementing the pads to act as media control keys and to send a push to talk key combination I moved on to controlling the volume of my music player. When I’m working I often listen to music in the background but I also want to hear my colleagues talking in the voice chat. This makes me change the volume of my music player often and it takes a fair number of clicks to do it. The control dials on my MIDI controller seemed to be a perfect fit for the task at hand. When they are turned they emit a “Control Change” message with their position mapped from 0 to 127. All I thought I had to do was listen to “Control Change” messages with the &lt;code&gt;listenCC&lt;/code&gt; function and then AutoHotKey will surely offer a function to change the volume of a program. The first part of it was true but AutoHotKey does not offer such functionality. I had to wander again into the depths of the internet to find an answer. This time I came back with the tool &lt;a href=&quot;https://www.nirsoft.net/utils/nircmd.html&quot; rel=&quot;noopener&quot;&gt;NirCmd&lt;/a&gt;. It is a command-line program that can be used to execute a set of tasks without any GUIs. One of those tasks is called &lt;code&gt;setappvolume&lt;/code&gt; and it can be used to set the volume of a specific application to a value between 0 and 1. So I wrote the following script.&lt;/p&gt;&lt;pre class=&quot;language-autohotkey&quot;&gt;&lt;code class=&quot;language-autohotkey&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; 1. param control number&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 2. param function name&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 3. param MIDI channel (0 == all)&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;listenCC&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;musicVolume&quot;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;

&lt;span class=&quot;token comment&quot;&gt;; 1. param control number&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 2. param control value&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;musicVolume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    volume &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nircmd&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;exe setappvolume MyMusicPlayer&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;exe &lt;span class=&quot;token variable&quot;&gt;%volume%&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I register the &lt;code&gt;musicVolume&lt;/code&gt; function to be executed every time the control with the number 4 is activated. The &lt;code&gt;musicVolume&lt;/code&gt; function receives the control number and the control value which is between 0 and 127 as inputs. In the function, I map the value to a number between 0 and 1 and execute the NirCmd &lt;code&gt;setappvolume&lt;/code&gt; command with the name of my music player and the calculated volume. This did work but I saw some problems with the implementation. For each value sent by the control dial, this function is executed and the volume updated. The control dial sends a value for each position that is passed while turning it. This could lead to a lot of calls and wasted CPU usage. I monitored my CPU usage when I turned the control dial and each turn let the CPU usage climb 40% higher. Unfortunately, AutoHotKey has no automatic debounce utilities when functions are used so I had to implement a simple version myself as shown in the following snippet.&lt;/p&gt;&lt;pre class=&quot;language-autohotkey&quot;&gt;&lt;code class=&quot;language-autohotkey&quot;&gt;&lt;span class=&quot;token comment&quot;&gt;; 1. param control number&lt;/span&gt;
&lt;span class=&quot;token comment&quot;&gt;; 2. param control value&lt;/span&gt;
&lt;span class=&quot;token function&quot;&gt;musicVolume&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;num&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;static&lt;/span&gt; lastCall &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;
    thisCall &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token constant&quot;&gt;A_TickCount&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;lastCall &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lastCall &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; thisCall
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;token keyword&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;thisCall &lt;span class=&quot;token operator&quot;&gt;-&lt;/span&gt; lastCall&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;&amp;lt;&lt;/span&gt; &lt;span class=&quot;token number&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;{&lt;/span&gt;
        lastCall &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; thisCall
        &lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;
    &lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;
    lastCall &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; thisCall
    volume &lt;span class=&quot;token operator&quot;&gt;:=&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;127&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;*&lt;/span&gt;val&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;token operator&quot;&gt;/&lt;/span&gt;&lt;span class=&quot;token number&quot;&gt;100&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;Run&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;,&lt;/span&gt; nircmd&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;exe setappvolume MyMusicPlayer&lt;span class=&quot;token operator&quot;&gt;.&lt;/span&gt;exe &lt;span class=&quot;token variable&quot;&gt;%volume%&lt;/span&gt;
    &lt;span class=&quot;token command selector&quot;&gt;return&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;To implement a simple debounce I used a &lt;code&gt;static&lt;/code&gt; function variable which keeps its value across executions of a function and the built-in variable &lt;code&gt;A_TickCount&lt;/code&gt; which contains the number of milliseconds since the system was started. By remembering when the function was called the last time I was able to only execute the NirCmd &lt;code&gt;setappvolume&lt;/code&gt; command when the last function execution was longer than 16 milliseconds ago.&lt;/p&gt;&lt;p&gt;The following video shows me turning the control dial and the volume bar of my music player moving up and down in sync with it.&lt;/p&gt;&lt;video controls=&quot;&quot; loop=&quot;&quot;&gt;&lt;source src=&quot;https://watzek.dev/videos/hotkeys/volume.webm&quot; type=&quot;video/webm&quot;&gt;&lt;source src=&quot;https://watzek.dev/videos/hotkeys/volume.mp4&quot; type=&quot;video/mp4&quot;&gt;Sorry, the video is not supported by your browser.&lt;/video&gt;&lt;p&gt;I highly recommend that you get your hands on a MIDI controller and use it to control your computer. It makes working from home feel like controlling a space ship 🚀.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>I Ate Some Delicious HTTPie</title>
    <link href="https://watzek.dev/posts/2020/03/15/i-ate-some-delicious-httpie/" />
    <updated>2020-03-15T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/03/15/i-ate-some-delicious-httpie/</id>
    <content type="html">&lt;p&gt;Everyone who works with websites and web APIs at one point will have to test or debug some HTTP calls. A vanilla HTTP GET request can be done with your browser of choice by entering the URL into the address bar. But what do you do if you want to make a call with another HTTP verb or set some headers? There are many graphical applications like &lt;a href=&quot;https://insomnia.rest/&quot; rel=&quot;noopener&quot;&gt;Insomnia&lt;/a&gt; or &lt;a href=&quot;https://www.postman.com/&quot; rel=&quot;noopener&quot;&gt;Postman&lt;/a&gt; and the almighty &lt;a href=&quot;https://curl.haxx.se/&quot; rel=&quot;noopener&quot;&gt;cURL&lt;/a&gt;. I have used all three of them to work with HTTP endpoints and they do a great job but there is a new kid in town.&lt;/p&gt;&lt;p&gt;&lt;a href=&quot;https://httpie.org/&quot; rel=&quot;noopener&quot;&gt;HTTPie&lt;/a&gt; is a command-line tool that can be used to interact with HTTP endpoints. It provides features like colorized output, JSON support, and intuitive usability.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; http httpbin.org/status/418
HTTP/1.1 418 I&#39;M A TEAPOT
Access-Control-Allow-Credentials: true
Access-Control-Allow-Origin: *
Connection: keep-alive
Content-Length: 135
Date: Sun, 15 Mar 2020 17:36:47 GMT
Server: gunicorn/19.9.0
x-more-info: http://tools.ietf.org/html/rfc2324


    -=[ teapot ]=-

       _...._
     .&#39;  _ _ `.
    | .&quot;` ^ `&quot;. _,
    &#92;_;`&quot;---&quot;`|//
      |       ;/
      &#92;_     _/
        `&quot;&quot;&quot;`&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The example above makes an HTTP GET request to &lt;code&gt;http://httpbin.org/status/418&lt;/code&gt; and prints the headers and body of the response. In the terminal, the headers are syntax highlighted. It looks similar to the following example.&lt;/p&gt;&lt;pre class=&quot;language-http&quot;&gt;&lt;code class=&quot;language-http&quot;&gt;&lt;span class=&quot;token response-status&quot;&gt;&lt;span class=&quot;token http-version property&quot;&gt;HTTP/1.1&lt;/span&gt; &lt;span class=&quot;token status-code number&quot;&gt;418&lt;/span&gt; &lt;span class=&quot;token reason-phrase string&quot;&gt;I&#39;M A TEAPOT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Access-Control-Allow-Credentials&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Access-Control-Allow-Origin&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;*&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Connection&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;keep-alive&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Content-Length&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;135&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;Sun, 15 Mar 2020 17:36:47 GMT&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;Server&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;gunicorn/19.9.0&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token header&quot;&gt;&lt;span class=&quot;token header-name keyword&quot;&gt;x-more-info&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token header-value&quot;&gt;http://tools.ietf.org/html/rfc2324&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is also possible to set headers for your request by writing them in the format &lt;code&gt;Header:Value&lt;/code&gt; after the URL. In the following example, I set the &lt;code&gt;User-Agent&lt;/code&gt; header for the request to &lt;code&gt;Ichiban&lt;/code&gt;. The &lt;code&gt;--body&lt;/code&gt; flag instructs HTTPie to only print the response body.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; http --body httpbin.org/headers User-Agent:Ichiban
{
    &quot;headers&quot;: {
        &quot;Accept&quot;: &quot;*/*&quot;,
        &quot;Accept-Encoding&quot;: &quot;gzip, deflate&quot;,
        &quot;Host&quot;: &quot;httpbin.org&quot;,
        &quot;User-Agent&quot;: &quot;Ichiban&quot;,
        &quot;X-Amzn-Trace-Id&quot;: &quot;Root=1-5e6e6be0-f16095947012ee8d94f75fe9&quot;
    }
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If another HTTP method then GET should be used the name of the method has to be written before the URL. Like in the following example where a DELETE call is made to &lt;code&gt;http://httpbin.org/anything/42&lt;/code&gt;.&lt;/p&gt;&lt;pre class=&quot;language-text&quot;&gt;&lt;code class=&quot;language-text&quot;&gt;&gt; http --body DELETE httpbin.org/anything/42
{
    &quot;args&quot;: {},
    &quot;data&quot;: &quot;&quot;,
    &quot;files&quot;: {},
    &quot;form&quot;: {},
    &quot;headers&quot;: {
        &quot;Accept&quot;: &quot;*/*&quot;,
        &quot;Accept-Encoding&quot;: &quot;gzip, deflate&quot;,
        &quot;Content-Length&quot;: &quot;0&quot;,
        &quot;Host&quot;: &quot;httpbin.org&quot;,
        &quot;User-Agent&quot;: &quot;HTTPie/0.9.8&quot;,
        &quot;X-Amzn-Trace-Id&quot;: &quot;Root=1-5e6e6e0d-fb2d984b569fadc4f6fddc92&quot;
    },
    &quot;json&quot;: null,
    &quot;method&quot;: &quot;DELETE&quot;,
    &quot;origin&quot;: &quot;91.115.48.164&quot;,
    &quot;url&quot;: &quot;http://httpbin.org/anything/42&quot;
}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;In my opinion, this tool doesn’t replace the other applications that I mentioned above. But when you quickly want to test if a call is working this is the right tool for the job. It is cross-platform, quick, and easy to use.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>All the Headers</title>
    <link href="https://watzek.dev/posts/2020/03/08/all-the-headers/" />
    <updated>2020-03-08T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/03/08/all-the-headers/</id>
    <content type="html">&lt;p&gt;I recently found out about the &lt;a href=&quot;https://observatory.mozilla.org/&quot; rel=&quot;noopener&quot;&gt;Mozilla Observatory&lt;/a&gt;. It is a website scanner that rates a website based on the implemented security features. Among others, it checks for the encryption standards used for HTTPS and the headers sent by the server. My page initially scored a C+. Thanks to &lt;a href=&quot;https://letsencrypt.org/&quot; rel=&quot;noopener&quot;&gt;Let’s Encrypt&lt;/a&gt; which provides my TLS certificate and the great &lt;a href=&quot;https://caddyserver.com/&quot; rel=&quot;noopener&quot;&gt;Caddy web server&lt;/a&gt; which serves my website and automatically provides HTTPS there were no improvements to be made in this area. But some important HTTP headers were missing. I actually didn’t know a lot about those headers and I want to document what I learned.&lt;/p&gt;&lt;p&gt;At the time of writing a call to &lt;code&gt;curl -I https://watzek.dev&lt;/code&gt;, which can be used to make an HTTP HEAD call that only fetches the headers of a document, returns the following.&lt;/p&gt;&lt;pre class=&quot;language-diff-http&quot;&gt;&lt;code class=&quot;language-diff-http&quot;&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; HTTP/2 200
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; accept-ranges: bytes
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; cache-control: no-cache
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-http&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; content-security-policy: default-src &#39;none&#39;; frame-ancestors &#39;none&#39;; form-action &#39;self&#39;; base-uri &#39;none&#39;; img-src &#39;self&#39;; script-src &#39;self&#39;; style-src &#39;self&#39;; manifest-src &#39;self&#39;; block-all-mixed-content; upgrade-insecure-requests;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; content-type: text/html; charset=utf-8
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; etag: &quot;q67wby54s&quot;
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-http&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; feature-policy: microphone &#39;none&#39;; geolocation &#39;none&#39;; autoplay &#39;none&#39;; accelerometer &#39;none&#39;; ambient-light-sensor &#39;none&#39;; battery &#39;none&#39;; camera &#39;none&#39;; display-capture &#39;none&#39;; document-domain &#39;none&#39;; encrypted-media &#39;none&#39;; fullscreen &#39;none&#39;; gyroscope &#39;none&#39;; magnetometer &#39;none&#39;; midi &#39;none&#39;; payment &#39;none&#39;; publickey-credentials &#39;none&#39;; sync-xhr &#39;none&#39;; usb &#39;none&#39;; wake-lock &#39;none&#39;; xr-spatial-tracking &#39;none&#39;
&lt;/span&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; last-modified: Mon, 24 Feb 2020 17:52:46 GMT
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-http&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; referrer-policy: same-origin
&lt;/span&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; server: Caddy
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-http&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; strict-transport-security: max-age=63072000; includeSubDomains; preload
&lt;/span&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; x-clacks-overhead: GNU Terry Pratchett
&lt;/span&gt;&lt;span class=&quot;token inserted-sign inserted language-http&quot;&gt;&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; x-content-type-options: nosniff
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; x-frame-options: DENY
&lt;span class=&quot;token prefix inserted&quot;&gt;+&lt;/span&gt; x-xss-protection: 1; mode=block
&lt;/span&gt;&lt;span class=&quot;token unchanged language-http&quot;&gt;&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; content-length: 6652
&lt;span class=&quot;token prefix unchanged&quot;&gt; &lt;/span&gt; date: Sat, 07 Mar 2020 17:41:33 GMT
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The important headers are highlighted in the code block above. They are &lt;code&gt;content-security-policy&lt;/code&gt;, &lt;code&gt;feature-policy&lt;/code&gt;, &lt;code&gt;referrer-policy&lt;/code&gt;, &lt;code&gt;strict-transport-security&lt;/code&gt;, &lt;code&gt;x-content-type-options&lt;/code&gt;, &lt;code&gt;x-frame-options&lt;/code&gt;, and &lt;code&gt;x-xss-protection&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;content-security-policy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#content-security-policy&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Content-Security-Policy&lt;/h2&gt;&lt;p&gt;A visit to a website leads to the download of multiple resources. When the homepage of my site is loaded an HTML file, a CSS stylesheet, a JavaScript script, an image, and favicon images are downloaded. By default, the browser would just load the files without looking if I request them from the same origin or from any other URL.&lt;/p&gt;&lt;p&gt;This is where the &lt;code&gt;Content-Security-Policy&lt;/code&gt; (CSP) header comes into play. Among other directives, it makes it possible to explicitly define which origins are allowed for resources. In my case I set the allowed origins for resources I use to &lt;code&gt;&#39;self&#39;&lt;/code&gt; which means that the browser should only load them if they are on the same origin. This also disallows inline scripts or stylesheets.&lt;/p&gt;&lt;p&gt;In addition to controlling the allowed origins of resources this header also controls to which origins forms can send data with the &lt;code&gt;form-action&lt;/code&gt; policy.&lt;/p&gt;&lt;p&gt;Setting the &lt;code&gt;frame-ancestors&lt;/code&gt; policy is similar to setting the &lt;code&gt;X-Frame-Options&lt;/code&gt; header. It controls where a website may be embedded.&lt;/p&gt;&lt;p&gt;My site also sets the &lt;code&gt;block-all-mixed-content&lt;/code&gt; policy and the &lt;code&gt;upgrade-insecure-requests&lt;/code&gt; policy. The first policy disallows the loading of resources that are loaded with HTTP when the site is loaded via HTTPS. The second policy instructs the browser to also use HTTPS when a resource is actually linked via HTTP.&lt;/p&gt;&lt;h2 id=&quot;feature-policy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#feature-policy&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Feature-Policy&lt;/h2&gt;&lt;p&gt;Modern browsers provide a variety of features that can be used by a website. Examples for such features are access to microphones or cameras of the user. Normally all those features are available and could be used by any code that is running on a site. The &lt;code&gt;Feature-Policy&lt;/code&gt; header can be used to explicitly set the features that are used by a page. This prevents the site to use features that are not explicitly enabled. For my site, I disabled nearly every feature because it doesn’t use any of them.&lt;/p&gt;&lt;h2 id=&quot;referrer-policy&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#referrer-policy&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Referrer-Policy&lt;/h2&gt;&lt;p&gt;This header can be used to prevent the leak of sensitive information and provide visitors of the site with more privacy. Browsers usually send a &lt;code&gt;Referer&lt;/code&gt; [sic] header which contains the address of the previously visited page. This information can be quite useful for some features but unfortunately, it can also be used for tracking or other malicious activities. For my site, I set the header to &lt;code&gt;same-origin&lt;/code&gt;. This means that for requests on the same origin the whole address is set as &lt;code&gt;Referer&lt;/code&gt; header value but for requests to other sites, so-called cross-origin requests, no value will be set. This prevents other sites from tracking that visitors are coming from my site.&lt;/p&gt;&lt;h2 id=&quot;strict-transport-security&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#strict-transport-security&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Strict-Transport-Security&lt;/h2&gt;&lt;p&gt;My website should only be reached via TLS encryption. By setting the &lt;code&gt;Strict-Transport-Security&lt;/code&gt; header browsers know that the site should only be accessed with HTTPS. This is also known as HTTP Strict Transport Security (HSTS).&lt;/p&gt;&lt;p&gt;The header has the value &lt;code&gt;max-age=63072000; includeSubDomains; preload&lt;/code&gt;. This means that browsers should remember to only access the site via HTTPS for 63072000 seconds (2 years). All subdomains should also be loaded via HTTPS. The &lt;code&gt;preload&lt;/code&gt; attribute is special as it is actually not part of the specification. It allows the site to be included in the &lt;abbr title=&quot;HTTP Strict Transport Security&quot;&gt;HSTS&lt;/abbr&gt; list of browsers. By being included in the list browsers know that the site should be loaded via HTTPS only and they won’t even try to use HTTP. Actually all &lt;code&gt;.dev&lt;/code&gt; domains are automatically included in the list.&lt;/p&gt;&lt;h2 id=&quot;x-content-type-options&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#x-content-type-options&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; X-Content-Type-Options&lt;/h2&gt;&lt;p&gt;When the stylesheet &lt;code&gt;/styles/styles.css&lt;/code&gt; of this site is requested a &lt;code&gt;Content-Type&lt;/code&gt; header is sent along with the response. This header has the value &lt;code&gt;text/css; charset=utf-8&lt;/code&gt;. By looking at the value of the header the browser knows how the response of the server should be interpreted. In addition to looking at the value of the header browsers sometimes perform operations to determine the data type of the response. If this is the case the browser probably won’t use the content type from the header. This could potentially lead to the transformation of non-executable types to executable ones. By setting the &lt;code&gt;X-Content-Type-Options&lt;/code&gt; header the browser adheres to the value of the &lt;code&gt;Content-Type&lt;/code&gt; header and won’t perform any of those operations.&lt;/p&gt;&lt;h2 id=&quot;x-frame-options&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#x-frame-options&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; X-Frame-Options&lt;/h2&gt;&lt;p&gt;Some HTML elements like &lt;code&gt;&amp;lt;iframe&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;object&amp;gt;&lt;/code&gt; allow a website to embed another site. This can be used to make visitors think that they are on one site when the site is actually only embedded into another website. Consider for example an online banking site that is embedded into another malicious site. To prevent browsers from embedding my site I set the &lt;code&gt;X-Frame-Options&lt;/code&gt; header to &lt;code&gt;DENY&lt;/code&gt;.&lt;/p&gt;&lt;h2 id=&quot;x-xss-protection&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#x-xss-protection&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; X-XSS-Protection&lt;/h2&gt;&lt;p&gt;This header is not implemented in all browsers and support for it will probably be removed in the future. It instructs the browser what to do if it detects a cross-site scripting (XSS) attack. The value &lt;code&gt;1; mode=block&lt;/code&gt; instructs the browser to not render the page when an attack is detected.&lt;/p&gt;&lt;h2 id=&quot;conclusion&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/03/08/all-the-headers/#conclusion&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Conclusion&lt;/h2&gt;&lt;p&gt;It was amazing for me to see how many headers are available to secure websites. They provide fine-grained control over the security of websites. By understanding and using them properly the web can get saver for users and also the privacy of users can be protected.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Static Is Fantastic</title>
    <link href="https://watzek.dev/posts/2020/02/23/static-is-fantastic/" />
    <updated>2020-02-23T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/02/23/static-is-fantastic/</id>
    <content type="html">&lt;p&gt;Static site generators are fantastic. They make it easy to build websites without the need for server-side code or databases. Most static site generators work similarly. They take several templates and some content as input and output a folder with a generated website. Some generators are very opinionated about how the source of the website is structured, which template types can be used, or what can be used as content. Other static site generators take a different approach and are more open about how the website is built. For the redesign of my page, I chose &lt;a href=&quot;https://www.11ty.dev/&quot; rel=&quot;noopener&quot;&gt;Eleventy&lt;/a&gt; as static site generator.&lt;/p&gt;&lt;p&gt;Eleventy is written in JavaScript and uses the Node.js runtime. This makes it easy to customize the build by using packages from the vast selection offered by the npm software registry. But not only is Eleventy easily extensible it also provides a lot of functionality out of the box.&lt;/p&gt;&lt;p&gt;For my website, I decided to write my templates in the &lt;a href=&quot;https://mozilla.github.io/nunjucks/&quot; rel=&quot;noopener&quot;&gt;Nunjucks&lt;/a&gt; template language. It is a very powerful templating language and similar to other templating languages I used before. The template for the header of a blog post looks similar to the following example.&lt;/p&gt;&lt;pre class=&quot;language-handlebars&quot;&gt;&lt;code class=&quot;language-handlebars&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;h1&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;mb-0 font-bold&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;title&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;h1&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {% if description %}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-sm italic leading-tight&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;description&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    {% endif %}
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;p&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;text-sm&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;time&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;datetime&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;htmlDateString&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
            &lt;span class=&quot;token handlebars language-handlebars&quot;&gt;&lt;span class=&quot;token delimiter punctuation&quot;&gt;{{&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;date&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token variable&quot;&gt;dateDisplay&lt;/span&gt; &lt;span class=&quot;token delimiter punctuation&quot;&gt;}}&lt;/span&gt;&lt;/span&gt;
        &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;time&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;p&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is possible to print out data from the processed content like the &lt;code&gt;title&lt;/code&gt; or the &lt;code&gt;description&lt;/code&gt;. Additionally, Eleventy provides functionalities to create filters or functions that can be used by multiple template engines. In the example above the &lt;code&gt;htmlDateString&lt;/code&gt; or the &lt;code&gt;dateDisplay&lt;/code&gt; filter are such functions.&lt;/p&gt;&lt;p&gt;Eleventy does not only support a variety of template languages it also makes it possible to use different kinds of data. For example, JSON files can be used as a data source or even JavaScript scripts that return data from an external source like a web API.&lt;/p&gt;&lt;p&gt;In contrast to other static site generators, Eleventy does not provide tools to process assets like stylesheets, scripts, and images by itself. In my opinion, this is a feature. It allows Eleventy to concentrate on the job of generating a folder structure and HTML files from the given data sources. Processing of assets can be added by choosing tools from the npm software repository. I integrated tools that process my stylesheets and images into the build. For example, the great &lt;code&gt;postcss&lt;/code&gt; package enables me to use tools like &lt;code&gt;cssnano&lt;/code&gt; which minifies the size of my CSS. And for the optimization of images, I use &lt;code&gt;imagemin&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;With the templates and build process in place, it is now easy for me to write blog posts without interruptions. I have to create a new Markdown file in the &lt;code&gt;posts&lt;/code&gt; folder add a title, a date, and optionally a description as front matter and can start writing. The front matter of this article looks similar to the following snippet.&lt;/p&gt;&lt;pre class=&quot;language-yaml&quot;&gt;&lt;code class=&quot;language-yaml&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;title&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Static is fantastic&quot;&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;date&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token datetime number&quot;&gt;2020-02-23&lt;/span&gt;
&lt;span class=&quot;token key atrule&quot;&gt;description&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Generating my website statically.&quot;&lt;/span&gt;
&lt;span class=&quot;token punctuation&quot;&gt;---&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Deployments of a new version of the site are very boring (in a positive way). I have to take the folder that is produced from the build process and transfer it onto my webserver. I don’t have to restart any services, run migrations, or do anything else. In the future, I want to look at projects other than blogs to see how static site generation can be a useful alternative to dynamic server-side rendered sites.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>The Quest for the Website Accessibility Grail</title>
    <link href="https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/" />
    <updated>2020-02-15T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/</id>
    <content type="html">&lt;p&gt;One of the key requirements that I set for the redesign of my website is accessibility. For me, accessibility means to enable as many people as possible to use my website without any additional effort for them. Information that I provide should be available to everyone. I don’t want to erect any barriers that could prevent people from accessing my content.&lt;/p&gt;&lt;p&gt;But how can a website be made accessible for people with and without disabilities, mobile users, or people with slow internet connections? In the following, I will explain how I tried to make my website accessible to everyone.&lt;/p&gt;&lt;h2 id=&quot;without-structure-we-are-barbarians&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#without-structure-we-are-barbarians&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Without structure we are barbarians&lt;/h2&gt;&lt;p&gt;Using the right HTML elements provides a good base for an accessible website. The markup language HTML offers a multitude of elements or tags that can be used to express the meaning of the content of a website, but the right elements must be used in the right context. Often HTML elements are misused. For example, a web application could use &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; elements to trigger JavaScript handlers. Which would be wrong because the meaning of an &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element is it to introduce a hyperlink. The element that should be used instead is a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element.&lt;/p&gt;&lt;p&gt;Not only does HTML give meaning to the content it also structures the page. The following code block shows a simplified version of the markup of the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element of this page.&lt;/p&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;body&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Can you guess where the sections of this page are in the markup? The &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; element contains the main navigation of the page wrapped in &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; tags. Inside of the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element is the article you are currently reading. This element contains the primary content of a page and an HTML document may only contain one &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element that is not hidden. The &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; contains some links to additional information about me and the website. If you look inside the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element you will find that it is structured similarly to the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element. In the following code block, you find a simplified structure of the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; element.&lt;/p&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;header&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;div&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
    &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;footer&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
  &lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;article&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;/&lt;/span&gt;main&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here it is visible that the &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt; content contains an &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; element. An &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; element is used for self-contained content that can be reused on its own. An &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; element can have a &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; and a &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; just like the &lt;code&gt;&amp;lt;body&amp;gt;&lt;/code&gt; element. The difference is that these elements have to be related to the enclosing &lt;code&gt;&amp;lt;article&amp;gt;&lt;/code&gt; element. Assistive technologies like screen readers can make use of those elements to allow visitors easier navigation on the site.&lt;/p&gt;&lt;p&gt;Those points only scratch the surface of what has to be thought of when structuring the HTML of an accessible website. An excellent source of information is this &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Learn/Accessibility/HTML&quot; rel=&quot;noopener&quot;&gt;MDN article about accessible HTML&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;paint-me-a-picture-with-words&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#paint-me-a-picture-with-words&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Paint me a picture with words&lt;/h2&gt;&lt;p&gt;Another important practice is it to provide textual descriptions of images. This can be done by setting &lt;code&gt;alt&lt;/code&gt; attributes on &lt;code&gt;&amp;lt;img&amp;gt;&lt;/code&gt; elements.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/LxHccfaWsf-267.webp 267w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/LxHccfaWsf-267.jpeg&quot; class=&quot;undefined&quot; alt=&quot;Johann the rooster&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;267&quot; height=&quot;400&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;For example, the image above is represented in the HTML with the following code.&lt;/p&gt;&lt;pre class=&quot;language-html&quot;&gt;&lt;code class=&quot;language-html&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token tag&quot;&gt;&lt;span class=&quot;token punctuation&quot;&gt;&amp;lt;&lt;/span&gt;img&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;src&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;./src/images/rooster.jpg&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token attr-name&quot;&gt;alt&lt;/span&gt;&lt;span class=&quot;token attr-value&quot;&gt;&lt;span class=&quot;token punctuation attr-equals&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;Johann the rooster&lt;span class=&quot;token punctuation&quot;&gt;&quot;&lt;/span&gt;&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;/&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;There is an &lt;code&gt;alt&lt;/code&gt; attribute that describes what can be seen in the image. This description can be used by assistive technology so that information provided by images is not lost. But &lt;code&gt;alt&lt;/code&gt; attributes can also be useful for people with slow network connections that have disabled the loading of images. Useful tips about the usage of the &lt;code&gt;alt&lt;/code&gt; attribute can be found in this &lt;a href=&quot;https://a11yproject.com/posts/alt-text/&quot; rel=&quot;noopener&quot;&gt;article about the alternate text attribute from The A11Y Project&lt;/a&gt;.&lt;/p&gt;&lt;h2 id=&quot;grow-into-your-design&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#grow-into-your-design&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Grow into your design&lt;/h2&gt;&lt;p&gt;A practical approach to designing a website is to start from mobile screen sizes and to work your way up. This so-called mobile-first approach ensures that a website works on the device with the most limitations in terms of screen size and network bandwidth first. As the development continues and the screen size grows features can be added if necessary. This design process feels more natural than going form big screen sizes to small screens and often leads to better results than other approaches.&lt;/p&gt;&lt;p&gt;In terms of accessibility this means that the page uses an appropriate font size for smaller displays, links or buttons are big enough to be tapped on a touchscreen, and the site is also usable with a slow network connection.&lt;/p&gt;&lt;h2 id=&quot;be-fast&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#be-fast&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Be fast&lt;/h2&gt;&lt;p&gt;It is a privilege to have access to a fast network connection and a website should not be built in a way that renders it useless when accessed via a slower connection.&lt;/p&gt;&lt;p&gt;In my quest to make my website small I abstained from using big CSS frameworks or custom fonts. Additionally, I decided to generate this website statically which reduces the response time of the server.&lt;/p&gt;&lt;h2 id=&quot;look-at-the-colors&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#look-at-the-colors&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Look at the colors&lt;/h2&gt;&lt;p&gt;A website without colors can look pretty dull. But colors should not be chosen carelessly. There are many forms of color blindness and colors should be chosen to still be distinguishable from each other for people with such disabilities. Another requirement for colors is their contrast. The contrast between background and foreground must be high enough so the can be distinguished from each other.&lt;/p&gt;&lt;h2 id=&quot;question-everything&quot; tabindex=&quot;-1&quot;&gt;&lt;a class=&quot;header-anchor&quot; href=&quot;https://watzek.dev/posts/2020/02/15/the-quest-for-the-website-accessibility-grail/#question-everything&quot; aria-hidden=&quot;true&quot; rel=&quot;noopener&quot;&gt;#&lt;/a&gt; Question everything&lt;/h2&gt;&lt;p&gt;Because there are a lot of things to consider it is important to test if there are no errors that negatively influence the accessibility of the website. To do so there are automatic tools that can identify accessibility problems like &lt;a href=&quot;https://wave.webaim.org/&quot; rel=&quot;noopener&quot;&gt;WAVE&lt;/a&gt;, the &lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Tools/Accessibility_inspector&quot; rel=&quot;noopener&quot;&gt;Accessibility Inspector&lt;/a&gt; built into the Firefox developer tools, or &lt;a href=&quot;https://developers.google.com/web/tools/lighthouse&quot; rel=&quot;noopener&quot;&gt;Google Lighthouse&lt;/a&gt;. Unfortunately, not everything can be tested automatically and it is a good practice to do some additional manual testing. For example try to navigate the website with the keyboard only, use a screen reader to explore the website, surf around with a throttled network connection, and check if everything works on an older smartphone.&lt;/p&gt;&lt;p&gt;So in the spirit of questioning everything please let me know if you find any accessibility issues or other problems with the redesign of my website. Hopefully this article helped you to lear a bit more about web accessibility and don’t forget to apply the things you learned to websites you build yourself.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Image Processing Experiments (3) – F#</title>
    <link href="https://watzek.dev/posts/2019/10/17/image-processing-experiments-(3)-f/" />
    <updated>2019-10-17T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2019/10/17/image-processing-experiments-(3)-f/</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; My F# version of the image processing experiments may not be as short as the bash version but it is by far the most beautiful to look at. Find the source code in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;em&gt;The introduction to the image processing experiments can be found in my &lt;a href=&quot;https://watzek.dev/posts/2019-05-18-image-processing-experiments-(1)-rust&quot; rel=&quot;noopener&quot;&gt;first article of the series&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Just to recall the program should do the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Read in an image as the first argument passed to it on the command line.&lt;/li&gt;&lt;li&gt;Print the file path, width and height of the image to the console.&lt;/li&gt;&lt;li&gt;Display the original image and a blurred version of the image.&lt;/li&gt;&lt;li&gt;Convert the image to grayscale and save it besides the original image as &lt;code&gt;gray-IMAGENAME&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;After implementing a solution in Rust and in Bash I decided to build the final implementation with a functional programming language. The first language I wanted to use was &lt;a href=&quot;https://www.haskell.org/&quot; rel=&quot;noopener&quot;&gt;Haskell&lt;/a&gt; but I was not familiar with the development processes and tooling of the language and I had a deadline to hold which repelled me from using it. Just recently I had looked into F# and because my professional job has provided me with some expertise with the .NET ecosystem I chose F# as the language for the third version of the program.&lt;/p&gt;&lt;p&gt;Programming in F# or other functional languages requires a different thinking process opposed to non functional languages. Combined with the syntax of the language this leads to more concise and readable code. While the syntax of the language is very concise and abstains from the myriad of brackets that other languages require it initially is a bit tricky and the indent based delimitation of code blocks can lead to some funny frustrating errors. A great page to learn the basics of the F# syntax is &lt;a href=&quot;https://fsharpforfunandprofit.com/posts/fsharp-in-60-seconds/&quot; rel=&quot;noopener&quot;&gt;F# syntax in 60 seconds&lt;/a&gt; from F# for fun and profit by Scott Wlaschin.&lt;/p&gt;&lt;pre class=&quot;language-fsharp&quot;&gt;&lt;code class=&quot;language-fsharp&quot;&gt;&lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; showImage path &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt;
        &lt;span class=&quot;token keyword&quot;&gt;let&lt;/span&gt; imgProcessInfo &lt;span class=&quot;token operator&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;token function&quot;&gt;ProcessStartInfo&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt;
        imgProcessInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;UseShellExecute &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; &lt;span class=&quot;token keyword&quot;&gt;true&lt;/span&gt;
        imgProcessInfo&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;FileName &lt;span class=&quot;token operator&quot;&gt;&amp;lt;-&lt;/span&gt; path
        Diagnostics&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;Process&lt;span class=&quot;token punctuation&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;token function&quot;&gt;Start&lt;/span&gt;&lt;span class=&quot;token punctuation&quot;&gt;(&lt;/span&gt;imgProcessInfo&lt;span class=&quot;token punctuation&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;token operator&quot;&gt;|&gt;&lt;/span&gt; ignore&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;As an example for a function I wrote you can see the &lt;code&gt;showImage&lt;/code&gt; function that accepts a &lt;code&gt;path&lt;/code&gt; to an image as parameter. Some interesting operators are the assignment operator &lt;code&gt;&amp;lt;-&lt;/code&gt; which can assign values to variables and the forward pipe operator &lt;code&gt;|&amp;gt;&lt;/code&gt; which can be used to pass the result of a function on the left side to the function on the right side. The &lt;code&gt;ignore&lt;/code&gt; function that gets the result of the &lt;code&gt;Diagnostics.Process.Start(imgProcessInfo)&lt;/code&gt; call can be used to explicitly ignore the result of a function. If the ignore function would not be used in this case a compile time warning would occur that the result of the function is not used. This prevents implicit side effects and unhandled results of function calls.&lt;/p&gt;&lt;p&gt;Because F# is part of the .NET ecosystem all .NET libraries can be used by it. This made finding an image library fairly easy. I chose the great &lt;a href=&quot;https://github.com/SixLabors/ImageSharp&quot; rel=&quot;noopener&quot;&gt;SixLabors.ImageSharp&lt;/a&gt; library which provided all the functionalities I needed and more. Using this .NET library which is written in C# is a bit different than using native F# libraries. Functions need brackets around them and parameters are savagely separated by commas instead of spaces only. Apart from those small annoyances using the library was really easy and enabled me to quickly implement a solution for the problem at hand.&lt;/p&gt;&lt;p&gt;In this article I wrote a little bit about F# and using it to implement a simple program. This was the last article of the image processing experiments series. The source code for all the implementations can be found in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Image Processing Experiments (2) - Bash/ImageMagick</title>
    <link href="https://watzek.dev/posts/2019/06/19/image-processing-experiments-(2)-bashimagemagick/" />
    <updated>2019-06-19T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2019/06/19/image-processing-experiments-(2)-bashimagemagick/</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; My bash version of the image processing experiments that I did in three languages is by far the shortest. Find the source code in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;&lt;em&gt;The introduction to the image processing experiments can be found in my &lt;a href=&quot;https://watzek.dev/posts/2019-05-18-image-processing-experiments-(1)-rust&quot; rel=&quot;noopener&quot;&gt;first article of the series&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;&lt;p&gt;Just to recall the program should do the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Read in an image as the first argument passed to it on the command line.&lt;/li&gt;&lt;li&gt;Print the file path, width and height of the image to the console.&lt;/li&gt;&lt;li&gt;Display the original image and a blurred version of the image.&lt;/li&gt;&lt;li&gt;Convert the image to grayscale and save it besides the original image as &lt;code&gt;gray-IMAGENAME&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;After implementing a solution in Rust I decided to look into Bash and &lt;a href=&quot;https://imagemagick.org&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt; which led to the following script.&lt;/p&gt;&lt;pre class=&quot;language-bash&quot;&gt;&lt;code class=&quot;language-bash&quot;&gt;&lt;span class=&quot;token shebang important&quot;&gt;#!/bin/bash&lt;/span&gt;
magick identify &lt;span class=&quot;token parameter variable&quot;&gt;-format&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;Using image file: %f, width %w, height %h&lt;span class=&quot;token entity&quot; title=&quot;&#92;n&quot;&gt;&#92;n&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1}&lt;/span&gt;&quot;&lt;/span&gt;
magick convert &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-blur&lt;/span&gt; 0x5 - &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
     magick montage &lt;span class=&quot;token parameter variable&quot;&gt;-geometry&lt;/span&gt; +1+1 &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1}&lt;/span&gt;&quot;&lt;/span&gt; - - &lt;span class=&quot;token operator&quot;&gt;|&lt;/span&gt; &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
     magick display&lt;span class=&quot;token operator&quot;&gt;&amp;amp;&lt;/span&gt;
magick convert &lt;span class=&quot;token string&quot;&gt;&quot;&lt;span class=&quot;token variable&quot;&gt;${1}&lt;/span&gt;&quot;&lt;/span&gt; &lt;span class=&quot;token parameter variable&quot;&gt;-colorspace&lt;/span&gt; gray &lt;span class=&quot;token punctuation&quot;&gt;&#92;&lt;/span&gt;
     &lt;span class=&quot;token parameter variable&quot;&gt;-set&lt;/span&gt; filename:f &lt;span class=&quot;token string&quot;&gt;&quot;%d/gray-%f&quot;&lt;/span&gt; &lt;span class=&quot;token string&quot;&gt;&quot;%[filename:f]&quot;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The first thing in this Bash script is the so called &lt;em&gt;shebang&lt;/em&gt; &lt;code&gt;#!/bin/bash&lt;/code&gt;. This combination of a number sign and an exclamation mark which is followed by a path and optionally arguments is used by the operating system to determine the interpreter that should execute the script. In my case I want to execute the script with Bash and my Bash executable can be called via &lt;code&gt;/bin/bash&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In my script I am executing a series of ImageMagick &lt;code&gt;magick&lt;/code&gt; commands that use the first parameter &lt;code&gt;$1&lt;/code&gt; passed to the script as image path. The zero parameter &lt;code&gt;$0&lt;/code&gt; would return the name of the script. To access the value of the &lt;code&gt;$1&lt;/code&gt; parameter I use the &lt;code&gt;${1}&lt;/code&gt; syntax instead of just accessing the variable via &lt;code&gt;$1&lt;/code&gt; because it &lt;a href=&quot;https://stackoverflow.com/questions/8748831/when-do-we-need-curly-braces-around-shell-variables#8748880&quot; rel=&quot;noopener&quot;&gt;prevents errors&lt;/a&gt; if variables are used inside of strings.&lt;/p&gt;&lt;p&gt;ImageMagick provides the &lt;code&gt;identify&lt;/code&gt; command which can extract information from an image. The &lt;code&gt;-format&lt;/code&gt; flag can be used to customize which values are returned and how they are printed to the console.&lt;/p&gt;&lt;p&gt;In the third line (continued via &lt;code&gt;&#92;&lt;/code&gt; for readability) the input image gets blurred, combined with the original image, and then displayed. The &lt;code&gt;convert&lt;/code&gt; and &lt;code&gt;montage&lt;/code&gt; commands do not write the generated image to a file but instead output the image to &lt;code&gt;stdout&lt;/code&gt; via the &lt;code&gt;-&lt;/code&gt; parameter. By using pipes &lt;code&gt;|&lt;/code&gt; the output of one command is passed to the input of the next command. Another operator that is used at the end of the line is the ampersand &lt;code&gt;&amp;amp;&lt;/code&gt; operator. This operator instructs the shell to fork an asynchronous sub-shell which executes the command while the parent-shell continues to execute the rest of the script. By using this operator the script is not waiting on the &lt;code&gt;display&lt;/code&gt; command but goes on to execute the last line of the script.&lt;/p&gt;&lt;p&gt;By using the mighty &lt;code&gt;convert&lt;/code&gt; command the image can be easily converted to grayscale. The &lt;code&gt;-set&lt;/code&gt; parameter allows the creation of a template that is used to determine the output file path and name by combining the original directory &lt;code&gt;%d&lt;/code&gt; with the original file name &lt;code&gt;%f&lt;/code&gt;. Therefore the grayscale image will be saved besides the original image but with the filename prefixed with &lt;code&gt;gray-&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;In this article I wrote about some features of Bash and ImageMagick. Hopefully you found it interesting. The next article of this series will discuss my solution in F#. The source code for all the implementations can be found in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Image Processing Experiments (1) - Rust</title>
    <link href="https://watzek.dev/posts/2019/05/18/image-processing-experiments-(1)-rust/" />
    <updated>2019-05-18T00:00:00Z</updated>
    <id>https://watzek.dev/posts/2019/05/18/image-processing-experiments-(1)-rust/</id>
    <content type="html">&lt;blockquote&gt;&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; I implemented an image processing experiment in Rust and was positively surprised. Find the source code in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;.&lt;/p&gt;&lt;/blockquote&gt;&lt;p&gt;In one of my courses I recently had to implement a program that satisfies the following criteria in three programming languages.&lt;/p&gt;&lt;p&gt;The program should do the following:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Read in an image as the first argument passed to it on the command line.&lt;/li&gt;&lt;li&gt;Print the file path, width and height of the image to the console.&lt;/li&gt;&lt;li&gt;Display the original image and a blurred version of the image.&lt;/li&gt;&lt;li&gt;Convert the image to grayscale and save it besides the original image as &lt;code&gt;gray-IMAGENAME&lt;/code&gt;.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;There was no constraint on the programming language that we used so i decided to implement my solution in &lt;a href=&quot;https://www.rust-lang.org/&quot; rel=&quot;noopener&quot;&gt;Rust&lt;/a&gt;, Bash (with &lt;a href=&quot;https://imagemagick.org&quot; rel=&quot;noopener&quot;&gt;ImageMagick&lt;/a&gt;), and &lt;a href=&quot;https://fsharp.org/&quot; rel=&quot;noopener&quot;&gt;F#&lt;/a&gt;. I dabbled in each of those languages a bit before but none of them can be considered a language in which I am fluent in.&lt;/p&gt;&lt;p&gt;In this article I would like to discuss my takeaways from implementing the assignment in Rust.&lt;/p&gt;&lt;p&gt;&lt;picture&gt;&lt;source type=&quot;image/webp&quot; srcset=&quot;https://watzek.dev/images/jhd1C6bYBZ-300.webp 300w, https://watzek.dev/images/jhd1C6bYBZ-600.webp 600w, https://watzek.dev/images/jhd1C6bYBZ-800.webp 800w, https://watzek.dev/images/jhd1C6bYBZ-1200.webp 1200w&quot; sizes=&quot;100vw&quot;&gt;&lt;img src=&quot;https://watzek.dev/images/jhd1C6bYBZ-1200.svg&quot; class=&quot;undefined&quot; alt=&quot;Ferris the crab, unofficial mascot for Rust&quot; loading=&quot;lazy&quot; decoding=&quot;async&quot; width=&quot;1200&quot; height=&quot;800&quot;&gt;&lt;/picture&gt;&lt;/p&gt;&lt;p&gt;Rust is a systems programming language that can be compared with C or C++ in that it does not run your program inside of a runtime or use a garbage collector. It can be used when performance is one of the main concerns but in opposite to C or similar languages it provides a memory management model that leads to memory and thread safety.&lt;/p&gt;&lt;p&gt;The installation instructions that are provided on the &lt;a href=&quot;https://www.rust-lang.org/&quot; rel=&quot;noopener&quot;&gt;Rust website&lt;/a&gt; worked without any problems on my Laptop that runs &lt;a href=&quot;https://www.archlinux.org/&quot; rel=&quot;noopener&quot;&gt;Arch Linux&lt;/a&gt;. I used &lt;a href=&quot;https://code.visualstudio.com/&quot; rel=&quot;noopener&quot;&gt;VS Code&lt;/a&gt; with the &lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=rust-lang.rust&quot; rel=&quot;noopener&quot;&gt;Rust (rls)&lt;/a&gt; extension to implement the assignment.&lt;/p&gt;&lt;p&gt;Rust has a package manager named Cargo that can be used to manage dependencies (in Rust slang those are called &lt;em&gt;crates&lt;/em&gt;) of the application - much like NPM for JavaScript or Nuget for .NET. In addition to package management Cargo can be used to run and build the project.&lt;/p&gt;&lt;p&gt;I did not want to write the image loading and transformation code from scratch so I looked for libraries that would do that task for me. I stumbled upon the &lt;a href=&quot;https://crates.io/crates/image&quot; rel=&quot;noopener&quot;&gt;image crate&lt;/a&gt; which can be used to work with images in rust. In addition to the image crate I found the &lt;a href=&quot;https://crates.io/crates/imageproc&quot; rel=&quot;noopener&quot;&gt;imageproc crate&lt;/a&gt; which extends the image crate with image processing functionalities. To fulfil the 3rd point of the requirements “Display the original image and a blurred version of the image.”&lt;/p&gt;&lt;p&gt;I wanted to use a GUI library or bindings to existing GUI libraries. I tried to use the GTK bindings for Rust but because of my lack of experience with Rust and GTK things got complicated really fast and I thought that there must be an easier way to display two images. As it is often the case the simple path was right in front of me the whole time. The imageproc crate provides the method &lt;code&gt;imageproc::window::display_multiple_images&lt;/code&gt; which was exactly what I needed. If you think that just adding the library to the dependencies file (&lt;code&gt;Cargo.toml&lt;/code&gt;) and calling the method would be enough you are wrong. I had to learn the hard way that crates can have feature flags that enable certain additional features of a dependency. In this case the &lt;code&gt;display-window&lt;/code&gt; flag has to be added which will internally add a dependency to &lt;a href=&quot;https://crates.io/crates/sdl2&quot; rel=&quot;noopener&quot;&gt;sdl2&lt;/a&gt; which provides rust bindings for &lt;a href=&quot;https://www.libsdl.org&quot; rel=&quot;noopener&quot;&gt;Simple Directmedia Layer&lt;/a&gt;. Going down this rabbit hole has shown me once again how important it is to be able to read other people’s code or I would never have found the definition of the features inside of the source code of the imageproc crate.&lt;/p&gt;&lt;p&gt;After I had all the libraries and a working development environment it was surprisingly easy to implement the task. Two things that seem to be common in Rust were &lt;code&gt;Option&lt;/code&gt; types returned by functions and macros like &lt;code&gt;println!&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;The &lt;code&gt;Option&lt;/code&gt; type can be seen as an alternative to returning &lt;code&gt;null&lt;/code&gt;. It can either have a value &lt;code&gt;Some&lt;/code&gt; or it can have no value &lt;code&gt;None&lt;/code&gt;. I recognized the construct from functional languages like F# or Haskell. The great thing about option types is that they make it explicit that a function could return nothing aka &lt;code&gt;null&lt;/code&gt; and that those cases should be handled or else you end up in the &lt;code&gt;WhoopsieThereIsNothingException&lt;/code&gt; club (aka &lt;code&gt;NullPointerException&lt;/code&gt; club or &lt;code&gt;NullReferenceException&lt;/code&gt; club).&lt;/p&gt;&lt;p&gt;Macros in Rust can be compared to function templates that will be replaced with a piece of code at compile time. Therefore the &lt;code&gt;println!&lt;/code&gt; macro can actually parse its format string and error if it is not given enough parameters.&lt;/p&gt;&lt;p&gt;This is the first article I wrote and I hope it contained something useful for you. I will follow up with articles about the two other implementations in Bash and F#. The source code for all the implementations can be found in this repository: &lt;a href=&quot;https://github.com/tobiaswatzek/image-processing-experiments&quot; rel=&quot;noopener&quot;&gt;tobiaswatzek/image-processing-experiments&lt;/a&gt;.&lt;/p&gt;</content>
  </entry>
</feed>