Sunday, 22 May 2016

Web Tutorial: The Javascript Clockface

Tick-tock!

Today's web tutorial will revolve around creating a clockface in JavaScript. It will move like a clock, with the hands of the clock pointing at the appropriate positions.

How is this useful?

Er, well actually it's not very useful. But it's good for exercising what you know about JavaScript Timer functions, transformations and the like.

Without further ado, here's the starting HTML.
<html>
    <head>
        <title>Clock</title>

        <style>
            #clockface
            {
                width:300px;
                height:300px;
                border-radius:50%;
                border:5px solid #FFDD00;
                margin-left:auto;
                margin-right:auto;
                margin-top:10%;
            }           
        </style>

        <script>

        </script>
    </head>
    <body>
        <div id="clockface">

        </div>
    </body>
</html>


So we have a div with an id of clockface, and this is what its CSS styling does.
- width:300px, height:300px sets the height and width.
- border-radius:50% makes it circular.
- border:5px solid #FFDD00 gives it a nice thick orange border.
- margin-left:auto, margin-right:auto places the clockface in the middle of the screen.
- margin-top:10% just gives the clockface a little spacing from the top.

It should look like this now.


Now for the numbers!

What's a clockface without numbers, right? Let's add those little suckers. We'll need some JavaScript for this. But first, the HTML and styling.
<html>
    <head>
        <title>Clock</title>

        <style>
            #clockface
            {
                width:300px;
                height:300px;
                border-radius:50%;
                border:5px solid #FFDD00;
                margin-left:auto;
                margin-right:auto;
                margin-top:10%;
            }

            .clocksegment
            {
                position:absolute;
                z-index:5;
                width:20px;
                height:150px;
                margin-left:140px;
                margin-top:0px;
                -webkit-transform-origin:50% 100%;
                transform-origin:50% 100%;
                border:1px solid #FF0000;
            }

            .clocksegmentno
            {
                width:100%;
                height:21px;
                -webkit-transform-origin:50% 50%;
                transform-origin:50% 50%;
                color:#444444;
                font-size:15px;
                text-align:center;
                border:1px solid #00FF00;
            }

            .clocksegmentno_offset
            {
                background-color:#444444;
                width:1px;
                height:5px;
                margin-left:auto;
                margin-right:auto;
                margin-top:-10px;
            }           
        </style>

        <script>

        </script>
    </head>
    <body>
        <div id="clockface">
            <div id="segment1" class="clocksegment">
                <div id="segmentno1" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
        </div>
    </body>
</html>


Here we've added one div with the id of segment1 and a CSS class of clocksegment within the clockface div, then nested two other divs inside the segment1 div. The first div contains the number and has an id of segmentno1 and a CSS class clocksegmentno. The second div has no id and is styled using the CSS class clocksegmentno_offset.

Here's what the clocksegment CSS class does.
- position:absolute, z-index:5 ensures that the segments can overlap within the clockface div.
- width:20px, height:150px because each segment is half the height of the clockface div, and should occupy only that much width.
- margin-left:140px,margin-top:0px since the width of the segment is 20 pixels, half of that would be 10. And since half the width of the clockface div is 150 pixels, offsetting the left side of the segment by 150-10=140 pixels places the segment dead center top of the clockface.
- transform-origin:50% 100% because you'll want to rotate the segment by its bottom center. This is gonna be important later.
- border:1px solid #FF0000 for now, so you can see what's taking place.

Here's what the clocksegmentno CSS class does.
- width:100% sets the width to be equal to that of its parent, which would be 20 pixels.
- height:20px to allocate that amount of space for the number.
- transform-origin:50% 50% because the numbers need to be rotated later, by the center point.
- color:#444444, font-size:15px are cosmetic values, change as you will. But the font size shouldn't be too large or too small. There's a certain amount of wriggle room.
- text-align:center to ensure that no matter what width the number takes up, its placement is consistent.
- border:1px solid #00FF00 for now, so you can see what's taking place.

Here's what the clocksegmentno_offset CSS class does. This is pretty much just a thin grey line pointing to the number.
- background-color:#444444, width:1px, height:5px turns this into a very thin div with a grey background color.
- margin-left:auto, margin-right:auto keeps this in the center of its parent div.
- margin-top:-10px reduces the spacing between the number and the pointer.

This is what you should have...


Now add eleven more of these segments, and number them accordingly. Remember to change the numbering of the nested divs as well!
        <div id="clockface">
            <div id="segment1" class="clocksegment">
                <div id="segmentno1" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment2" class="clocksegment">
                <div id="segmentno2" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment3" class="clocksegment">
                <div id="segmentno3" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment4" class="clocksegment">
                <div id="segmentno4" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment5" class="clocksegment">
                <div id="segmentno5" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment6" class="clocksegment">
                <div id="segmentno6" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment7" class="clocksegment">
                <div id="segmentno7" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment8" class="clocksegment">
                <div id="segmentno8" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment9" class="clocksegment">
                <div id="segmentno9" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment10" class="clocksegment">
                <div id="segmentno10" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment11" class="clocksegment">
                <div id="segmentno11" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment12" class="clocksegment">
                <div id="segmentno12" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
        </div>


Now see what you did. They're all overlapped, aren't they? So you will see no apparent difference in the output. We're going to rectify that with some JavaScript! Add the startclock() function to your script tag. Here, we run a For loop which iterates through all twelve segments.
        <script>
            function startclock()
            {
                for (i=1;i<=12;i++)
                {

                }
            }
        </script>


