Sunday 30 July 2017

Mozat's Culture of Sharing

A month back, I had the privilege of interviewing for an open position at a company near my place - Mozat Pte Ltd. The hokey name notwithstanding, it was six bus stops away, a cozy environment, and seemed like a nice setup. Well, nicer than some of the really spartan setups I've encountered. And trust me, I've had plenty.

No, I didn't get the job. As to why, this will be elaborated on in due course, but that's not why I called it a "privilege".

I showed up at the interview dressed in a shirt and tie, and immediately felt out of place when I observed that half of the employees were wearing shorts and sandals. It wasn't even a Friday! Casual dress code - immediate points! My interviewer was this young Kingscar Jin lookalike - he had an untidy goatee, ponytail, and dressed like how I would have dressed 20 years ago. (See pic below) However, he was both courteous, formal and knowledgeable.

Image looted from PCLADY.

One thing I need to say about Mozat - the demographic is predominantly Chinese. The PRC Chinese variety, that is. Not that I have anything against that, but trying to explain CSS and database concepts in Mandarin to my interviewer was... interesting. And strenuous. Thankfully I had prepared a notebook and pen, and in place of my stuttering Cheenatech babble, I could sketch things out in lieu of an eloquent verbal answer.

As the interview went on, it soon became apparent that I was outclassed in the knowledge department.

Paraphrased... (and translated)
Him: If you float a div, what happens?

Me: The containing div collapses if the height is set to auto.

Him: How do you fix this?

Me: Well, two ways.... [insert boring technical explanation here]

Him: Why does this happen?

Now, I'm no slouch, but in most cases, my knowledge is limited to what and how. This interviewer was asking me why. And when I couldn't supply the answer, he kindly walked me through it.

It was a class move. And honestly, he didn't need to. This was what impressed me. Not his knowledge. Knowledge is comparatively easy to obtain - read the right sources, put in your hours, pay your dues. No, it was his unselfishness that impressed me.

I'd pretty much failed the interview at this point and it would be reasonable for him to assume that he would probably never see me again. He could have just informed me that my answers were inadequate, concluded that I wasn't good enough to work there, and moved on. And I wouldn't have faulted him for it. Yet he took the trouble to explain in patient detail why certain things happened when I did things a certain way, the edge cases I needed to look out for, and so on. Not just in that one technical question, but all the other questions he asked me that day - JavaScript, HTML, CSS. Things I consider my bread and butter.

In short, he took me to school. Almost literally.

Sharing as a Culture

Tech has flourished in part due to this culture of sharing that my young interviewer exemplified. We post code on our GitHub repositories, share findings on our blogs, and participate in forums. We help solve each other's problems, and obligingly patch holes in each other's knowledge gaps. That is how developers improve - by sharing. And this is how techies become good enough to get stuff done - because other techies take the trouble to share. For no immediate gain whatsoever.

And this is why it was a privilege. The courtesy of that kind gesture was not lost on me. I can only say that if my interviewer's generosity of spirit is indicative of the culture in the company, Mozat is a place I would gladly ply my trade.

Moz sincerely,
T___T

Tuesday 25 July 2017

Textboxes and the Wrap Attribute

In the HTML5 Specification, there exists a new attribute for the textarea element - wrap. Both soft and hard are possible values for this attribute.The default value for this is soft.

OK, I promise not to break into a juvenile repeat of what I did with Soft and Hard Bounces. What I would like to do here, is illustrate the difference between a Soft Wrap and a Hard Wrap.

Consider this PHP code. What this is meant to accomplish, is for you to be able to enter a value in the textbox, which will then be displayed once you click the "Submit" button.
<!DOCTYPE html>
<html>
    <head>
        <title>Wrap test</title>
    </head>
    <body>
        Soft Wrap Output:
        <div style="width=100%;outline:1px solid black;padding:10px">
            <pre>
<?php
if (isset($_POST["test"]))
{
    echo $_POST["test"];
}
?>
            </pre>
        </div>

        <br />

        <form method="POST">
            Test:
            <textarea name="test" rows="10" cols="30" wrap="soft"></textarea>
            <input type="submit" value="Test!">
        </form>
    </body>
