Available For Hire

I am currently available for new projects and employment opportunities.

I am also open to taking on workflow overflow that you may need help with, but not necessarily are looking to hire for a contract term or direct placement.

My specialties are:

  • C#
  • HTML5
  • Microsoft Azure
  • Angular
  • AngularJS
  • JavaScript
  • TypeScript
  • Node.JS
  • jQuery
  • SQL
  • MySQL
  • WordPress

I also have familiarity with:

  • PHP (non-WordPress)
  • Python
  • Amazon AWS

For onsite work, I'm looking to stay in the northern Seattle area, including Eastside (Redmond/Bellevue/Kirkland), and Everett, and am also looking in the Bellingham WA area as well.

I will consider relocation to the Los Angeles CA or San Diego CA areas for the right opportunities.

I am very open to remote opportunities as well.

Contact me at my LinkedIn for details, rates, and questions. If making a connection request, please write a note or message with your details.

Slight Refocus

I'm going to be refocusing NodeOmega just a bit. For the few posts that existed up to this point, it's been mainly a technical blog. Not everyone's a programmer, of course. And of course, I also like a great many more things, such as music, games, movies, that sort of thing.

In the coming months, I hope to write quite a bit more on technical topics and more, I suppose you could say "nerdy/geeky" topics.

I don't have a comment section set up here, so take that as you will. I'll have to gauge what will be appropriate to put here. Maybe even some editorials on various topics like a former blog I once had (which I still have the files for, but I'd rather not repost those. I don't think the content has aged well).

I'll also need to update my portfolio a bit, I should have a couple more recent projects to post on that. Really, I need to actually reformat that entire thing. It's a bit stale.

At any rate, it's time to spread wings and soar. Now, a good question: Is it wise to attempt making an omelette out of phoenix eggs?

I'll let you ponder that. Good night. Or morning, rather.


A gotcha I found with Font Awesome's CDN

Something I was running into when I was programming something involving dynamic classes in the DOM using AngularJS's ngClass... The Font Awesome icon wasn't loading.

It was quite strange, as if I had the class declared on page load, it would show the icon.

As I delved deeper into this, and looked at the active page source in Chrome's developer tools, I noticed that instead of the DOM element with a class name, I was seeing SVG.

Well, guess what: In my efforts to asynchronize a few things, I had used the SVG & JS method of using their deferred script tag:

<script defer src="" integrity="sha384-4oV5EgaV02iISL2ban6c/RmotsABqE4yZxZLcYMAdG7FAPsyHYAPpywE9PJo+Khy" crossorigin="anonymous"></script>

As you can see, this was not the best decision for my use-case.

Gotta use the Web Fonts/CSS method:

<link rel="stylesheet" href="" integrity="sha384-hWVjflwFxL6sNzntih27bfxkr27PmbbK/iSvJ+a4+0owXq79v+lsFkW54bOGbiDQ" crossorigin="anonymous">

Note that 5.2.0 was their current version. Your mileage may vary.

So yeah, if you're going to do dynamic class adding or deletion to anything in your web project, and you want to use their CDN, best use the CSS version. Otherwise, you could wind up spending upwards of three hours wondering why the icon's not showing up and thinking AngularJS isn't rendering the class change or something.

(It was late and I was tired. Shuddup).

Forgive the recent downtime

You'll have to excuse the recent downtime.

My previous webhost moved my domain to a new server without notifying me ahead of time (they notified me after the fact), and in the process, also moved my hosted email to another server as well. This not only resulted in unexpected downtime, but also lost email I did not have the chance to download before they did (so I lost two days).

As a result, I am moving as much over to the Azure platform as possible and have already transferred my DNS to allow for this to come back up. It took a few days longer than expected.

But as I have seen that the MVC and iCal article was popular, I did what I could to bring this domain back up at least. I think that particular article is helping people out, and now that I have this page up again on a new provider, I hope it will continue to help people out.

Thankfully I kept data sources separate from the previous web host. So all I had to do was point DNS, wait a little, and we were good to go.

Email will be a different story. But hey, as much spam was making it through unfiltered, no big loss, right?