And modify your HTML. This ensures that startclock() is fired off right at the start.
    <body onload="startclock();">


Now let's get back to the startclock() function. First of all, rotate each segment by an increasing 360/12=30 degrees. segment1 will be rotated by 30 degrees, segment2 by 60 degrees, etc. Unless it's segment12 - segment12 stays put.
function startclock()
{
    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";

        }
    }
}


Presto!


Now to fill in the numbers. This line of code is outside of the If-else block because segment12 also needs to have its number displayed.
    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";          
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }


Here we are, the numbers are in! But do you see a problem? That's right, the numbers have been rotated along with their parent divs. This is bad because "6" now looks like "9".


We're going to rotate all the number divs independently of their parent divs. This line of code rotates the ppropriate div in the opposite direction of which their parent div was rotated.
    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.transform="rotate(-"+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.webkitTransform="rotate(-"+(i*30)+"deg)";              
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }


Like magic!


Now set all the border properties of the clocksegment and clocksegmentno CSS classes to 0px.
            .clocksegment
            {
                position:absolute;
                z-index:5;
                width:20px;
                height:150px;
                margin-left:140px;
                margin-top:0px;
                -webkit-transform-origin:50% 100%;
                transform-origin:50% 100%;
                border:0px solid #FF0000;
            }

            .clocksegmentno
            {
                width:100%;
                height:21px;
                -webkit-transform-origin:50% 50%; 
                transform-origin:50% 50%;
                color:#444444;
                font-size:15px;
                text-align:center;
                border:0px solid #00FF00;
            }
           

There you go.


Time to add the hands!

Specifically, the hour hand, minute hand and second hand.
        <div id="clockface">
            <div id="segment1" class="clocksegment">
                <div id="segmentno1" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment2" class="clocksegment">
                <div id="segmentno2" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment3" class="clocksegment">
                <div id="segmentno3" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment4" class="clocksegment">
                <div id="segmentno4" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment5" class="clocksegment">
                <div id="segmentno5" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment6" class="clocksegment">
                <div id="segmentno6" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment7" class="clocksegment">
                <div id="segmentno7" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment8" class="clocksegment">
                <div id="segmentno8" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment9" class="clocksegment">
                <div id="segmentno9" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment10" class="clocksegment">
                <div id="segmentno10" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment11" class="clocksegment">
                <div id="segmentno11" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>
            <div id="segment12" class="clocksegment">
                <div id="segmentno12" class="clocksegmentno"></div>
                <br />
                <div class="clocksegmentno_offset"></div>
            </div>

            <div id="secondhand" class="hand"></div>
            <div id="minutehand" class="hand"></div>
            <div id="hourhand" class="hand"></div>
        </div>


Here's the CSS styling.
        <style>
            #clockface
            {
                width:300px;
                height:300px;
                border-radius:50%;
                border:5px solid #FFDD00;
                margin-left:auto;
                margin-right:auto;
                margin-top:10%;
            }

            #clockcenter
            {
                position:absolute;
                z-index:20;
                width:11px;
                height:11px;
                border-radius:50%;
                background-color: #444444;
                margin-left:145px;
                margin-top:145px;
            }

            .clocksegment
            {
                position:absolute;
                z-index:5;
                width:20px;
                height:150px;
                margin-left:140px;
                margin-top:0px;
                -webkit-transform-origin:50% 100%; 
                transform-origin:50% 100%;
            }

            .clocksegmentno
            {
                width:100%;
                height:20px;
                -webkit-transform-origin:50% 50%; 
                transform-origin:50% 50%;
                color:#444444;
                font-size:15px;
                text-align:center;
            }

            .clocksegmentno_offset
            {
                background-color:#444444;
                width:1px;
                height:5px;
                margin-left:auto;
                margin-right:auto;
                margin-top:-10px;
            }

            .hand
            {
                position:absolute;
                z-index:10;
                -webkit-transform-origin:50% 100%;
                -webkit-transform:rotate(0deg);
                transform-origin:50% 100%;
                transform:rotate(0deg);
            }

            #secondhand
            {
                margin-left:149px;
                margin-top:50px;
                width:3px;
                height:100px;
                background-color:#444444;
            }

            #minutehand
            {
                margin-left:148px;
                margin-top:20px;
                width:5px;
                height:130px;
                background-color:#222222;
            }

            #hourhand
            {
                margin-left:147px;
                margin-top:70px;
                width:7px;
                height:80px;
                background-color:#000000;
            }           
        </style>


The CSS class hand is a style that encompasses all the properties that the hour hand, minute hand and second hand have in common.
- position:absolute,z-index:10 because you need these guys to overlap, and stay above the segments.
- transform-origin:50% 100% because each of these "hands" is actually a rectangular div which will be rotated by its bottom center,
- transform:rotate(0deg) is the default rotation for all of them. This needs to be specified because it will change later.

The hourhand, minutehand and secondhand divs all have different heights, widths and background colors. And because they have different heights and widths, they have different margin-top and margin-left properties too. The end result is that their center bottom has to be pointing dead center of the clockface div. The math is pretty simple, figure it out yourself.

This is what you should have right now. Of course, it's not displaying the time correctly, unless it happens to be 12 noon or 12 midnight for you. We'll move on to that next.