</html>


This is what it's meant to look like. I know this UI is ugly as sin, but my priority here is for it to work, so bear with me.


Let's try some input, shall we? Here are a few paragraphs of Lorem Ipsum text...


And this is what you get after clicking "Submit". See how the three paragraphs become three long lines? That's a Soft Wrap. However nicely it fits in the textbox, the output does not include the breaks appearing in the textbox. Only user-specified paragraph breaks have been preserved.


Now change the code. Let's try a Hard Wrap.
<!DOCTYPE html>
<html>
    <head>
        <title>Wrap test</title>
    </head>
    <body>
        Hard Wrap Output:
        <div style="width=100%;outline:1px solid black;padding:10px">
            <pre>
<?php
if (isset($_POST["test"]))
{
    echo $_POST["test"];
}
?>
            </pre>
        </div>

        <br />

        <form method="POST">
            Test:
            <textarea name="test" rows="10" cols="30" wrap="hard"></textarea>
            <input type="submit" value="Test!">
        </form>
    </body>
</html>


Let's test using those paragraphs of Lorem Ipsum text...


And this is the result of clicking "Submit". The three paragraphs look exactly like how they look in the textbox. The line breaks enforced by the textbox have been preserved, as well as user-specified paragraph breaks!


And that, ladies, gentlemen, dear readers of all ages; is the difference between a Hard Wrap and a Soft Wrap.

And... that's a wrap!
T___T

Saturday 22 July 2017

Fancy, but ineffectual

User interfaces have achieved great progress over the years. You'll see ample evidence of it everywhere. Now things are animated. They move at a touch of your finger. Information's all over the place - more information than you could wish for.

Tragically, sometimes when attempting to improve the user-friendliness of a product by improving the interface, the opposite happens. The product actually becomes less usable. Here's one example...

Last year, I was in Vivocity, engaging in one of the activities I despise most - shopping.

Unfortunately this was unavoidable as my little nephew Paul had his third birthday coming up. So, lost amid the multitude of shops in Vivocity's bewildering layout, I resorted to using the mall directory. It was this stand-alone booth, with a large screen. On that screen was a user menu where you could search for the shop, either by name or category. Upon selecting a shop, nifty animated graphics would point you to the direction you needed to head. It was all very pretty. Very high tech.

The touch-screen
experience.

The only problem? It was a huge, glaring one. I had to wait in line to use it.

Now, that normally wouldn't be a problem - if the people in front of me made it quick. But no, presented with all that information regarding eating places (they were figuring out where to have dinner), they took their time examining each option and seeing how far they had to go for each one. And that's not even counting the clueless noobs who have to figure out how to use the interface!

Eventually, after a bit of walking, I found myself in the adjoining building, Harbourfront Centre. There, I saw a sight for sore eyes. Yep, a good old-fashioned physical mall directory. The kind with a big floor plan and a list of all shops on a board, with their names and unit numbers. And most importantly, several people standing around using it. Simultaneously.

Sometimes old-school is better.

They were all, I assume, going to different places within Harbourfront Centre. However, instead of having to wait in line while the ones using the directory hogged the damn system, they were able to consult it at the same damn time.

A compromise?

Now, I'm not saying that the UI in Vivocity was a bad idea. It was a pretty nifty idea. With a shitty-as-fuck implementation.

The way it was implemented saved space while cramming a lot of groovy functionality into one screen. A screen that only one party could use at any one time. Which kind of defeated the purpose. I'm not saying it should be abolished. I'm saying that it should have served as a value-add to the old-fashioned physical mall directory, instead of trying to replace it.

Imagine a list of shops and an actual physical map, next to the booth with the UI. Now those who want to be spoonfed by a computer system can use the UI, while those who just want a goddamn shop name and number can just look at the physical directory!

Moral of the story

Having a cool snazzy UI is neat and all. Just bear in mind why you're applying that UI, and be careful not to miss the forest for the trees.

How about a more mall-tifaceted solution?
T___T

Wednesday 19 July 2017

Bracket Like An Egyptian

Cheesy 80s pop reference, check! OK, on to serious business.