Pseudo-Linear Gradient Top and Bottom Borders in CSS3 and LESS

While doing yet another tweak to my portfolio, I decided to see if there was a native way in CSS to get a linear gradient on a border of an element. Inherently, no. Not really.

However... there is a way to mimic this effect in CSS3 for the top and bottom borders, through the magic of the ::before and ::after pseudoclasses.

For the purposes example, I'm going to put this in LESS as well, as that's how I did it. You can use pure CSS3 if you so desire, however.

In this example, we are using a <div> element with the class foo, and having it stretch across the screen with negative margins to get around the padding-left and padding-right inherent in Bootstrap's container-fluid class.

<div class="foo">
    <p>Here is some content</p>
    <p>Here is some more content.</p>

Now, let's set up the initial CSS for foo:

.foo {
    background: linear-gradient(darken(@white, 30%), darken(@white, 5%), darken(@white, 30%));
    margin-left: -15px;
    margin-right: -15px;
    padding: 0 25px;

But we don't have our borders... or linear-gradient top and bottom borders for that matter. Well, this is where the ::before and ::after pseudo-classes come in!

.foo::before {
    display: block;
    height: 2px;
    width: calc(~"100% + 50px");
    margin: 0 -25px 10px;
    background: linear-gradient(to left, lighten(@black, 5%), lighten(@black, 15%),lighten(@black, 60%),lighten(@black, 5%));
content: "";

.foo::after {
    display: block;
    height: 2px;
    width: calc(~"100% + 50px");
    margin: 10px -25px 0;
    background: linear-gradient(to left, lighten(@black, 5%), lighten(@black, 15%),lighten(@black, 60%),lighten(@black, 5%));

What happens is, before the <div>, the ::before is creating a block-level element with a height of 2px, a width of 100% plus the 50px to ensure our border will be long enough to compensate for the 25px padding, and setting margins for no top margin, side margins of -25px (to further compensate for the main <div>'s padding), and a 10px bottom margin to emulate what would have been a top padding of 10px on the main div. (Note: in LESS, you will need a tilde and to put the 100% + 50px in quotes, otherwise the LESS compiler will interpret it as 150%, and that tends to cause horizontal scrollbars to show up, which we don't want. Pure CSS3 if using a .css file will not require that.).

IMPORTANT: The content: ""; part is VERY important. Without that line in your ::before and ::after, the pseudo-gradient border will not render!

The ::after on the <div> is similar but uses the margin of 10px for the top (to simulate 10px bottom padding on the main <div>), -25px side margins as before, and no bottom margin.

The linear-gradient on the background for the ::before and ::after is where the magic happens. You can set this gradient to be what you want. In this case, I'm lightening a LESS class of @black, or hex color code #000000 (or #000 if you want the shorthand notation).

Below is the non-LESS CSS version of the above:

.foo {
  background: linear-gradient(#b3b3b3, #f2f2f2, #b3b3b3);
  margin-left: -15px;
  margin-right: -15px;
  padding: 0 25px;

.foo::before {
  display: block;
  height: 2px;
  width: calc(100% + 50px);
  margin: 0 -25px 10px;
  background: linear-gradient(to left, #0d0d0d, #262626, #999999, #0d0d0d);
content: "";

.foo::after {
  display: block;
  height: 2px;
  width: calc(100% + 50px);
  margin: 10px -25px 0;
  background: linear-gradient(to left, #0d0d0d, #262626, #999999, #0d0d0d);
content: "";

Feel free to copy it into your code and try it out. You may need to adjust width if you're not using Bootstrap, or not using container-fluid in your Bootstrap code (or if you've adjusted the margins and padding on those classes).

Slight Redesign

Not much to report on. Just gave the site another slight redesign. Decided the reflective header was too large so I opted for the current logo placement.

I've also redone the Portfolio again somewhat, and experimented somewhat more with some CSS3 techniques. It's actually pretty cool. I'll be making another entry later in the day to explain how to do a pseudo-gradient border (top and bottom only) in CSS3 for elements.

Durandal and Google Analytics

Something I've noticed in the development of a recent application of mine, (the new front-end for DracoTal, was that Google Analytics wasn't recording all of the hits in the real-time monitor when testing on my iPhone. Granted, I had a filter set up for my home IP, but browsing through my cell network should have been landing more hits than it should have.

As it turns out, Google Analytics was only seeing the initial page hit, but not the further navigation. At least, not until the user reloaded the page, and then it would only grab the hit for the page that was reloaded. This was a bit of a problem as I wanted to see all of the hits during navigation! Fortunately I managed to get through this problem.

For the purposes of describing how I resolved this, I'll make the assumption that the code is using ASP.NET MVC as the front-end template, like say if you're using the Durandal Starter Kit as a template.

In the Home/Index.cshtml view, I had to ensure that I had this code in the head section of the HTML:

    Layout = null;
<!DOCTYPE html>
    <meta charset="utf-8" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <meta name="format-detection" content="telephone=no" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="fragment" content="!">
    <meta name="p:domain_verify" content="390ee599d576a99e7419550da9282ae5" />
    <link rel="icon" href="" />
<!-- snip irrelevant metas -->
    <!-- Google Analytics -->
        (function (i, s, o, g, r, a, m) {
            i['GoogleAnalyticsObject'] = r; i[r] = i[r] || function () {
                (i[r].q = i[r].q || []).push(arguments);
            }, i[r].l = 1 * new Date(); a = s.createElement(o),
            m = s.getElementsByTagName(o)[0]; a.async = 1; a.src = g; m.parentNode.insertBefore(a, m);
        })(window, document, 'script', '//', 'ga');

        ga('create', 'UA-{my Google Analytics ID}', '{specific site}');
        ga('send', 'pageview');

    <!-- End Google Analytics -->

    <script type="text/javascript">
        if (navigator.userAgent.match(/IEMobile\/10\.0/)) {
            var msViewportStyle = document.createElement("style");
            var mq = "@@-ms-viewport{width:auto!important}";

That {specific site} part turned out to be very important. The default in Google's example was 'auto', which didn't work for me.

Anyways, the next step. One example by Peter Morlion I did see that referenced Durandal and Google Analtyics seemed to put the next code in the router activate method in the shell.js file like this:

router.on('router:navigation:complete', function (instance, instruction) {
    ga('create', 'UA-XXXXXXX-X', '');
    ga('send', 'pageview');

I have opted not to use the create line, and I wanted more control over the page title that would be sent, as the way it was, it going to be the default title not formatted for parameters in the Products page (which I wanted to record when they were hit). If not for this, I could have left it in the above call. For my purposes, instead of placing my solution in the router.on('router:navigation:complete' ...) part, I decided to place the code in my viewmodel's attached: property instead. Observe:

// Other Durandal viewmodel setup...
var section = ko.observable();

return {
    section: section,
    displayName: 'Products', // a default value for this page.
    GetProductImageByColorId: getProductImageByColorId, // later function
    attached: function () {
        var that = this;

        ga('set', { [age: window.location.pathname, title: that.displayName + ' | [Site Name]' }); // matches my Title convention for this site
        ga('send', 'pageview');
    activate: function (mainsection, subsection, subsubsection, subsubsubsection) {
// more code...

The ugly part is, I would have to do this in every page's attached: call, and fortunately it was just a couple pages. If you have dozens of views or more, you would want to come up with a different way. For my purposes, though, this will do.

Once I implemented this and deployed, and then checked my Google Analytics realtime monitoring with my phone, I did see each individual hit recorded as I wanted them to be. And later on in the day, I checked the Behavior overview for that same day, and the site hits were in fact recorded! Granted, my analytics data for today and yesterday aren't going to be accurate, but it was in the name of testing and experimentation, and it's one of my sites, so in this case I feel it was worth it.

I suppose you could use the create line too. However, according to Google Analytics Unversal Analytics Guide section on single page applications:

Do not create new trackers in a single page app in an attempt to mimic what the analytics.js snippet code does for traditional websites. Doing so runs the risk of sending an incorrect referrer as well as incorrect campaign data as described above.

Going by that, that could skew your results depending on your goals. It seems to work fine for me without having the ga create part in the attached: portion of my viewmodel, so I'm going with that.

P.S. Yes, that activate has some pretty ugly names for parameters, but it made sense for the purposes of the site and how they were used. And yes, you can do multiple optional parameters in Durandal routing!

And to demonstrate, in the shell.js...

define(['plugins/router', 'durandal/app', 'knockout'], function (router, app, ko) {
    return {
        copyright: ko.observable('© 2009 - '
            + new Date().getFullYear()
            + ' [Site Name].  Store powered by <a href="">CafePress</a>.'),
        router: router,
        activate: function () {
                { route: '', title: 'Home', moduleId: 'viewmodels/home', nav: true },
                { route: 'Products(/:section)(/:subsection)(/:subsubsection)(/:subsubsubsection)', moduleId: 'viewmodels/Products/index', title: 'Products', hash: '#Products', nav: true },
                // Snip other routes...
                { route: 'NotFound', moduleId: 'viewmodels/not-found', title: '404 - Not Found', nav: false}
            .mapUnknownRoutes('viewmodels/not-found', 'not-found');
            return router.activate({ pushState: true });

Have fun with Durandal, and feel free to leave feedback! (Once I implement commenting on this blog, that is.)

Creating iCal files in ASP.NET MVC and C#

Recently, during a project I was working on for a client, I was tasked with adding events to a user's iCal in addition to being able to add events to Google Calendar. In researching this, I found that this would necessitate creating a .ics file for each event. Given that my client's site had hundreds if not thousands of events, that would be a lot of .ics files sitting around. There had to be a solution to this.

Fortunately, there is, and ASP.NET MVC and C# makes this surprisingly painless.

First, we need to know what goes into a iCal .ics file. This PasteBin page gives a very rough overview of a partial iCal format (the parts that I needed, as a bonus). It's at the bottom of the window, below the Google Calendar API and Yahoo Calendar API if you need those as well.

Now, while I had some of the spec, I needed a way to put it together into a .ics file. Now, there's some libraries out there, but I ran into this post at Balajiprasad's useful codes that had some sample code that I could adapt. He used a library for his solution. I opted for a simple Stringbuilder approach to start.

/// <summary>
/// Generates an iCalendar .ics link and returns it to the user.
/// </summary>
/// <param name="downloadFileName">Name of the download file to return.</param>
/// <param name="evt">The Event.</param>
/// <returns>The .ics file.</returns>
public ActionResult AddToICalendar(string downloadFileName, int eventId)
    // replace db with however you call your Entity Framework or however you get your data.
    // In this example, we have an Events collection in our model.
    using (
        var db =
            new ExampleEntities(
        // Alternatively, you may use db.Events.Find(eventId) if this fits better.
        var demoEvent = db.Events.Single(getEvent => getEvent.ID == eventId);

        var icalStringbuilder = new StringBuilder();


        icalStringbuilder.AppendLine("SUMMARY;LANGUAGE=en-us:" + demoEvent.EventName);
        icalStringbuilder.AppendLine(string.Format("CREATED:{0:yyyyMMddTHHmmssZ}", DateTime.UtcNow));
        icalStringbuilder.AppendLine("DESCRIPTION:" + demoEvent.Description);
        icalStringbuilder.AppendLine(string.Format("DTSTART:{0:yyyyMMddTHHmmssZ}", demoEvent.StartDateTime));
        icalStringbuilder.AppendLine(string.Format("DTEND:{0:yyyyMMddTHHmmssZ}", demoEvent.EndDateTime));
        icalStringbuilder.AppendLine("UID:" + Guid.NewGuid());
                "LOCATION:{0}\\, {1}\\, {2}\\, {3} {4}",

        var bytes = Encoding.UTF8.GetBytes(icalStringbuilder.ToString());

        return this.File(bytes, "text/calendar", downloadFileName);

And then, within the .cshtml file in question, we call it like this:

@using (Html.BeginForm("AddToICalendar", "Home", new { downloadFileName = "thisEvent.ics", eventId = Model.TheEvent.ID }))
    <input type="submit" value="Add to iCal" class="ical-button" id="button" /><br />

This will generate a form and will, when clicked, call the AddToICalendar function. It will then retrieve the event data and populate the fields in the generated .ics file with the relevant data. As a bonus, should you have the actual location data, someone on an iPhone or iPad can actually call up the map and get directions to go to wherever the event is located. (Now, how to call something other than Apple Maps as a default for this, I've yet to figure out and would prefer Google Maps or, better yet in my case, Waze).

The new Guid for the UID line will assign a new ID to this event. Now, if you had an event that would be updated, and wanted someone to just click on the same link and update their calendar that way, I would wager that you would have a different way of setting the UID earlier on and keeping it the same for the same event. I haven't had the opportunity to test this yet, but that's my understanding based on my research.

As an additional bonus, you can even download the .ics file on a Windows system and if you have Outlook, you can import that as an event into that calendar. I haven't tested this with other calendar applications or even Google Calendar. So in the end, this proved to not just be for iCal.

Also, if you wanted to keep the start and end date/times to a local time, you could remove the Z part in the string.Format strings for each, which would relegate it to be local to the user's time. Still, it's much better to use UTC-based time for everything in my opinion when feasible. As far as the formatting string, the {0:yyyyMMddTHHmmssZ} part would map to 20150601T123000Z if given a date/time of June 1st, 2015 at 12:30 UTC. Eliminate the Z part, and it becomes June 1st, 2015 at 12:30PM local time.

You'll also notice the double-backslash in front of the commas. For some reason, iCal needs those commas to be escaped, and a single backslash just won't do the job using this method. I've tested this without doing that in the generated iCal file, and it didn't work very well. On my iPhone, it would only put the LocationName before the first comma into the Location field on the Event entry in my iCal. With the proper escaping, it puts the entire address. I haven't tested what happens if you have a comma in one of those fields, so if you run into any problems you could easily put a .Replace(",", "\\,") onto the appropriate lines. The DESCRIPTION line, however, did not have this problem and did not seem to require the escaped commas, so it may just be for the LOCATION line. Have no worries about importing this into Outlook, as the backslashes won't show in the Location line on those events, at least not on my local Outlook with Office 365.

In closing, this should be a help to anyone who needs a basic iCal file to share events and reminders. I imagine the process would be similar if you wanted to make Reminders for iPhone/iPads the same way, and there are other fields that are available to put in these files too. This documentation at should also be useful, however I don't really know if this is up to date. It's worth a shot though if you want to experiment. Happy coding.

New Portfolio Entries

Over the past couple weeks I've managed to get a couple more portfolio pieces up.

For the sites portfolio, I've recently completed some maintenance over at MyGiraffe, where users can submit events, sporting goods and other gatherings in the Greater Seattle Area. There's quite a few events to choose from, and my updates should add to its utility. In addition to Google Calendar links for each event, there's now the ability to add to iCal as well (for users on iPhones, Macs, etc.). In fact, even Microsoft Outlook users can import the iCal files and add these events to their calendars too, which is a nice plus. I'll be posting soon about how to create an .ics file in MVC using C# (which can be easily adapted to VB.NET if needed).

Another portfolio entry involves Mike & Jean's Berry Farm. They needed a couple pages for an equipment auction they held, and I was tasked with putting up the temporary pages for this (which are offline now). I decided to also make a couple header images to have the pages match the layout of the rest of the site. I adapted these from the original source materials when the site was created a few years back, and was able to replicate an image mask to have the images look similar in feel to the other images on the site. (The original file was missing this mask, so I had to find a way to remake it). Those header images are in my graphics portfolio.

It was a pleasure working with both clients, and would gladly work with them again!

Graphics Portfolio Up

After a decent bit of work, I have uploaded a small sampling of a graphics portfolio to supplement my sites portfolio. I have also added a couple more sites to the sites portfolio to further flesh out previous work.

While I am capable of some graphical work, my primary focus remains in the field of web development, both front-end and back-end.

In a pinch, I can do Photoshop/Illustrator/Fireworks, and have touched InDesign a bit. Get me behind a Visual Studio setup though, and that's where I shine much brighter.