Displaying the correct time at the start

Modify your startclock() function like so. This declares the nowdate variable, which is set to the current date and time. Also the variables secondpos, minutepos and hourpos, which will hold the values for degrees of the rotation of your hands.

For more on JavaScript Date functions, check the following link out: (http://www.w3schools.com/js/js_dates.asp)

function startclock()
{
    var nowdate=new Date();
    var secondpos,minutepos,hourpos;


    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.transform="rotate(-"+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.webkitTransform="rotate(-"+(i*30)+"deg)";              
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }
}


Now this. Every second, the second hand moves by 6 degrees. How to we figure that? Well, the second hand moves 60 times in one revolution. One revolution is 360 degrees. So 360/60 is 6! So here we get the current value of nowdate's seconds multiplied by 6 and assign the value to the variable secondpos. If the result is 360, we don't need to move the second hand at all. Otherwise, we move the second hand by secondpos degrees.
function startclock()
{
    var nowdate=new Date();
    var secondpos,minutepos,hourpos;

    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.transform="rotate(-"+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.webkitTransform="rotate(-"+(i*30)+"deg)";              
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }

    secondpos=nowdate.getSeconds()*6;
    secondpos=(secondpos==360?0:secondpos);

    document.getElementById("secondhand").style.transform="rotate("+secondpos+"deg)";

    document.getElementById("secondhand").style.webkitTransform="rotate("+secondpos+"deg)";

}


The logic is repeated for minutes.
function startclock()
{
    var nowdate=new Date();
    var secondpos,minutepos,hourpos;

    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.transform="rotate(-"+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.webkitTransform="rotate(-"+(i*30)+"deg)";              
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }

    secondpos=nowdate.getSeconds()*6;
    secondpos=(secondpos==360?0:secondpos);

    minutepos=nowdate.getMinutes()*6;
    minutepos=(minutepos==360?0:minutepos);


    document.getElementById("secondhand").style.transform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.transform="rotate("+minutepos+"deg)";

    document.getElementById("secondhand").style.webkitTransform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+minutepos+"deg)";
}


Next, the hour hand. This one is slightly different. The time operates on a 24 hour format, so you need to figure out which number the hour hand points towards. For instance, whether the hour 4 or 16 (4am or 4pm), the hour hand should still point towards "4".

So first, we get the number of hours in the nowdate variable. If the value's greater than 12, subtract 12. Then multiply by 30. Why 30? Because there are 12 numbers, each one separated by 30 degrees! If the result is 360, change it to 0, because that would mean the hour hand doesn't budge.

But that's not all. The hour hand doesn't point right at the number unless the minute hand is pointing at 12, ie on the hour. So we figure out how much more the hour hand needs to move. Each number is separated by 30 degrees. So we take the minute hand's position and divide by 360 to obtain the ratio. The ratio is then multiplied by 30, and the result added to hourpos.
function startclock()
{
    var nowdate=new Date();
    var secondpos,minutepos,hourpos;

    for (i=1;i<=12;i++)
    {
        if (i<12)
        {
            document.getElementById("segment"+i).style.transform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.transform="rotate(-"+(i*30)+"deg)";
            document.getElementById("segment"+i).style.webkitTransform="rotate("+(i*30)+"deg)";
            document.getElementById("segmentno"+i).style.webkitTransform="rotate(-"+(i*30)+"deg)";              
        }

        document.getElementById("segmentno"+i).innerHTML=i;
    }

    secondpos=nowdate.getSeconds()*6;
    secondpos=(secondpos==360?0:secondpos);

    minutepos=nowdate.getMinutes()*6;
    minutepos=(minutepos==360?0:minutepos);

    hourpos=nowdate.getHours();
    hourpos=(hourpos>12?hourpos-12:hourpos)*30;
    hourpos=(hourpos==360?0:hourpos);
    hourpos=hourpos+((minutepos/360)*30);


    document.getElementById("secondhand").style.transform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.transform="rotate("+minutepos+"deg)";
    document.getElementById("hourhand").style.transform="rotate("+hourpos+"deg)";

    document.getElementById("secondhand").style.webkitTransform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+minutepos+"deg)";
    document.getElementById("hourhand").style.webkitTransform="rotate("+hourpos+"deg)";
}


There you go, now it tells the time! But since it's not moving, it only tells the correct time twice a day. Heh heh.


Making the hands move

Now this is simple. We could simply make this program calculate the time and display the positions of the hands every second. But that's lazy and inefficient. The minute and hour hands only move once a minute. So let's add the move_secondhand() function, and set it to an interval of 1 second.

Check out this link to learn more about JavaScript Timer functions! (http://www.w3schools.com/js/js_timing.asp)
    document.getElementById("secondhand").style.webkitTransform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+minutepos+"deg)";
    document.getElementById("hourhand").style.webkitTransform="rotate("+hourpos+"deg)";
  
    setInterval(function(){move_secondhand();},1000);
}

function move_secondhand()
{

}


First, we get the current position of the second hand. For this, because we'll be doing this repeatedly for different hands, we'll make the getcurrentpos() function. This function accepts the name of the object to be moved, and returns a number that says how many degrees this element is currently rotated by. For example, if the current value of the secondhand div's transform property is "rotate(20deg)", we strip out "rotate(" and "deg)" to give you the number.