Languages such as C (and all its incarnations such as C++, C#, et al), Java, JavaScript and PHP share very similar syntaxes. And some of the most ubiquitous features in the syntax are curly brackets and semi-colons.

And today, I'm going to discuss curly brackets. Specifically, placement of curly brackets. There are mainly two conventions, and variations on each one. For a more comprehensive listing, check out the Wikipedia entry at https://en.wikipedia.org/wiki/Indent_style. Every coder has a preferred style, regardless of the one imposed by technical specifications in his or her day job.

My personal preference is this, and it's a pretty old style. Some call it the Allman style, after Eric Allman. I have no idea why I prefer it this way, but it's probably more out of habit than anything, and the style I default to when IDEs and technical specs aren't imposing a preferred style on me. In this style, the opening and closing curly brackets are positioned on their own line, on the left.
if (x == y)
{
    //code here
}


The other style is known as the K&R style, named after Brian Kerningham and Dennis Ritchie. Kerningham & Ritchie, geddit?

In this style, the opening bracket is positioned on the same line as the opening control statement (be it an If, For, While or function) while the closing bracket occupies its own line...
if (x == y) {
    //code here
}


...unless there's an Else condition or something.
if (x == y) {
    //code here
} else {
    //code here
}


Fun fact: in such a case, this is known as a Cuddled Else!

Watch the hands!

The K&R style is also known slangily as Egyptian Brackets. Because, if the brackets were hands, the pose would resemble a very stereotypical Egyptian dance pose. It's also the style IDEs such as Eclipse and Visual Studio, and text editors such as Atom and Sublime Text Editor, impose upon its users.

Egyptian Brackets are mostly preferred for a couple reasons.

Left? Right?

Aesthetics - someone I know claims that the way it reads is more natural. The whole right-to-left thing. Personally, I can't see it. But... you know, whatevs.

Look at all the paper you save!

Saving line space - as this style does not involve the opening curly bracket taking up its own line, that makes less lines of code. And while it only saves one line per code block, I guess it all adds up. And a lecturer once told me printing out a hardcopy of code using Egyptian Brackets would ultimately save paper.

Where should the brackets go?

Doesn't really matter to a machine, does it? Ultimately, it winds up in the compiler. Or interpreter. The only real difference style makes is when you're coding within a team and need to maintain consistency. Really, who cares about saving line space these days?

That's all for now. I'll be brack!
T___T

Saturday 15 July 2017

App Review: Zombie Defense

Today, I'm back with a review of another game app - Zombie Defense by Home Net Games.

I love zombies. There's just something deeply cool about mowing down these undead buggers and watching blood and gore splatter. I also like the Tower Defense genre. It never gets old.

Zombie Defense

Zombie Defense is a zombie-themed Tower Defense game. Two of my favorite game genres rolled into one app. Now that's something I can get behind!

The Premise

Cities are being overrun by the walking (and in some cases, running) dead and you need to deploy troops to clear them out. The troops you have vary in range and effectiveness against different zombie types, and what units you have at your disposal depends on how far in the game you've progressed, and the stage itself. Killing zombies and finishing stages nets you in-game credits, which you can use to improve your unit types with plenty of groovy options.

As for the story... hell, this is a zombie-themed Tower Defense game, we don't need no stinkin' story.

The Aesthetics

Zombie Defense is gorgeous. 3D rendered zombies rushing your troops from all directions, bullets lighting up the area, grenade blasts brilliantly exploding, blood splattering each time your units die...

Ahem. Well, you get the idea. Where action animation is concerned, I find very little to complain about here. Even the sound effects of bullets striking and bones crunching, add to the atmosphere, though they do get repetitive after a while.

I find the green-on-black interface particularly apt. It's a zombie apocalypse. It's grim, see?

Nice color scheme!

More of that color scheme.

The Experience

In the beginning stages where you start out with very little, you're kept frequently on your toes. And figuring out where to fling your limited supply of dynamite can be an adventure in itself. You'll feel stabs of fear as the first huge zombie boss appears. I just about lost my shit the first time a zombie boss started flinging missile attacks at me.

Missile attack!

Downing an extra-large zombie boss.

Mad zombie rush!


Seeing my units become more powerful spurred me on, and I found myself quite immersed as I directed my units to hide behind crates, take cover behind barb wire fences and fall back to safer ground when there were too many of the critters advancing. Until, of course, my units grew powerful enough for me to simply fast-forward through the action. Though this took the better part of two weeks. (OK, maybe I just suck. Maybe you'll do better)

The Interface

It's basically tap and swipe, and other than watch the action unfold, occasionally pause to make strategic adjustments (like, re-positioning your tower units, for instance) and watch zombies die by the dozens, there's not a lot complicated about the interface. As you grow more powerful, you'll be needing the fast-forward button a lot.

Position your units...

Select from a wide range of units.

Set up traps and choke points.

Get little care packages!

Promote a unit for better stats...

...and watch the zombies go splat!


What I liked

This game is very definitely playable without needing to spend a single dime. Always a plus.

Movable tower units! In many Tower Defense games, the tower units are immovable once positioned. Zombie Defense does away with this, giving you tower units who will run to a new spot of your choosing and throw grenades! In later stages, you can even move the spots!

An environment which is partially malleable and can be engineered to your advantage. Throw fire bombs that form barriers, forcing zombies to circumnavigate. Though why zombies would feel the need to avoid fire is a mystery to me. They're certainly not bothering to dodge your bullets.

Using fire.

The upgrades are certainly very interesting.

Normal and Gold upgrades.

The Normal tech tree.

The Gold tech tree.
The Freeze Gun. 'Nuff said.
Woo-hoo, freeze!

What I didn't

Single-unit stages. Stages where you can only deploy one type of unit. I'd like to personally strangle whoever thought this up. It's not just difficult - difficult can be fun. This one is difficult in a distinctively tedious and un-fun way.

Really, dudes?
The Decoy. Basically serves as bait. Really?

The app consumes an ungodly amount of battery power. Though I suppose that's unavoidable considering the graphics and animation.

Boring, boring text.

Zombie Defense really doesn't have much in the way of personality. The mission briefings seem altogether too serious and, well, boring. There's a lot of text that I just can't be bothered to read because it provides no real information and has no entertainment value whatsoever.

Conclusion

Though it suffers from the usual Tower Defense game hazards of repetitiveness and eventual burnout, this is a game that has the potential to keep you occupied for a long time. Or at least, through the first run.

My Rating

6.5 / 10

You'll keep playing this game in the (un)dead of the night!
T___T

Tuesday 11 July 2017

Five comparisons between Twitter Bootstrap and Semantic UI

There are plenty of CSS and layout frameworks on the market. I've had the privilege of using two of them - Twitter Bootstrap and Semantic UI. Both frameworks are used to provide responsive layouts and improve the UI. While they're similar in very basic ways, they are different enough to warrant a comparison.



Here are 5 ways they compare...

1. Features

Both Twitter Bootstrap and Semantic UI use a grid layout for responsiveness. Twitter Bootstrap uses as 12-column grid while Semantic UI uses a default of 16 columns (which can be changed). Some say this increased granularity gives Semantic UI the edge, but from my perspective I'm not sure it makes that much of a difference.

They both have features like button groups, accordions and beautified form inputs. This is where Semantic UI arguably has the advantage, as it boasts many more variations on those features than Twitter Bootstrap. Semantic UI's icon set, for example, has about 617 in 25 categories to Twitter Bootstrap's 264... the free ones anyway.

Twitter Bootstrap's icon set (left)
Semantic UI's icon set (right)

Both Twitter Bootstrap and Semantic UI offer features that the other do not. For example, Twitter Bootstrap has a Jumbotron feature, and Semantic UI has a really nifty-looking rotating cube feature. But even on quantity alone, Semantic UI has the obvious edge.

2. Marketability

Twitter Bootstrap is almost a household name among developers. Most devs have used it since 2011, at one time or other, to do up websites. Semantic UI, on the other hand, is the relative new kid on the block, having been here since only 2015, and at this time does not even have a Wikipedia entry.

The obvious inference here, of course, is that Twitter Bootstrap has way more support and documentation.

I know Bootstrap!

But the real edge is that almost everyone has heard of Twitter Bootstrap, whereas fewer have heard of Semantic UI. So putting Twitter Bootstrap on your resume is more likely to get you jobs. Even during interviews, I've had interviewers nod sagely when I mentioned Twitter Bootstrap, and give me blank looks when I mentioned Semantic UI.

If you mention Twitter Bootstrap, most people will immediately assume you understand Responsive Design. Which isn't strictly true, but perception is what it is.

3. Look-and-feel

Twitter Bootstrap isn't considered generic without good reason. Most people use the free default theme, which results in Every Fucking Bootstrap Website Ever. Which can be a shame because Twitter Bootstrap can look really good, if you're willing to pay for different themes. Even if not, contributors have offered their own Twitter Bootstrap themes, which can add some variety.

Semantic UI's default and optional themes, all free of charge, already promise a dizzying array of effects that won't immediately scream SEMANTIC to people who view them. Of course, the fact that Semantic UI isn't famous like Twitter Bootstrap, probably helps.

Twitter Bootstrap's color scheme (top)
Semantic UI's color scheme (bottom)

If you want hard numbers though, just consider color schemes. Twitter Bootstraps default theme comes with 6 colors. Semantic UI has 12 (in both cases, we're not counting black). And this can be a simple but huge factor in flexibility of design.

Also, Twitter Bootstrap has 4 size categories - lg (large), md (medium), sm (small) and xs (extra small). Semantic UI has 8 of them - massive, huge, big, large, medium, small, tiny and mini. Though it's worth noting that Semantic UI's size categories aren't rendered with perfect consistency throughout their elements.

4. Difficulty level

Twitter Bootstrap is easier for someone with totally no jQuery knowledge, to use effectively. For Semantic UI, it's a lot harder because jQuery is used often, either as an initializer or to manipulate the controls. Even though many features have a non-jQuery version, this results in a drastically less rich user interface.

If you're only using Twitter Bootstrap or Semantic UI for responsiveness, then this doesn't matter.

Semantic UI's code sample

Personally, I like Semantic UI's style. The jQuery animation features in its feature set allows me to do a lot of cool things without actually writing my own.

Although if you want to really customize stuff, it might be easier in Twitter Bootstrap. Semantic UI offers a lot of customization options, but it can't cover everything and sometimes trying to shoehorn their existing options into what you want exactly, is a pain in the ass.

5. Class names

Twitter Bootstrap uses class names that don't read like normal English.
<div class="row">
    <div class="col-xs-12 col-sm-6 col-md-8">Column 1</div>
    <div class="col-xs-6 col-md-4">Column 2</div>
</div>


Semantic UI's class names look like this.
<div class="row">
    <div class="twelve wide computer six wide phone eight wide tablet column">Column 1</div>
    <div class="six wide computer four wide tablet column">Column 2</div>
</div>


Some say this makes Semantic UI friendlier to use. I'm ambivalent here. See, I have this habit of writing my own CSS classes. And it's far harder to clash with CSS class names like btn-lg than with names like segment or basic.

Conclusion

Personally, I'd take Semantic UI any day of the week. It's got features I need, and I don't have a problem with using jQuery. But Twitter Bootstrap might be better for people who want decent results with a minimum of fuss, and more if they're willing to cough up the cash. And in any given professional environment, the chances of Twitter Bootstrap being already in use are astronomically higher.

Pick up a CSS framework today. It's a xs effort for massive gains.
T___T

Saturday 8 July 2017

Job Security: The Real KPI

Job security is something many people obsess over when searching for employment. The company has to be financially stable. Your superiors and colleagues have to be easy to work with. If turnover is too high, likely there's a problem. If you're too easily replaced, it's also a problem.

I've seen people worry too much about being replaced, or about becoming obsolete in the company. They do whatever they can to hold on to what they have. There's a long laundry list of petty office politics which doesn't make for pleasant reading.

Examples

User resistance. You could have users deliberately not use the newly-minted web application because it involves redefining the workflow and possibly rendering them obsolete in the long run. Or users deliberately asking for advanced features for the system at the last minute so that if you fail to deliver, they can always declare that the new computerized system is inadequate for their purposes, therefore best to continue doing things the old way.

Finger in every pie. Or you could have colleagues try to stick their noses into your assignments and offer unsolicited advice - not because they think they can do better, but because they're desperately trying to appear relevant.

Apple polishing. Then there are those who try really hard to get the higher-ups to like them, just so that they get to stay on no matter how goddamn useless they are.  They'll send emails at ungodly hours to appear as though they're working really late, or stay after office hours even though they ain't got jackshit to do. I don't really need to go into that, do I?

Need more examples? I could go on, but in a nutshell, all this occurs because people are insecure. And they are insecure because they lack quality. And they lack quality either because they were a misfit for the job in the first place, or because they failed to keep up with evolving workplace changes. And decided that instead of evolving with the workplace, they should try to make the workplace evolve to suit them.

This is a tragic case of tunnel vision because they have a flawed idea of what job security is. They think it involves hanging on for dear life to their rice bowl at all cost, being indispensable to the company, or being on good terms with those in a position to keep your job safe. It's not wrong. It is just terribly limited.

Newflash

Nobody is indispensable. Some are simply more dispensable than others.

Jobs evolve. Deal with it.

The world changes. Change with it or get left behind.

Think about it

If you no longer had this job, how long would you take to find another job?

If you updated your resume on LinkedIn, how many calls would you get in the following days?

If no other company would hire you at that price, why in the bloody heck should your company keep you on?

Feeling secure yet?

All this applies equally, whether you're an office peon or the general manager. You think you're building a secure fence around your territory with all these little tricks? Wrong. You are merely reinforcing the bars of your cage, a cage that will keep you imprisoned within this job. When your better-qualified colleagues move on to greener pastures, you will be stuck where you are precisely because you can go nowhere else. And if this ship that you've bound yourself to, ever sinks, God help you.

So what exactly is job security?

Yes, what's job security when your boss can decide to make life difficult for you just because his mother doesn't like your face? What's job security when the skills you learn today might be obsolete next year? What's job security when company structures can shift and leave you out in the cold, any given instant?

Job security is not about ensuring that your company will always need you. Job security is about ensuring that in the event that your company no longer needs you, ten other companies do. It is about keeping yourself relevant - not just to your company, but to your industry. It is about ensuring that you're always in demand.

When people think of job security, they think of it as securing a job in one particular company. Think bigger. Make sure you'll always have a job wherever you are, period.

That's the real KPI you should be looking at.

Taking control

As for your company, can you guarantee it will be around the next five years, or ten? No? That's because it's not within your control. In fact, very little is within your control. Trying to dominate the playing field is a constant struggle. You'll always be looking over your shoulder.

But you can control yourself. Learn that new skill. Take that course. Earn that certification. Ensure that your value to the company does not outstrip your value to your industry. Obtain the confidence that your experience and skillset transcend the boundaries of your company.

Once you do that, you'll have achieved job security. You will find that petty office politics are beneath you. And instead of spending time and effort to hang on to your job, you will be spending time actually doing it. Don't secure your job. Secure your career!

Indispensable regards,
T___T

Tuesday 4 July 2017

Web Tutorial: The Line Graph (Part 2/2)

Yo, dudes.

We last left off with what looked like a really messy line graph layout. This was deliberate, because now with all the individual column groups separated out, it's easier to test your data. Things are going to get a little math-heavy.

Let's begin by modifying the displayData() function. After deriving max, we declare a whole series of variables - percentage, nextpercentage, actual, nextactual, margintop and nextmargintop. These are the variables needed for node and line manipulation.

Then there are the variables node, line and tx. These are the variables that are objects. More on that later.

For now, create a nested For loop. The outer loop should iterate through the cols array of the graphdata object while the inner loop should iterate through the rows array of the graphdata object.
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {

                    }
                }

                displayScale(max);
            }


Now, right in the inner loop, set actual by running the getStatistic() function. The function is pretty much identical to the one we created for the bar chart...
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                    }
                }

                displayScale(max);
            }

            function getStatistic(data,row,stat)
            {
                var temp;

                temp = data.stats.filter(function (x) {return x.year==row;});

                if (temp.length>0)
                {
                    return temp[0][stat];
                }

                return 0;
            }


