Noah W.org

Noah's Blog

Noah W.'s blog is full of technological exploration, findings, programming, and the life of a young developer.


New Site Feature

I know it's been a bit quiet here the last few weeks and I've been quite busy working on a few updates to Furthermark and to my website. In the meantime, I've introduced a new dark mode theme to Noah W.org. It works by toggling between two CSS themes and determining which one to use by storing a cookie.

Access the new feature by toggling modes by clicking (or tapping) on the sun or moon icon in the top-right of the page.

Note: The cookie stored is only used to identify the theme and not used to track you at all. I'm not smart enough to try to track someone anyway.

Published: 12/15/2018 7:42PM

Article by: Noah Wood

Furthermark

In a recent blog post on my website redesign, I remarked that I had been using Markdown to write my blog posts. I have come to quite like Markdown as a format to use to write sine it keeps my hands on the keyboard and enables an easy way to format my posts without the need to use a full-featured work processing application.

I have been using MarkdownPad as my markdown editor as it appeared to be the only good editor available on Windows (side note: why is it that all of the interesting markdown development is happening on macOS?). The problem with MarkdownPad is that it hasn't been updated in some time (it seems new development has stopped) and on Windows 10 it requires some very specific developer library to be installed in order to get the live preview functionality to work. I haven't seen any good UWP markdown editors in the Windows Store, so I thought: why don't I make one myself?

Futhermark

Enter Furthermark. I created Furthermark as a simple UWP markdown editor that uses some of my favorite features of MarkdownPad. Namely:

  • A simple toolbar with common functions
  • Ability to insert a timestamp (I actually use this a lot)
  • A status bar to show common text editor items
  • An easy way to resize the live preview

I think I came pretty close. There's still a long road ahead, but I think I have a pretty good version 1.0.

Furthermark main interface

UWP is an interesting platform to develop for; the sandboxing and the built-in controls don't have the flexibility that Win32 or traditional .NET development allow for. As an application that will only target desktop platforms, Furthermark had to sacrifice some "traditional" application paradigms in order to get the results I wanted.

The biggest example is the resize slider for the preview window. Most windows control libraries (WinForms, WPF, MFC) have a control for a resize panel or a slider. UWP does not. I could (and would like to in a future version) implement my own control that behaves similarly to a resize panel, but it would take a lot of extra effort. So for now, I implemented a slider control to change the size. Not ideal, but it works.

The other example is the status bar. I wanted a status bar to show

  1. Word count
  2. Line count
  3. Character count/length
  4. Current line number and column

These are things that a lot of people expect from a text editor. Common editors from Notepad to Microsoft Word have some variation of these features. Should be pretty easy, right? As it turns out the status bar became the hardest part to implement. Getting the bar with no content was easy. Furthermark uses a single frame with a UWP Grid control, so my "status bar" is just the last row in my grid. Done.

The hard part were the items inside the status bar. In my first out of (hopefully) many posts, I want to outline how I got the status bar working the way I wanted it to. I plan to opensource Furthermark once I get it working the way I want and clean up some of the code.

Also, I want to warn you all, I wanted to brush up on some of my modern .NET Syntax, and I was lacking in the VB.NET arena, so as a challenge to myself, I wrote Furthermark in VB.NET. If you don't like it, you certainly don't have to keep reading.

Word Count

This was probably one of the easier ones. I made a read-only property for wordcount that I raise a property changed notification for when the text changes. This property calls a RegEx match to split out on certain types of whitespace. It's fast and fairly accurate.

Public ReadOnly Property WordCount As String
    Get
        Dim EditorString As String, Count As Integer = 0
        Editor.Document.GetText(Windows.UI.Text.TextGetOptions.None, EditorString)
        If Not String.IsNullOrWhiteSpace(EditorString) Then
            Dim col As MatchCollection = Regex.Matches(EditorString, "[\S]+")
            Count = col.Count
        End If
        Return Count.ToString("N0")
    End Get
End Property

Total Lines & Total Length