Check out JavaScript's String replace() method. (http://www.w3schools.com/jsref/jsref_replace.asp)
    document.getElementById("secondhand").style.webkitTransform="rotate("+secondpos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+minutepos+"deg)";
    document.getElementById("hourhand").style.webkitTransform="rotate("+hourpos+"deg)";
  
    setInterval(function(){move_secondhand();},1000);
}

function getcurrentpos(varobj)
{
    var currentpos=document.getElementById(varobj).style.transform+document.getElementById(varobj).style.webkitTransform;
    currentpos=currentpos.replace("rotate(","");
    currentpos=currentpos.replace("deg)","");
    currentpos=parseInt(currentpos);

    return currentpos;
}


function move_secondhand()
{
    var pos=getcurrentpos("secondhand");
}


Then we increment that value by 6. The second hand moves by 6 degrees every time, remember? And then we rotate the secondhand div.
function move_secondhand()
{
    var pos=getcurrentpos("secondhand");
    pos+=6;

    document.getElementById("secondhand").style.transform="rotate("+pos+"deg)";
    document.getElementById("secondhand").style.webkitTransform="rotate("+pos+"deg)";

}


But wait, there's more. If the second hand has moved to 360 degrees, we reset the rotation to 0, and then call the move_minutehand() function. Because once the second hand moves to 12, the minute hand moves!
function move_secondhand()
{
    var pos=getcurrentpos("secondhand");
    pos+=6;

    if (pos==360)
    {
        pos=0;
        move_minutehand();
    }


    document.getElementById("secondhand").style.transform="rotate("+pos+"deg)";
    document.getElementById("secondhand").style.webkitTransform="rotate("+pos+"deg)";
}

function move_minutehand()
{

 
}


So the first part of the move_minutehand() function is almost identical to the move_secondhand() function...
function move_minutehand()
{
    var pos=getcurrentpos("minutehand");
    pos+=6;

    if (pos==360)
    {
        pos=0;
    }

    document.getElementById("minutehand").style.transform="rotate("+pos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+pos+"deg)"; 
 
}


...albeit with a slight difference. The hour hand only moves when the minute hand has travelled in multiples of 72 degrees. That means it only moves when the minute hand has moved 72, 144, 216, 288 or 360 degrees. Take note, that's 5 instances. Why's that? Because in the space of one hour, the hour hand only moves 30 degrees. And since each movement is 6 degrees, that means it only moves 30/6=5 times. 360 degrees divided by 5 (5 instances, remember?) is 72 degrees. So for every 72 degrees the minute hand travels, the hour hand moves.
function move_minutehand()
{
    var pos=getcurrentpos("minutehand");
    pos+=6;

    if (pos%72==0)
    {
        move_hourhand();
    }


    if (pos==360)
    {
        pos=0;
    }

    document.getElementById("minutehand").style.transform="rotate("+pos+"deg)";
    document.getElementById("minutehand").style.webkitTransform="rotate("+pos+"deg)";  
}

function move_hourhand()
{

}


And of course, the move_hourhand() function performs pretty much like the others, except that it doesn't cause any other parts of the clock to move.
function move_hourhand()
{
    var pos=getcurrentpos("hourhand");
    pos+=6;

    if (pos==360)
    {
        pos=0;
    }

    document.getElementById("hourhand").style.transform="rotate("+pos+"deg)";
    document.getElementById("hourhand").style.webkitTransform="rotate("+pos+"deg)";

}


There you go, check this demo out!
















Now, wasn't that a handy exercise?
T___T

Thursday, 19 May 2016

Film Review: Grace

Yet another Social Media-themed horror movie? Is this turning into a thing?

When Grace, also known by its Thai title as Awasarn Lok Suay, appeared in theaters here, I was leery of throwing away another twelve bucks and another couple hours of my time after what happened with Friend Request. Only one thing spurred me on. Grace is a Thai horror thriller. And no one does horror like the Thais. Only the Japs and Koreans come a close second.


Grace is meant as a cautionary tale about the hazards of Internet popularity, with Facebook again one of the driving forces behind the plot. But by the third blood-splattered corpse, you'll probably forget about all that and enjoy the violence. Because no one does violence like the Thais.

There's also a fair bit of very unsexy sex, most of it off-screen, but in a context horrific enough to capture the imagination. Because no one does rough sex like - well, you get the idea.

Warning - mild spoilers ahead, sexual scenes and violence

Because I think this movie is worth a watch, I'll endeavor to keep spoilage to a minimum. Not that there's all that much plot in there. It's all about the execution, see? (Execution, geddit? Hur hur)

The Premise

Care is an Internet idol, whose existence seems to be defined by the number of comments, Likes and Shares on her Facebook page and blog, Summer Trick. Ple, her best friend and online fiction writer, maintains that blog.

Grace is a former Internet idol for reasons which you will discover during the show. In her jealousy, she and her accomplice Jack kidnap Care and Ple, and the torture begins.

The Characters

Apinya Sakuljaroensuk as the titular character, an Internet idol fallen from grace (snicker). She was cute enough, but it was the acting that wowed me. Got a little over-the-top towards the end, but perhaps that's the nature of the business when playing psycopaths.

Nuttasit Kotimanuswanich as Jack, Grace's pervy and lecherous companion who likes to leer at cute girls in Cosplay uniforms and jerk off to Hentai porn. What an amazingly loathsome performance. One-dimensional, sure. But he fit the role so well, I hated him way before he did anything nasty.