...except for this. This is necessary because in some cases, data would be undefined.
            function getStatistic(data,row,stat)
            {
                if (data==undefined) return 0;

                var temp;

                temp = data.stats.filter(function (x) {return x.year==row;});

                if (temp.length>0)
                {
                    return temp[0][stat];
                }

                return 0;
            }


Now that we have the value of the statistic set to the variable actual, just as for the bar chart, we define percentage. margintop will use percentage to determine how many pixels the node needs to be from the top, going by a height of 80% of 500 pixels. Minus 5 because the node is 10 pixels in height and we want its center to be at the level specified, see?
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                        percentage = 100 - ((actual/max) * 100);
                        margintop = ((percentage/100 * (0.8*500)) -5);
                    }
                }

                displayScale(max);
            }


And the we set the variable node to the element whose id corresponds to the i and j value (remember setting ids for those nodes?), set the margin-top property using the margintop variable, and display the value of actual using the innerHTML property for good measure.
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                        percentage = 100 - ((actual/max) * 100);
                        margintop = ((percentage/100 * (0.8*500)) -5);

                        node = document.getElementById("node_" + i + "_" +j);
                        node.style.marginTop = margintop + "px";
                        node.innerHTML = actual;
                    }
                }

                displayScale(max);
            }


Now, you see that the nodes have shifted. The values displayed in each node should correspond with the scale.