These two were the easiest. The RichEditBox control I'm using for the editor has methods to get the line count (and strings have a length property). All I do is set a private Integer variable to the line count and length and have read-only public properties to call ToString("N0") on these in order to give myself the comma separator for when we go over one thousand lines or characters.

The Status Notification

When you save the document or copy the HTML of the Markdown, you get a notification that fades in and out temporarily from the status bar. I have a method to raise a notification:

Private Async Sub ShowStatusNotification(ByVal Text As String)
    StatusText = Text
    RaisePropertyChanged(NameOf(StatusText))
    RaiseStatusText.Begin()
    Await Task.Delay(4000)
    ExitStatusText.Begin()
End Sub

I have a storyboard in the XAML to fade in and out over a duration of 1000ms (one second). This method sets the status text, brings in the text, waits for 4000ms (four seconds) and then fades it back out.

<StackPanel.Resources>
    <Storyboard x:Name="RaiseStatusText">
        <FadeInThemeAnimation TargetName="StatusTextTextBlock" Duration="1000"/>
    </Storyboard>
    <Storyboard x:Name="ExitStatusText">
        <FadeOutThemeAnimation TargetName="StatusTextTextBlock" Duration="1000"/>
    </Storyboard>
</StackPanel.Resources>

Ln & Col

This one wasn't a complete necessary, but I really wanted to have this implemented. The most simple text editor on Windows (Notepad) has this feature (event if it wasn't as simple as it seemed) and I wanted it too. My problem was overthinking the math behind trying to figure this out, and the inconsistent nature of pulling the text from the RichEditBox. The most important thing I learned was getting the text and replacing the line endings to use Lf rather than the Windows default of CrLf.

The next step was splitting the text and getting the cursor position to determine where in the line the cursor was.

Dim Col As Integer, LastNewLine As Integer, Text As String
ThisEditor.Document.GetText(TextGetOptions.UseLf, Text)
Col = ThisEditor.Document.Selection.EndPosition
While Col > Text.Length And Col > 0
    Col -= 1
End While
Text = Text.Substring(0, Col)
LastNewLine = Text.LastIndexOf(vbLf)
Me.Col = Col - LastNewLine
RaisePropertyChanged(NameOf(Col))

_Line = Text.Count(Function(a) a = CChar(vbLf)) + 1
RaisePropertyChanged(NameOf(Line))

What I do here is:

  1. Get the position of the cursor (if there text selected, get me the last position of the selected text)
  2. Subtract the column variable to equal either zero or the length of the document, whatever comes first (this is due to the CrLf issues I've had with this control)
  3. Get the text up to the position of the cursor
  4. Get the position of the last newline character in this substring
  5. The column cursor position in the line is knowable by taking the position of the column in the document and subtracting it by the position of the last newline in the document
  6. The line position is as easy as counting the number of newlines in the substring and adding 1 (since this is a zero-based number)

INS/OVR

This is the one item I actually really didn't want to have, but unfortunaly the RichEditBox control supports using the Insert key to change the text typing mode from insert to overwrite, and I wanted a way to show the user how the editor was going to behave. This wasn't super hard to implement in the end. It turns out the RichEditBox control will show what the type mode is as a enumeration flag when looking at the Document's Options property. So I have an event handler on the PreviewKeyDown event of the edit box (regular KeyDown doesn't register the "Insert" key) to set a private variable.

_IsOverwrite = Not ThisTextBox.Document.Selection.Options.HasFlag(SelectionOptions.Overtype)
RaisePropertyChanged(NameOf(InsOvr))

Then a read-only string property will get the text result of the status.

Public ReadOnly Property InsOvr As String
    Get
        Dim Result As String = "INS"
        If _IsOverwrite Then
            Result = "OVR"
        End If
        Return Result
    End Get
End Property

The Future

Like I mentioned above, I'd like to make the application opensource at some point, but I'm not quite ready for the world to see my mess of code. I will also post an update once the application is available on the Windows store, it's currently going through the certification process. I will post updates here on my blog as well as on the official website Furthermark.com.

Published: 7/9/2018 9:04PM

Article by: Noah Wood

Website Redesign