Napasasi Surawan as Care, the annoyingly perky and cute Internet idol. Not sure I blame Grace for wanting to torture and kill her. Within minutes of watching her crap,I was fantasizing about strangling her myself. Jack does filthy things to her during the show, and even then I'm not sure I sympathize. She definitely gets the lion's share of the torture. On-screen punching bag, that's the character.

Latthgarmon Pinrojnkeerathi as Ple. The role took a back seat to Care, and it's something of a surprise at the end when she gets the spotlight shone on her, and she gets her turn as psycho bitch. But by then, one gets the sense that the movie has gone on just a wee bit too long.

Unknown actor as Nay, the shy and quiet nerd who is Care and Ple's photographer and unfortunately gets a serious case of "wrong place, wrong time". He wasn't even the target, and he was just minding his own business.

Unknown actress as Pim, one of the villains of the film, who contribute to Grace's eventual derangement with a cruel prank taken to extremes.

Unknown actor as Earth, the obligatory pretty boy role that turns out to be Grace's undoing. He was pretty enough, but bland. With a capital B.

The Mood

Mostly bouncy and cheerful in the beginning as we walked through the sheer joy that is Care's seemingly perfect life. Got bloody midway through, with loads of tension throughout. Somewhere at the end, while remaining dark, the violence got a bit corny. Be warned though, that this tale is not told in a totally linear way. Scenes are intercut with flashbacks which aren't obviously so, thus a bit of confusion might result.

What I liked

The acting - have I mentioned that Nuttasit Kotimanuswanich and Apinya Sakuljaroensuk really got their game faces on for this one? Apinya in particular. She really pulled out a whole range of expressions - meek and awkward at first, confident later, then crushed and depressed followed by cynical, sadistic and homicidal. As mentioned earlier, the entire movie is intercut with flashbacks, and it's not always clear when a certain scene is happening. Apinya takes care of that very well with the on-screen contrasts in personality.

The glorious, glorious violence. Now if there's something Thai horror always delivers, it's this.

What I didn't

The whole gang-rape/Internet shaming thing that eventually led to Grace wandering up the psycho path. I get why it was a necessary plot device. It just felt totally overused to me and I could see it coming a mile off. Having seen it in Shutter and more recently, Senior, this was a serious case of deja vu. Still, compelling enough as a reason to start killing people, I suppose.

Cosplay's not really my thing. Again, I get why it was a necessary plot device, with Grace being a seamstress and into making costumes and all. I get it, yes. But it does nothing for me and I just don't have to particularly like it.

The ending, with the chase scene between Ple and Grace, felt awkward and forced, like the movie was extending way past its closing time.

Conclusion

Moral of the story: Don't piss off Thai women. They're fucking nuts.

My Rating

7 / 10

Watchable. Take a stab at it!
T___T

Friday, 13 May 2016

Ten Job Ad Descriptions to run away from, really fast

Every six months, I make it a point to survey the market and read job advertisements. Not because I want to leave my job, but because I want to keep my finger on the pulse of the web industry. However, due to being hellishly occupied with work and school, I shelved this for pretty much the whole of 2015.

When I finally got back to it recently, I was in for a rude shock. Appalled does not even begin to cover it. Was it me, or had the standards of job advertisements in the web industry fallen shockingly low since I last checked? That was when I started compiling this list of job ad sins. It was a bit of a struggle deciding on a title for this. "Ten ways to look like a complete amateur on a job ad"? "Ten ways to ensure no self-respecting web developer applies for your company, ever"?

No, I will, for the most part, not be including live examples. Because if I did, let me assure you that this list would not be stopping at ten. I will, however, do some paraphrasing.


1. Horrible grammar

This one's a bit of a no-brainer. The odd spelling error here and there is forgivable - actually, hell no it isn't. Not in light of the fact that there's spellcheck and all sorts of nifty tools available these days, and the fact that you're generally supposed to proofread your shit before making it live. That said, I'm prepared to overlook the odd spelling error or two. But when you add bad punctuation and inconsistent capitalization into the mix, I am not going to be regarding your job ad with anything other than derision.

Learn to spell, will ya?

I am a professional, and good golly, I expect - no, demand - a reasonable standard of English from my prospective employers. On the other hand, web developers whose first language isn't English should not mind too much - they probably won't even notice.

Come on. I know us web developers don't earn big money, but calling us "developoors" is a gross exaggeration.
MNC seeks an experienced web developoor.

More examples...
- applicants are expected to have proificiency in Java and Ruby.
- You will be working on our propreitory database management system.

The cruelest irony
- Should conversed well in English and Bahasa.


2. Glaring technical errors

The forward slash is meant to be used when certain things belong together, or are interchangeable.

An example would be
- HTML5/CSS3
- LESS/SASS
- MySQL/Sql Server/Oracle
- XML/JSON

But not this. I'm not sure any web developer wants to work for people who don't understand that Java and JavaScript don't belong together.
- Java/JavaScript
- SDLC/MVC

Odd one out.

And then you have this beauty...
- you should be well-versed in programming languages such as AJAX, ASP.NET, jQuery, MySQL, HTML, Python

Of the above, only one could be classified as a programming language. Don't laugh. It's on so many job ads, in varying permutations, that it stopped being funny a while back. Developers generally want to work for, or alongside people who know what they're doing.