Next, you want to start rotating the lines so that they join to the next node. First, we need an If block to ensure that we only attempt this for columns preceding the final column (which only has a node and no line to manipulate).
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                        percentage = 100 - ((actual/max) * 100);
                        margintop = ((percentage/100 * (0.8*500)) -5);

                        node = document.getElementById("node_" + i + "_" +j);
                        node.style.marginTop = margintop + "px";
                        node.innerHTML = actual;

                        if (j<graphdata.rows.length-1)
                        {

                        }
                    }
                }

                displayScale(max);
            }


Before going further, I want to bring to your attention Pythagoras's Theorem. Your objective is to rotate the magenta line so that the end touches the next node. The length of the line will need to be increased as well, naturally. So as per the diagram below, we need the angle x and we need the hypotenuse, h. a is definitely 200 pixels due to the specification of the col_container CSS class.

The length of o needs to be derived. It's not as simple as 24 - 14 = 10. We have to take into account that the number of pixels is based on a height of 80% of 500 pixels!

But basically, once you have o and a, you can get h and x.


We'll begin by getting the statistic of the next node, running getStatistic() and setting the returned value to the variable nextactual. nextpercentage and nextmargintop are then derived the same way as percentage and margintop.

Then we set the variable tx by running getLineTransform(), which will return an object. We will pass in the difference between margintop and nextmargintop, and the number 200 as arguments. And of course, we'll create the getLineTransform() function.
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                        percentage = 100 - ((actual/max) * 100);
                        margintop = ((percentage/100 * (0.8*500)) -5);

                        node = document.getElementById("node_" + i + "_" +j);
                        node.style.marginTop = margintop + "px";
                        node.innerHTML = actual;

                        if (j<graphdata.rows.length-1)
                        {
                            nextactual = getStatistic(graphdata.cols[i],graphdata.rows[j+1],stat);
                            nextpercentage = 100 - ((nextactual/max) * 100);
                            nextmargintop = ((nextpercentage/100 * (0.8*500)) -5);

                            tx = getLineTransform((margintop-nextmargintop),200);
                        }
                    }
                }

                displayScale(max);
            }

            function getLineTransform(o,a)
            {

            }


