Added truncated post excerpt with markdown rendering to main home page feed of posts

Learning point: Features I thought was pretty simple seem to end up being 2-3x more complex and 10x more time and effort to solve. This was one of them. Just do a simple truncate filter to create a post excerpt, and just {{ post.content | truncate }}, right? NOOOO. Because I did that and realised that it wouldn't render markdown which I use in the app πŸ€¦β€β™‚οΈ So I had to find a way to first render the content in md, then truncate. But the truncate package only limit characters and not words, so I had to write another replace() function for it! 😩

------------

Notes to self about how to read the below regular expression:
-- A regular expression is a sequence of characters that forms a search pattern. The search pattern can be used for text search and text replace operations.
-- a literal regex is enclosed within fwd slashes //
-- ^ is a boundary-type assertion = the start of the input
-- () captures a group of characters and remembers it, in this case, to rem for the excerpt
-- . matches any single character except line terminators, eg /.y/ matches "my" and "ay", but not "yes"
-- {n} is a quantifier where "n" is a positive integer, matches exactly "n" occurrences of the preceding item "x". For example, /a{2}/ doesn't match the "a" in "candy", but it matches all of the "a"'s in "caandy", and the first two "a"'s in "caaandy". In this case, matching 440 chars after the start
-- [^xyz] is a negated char set, matches anything that is not enclosed in the brackets. in this case
-- \s is a char class that matches a single white space character, including space, tab, form feed, line feed, and other Unicode spaces
-- * is a quantifier matches the preceding item "x" 0 or more times, eg bo* matches "boooo" in "A ghost booooed" and "b" in "A bird warbled", but nothing in "A goat grunted".
-- ().* is to match to whatever that's left of truncatedMdtext after the initial 440 char group (because the whole regex has to match up with truncatedMdtext and we can't forget to account for them in the searchvalue). eg. the pattern /ab*c/: the * after "b" means "0 or more occurrences of the preceding item." In the string "cbbabbbbcdebc", this pattern will match the substring "abbbbc".
-- '$1' is to access the first result match from the captured group of chars within (). Matches are accessed using the index of the the result's elements ([1], ..., [n]) or from the predefined RegExp object's properties ($1, ..., $9).
-- , '$1' means to replace with a string of the first match from the regex, where string.replace(searchvalue, newvalue), where searchvalue is the required value, or regular expression, that will be replaced by the new value, while newvalueis the required value to replace the search value with
== hence in layman, the regex = search from start of truncatedMdtext, remember first 440 chars of truncatedMdtext excluding all single white spaces, and replace truncatedMdtext with the remembered result
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Cheatsheet
-- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/n