Sure, perhaps a non-technical person such as HR wrote the job ad. Pray tell, what kind of company has so little common sense that it would allow a non-technical person to draft a technical job description?


3. Meaningless requirements

You get this in a huge number of job ads, and they all mean the same thing - absolute squat.

You should be:
- self-motivated
- self-starter
- creative, out-of-the-box thinker
- enthusiastic
- detail-oriented
- a great team player

These things are nigh impossible to prove. Everyone and his dog thinks he's creative and a self-starter. No web developer is going to tell you "I'm boring and unimaginative, and I need the occasional pep talk to keep me motivated".

An out-of-the-box developer?

How is a web developer supposed to prove enthusiasm? Have a GitHub account and contribute to the open source community? Well, then perhaps your job ad should have said this...
You should have a GitHub account which showcases your contributions to the open source community.

See? Say what you actually mean. Wasn't that easy? And way more meaningful than copying some clever-sounding buzzwords from everyone else's job ad?


4. Requirement creep

And sometimes, you get a job description like this. The requirements are so numerous, it makes one wonder how it is humanly possible for any one individual to have them all.
- Well-versed in CMSs such as WordPress and Drupal.
- Expertise in e-Commerce technologies.
- Comfortable with both Windows and Linux environment.
- Have previously participated in projects through at least two successful SDLCs.
- Extensive experience in database systems, preferably SQL Server.
- Mastery of Photoshop and other image-editing tools.
- Expertise in HTML and CSS, and CSS preprocessors like LESS and SASS.
- Must be well-versed in design and UI.
- Mastery of SEO and internet marketing.
- Proficiency in JavaScript and JavaScript libraries like jQuery and MooTools, as well as AngularJS, React and Meteor.
- Deep knowledge of C# is a must.
- Experience in other programming languages such as Java and Ruby.
- Have written apps for both iOS and Android.
- Should be able to deliver twice as fast as the average developer.
- Typing speed of at least 80 wpm.

We call this "requirement creep" because it probably started out as a small and reasonable list of technologies the new hire is supposed to be proficient in, and somewhere along the way, some bright spark remarked "it would be better for him to have deep knowledge as opposed to merely proficiency in C#" or "knowing AngularJS isn't enough - we do a bit of React too, and some Meteor", or even "it would be nice if he was a fast typer as well".

What else did you miss out? Should he fart unit tests and burp functioning code, too?

Santa's in town,
and he'll grant all
your wishes.
I get it - you want the best possible value for your money. So does everyone else. But bear in mind that such a talented individual will probably not be applying for a job - you'll have to poach him from somewhere else and extract him from his current employer's cold dead fingers. And that's if you could afford him.


5. Stupid descriptions

This one really makes me cringe.
Groovy, exciting startup looking for PHP Jedi whose Force is strong in the code.

Looking for a ninja?

Seriously, don't be an idiot. This goes double for job ads with descriptions like "Database Guru", "Digital Samurai", "Code Ninja", "Software Wizard", "Rockstar Programmer", "Superstar Developer" and so on. The web development industry already seems to have problems being taken seriously - people seem to think all we do is make websites. And buddy, you're not helping.

If this is an attempt to appear witty, stop trying so hard. Because it's producing the opposite effect.



6. Too many words

Being grammatically and technically correct won't save you if you don't know when to stop writing your description. Some descriptions are so convoluted with overly long words that by the time the developer's finished reading it all, he's lost the sense of what the ad was trying to say.

Wall of Text effect.

Take this for example
You will be expected to gather clients' requirements through regular meetings and collate them into system specifications for your fellow developers and document these requirements using accepted conventions to illustrate data flow and entity relationships. Upon which you will implement these requirements in proper object-oriented design following industry-accepted best practices and...

Oh snap, where was I? I probably nodded off after the second sentence. Couldn't all that have been adequately condensed into this?
Must be familiar with the Software Development Life Cycle.

Everybody wants to sound like they know stuff, and that unfortunately tends to translate to using ten sentences when you only need ten words. Well, here's something else for you to know - keep it short, and cut the crap.



7. Obsession with years of experience

I understand that in many industries, years of experience is a relevant metric. But where web development - indeed, any kind of software development - is concerned, all bets are off. Web development is such a wide and varied field that no two web developers are likely to have the exact same skillset or progression. Unlike, say, bricklaying. It would be a safe bet to say that a bricklayer with 10 years' experience would have developed muscles pertinent to mixing cement and laying bricks.

EDITOR'S NOTE: If you are a bricklayer and feel that your job scope is way more complex than just mixing cement and laying bricks, please accept my humble apologies.

But for something like this...
- must have 5+ years' experience in software development, at least 2 in mobile environment

It takes someone of average intelligence to learn the basics of software development anywhere between 6 months to a year, perhaps less for those among us who are more gifted. Beyond that, a developer could spend the next few years just reusing those basics and getting better at them, or branching out into other aspects of software development - different technologies, different methodologies, and so on. Just what is having "5+ years' experience in software development" supposed to prove?

Think this guy has
enough experience?

This next one takes that obsession to a new extreme.
- 7 years experience in Java.
- 4 years of experience in Windows server environment.
- 5 years or more in Social Media technologies.
- At least 3 years using C++
- 2 years or more in HTML/CSS/JavaScript
- 2 years or more in...

Or for the utterly ridiculous
- 5 years or more in HTML5.