For the getLineTransform() function, we first declare the variables angle and hypotenuse. These will be returned in an object.
            function getLineTransform(o,a)
            {
                var angle, hypotenuse;

                return {"rotate":angle,"width":hypotenuse+100};
            }


If o is zero, this means that the next node has the same value as the previous node. This in turn means that the line does not need to be rotated, and the length of the line is the same as a.
            function getLineTransform(o,a)
            {
                var angle, hypotenuse;

                if (o==0)
                {
                    hypotenuse = a;
                    angle = 0;
                }
                else
                {

                }

                return {"rotate":angle,"width":hypotenuse+100};
            }


Otherwise, derive hypotenuse by using the hypot() method of the Math object. It will give you the square root of the sum of all arguments squared. Remember to use the absolute value of o instead.
            function getLineTransform(o,a)
            {
                var angle, hypotenuse;

                if (o==0)
                {
                    hypotenuse = a;
                    angle = 0;
                }
                else
                {
                    hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);
                }

                return {"rotate":angle,"width":hypotenuse+100};
            }


Then derive angle. The tangent of angle is o divided by a, so you can derive angle by getting the arctangent of the result of dividing o by a. Again, remember to use the absolute value of o. The result is in radians, and we need to convert it to degrees using the getDegrees() function.
            function getLineTransform(o,a)
            {
                var angle, hypotenuse;

                if (o==0)
                {
                    hypotenuse = a;
                    angle = 0;
                }
                else
                {
                    hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);

                    angle = getDegrees(Math.atan((Math.abs(o)/a))).toFixed(1);
                }

                return {"rotate":angle,"width":hypotenuse+100};
            }

            function getDegrees(radians) {
                return radians * 180 / Math.PI;
            };