On March 23rd, I was finally able to update my website with a new version I had been working on. My previous website was old, and written in ASP.NET 3 Web forms using VB.NET (as an aside, I have no problem with VB.NET, it compiles to the same bytecode as C#, sometimes I actually like the syntax too). Yuck. When I first began the previous implementation of the site, I had only just started learning .NET and the only other backend web technology I knew was PHP at the time. A lot has changed since then and it was time for a more modern look.

My goals with the new design were to:

  1. Create a new design starting from zero; a new color scheme and layout were to be had
  2. Use a more modern web technology with a focus on speed both for development and implementation
  3. Integrate my blog while using Markdown to convert my posts to HTML
  4. Have my blog's admin section secured using proper cookies and authentication standards
  5. Use as little client-side code as possible

I'd like to dig into each of these goals in a bit more detail and explain my thought process behind them.

Design

The Design I had was showing its age. I wanted something simple, modern, and I wanted it to use a dark green color. I had always liked the design of (the now defunct site) Newsvine. So I found a color scheme that used dark green for the header and a light grey for the background. The header is simple, with the links following as buttons and as a "hamburger" menu for the mobile version.

Web Technology

When Microsoft announced .NET Core, I was excited. I had become pretty familiar with .NET/C#/VB.NET over the years and was happy to hear there was a version that was both cross-platform and even faster than the .NET Framework implementation. I chose to use .NET Core (in C# this time, so calm down) and implement a hybrid approach and use both MVC where it makes sense and Razor pages for everything else. Essentially the MVC approach was used for the blog and the admin console and pages for everything else. Starting with an empty project makes this pretty trivial to implement.

While I could have learned something new, I'm not particular enamored with today's web technologies. I was never a fan of writing Javascript as a server-side language and most of the newest trends seem to focus on that metaphor: using Node.js and some library like Angular or React. There are certainly other choices such as Ruby, modern PHP, Java, or Python as examples. However as my server runs Windows Server anyway, ASP.NET Core seemed appropriate to learn modern .NET for the web.

Markdown

I use Markdown to write my blog posts. I wanted my blog engine to store the markdown post in the database and pull it, and convert it into HTML on-the-fly. Using Markdig made this easy. A simple (razor) call to the library worked.

@Html.Raw(Markdown.ToHtml(Model.Post.PostContent, null))

That was it. Problem solved.

Admin Section

There is an admin section. It's not complete yet, but it's there. There is currently no way to register (aside from manually inserting into the database) to be a User, but the login works well. I'm using salted Blowfish encryption. Much like Markdig made markdown easy, BCrypt.NET-Core makes it easy to implement this encryption. Using a combination of:

  • Strong, salted, encryption
  • Anti-forgery tokens built into .NET
  • HTTPS

I think I have a solid implementation of a secured area of my site. The built-in methods for authentication in .NET Core make using cookies to control authorization quite easy.

Client-Side Code

The web in 2018 is awful. Going to almost any website requires downloading of countless trackers and libraries. It's bad enough when websites need 30 KB of jQuery and 140 KB of Bootstrap just to render a couple of small modules; the web today requires tens of trackers and javascript libraries that add up to pages that are slow and unmanageable. It is 2018 and HTML hasn't changed much in twenty years: why are websites so slow?

Too many Items!

Going to The Verge loads (according to Ghostery) 44 trackers. Loading the console in Firefox shows request after request after request. We have all been to websites where the site seems to load slowly, piece by piece. This is the world we live in. Today's "modern" website requires a large amount of Javascript that—in my opinion—ruins the experience of the web.

This site uses one Javascript function, the only need is to hide/show the mobile menu when the "hamburger" is tapped. That's it. All of the CSS is my own except (and there always is an exception) two Google Fonts (Inder and Lusitana) and the Font Awesome library for the "brand" icons in the menu bar.

The Future

Going forward, as I finish up the final pieces of the backend site, I'd like to open source some of my code. Look forward to part two of this post in the near future!

Published: 3/31/2018 12:08PM

Article by: Noah Wood

Prev | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Next