Category: Javascript

Seems like only yesterday that I discovered this strange yet exciting new language for the web. Boy, have we come a very long way since that crude beginning twenty years ago! Thank you so very much for making my life a more enjoyable adventure into the unknown.

"Originally just a sidekick scripting language for Java, created at Netscape in a ten-day hack, it was shipped first as a de facto Web standard and eventually became the world's most widely used programming language..."

"...In 2020, the World Wide Web is ubiquitous with over a billion websites accessible from billions of Web-connected devices. Each of those devices runs a Web browser or similar program which is able to process and display pages from those sites. The majority of those pages embed or load source code written in the JavaScript programming language..."

"...In 2020, JavaScript is arguably the world's most broadly deployed programming language."

Read more about it here: JavaScript: The First 20 Years.

Had a real blast giving a presentation at the latest Ember Amsterdam Meetup, explaining about my experiences integrating that wonderful JavaScript framework with Ruby on Rails.

There were many experts present so I was slightly nervous, but keeping things high level and presenting my story in a fun and relaxing way was the right approach.

ember-rails-and-json-api.png
Download presentation

Here are a couple interesting trivia items that I learned about JavaScript functions in Chapter 15 of the book Speaking JavaScript:

var add = new Function('x', 'y', 'return x + y');
console.log(add(2, 3); // 5
console.log(add instanceof Function); // true

Ever just wanted to shuffle your Ember model contents around in a random way? Well, here's something I happened to discover while perusing through the Liquid-fire github. This is such a real jewel, so elegant and simple that I had to share it.

shuffle: function() {
  this.get('model').forEach((item) => {
    item._random = Math.random();
  });
  this.set('model', this.get('model').sortBy('_random'));
}

So here I am claiming to be a so-called "experienced" JavaScript developer, thinking that I pretty much know everything about this language, but in fact I don't. It's like driving a car for many years, and you start thinking that you're the best. The true fact of the matter is that your driving skills are becoming quite rusty and if you are not careful it will become very dangerous.

It never hurts to have a serious look at yourself in the mirror once in awhile, do it sometime and you'll feel much better about yourself.

Since I am never too old to learn new stuff, I like to discover the future while it is still happening. More and more I've been getting interesting in how the JavaScript language is evolving, namely the new and upcoming ES6 features. This is really cool stuff and shouldn't be missed.

So I went over to the Leanpub website and found myself a couple good sources, namely Exploring ES6 and Setting up ES6 by Axel Rauschmayer.

These books are fairly technical and difficult to understand, but beautifully written nonetheless. I figured that in order to maximize my learning experience I'd have to take one step back and refresh my knowledge of the JavaScript basics. Not just some easy introductory book but an intense and truly in-depth exploration of the technical details.

Since this Axel person is obviously very smart I figured what the heck three books by him must be the magical combination to success. So I bought his other book.

speaking-javascript.png

A tough but fulfilling read, highly recommended. Speaking JavaScript is also available for free online, but I'm still pretty old-fashioned and had to have another book for my immense computer library.

Express is a fast, unopinionated, minimalist web framework for Node.js. That's why I had to purchase yet another book to add to my huge collection of computer books.

express-in-action.png

I sawed through this book in a single day. While it may not be intended for super advanced developers, I found it a refreshing overview of express, node, npm and all those powerful tools and techniques any engineer worth his salt should know.

There's also a nice section covering testing with mocha and supertest, as well as chapters about mongodb, less, browserify and coffeescript.

Highly recommended by someone who is never too old to learn new stuff.

If you ever want to setup an automated test platform for an important project, I can certainly recommend Webdrverio very much.

webdriverio.png

This advanced open source testing utility for Node.js makes it possible to write super easy selenium tests with JavaScript for the a number of TDD test frameworks, for example the Mocha test framework.

The extensive API is quite powerful and lets you do just about anything.

One should not take the precise definition of 'undefined' too non-nonchalantly as it forms an important basis for understanding the JavaScript fundamentals. I'd be curious to know how many so-called expert JavaScript developers really understand what it is. In my many years of learning the ins and outs of the JavaScript programming language, this is perhaps the best explanation of 'undefined' that I've ever heard.

"When I declare 'var a', 'a' is placed into memory during the creation phase. So the execution context saw 'var a' and setup 'a' in memory. And so even though I haven't set it to a value, the JavaScript engine, which is doing more than what I'm just writing in my code, already set it to the special value called 'undefined'. So 'undefined' is not like empty, or doesn't exist, it doesn't literally not exist. It's actually a value, it's actually taking up memory space. It's a special keyword, a special value that means this is the value that was initially set by JavaScript. And that leads to a little bit of a warning... Never set yourself a variable equal to 'undefined'. Because actually you can... That's perfectly valid JavaScript, but it's a little dangerous. It's better to let 'undefined', that special keyword, mean I, the programmer, never set the value..."

undefined-javascript.png

"... That will really help you when debugging code. If you make a habit of setting values equal to 'undefined', then it's really hard to tell if something is 'undefined' because you set it or because the JavaScript engine set it and you never set it to anything else. It's always better to let 'undefined' mean I never set this value. That's really useful, and it will help you in your debugging. So 'undefined', this is a special value, that is also a special keyword in JavaScript, and it's the value that variables receive during the creation phase, the first phase of creating an execution context, sets up the memory of the variable, and in that memory space puts the value called 'undefined'. I would have called it something else personally like 'not set' but that's what JavaScript calls it, 'undefined'. And if you don't in your code set it to anything else, that is what it will be. Or if you set it to something else later, and use it beforehand that is what it will be. Alright, so that's JavaScript and 'undefined'."

JavaScript: Understanding the Weird Parts, Lecture 11: Conceptual Aside: JavaScript and 'undefined'

"You should begin moving your app to Ember CLI as soon as possible," writes Tom Dale in The Road to Ember 2.0 RFC #15. My future famous Ember HAL Client has been migrated successfully, thanks.

The decision will certainly become a bureaucratic one made by management folks who don't care about the low-level details. So which one will it be?

  • Ember.js – "Framework for Creating Ambitious Web Applications", or
  • AngularJS – "Superheroic JavaScript MVW Framework"

So if these so-called enterprise companies have to undergo a big change anyway, and they have to choose between something totally new (Ember) or something new but kind of related to a previous version (Angular), then the choice seems completely predictable though perhaps not the most logical.

Who cares if the next version of Angular is a complete rewrite, oh dear.

Ember will have to appeal to the rest of the world, all those early adopters, starters, and other small to medium sized companies ready to make the transition and start with a clean slate.

For me Angular is merely a tool to build web frameworks (although they claim otherwise), whereas Ember is truly a web framework in itself. I'm choosing to go the Ember way despite the somewhat steep learning curve, never could get into Angular anyway.

The following quote has been borrowed from the Ember Core Concepts section, and explains the essence of this fine framework.

Ember_or_AngularJS.png

"Ember.js is designed to help developers build ambitiously large web applications that are competitive with native apps. Doing so requires both new tools and a new vocabulary of concepts. We've spent a lot of time borrowing ideas pioneered by native application frameworks like Cocoa and Smalltalk.

However, it's important to remember what makes the web special. Many people think that something is a web application because it uses technologies like HTML, CSS and JavaScript. In reality, these are just implementation details.

Instead, the web derives its power from the ability to bookmark and share URLs. URLs are the key feature that give web applications superior shareability and collaboration. Today, most JavaScript frameworks treat the URL as an afterthought, instead of the primary reason for the web's success.

Ember.js, therefore, marries the tools and concepts of native GUI frameworks with support for the feature that makes the web so powerful: the URL."

A promise can either be fulfilled or it can be rejected.

var promise = fetchTheAnswer();

promise.then(fulfill, reject);

function fulfill(answer) {
  console.log("The answer is " + answer);
}

function reject(reason) {
  console.log("Broken promise! Reason: " + reason);
}

You can even chain promises together with .then() and run through a whole slew of asynchronous actions.

var promise = Ember.$.getJSON('/promise-me.json');

promise.then(doThis)
       .then(doThat)
       .then(doOtherStuff)
       .then(handleFulfill, handleReject);

A promise is a method with asynchronous behavior. It returns a "thennable" which is an object that reflects fulfillment (success) or unfulfillment (failure).

You attach handlers for these states using the then method. Each then returns yet another promise.

More about asynchronous routing.

Here's a simple recipe for creating your own progress bar using some javascript and css. This is what I implemented on my Rand() page.

<script type="text/javascript">
  $(document).ready(function() {
    function progress(n) {
      (function loop() {
        $('#progress-bar').html(n ? n*10 : "");
        $('#progress-bar').css("width", n*10 + '%');
        if (n--) {
          setTimeout(loop, 100);
        } else {
          // do something
        }
      })();
    }
    progress(10);
  });
</script>

In order to make it work, place a div somewhere on your page where you want the progress bar to appear.

<div id="progress-bar"></div>

I added some styling to make the progress bar look a bit more professional.

#progress-bar {
  background-color: #2A83A2;
  background-image: linear-gradient(#2A83A2, #165E83);
  border-color: #165E83;
  height: 20px;
}

Here's a demo you can try out for yourself.

Progress bar (100)
Do something!

It can be quite a chore wrapping your head around some of the new-fangled concepts inherent to JavaScript. But the mental struggle is more often than not more than worth it.

The essence of understanding and becoming enchanted by JavaScript's wow factor is well illustrated by the following fragment. Seemingly short and trivial, but it nonetheless carries quite an amazing punch.

Fortunately, it is easy to create an operator that implements true prototypal inheritance. The object function untangles JavaScript's constructor pattern, achieving true prototypal inheritance. It takes an old object as a parameter and returns an empty new object that inherits from the old one.

Finally being able to ride the gentle quantum leap to higher levels of awareness gives one a good feeling. There's still hope for this little brain of mine, for it is being kept fit and in good form with abstract concepts and constant mental gyrations.

function object(o) {
    function F() {}
    F.prototype = o;
    return new F();
}

Make sure that you understand this perfectly before daring to proceed further in life as a so-called professional software engineer.

It's been quite some time since I did any Flash, besides so much has changed since then that I doubt I could make anything spectacular anymore.

However, in the meantime I've been having a look at the new <canvas> tag, which in my opinion does alot of interesting graphics stuff for JavaScript. Nothing super fancy, but fun to play around with anyway.

Here's a simple example that draws two intersecting rectangles, one of which has alpha transparency:

<script type="application/x-javascript">
function draw_rects() {
    var canvas = document.getElementById("canvas");
    var ctx = canvas.getContext("2d");

    ctx.fillStyle = "rgb(200,0,0)";
    ctx.fillRect (10, 10, 55, 50);

    ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
    ctx.fillRect (30, 30, 55, 50);
}
</script>

<canvas id="canvas"></canvas>

 

Some related links:

Let's say that you want to insert a node somewhere in the DOM, but if it is a string, then you want to insert it automatically as a text node. Here is how you might want to achieve this:

function checkElem(elem)
{
    // If only a string was provided, convert it to a Text Node
    return elem && elem.constructor == String ?
        document.createTextNode(elem) : elem;
}

Borrowed from the book Pro JavaScript Techniques by John Resig.

Let's say that you want to select a random link within a certain div tag which has a given id and then jump to where that link points to. Here's a way to do that:

function random_link(id)
{
    var obj = document.getElementById(id);
    var links = obj.getElementsByTagName("a");
    var offset = Math.floor(Math.random() * links.length);
    document.location = links[offset].href;
}
typeof(NaN) == 'number'

NaN stands for 'Not a Number' but as you can see it is a 'number' nonetheless.

null is a value that isn't anything.

undefined is a value that isn't even that.

A variable can be both defined and have a value of undefined at the same time.

boolean("0") = true

boolean("false") = true

+"42" = 42

!! produces a boolean

All of the above have been taken from the following video tutorial.

Today I learned that there is more than one way to implement Ajax, namely:

  • using a hidden frame
  • using a hidden iFrame
  • calling ActiveXObject() for IE6 or earlier
  • calling XMLHttpRequest() for all other browsers
  • using a third party library like zXML or DWR
  • creating dynamic images by using new Image() on the fly
  • dynamic script loading by using document.createElement("script")

The book I am reading is called Professional Ajax.

| 2 Comments

So I decided to be clever again, but this time around in an unusual javascript kind of way.

Do you see all of those links down the left and right columns? Well, if you have javascript enabled in your browser, then you should see a bunch of messed-up looking links which are randomly sized, some bold, some italic and others just plain old normal.

For those lucky enough to be knowledgeable in the fine art of Dynamic HTML, I will reveal my secret.

Here it is:

<script type="text/javascript">
// Creates random fontsize, bold, italic or normal for links.
function resize_menu_links(class_name, h2_names, min, max) {
  var divs = document.getElementsByTagName('div');
  for (var i = 0; i < divs.length; i++) {
    if (typeof(divs[i].className) == 'string' && divs[i].className == class_name) {
      var h2s = divs[i].getElementsByTagName('h2');
      if (h2s && is_in_array(h2_names, h2s[0].innerHTML)) {
        var a_links = divs[i].getElementsByTagName('a');
        for (var j = 0; j < a_links.length; j++) {
          var fs = min + Math.floor(Math.random()*(max-min+1));
          a_links[j].style.fontSize = fs+'px';
          var rx = Math.floor(Math.random()*4);
          if (rx == 1) { // bold
            a_links[j].style.fontWeight = 'bold';
          } else if (rx == 2) { // italic
            a_links[j].style.fontStyle = 'italic';
          } else if (rx == 3) { // both
            a_links[j].style.fontWeight = 'bold';
            a_links[j].style.fontStyle = 'italic';
          }
        }  
      }
    }
  }
}

The function is_in_array checks to see if a given string matches one of the elements of the array, returning true or false.

function is_in_array(the_array, what) {
  for (var i = 0; i < the_array.length; i++) {
    if (the_array[i] == what) return true;
  }
  return false;
}

Finally, I use the standard setTimeout function to make sure that the changes are made once the page has been loaded, waiting 50 milliseconds before firing off the function resize_menu_links , passing the array of allowed section strings as well as the range of font sizes, in this example 9-17px.

// Only for titles given in 2nd array parameter.
setTimeout("resize_menu_links('menu_subsection', ['Categories', 'More links', 'Archives', 'More entries'], 9, 17)", 50);
</script>

Do you have javascript enabled?

The answer is:

Found at The JavaScript Source

| 3 Comments

So you thought that you knew everything there was to good old javascript did you? Well then, check out the slayeroffice web site first and then ask yourself again whether or not you are such a big shot afterall.

Personally, I found the Mouseover DOM Inspector the most impressive of all.

| 5 Comments

Don't you just hate it when you include some external piece of (javascript) code from another site, and it breaks up the layout of your web page?

This is the problem I've been having with Blogsnob. The kind folks there provide a free service for members of the blogging community, enabling you to increase your blog's exposure via simple text-based ads.

All you have to do is insert the following code somewhere and hope everything works out okay:

<script src="http://blogsnob.idya.net/ad.php?id=n"
  type="text/javascript"></script>

I have placed this at the very bottom of my right-hand margin. This margin is set to a width of around 160 pixels and it is meant to stay that way.

The only problem with inserting this piece of code is that you do not know ahead of time how much text and/or images might be thrown into your site. Now I trust the service for what it is, and most of the time it seems to be working alright. However, sometimes I get really long words containing say 50 characters in a row. This breaks up my layout. In order to accommodate this exceptionally long word, the poor 160 pixel wide margin is expanded so that a much larger vertical bar appears. You see, HTML has no character wrapping, only word wrapping at the white-space borders. My main content area is covered up on the right side and shifted to the left. Bummer, man.

So what's a poor soul like myself going to do? Well, I have chosen to solve this using good old Javascript based on the DOM-interface. Here is what I do. First of all, I bracket the inserted code above like this:

<div id="blogsnob">
<script src="http://blogsnob.idya.net/ad.php?id=n"
  type="text/javascript"></script>
</div>

Note that I have wrapped the ill-behaved section of code with another div-tag and I have labeled it "blogsnob" accordingly.

Then just right after the spot where I have inserted the code above, I add the following function call:

<script language="javascript" type="text/javascript">
<!--
mxw("blogsnob");
//-->
</script>

Alright, so what does this magical function called mxw() really do? Well, it looks within the div-tag that I labeled as "blogsnob", recursively parses through the nodes and child-nodes, and grabs each TEXT-element. If it then finds any words which are longer than a pre-defined maximum length, say 20 characters (mxw_max = 20;), then it will truncate this bugger to this given length. Pretty neat, huh?

Just in case you were curious what this function looks like, and assuming that you not only have a good knowledge of Javascript, but also at least some limited experience with that thing called DOM, then here it is:

<script language="javascript" type="text/javascript">
<!--
// Maximum allowed length of text words.
var mxw_max = 20;
//
// mxw(id)
//
// Wrapper function which checks that certain DOM
// is supported before calling recursive mxw2(). 
//
function mxw(id)
{
  if (!document.getElementById) return;
  var n = document.getElementById(id);
  if (!n || !n.nodeType) return;
  mxw2(n);
}
//
// mxw2(id)
//
// Checks all text of given node and all its
// descendant nodes, truncating each word that
// is longer than allowed length mxw_max. This
// is an extra safety valve preventing unwanted
// overrun, e.g. of sidebars and/or margins.
function mxw2(n)
{
  if (n.nodeType == 3 /*Node.TEXT_NODE*/)
  {
    var flag = 0;
      var words = n.data.split(" ");
    for (var i = 0; i < words.length; i++)
    {
      if (words[i].length > mxw_max)
      {
        flag++;
        words[i] = words[i].substr(0, mxw_max-2) + ". "; 
      }
    }
    if (flag > 0) n.data = words.join(" ");
  }
  var children = n.childNodes;
  for (var i = 0; i < children.length; i++) mxw2(children[i]); 
}
//-->
</script>

Hope you like it and find it useful for your own web pages somewhere. You're welcome.

| 3 Comments

Here's some more javascript stuff which you too can use in order to achieve yet another truly amazing affect. Boggle the minds of your readers and make new friends. Just click on the following button to find out for yourself:

| 5 Comments

Alright, so I get kind of bored once in awhile, so what? Everyone has those periods when all you feel like doing is doing nothing at all. Nothing at all like a good old bout with some amazing Javascript, that is.

Here is something I created this morning, something quite silly but still entertaining nonetheless. And to think that I did it all with some simple Javascript. Try it out please.

Speed:   Sleep:     

Kind of reminds me of that program called Life, remember it? A simulation of living objects based on the population, available food and number of neighbors and other predators. I guess I could enhance this simple creation to include some kind of intelligence, but that will have to wait until another day (sorry).

If worse comes to worse, I can always find a job somewhere as a Javascript programmer. Want to hire me?

Hope this made your day. It did mine.

| 2 Comments

Alright, so I finally have gotten so overly fed up with those droves of spam emails the flood my in-box everyday that it is time to do something about them. After having read the article Spam-Proofing Your Website, I have taken some of their suggestions at heart and have begun to cloak all email addresses displayed on my homepage and blog. You see, there are millions of so-called spam harvesters crawling all over the Internet, consuming page after page and extracting all the discovered emails. To be used for junk email, those jerks. I am curious if I will now be getting less junk email (about 60 per day and growing).

<script language="javascript" type="text/javascript">
<!--
// Try and spoof those nasty spambots!
  document.write('<a href="' + 'ma' + 'ilto:k');
  document.write('iffin' + '&#064;' + 'cyber-gi');
  document.write('sh.com' + '">' + 'kiffin (at) ');
  document.write('cyber-gish' + '</a>');
//-->
</script>

And this is what all that mumbo-jumbo code (hopefully) looks like after it has been parsed by your internet browser and presented in readable form: [ ]

Now be honest. If you were a spam-bot, would you be able to figure out what this is? I've heard that they are getting pretty smart lately, even able to scan Javascript code and understanding it, but I doubt very much if this can ever be understood.

Here is an email encoder site which you might find useful, or you can also try out this anti-spam obfuscator.

| 2 Comments

Since my birthday is coming up soon, I decided to order two more books from Amazon in the hopes that my shipment will arrive in time for the big event. They've got less than nine days, but so far I have never had to wait more than a week. Now I will tell you which two books I ordered if you promise not to make fun of me. The titles are:

  • The Java Programming Language by Ken Arnold et al.
  • Effective Java by Josh Bloch.

I figured that I have pretty much learned enough about HTML, Javascript, XML, CSS, Perl and CGI that it is time to take the next jump into the most obvious domain about which I need (would like) to be more knowledgeable. I have heard so much about the wonderful world of Java and at the same time know so very little about it. Time to expand my knowledge another slight click. Is this a sign of getting senile, crazy, too old for this stuff or am I simply a born techie guru type at heart? Take your pick. Any way, the books will add a pleasant counterweight to whatever it is I am planning to do in the not so near future.

| 6 Comments

Right now I am reading a bunch of books simultaneously in my never-ending attempt to acquire infinite knowledge. Nine books all in a row, to be more precise. Depending on my mood at a given instant, I will jump from the one book to the other. Sometimes smack dab in the middle of a chapter, I will close the book I am currently reading and open up another book. Some reading sessions consist of switching books five or six times an hour. I suffer from an insecure feeling that perhaps I am not increasing my knowledge as productively as I should. That what I am reading at the moment is not valuable enough in the sense that I am suffering an acute shortage by not improving my life sufficiently and quickly enough! This can be very frustrating. The innocent pleasures of reading suffer in that regard which is ironic to say the least. There is this endless source of interesting information out there, and there is absolutely no way I can absorb it all in the limited span of my lifetime. By nature I have an addictive tendency, a proclivity which bends me in fixations. Especially when it is in any way remotely related to the esoteric acquisition of knowledge. So why bother in the first place? Infinite knowledge is preferable, of course. Unfortunately one cannot attain this by reading, even if one could have enough time to read every single book in every possible language that has ever been written in the history of civilization. In order to give my readers a hint of how far along this reading adventure I have come, here is a list of books that currently lie on the reading table next to my bedside. The order is from bottom to top, or since I am by nature symetrical and neat, from the widest (usually the thickest, but now always) in decreasing order:

  • Programming Perl, Third Edition by Larry Wall et al.
  • Javascript, The Definitive Guide by David Flanagan.
  • The 7 Habits of Highly Effective People by Stephen R. Covey.
  • Essential CSS & DHTML for Web Professionals by Dan Livingston.
  • Web Design in a Nutshell by Jennifer Niederst.
  • Cascading Style Sheets 2.0 Programmer's Reference by Eric A. Meyer.
  • A Beautiful Mind by Sylvia Nasar.
  • The Power of Now by Eckhart Tolle.
  • Caesar by Colleen McCullough.

As you can see from the list I am pretty much obsessed with Web-related stuff at the moment. However, I try my best to balance this with other non-Web stuff. Reading one book at a time would probably be much more efficient, but I am too impatient. For the sake of simplicity, let's just say that I am about half-way along the pursuit of infinite knowledge.

When I arrived home I was so very excited to receive my latest order from amazon.co.uk that my heart started beating wildly. I ripped open the box and let out a gleeful shout. The reason:

Javascript, The Definitive Guide by David Flanagan.

Yes, yes, yes. As if there is still even more to learn. Haven't I learned enough? This weekend I plan to saw through the exciting thickness and learn as much as I can about javascript this wonderful programming language for the web.

At heart I guess I remain quite an Internet geek.

Random entries

Here are some random entries that you might be interested in:

Recent Assets

  • tegen-par-2024-2nd-place.jpeg
  • stanford-reunion.png
  • kiff.png
  • hoid.png
  • Dad-in-front-of-log-cabin.png
  • mistborn-trilogy.png
  • 2024-03-Heren1-27h.png
  • three-body-problem.png
  • 10CC.png
  • minds-and-machines.jpeg
  • puglia.png
  • 2023-09-24-jong-tegen-oud-1.jpg

Recent Comments

  • Long time no see: I remember them, as well. I remember Donald was my ...
    - Charles
  • Bridge to the moon: Yes it was a drawing and my older brother told me ...
    - jpmcfarlane
  • Bridge to the moon: Wow, that's quite a coincidence that we both sent ...
    - Kiffin
  • Bridge to the moon: Hello I was in my teens when Gemini 4 went up that ...
    - jpmcfarlane
  • Back to work: Congratulations Kiffin, I hope it is something you ...
    - KathleenC

Golf Handicap

Information

This personal weblog was started way back on July 21, 2001 which means that it is 7-21-2001 old.

So far this blog contains no less than 2518 entries and as many as 1877 comments.

Important events

Graduated from Stanford 6-5-1979 ago.

Kiffin Rockwell was shot down and killed 9-23-1916 ago.

Believe it or not but I am 10-11-1957 young.

First met Thea in Balestrand, Norway 6-14-1980 ago.

Began well-balanced and healthy life style 1-8-2013 ago.

My father passed away 10-20-2000 ago.

My mother passed away 3-27-2018 ago.

Started Gishtech 04-25-2016 ago.