Now, if o is greater than 0, it means that the value of the next node is higher and therefore the line needs to be rotated counter-clockwise rather than clockwise. So make sure the value is negative.
            function getLineTransform(o,a)
            {
                var angle, hypotenuse;

                if (o==0)
                {
                    hypotenuse = a;
                    angle = 0;
                }
                else
                {
                    hypotenuse = Math.hypot(Math.abs(o),a).toFixed(0);

                    angle = getDegrees(Math.atan((Math.abs(o)/a))).toFixed(1);

                    if (o>0)
                    {
                        angle = angle * -1;
                    }
                }

                return {"rotate":angle,"width":hypotenuse+100};
            }

            function getDegrees(radians) {
                return radians * 180 / Math.PI;
            };


Back to the displayData() function, we access the element that corresponds to the id derived from i and j, assign it to the variable line, then set the rotation and length of line using the object tx.
            function displayData()
            {
                var stat = document.getElementById("ddlStat").value;

                var max = getMaxStatistic(stat);
                var percentage,nextpercentage,actual,nextactual,margintop,nextmargintop;
                var node,line,tx;

                for (var i=0;i<graphdata.cols.length;i++)
                {
                    for (var j=0;j<graphdata.rows.length;j++)
                    {
                        actual = getStatistic(graphdata.cols[i],graphdata.rows[j],stat);
                        percentage = 100 - ((actual/max) * 100);
                        margintop = ((percentage/100 * (0.8*500)) -5);

                        node = document.getElementById("node_" + i + "_" +j);
                        node.style.marginTop = margintop + "px";
                        node.innerHTML = actual;

                        if (j<graphdata.rows.length-1)
                        {
                            nextactual = getStatistic(graphdata.cols[i],graphdata.rows[j+1],stat);
                            nextpercentage = 100 - ((nextactual/max) * 100);
                            nextmargintop = ((nextpercentage/100 * (0.8*500)) -5);

                            tx = getLineTransform((margintop-nextmargintop),200);

                            line = document.getElementById("line_" + i + "_" + j);
                            line.style.width = tx.width + "px";
                            line.style.WebkitTransform = "rotate(" + tx.rotate + "deg)";
                            line.style.transform = "rotate(" + tx.rotate + "deg)";
                        }
                    }
                }

                displayScale(max);
            }


And there you are!


Change this back,
            .col_container
            {
                height:100%;
                width:95%;
                float:left;
                margin-right:-100%;
            }


Taking shape now...


And of course, do this...
div {outline:0px solid #FFAA00;}


Here's your line graph! Try changing the value in the drop-down list. Do the lines and nodes shift?


There was even more math than the last one, and we even had to pull out good ol' Pythagoras. It's a hoot though - we got the job done! There's plenty of room for improvement, and plenty of variations on this idea, so have at it.

Wasn't that easy? There's node-thing to it,
T___T