HTML5's specification was stable in 2014. It first became a thing back in 2008. It is now 2016. In order for the applicant to have 5 years' experience in HTML5, he would have to be one of the founding members of the HTML5 specification. Let's say I cut the job ad some slack and assume the poster meant HTML instead of HTML5. This is HTML, dude. A school kid could learn HTML in a few weeks. Exactly what is an individual with 5 years' experience in HTML supposed to do for you? Do nothing but write EDMs? Write a book on the history of Netscape, maybe?

HR needs to stop trying to apply general rule-of-thumb to web development. It doesn't work.



8. Needing that degree

Many job ads seem more interested in the web developer's academic qualifications rather than his ability.
- Candidate should possess a Bachelor's degree in Computer Science, Information Technology or any other related discipline.

Having one degree and three diplomas, I probably should be the last to complain about this. But the fact is that this betrays a lack of understanding as to how web development works. A vast percentage of the web is created by self-taught developers with little to no formal computer science education. Why's that? Because you don't need a goddamn Computer Science degree to do web development. There's plenty of learning material on (gasp!) the web.

Degrees FTW!

A formal Computer Science degree simply gives a better idea of what the developer should have learned in school, but even that is a poor gauge for what skills he has kept current. I remember learning data structures and memory management and Boolean Algebra when I was a teenager, mugging for exams. How much of it have I actively used in web development? Pitifully little. Academic qualifications mean very little, especially if said qualifications were earned ten years ago and you're in a field that evolves every couple years.



9. Laundry list

I wrote about this back in February in response to something the managing director of Fixx Digital said, but it bears repeating.

Some job ads have a very specific list of skills they want their web developer to have. Way too specific.
- proficient in PHP 5.5 and above.
- knowledge of CodeIgniter.
- familiar with MySQL version 6.0 onwards.

I get it. I do! You want someone to fit into that pigeonhole, someone you don't have to spend time training, who may just up and leave anyway. I've said it before and I'll say it again - this is terribly shortsighted. You're missing out on potentially great hires as opposed to people who simply happen to have that specific skillset.

Check those boxes!

Try this instead.
- proficient in PHP, preferably version 5.5 and above.
- knowledge of MVC frameworks such as CodeIgniter.
- familiar with SQL databases such as MySQL.

See what I did there? The general skill was listed there, followed by a preference or example. This means that candidates applying for the job will at least have the foundation rather than the specific skillset. And if they don't know the specific technology, they can always learn. They have the foundation, don't they?

If you're not willing to invest in people, why should people invest the best years of their youth in your company?



10. Outright douchebaggery

I don't actually want to flog that dead horse and bring to life the specter of Sugar Technologies' disastrous job ad back in 2014, but this serves as a great example of what to write if you want to piss developers off.

Watch the attitude.


Who We Want:
  • You are ambitious, intense and entrepreneurial, and you dream of doing truly epic shit with your life. You recoil at the thought of having 1.15 kids, balloting for a flat, saving up for a Toyota and waiting to withdraw your CPF savings at 65 (or 70, or 80, or 120).
Who We Don't Want:
  • You are a shallow social climber whose dream is to work for Goldman Sachs because it "looks good on your CV". You went to an Ivy League university because it would "look good on your CV". But you couldn't get into Goldman Sachs (or Citibank, or HSBC) because you actually have no passion for finance, just like how you want to buy that Louis Vuitton bag (because it will "look good on your social CV") despite having no passion for design and craftsmanship. Guess what? If you were rejected by Goldman Sachs, you will be rejected by us too. For exactly the same reasons. Advice: try applying for a government job.
  • You are a social misfit. You like technology only because you can't relate to real people. You spend your weekends playing DOTA and in your sleep you dream about your hero reaching Level 38. At school, you were always the last to get picked on any project or sports team. Having a conversation with you is about as interesting as watching paint dry. Advice: go seek help from a mental health professional.

A certain amount of arrogance is healthy. But if you're going to act like people should be grateful to be working for you, you'd better be more than just another start-up out of millions in sunny Singapore. And yes, almost all start-ups will claim they are that special. Let's just see who's left standing after the next few years, shall we?

At the end of the day...

Your company's job ad affects the kind of applicants you get, which in turn affects the kind of employees you eventually get. Many job ads I see could be worded a little more carefully and some of them just need to be killed with fire.

Web developer looking for a halfway decent job ad,
T___T

Sunday, 8 May 2016

Film Review: Friend Request

Another Social Media-themed horror flick has emerged since last year's Unfriended. It's titled, rather unimaginatively, Friend Request. Unfortunately, this is not the only thing unimaginative about this trite, generic fright-fest wannabe. This feels like a poor distant cousin to Unfriended. A far poorer distant cousin.


The Premise

Laura Woodson is a popular student who accepts a Facebook Friend Request from a lonely (and creepy) fellow student, Marina Mills, who starts latching on to her like a crazed stalker. When Laura eventually rebuffs Marina, Marina takes suicide and, through the use of a curse spreading through Social Media, starts killing off Laura's friends one by one.

Warning: Spoilers ahead

Fortunately though, this film is so utterly predictable and lacking in depth that not spoiling it wouldn't make a whole lot of difference. At the end of it, you probably won't even care if the protagonist lives or dies. I know I didn't.

The Characters

Alycia Debnam-Carey as Laura Woodson, looking like a cross between Jennifer Love Hewitt and Reese Witherspoon. Gorgeous. Also wooden and uninspiring.

Liesl Ahlers as Marina Mills, reminiscent of Rooney Mara's turn as Lisbeth Salander due to the complexion and the hoodie, but not much else.

Connor Paolo as Kobe. A decent performance as the cool tattooed tech geek, right up to his rather unbelievable and poorly-executed face-heel turn.

William Moseley as Tyler, Laura's boyfriend. Did little more than sit around, look pretty, deliver some insipid dialogue and die. How far he's fallen since The Chronicles of Narnia.

Brit Morgan as Olivia, a snippy bitchy lankly blonde. Nothing special. I felt nothing when she died.

Sean Marquette as Gustavo. Did what he could with a weak script to deliver some insensitive remarks here and there, but ultimately just cannon fodder.

Brooke Markham as Isabel. More cannon fodder.

Shashawnee Hall as Detective Cameron, a snarky black detective with some chuckle-worthy lines. Check out what he says upon seeing a battered and bloodied body in a hospital bed.

Cameron: Looks like someone's had a rough day.

The Mood

Slickly produced, with dimmed lighting when appropriate. Stereotypically dark during scary moments, and bright during light-hearted ones.

What I liked

Plenty of creepy gore. That's one thing they did right.

A close-up of Alycia Debnam-Carey's delectable ass. Easily one small highlight of the entire film, which is telling.

The concept of Marina Mills posting snuff films of Laura's friends dying, under Laura's own account? Clever.

What I didn't

Lame dialogue (including Tyler's brief jealous boyfriend act).

Cheap loud scares with predictable buildup. There was nothing psychological about the horror.

The focus on Laura's diminishing number of friends on her Facebook. Like, who the hell cares?

We never got the sense that the retribution was justified in any way. In a way, this is supposed to make their deaths even more horrifying, but somehow... meh. Coming up empty. Didn't like the characters, didn't dislike them... and their deaths, other than the shock quotient, were met with indifference by this viewer.

Conclusion

Unfriended is an infinitely superior offering to this lacklustre thriller. Watch Friend Request if you have time to waste, and like cheap jump scares.

My Rating

3 / 10

Not worth a Share,
T___T

Monday, 2 May 2016

Why Emails Bounce

During an email marketing campaign (with EDMs and such), tons of complicated-looking reports tend to get churned out. And among the things normally asked by salespersons, one stands out as the most frequent of all.

"What's the difference between a hard bounce and a soft bounce?"

I'm tempted to troll that question with a pic like this one:

*snicker*

OK I'm sorry. That was udderly juvenile of me. Just wanted to get this off my chest. I'll be on my breast behavior from now on. Impeccable, even.

Ahem.

Well, to answer that question, we would first have to examine the similarities before we can understand the differences. I will endeavor to keep this simple.

Similarities - Hard Bounce and Soft Bounce both mean that your email was not delivered.

Differences - where they differ is the reason behind the email not being delivered.

Basically the terms Hard Bounce and Soft Bounce are meant to categorize the reasons why your email to this address is not being delivered. That said, we'll now delve into what constitutes a Hard or Soft Bounce.

Hard Bounce

A Hard Bounce occurs when you have an undeliverable email address. Take, for example, the address <info@teochew.thunder.com>. Perhaps the domain teochew.thunder.com does not exist at all. The other possibility is - teochew.thunder.com does exist - but there is no mailbox info in that domain! This means, the address info@teochew.thunder.com, in either of these cases, is undeliverable.

Think of yourself as the mailman, delivering a package. The package is addressed to, for example, Mr Info. He lives at Teochew Avenue, Thunder District. You make your way merrily to Thunder District and find, to your consternation, that there is a Canto Avenue, a Hakka Avenue and a Hainan Avenue. But no Teochew Avenue!

OK, now let's assume there is a Teochew Avenue in Thunder District. But there is no Mr Info along Teochew Avenue!

OK, where exactly does this
guy live, again?

Your package cannot be delivered because there's something wrong with the address. There's a possibility your address was spelled incorrectly. Or perhaps that person simply no longer lives there. That's a Hard Bounce.

In these cases, your best bet is to permanently remove those email addresses from your mailing list.

Soft Bounce

A Soft Bounce occurs when the email address does exist. But some temporary situation is preventing the email from being delivered. Perhaps the email server is undergoing some lag at the moment. Perhaps it's temporarily down for some maintenance. Perhaps the mailbox is full. Or perhaps the email you are sending happens to be too big for the mailbox, full or not full.

Again, think of yourself as the mailman. You know Mr Info lives along Teochew Avenue in Thunder District. But Teochew Avenue is experiencing heavy traffic at the moment, and your delivery van hasn't moved for the past hour. Perhaps there was a traffic accident, and Teochew Avenue is temporarily closed.

Sometimes this is why your
package doesn't reach you.

Or maybe it's neither. You get to Mr Info's house along Teochew Avenue just fine. But his mailbox is full and there's no way you can squeeze your package in there. (OK, no filthy jokes about squeezing your package in anywhere, please!) Or, you're delivering a refrigerator instead of a letter. and there's no way that sucker is going in the mailbox.

So in the event of a Soft Bounce, monitor those email addresses carefully. If they crop up too often, remove them. Either temporarily or permanently. Your call!

That's all from me today. Certainly hope I delivered!
T___T