Saturday, 20 August 2022

Button and Input tags in HTML5

In HTML5, there are two tags that produce visually similar results. They are the button tag and the input tag with the type attribute set to a certain value - button, submit or reset.

Here are examples.

For the input tag, the type attribute is required if the tag is to be rendered as a button.
<input type="button" value="Example"/>




For the button tag, the type attribute is not required for it to render as a button. A value of submit is the default.
<button>Example</button>




Their use cases tend to overlap, but these are distinct tags and there are scenarios when one is more suitable than the other. First, let's take a look at all the possible values for the type attribute, which can apply to the button tag as well.

button - self explanatory. This will cause the input tag to appear as a button, but do nothing for the button tag.
submit - if the button or input tag is inside a form tag, clicking it will cause the form to submit.
reset - if the button or input tag is inside a form tag, clicking it will cause the form to reset.

And finally, we have the value image. This is only applicable for input tags, and the src attribute is required as well. The resultant button will have an image and act as a Submit button.

For this, we will use the image below.
1_0.jpg

<input type="image" src="http://www.teochewthunder.com/posts/2022/8/1_0.jpg"/>




For button tags, HTML is allowed between the opening and closing tags, so we could do this.
<button><img src="http://www.teochewthunder.com/posts/2022/8/1_0.jpg"></button>




Which is better?

As always, that really depends on context. In most contexts, the functionality of the input and button tags are interchangeable.

However, again, in most contexts, the button tag is semantically more obvious. And since we don't have to bother with Internet Explorer any more (the button tag had an issue there) we can just use the button tag for most cases. Also, remember that it is possible to embed HTML within the button tag. This makes it infinitely more useful from a design perspective. Sure, we could apply CSS on the input tag the same way as a button tag, but why complicate matters?

One use case for the input tag does stand out, though. Consider this code. In here, we dynamically change the type of the input tag upon clicking, which would not be possible for the button tag.
<input type="button" value="Click me for a magic trick!" onclick="this.type = (this.type == 'button' ? 'checkbox' : 'button')" />


Click on it, see what happens!


Cool parlor trick, eh? Unfortunately, that is also a very niche case... and quite frankly, plainly ridiculous.

Conclusion

The button tag is coolness exemplified, and with the advent of HTML5, on firm ground. There are still uses for the input tag, though perhaps not as a button anymore.

On to more pressing issues!
T___T

Saturday, 13 August 2022

Trail of the Catfish (Part 2/2)

It would be a full three weeks before I got back to this issue. Life had gotten very busy at the office. I was drowning in the workload. When I finally caught a break, my friend sent me a WhatsApp message to excitedly say that his girlfriend was due in Singapore in six hours. I was relieved to see that - at least there was going to be some live interaction now.

At the end of the workday, however, things had changed. He despondently told me that there had been a last-minute change in plans, and she was not coming over after all. I can't say I was too surprised at this news.

One week later...

There was more news. I learned that she had completely disappeared. According to my friend, she was no longer communicating with him.

Up in smoke.

My friend told me that the site he had been investing in, had suddenly gone down, and with it, his outlay of twenty thousand dollars. Well, I reasoned, at least he had been withdrawing his earnings regularly, right? Erm, right?!

Nope!

According to him, that would have made less sense than leaving the money there to continue rolling for him. So he'd lost everything in there, and when he tried asking for his ten thousand dollars back from his girlfriend to mitigate his losses (this really made me roll my eyes, by the way), she ghosted him.

With that in mind, my follow-up question was: if he hadn't been withdrawing the money from the site, where did he get the money to send to his girlfriend?

The answer made me facepalm really hard. He had taken the money from his own bank account. Because he had assumed that he would be able to withdraw the money from that site any time he wanted.

Wow. The sheer fucking recklessness of this made me see stars. How had that seemed like a good idea? Now he had not only lost the ten thousand he had sent, he had also lost the twenty thousand that was his initial investment. Thirty thousand in all - which, coincidentally, was about the sum I laid down as a deposit when I bought my own place back in 2014.

Evidence

Despite all that, my friend was adamant that what they had shared was real. That she had been real. And he would listen to nothing else.

That was when something clicked inside me. I took the initial image he had shown me and did a Google image search for this. What I found made my blood run cold. The image turned up several similar images of the same woman. It led me to a Weibo blog. This blog belonged to an influencer from Zhejiang in China. As a matter of fact, the very image she had sent him, was a duplicate of the one found on the blog, with one small change: the Weibo icon had been snipped off.

Before and after.

His girlfriend was a famous local celebrity in Zhejiang, who was already engaged to another man.

When I confronted my friend about this, he told me he wanted to drop the matter entirely, and asked me to stop talking about this to him. And, feeling that my point had been made, I saw no need to belabor it.

Lessons this taught me

Just because someone is older than me, does not make them smarter. Quite the contrary, they can be easier to fool precisely because they think they are immune to being taken for a ride. When considering if someone is capable of prudent decision-making, it helps to consider the context. My friend wasn't stupid normally, but in matters of romance, he has had woefully little experience. This clouded his judgment, and ultimately turned out to be his downfall.

Do I feel guilty for my lack of action? No. Undeniably, I prioritized my own shit over that of my friend's, precisely because I trusted him to be able to handle his own shit. We've all got our crosses to bear.

But I do feel ashamed. As a tech professional, doing that Google image search should have been the first thing that came to me - not almost a month after the fact.

And I feel embarrassed. Some catfisher in China probably now thinks that Singaporean men are so goddamn easy to fool - using pictures of a local celebrity, no less! All she (or maybe even he) really needed to do was sweet-talk some idiot on WeChat for a few months, with a five-digit figure payoff to show for it. Now, if only earning honest money were that easy.

(cat)fishing in troubled waters!
T___T

Thursday, 11 August 2022

Trail of the Catfish (Part 1/2)

Several dangers lurk amidst the glitzy facade of the webosphere, and one of these is catfishing. This is a scenario where one person masquerades as someone else to manipulate a user into doing something foolish - usually parting with money or information. I was unfamiliar with the term (but not the phenomenon) until a friend of mine got hit with a particularly bad case of it.

At the time, I was an agency-contracted software developer, considering further education, struggling with my new role as a husband. I was being besieged on multiple fronts - professional, academic and personal. I had my fingers in way too many pies as it was. And thus I did not deal with this as immediately and firmly as I might have. Also, strictly speaking, it wasn't my situation to deal with.

How it went down

Over dinner one evening, this friend of mine told me he was in love. He had met this woman online through WeChat, and she was now his girlfriend. When he showed me a picture, I was impressed. Cute, great figure. The only question was - why would she be interested in my friend? He wasn't ugly, but appearance-wise, he was no hunk. He was in his mid-forties, not particularly well-off, and I had never been under the impression that he was especially charming. Indeed, I had been laboring under the impression that he was the archetypical 40-year old virgin.

A pretty girl on camera.

Still, stranger things have happened. I was prepared to be happy for my friend. Why look a gift horse in the mouth, eh?

It was about to get stranger still, however. He told me he had never heard her voice "live", only through sound clips. And whenever he tried to video call her, she never picked up. But they spent several nights chatting away (via text and sound clips) into the wee hours of the morning, and he even serenaded her with his own singing several times.

So, I was supposed to believe that he hooked her with his singing voice? A lot less believable, but maybe there was something about my friend that I, as a heterosexual male, wasn't seeing.

Of more concern was the fact that he had never interacted with her live. In this day and age of deepfake technology, even interacting live isn't such a big deal for authenticity anymore. But not even having that? Fishier than the River Nile. The words of my favorite comedian Bill Burr comes to mind.

"That's not a red flag; that's the fucking Soviet Union."


The Investment

As we spoke, my friend also revealed that his girlfriend (though I was having serious doubts about the veracity of that term by that point) had introduced him an investment site where he had already put in twenty thousand dollars of his own money, and according to him, he had already earned more than ten thousand off his initial investment. Now I'm no expert in that department, but that seemed like a decent return. Actually, it was positively too good to be true.

Investment site.

He wanted to send this woman ten thousand dollars of his earnings as a gesture of appreciation. This seemed like good news - at least he could draw out his earnings, right? He had already tried to send the money, and the bank had refused to facilitate this transfer because it fit the pattern of many online scams. Thus he was wondering if he could transfer me some funds so I could affect part of the transfer on his behalf. I declined, not because I was against him sending money - that was frankly none of my business - but because, in principle, I am deeply uncomfortable with anyone messing with my account. Eventually, later on, he finally managed to send the money, in three separate installments.

I had a look at the site. And then I Googled it. What came up was, well, more red flags. Apparently this site was being flagged for suspicious activity. When I pointed it out, my friend said that he was aware. That figured; he was a grown-ass man and he could do his own damn Google search, right?

My initial reaction

If you're anything resembling a normal person, reading this, your inbuilt danger alarm must be ringing like mad by now. There were way too many things about his story that stank. In fact, the entire story stank. It reeked like an open sewer. It sounded like my friend was getting in dangerously deep and needed a reality check.

But from whom would that reality check come? Me?

Don't stick your
hand into that fire!

In all honesty, I chose to deal with my own immediate problems and largely left it to my friend's common sense to help him navigate this potential minefield. After all, it wasn't like I had any personal experience with pursuing online romances - what did I know, really?

Sure, I could have said something. Anything. I could have pointed out how ridiculous all this sounded. But would I be pointing out anything he himself didn't realize and didn't want to admit to himself? Would I be saying anything that his mother and siblings had not already been saying? Why would he listen to me, if he was already ignoring their counsel? With that in mind, would talking actually accomplish anything, or would it just be talking for the sake of talking?

I'm a software developer. What kind of software developer does something for the sake of achieving nothing? A fucking useless one, that's what.

No. If I was going to say anything, I was going to have to do better than just talk. I was going to have to present evidence of some sort. Thus, I wished him the best of luck and went on my way. I did eventually discover evidence, but it was too little, too late.

Next

The fallout!

Wednesday, 3 August 2022

The Sheetbend-Bowline Analogy

Knots are a scientific marvel, and I say that with no irony whatsoever. They are capable of accomplishing so many different things - joining lines, forming loops, stopping, and hitching, among other things. And sometimes, when you look at different knots closely, you will find that they are actually astonishingly similar. Some of them may even be identical, but serve a different function!

One of the knots that we are examining today is the Sheetbend, which is a fairly common knot used to join two lines. This is the form it commonly takes.

From Animated Knots

The other knot we are going to examine is the Bowline, which is a knot used to form a loop. This is one of the most ubiquitous sailors' knots in existence today.

From Animated Knots

Now, if you zoom in on the Bowline, like so...

This resembles a Sheetbend.


...you'll notice that the Bowline is just about identical to the Sheetbend. In fact, one could even say that the Bowline is simply a Sheetbend that has been tied back upon its rope, to form a loop!

There's an equivalent in the world of programming. Ever heard of Linked Lists? The nodes in a linked list are basically made of two parts - the data, and a pointer to the next node. If there is no next node, the pointer is null.


A Linked List consisting
of two nodes

Just like a Sheetbend, connecting two different lines!

However, there is another type of Linked List, called a Circular Linked List. This is when the last node does not point to a null, but back to the first node.

A Circular Linked List
consisting of one node.

In the example above, that is a singular node Circular Linked List! Basically, a Circular Linked List with only one node.

When you take those parallels with the Sheetbend into account, would that resemble a Bowline? You know it does!

Finally

This was totally whimsical. But the parallels are uncanny. Granted, not everyone is a knot nerd and a computer geek like myself, so I'll understand if the analogy is lost.

Keeping you in the loop,
T___T

Wednesday, 27 July 2022

Code indentation reduction using Guard Clauses

Excessive indentation seems to be a common plague among programmers, regardless of your years of experience. Ever so often, I find myself getting caught in multiple levels of logic, and the inevitable nesting that follows, makes my code terribly hard to read. I should know; I've been called out plenty of times on this!

Take this example in JavaScript.

function f (x, y)
{
  if (x)
  {
    if (y)
    {
      return y;
    }
    else
    {
      return x;
    }
  }
}


To combat this, programmers use early exit techniques, and recently, I discovered there was actually a term for this: guard clauses.

That's right, decades after my first Hello World program, I'm still learning new stuff. Perhaps even stuff I should have learned a long time ago. But, as they say, better late than never.

Now, in the example above, we don't do anything if x is false. There's no Else clause. So what we do, is exit early.

function f (x, y)
{
  if(!x) return;
 
  //if (x)
  //{
    if (y)
    {
      return y;
    }
    else
    {
      return x;
    }
  //}
}


So the code now looks like this.
function f (x, y)
{
  if(!x) return;
 
  if (y)
  {
    return y;
  }
  else
  {
    return x;
  }
}


See what we did there? We cut to the chase. Now the code just has one less If clause taking up the cognitive load. But is that enough? Not at all; we can actually simplify this further.

function f (x, y)
{
  if(!x) return;
  if(!y) return x;

  //if (y)
  //{
    return y;
  //}
  //else
  //{
  //  return x;
  //}
}


The code now looks like this. What we did was make the code return x automatically if y is false. And then the code returns y by default, but that line will never be reached if x or y is false. And there! Now there are no further If clauses to deal with. Two levels of nesting have been removed, and the code looks cleaner already.
function f (x, y)
{
  if(!x) return;
  if(!y) return x;

  return y;
}


So, these are guard clauses. They conduct checks, and pre-emptively exit the code when appropriate, so that no further processing needs to be done. Other than the obvious computational savings though, this also serves to reduce the cognitive load on the reader of the code.

A guard clause is like security
ejecting you early.

Think of guard clauses as bouncers at a night club guarding against unruly patrons and ejecting them when necessary.

An actual working example

Now let's take a look at an actual back-end script in PHP. This was used to create a temporary CSV file, write to it using data based on the current day, and then send to an FTP address. Obviously, the FTP login and password has been fudged here, but the rest of it is authentic enough. $result in this case is an array of objects derived from executing a query to the database. From $result, we want to write to the CSV file all data that does not fall on a weekend, and the total has to be a positive number.

function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if ($login_result)
    {
        $folder = ABSPATH . 'cron/01/transactions/daily/';
        $filename =  "transactions_" . date('Y_m_d') . ".csv";
        $fp = fopen($folder . $filename, 'w');

        if ($fp)
        {
            $counter = 0;
        
            foreach ($result as $r)
            {
                if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
                {
                    if ($r->total > 0)
                    {
                        $counter++;
                        fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
                    }
                }
            }
        
            ftp_pasv($conn_id, true);
            $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);
            
            if ($uploaded)
            {
                unlink($folder . $filename);
            }
            else
            {
                return false;
            }

            fclose($fp);
        }
        else
        {
            return false;
        }
    }
    else
    {
        return false;
    }
    
    return true;
}


Now, for what amounts to a very simple script, there's too much indentation and it is hard to read. It would be a lot harder to read once the code inevitably gets extended or altered. To remove the first level of indentation, we introduce a guard clause to check for the value of $login_result. If it is false, exit early, returning false.
function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if (!$login_result) return false;

    //if ($login_result)
    //{
        $folder = ABSPATH . 'cron/01/transactions/daily/';
        $filename =  "transactions_" . date('Y_m_d') . ".csv";
        $fp = fopen($folder . $filename, 'w');

        if ($fp)
        {
            $counter = 0;
        
            foreach ($result as $r)
            {
                if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
                {
                    if ($r->total > 0)
                    {
                        $counter++;
                        fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
                    }
                }
            }
        
            ftp_pasv($conn_id, true);
            $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);
            
            if ($uploaded)
            {
                unlink($folder . $filename);
            }
            else
            {
                return false;
            }

            fclose($fp);
        }
        else
        {
            return false;
        }
    //}
    //else
    //{
    //    return false;
    //}
    
    return true;
}


At this point, $login_result is definitely true if the code is still running. The next check is for $fp to be true.
function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if (!$login_result) return false;

    $folder = ABSPATH . 'cron/01/transactions/daily/';
    $filename =  "transactions_" . date('Y_m_d') . ".csv";
    $fp = fopen($folder . $filename, 'w');

    if ($fp)
    {
        $counter = 0;

        foreach ($result as $r)
        {
            if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
            {
                if ($r->total > 0)
                {
                    $counter++;
                    fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
                }
            }
        }

        ftp_pasv($conn_id, true);
        $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);

        if ($uploaded)
        {
            unlink($folder . $filename);
        }
        else
        {
            return false;
        }

        fclose($fp);
    }
    else
    {
        return false;
    }
    
    return true;
}


Therefore, the next guard clause checks for the value of $fp, and exits early if false. Thus, we remove another level of indentation.
function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if (!$login_result) return false;

    $folder = ABSPATH . 'cron/01/transactions/daily/';
    $filename =  "transactions_" . date('Y_m_d') . ".csv";
    $fp = fopen($folder . $filename, 'w');
    
    if (!$fp) return false;

    //if ($fp)
    //{
        $counter = 0;

        foreach ($result as $r)
        {
            if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
            {
                if ($r->total > 0)
                {
                    $counter++;
                    fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
                }
            }
        }

        ftp_pasv($conn_id, true);
        $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);

        if ($uploaded)
        {
            unlink($folder . $filename);
        }
        else
        {
            return false;
        }

        fclose($fp);
    //}
    //else
    //{
    //    return false;
    //}
    
    return true;
}


Can this be improved? Undoubtedly. There's too much indentation within the For-each loop.
function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if (!$login_result) return false;

    $folder = ABSPATH . 'cron/01/transactions/daily/';
    $filename =  "transactions_" . date('Y_m_d') . ".csv";
    $fp = fopen($folder . $filename, 'w');
    
    if (!$fp) return false;

    $counter = 0;

    foreach ($result as $r)
    {
        if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
        {
            if ($r->total > 0)
            {
                $counter++;
                fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
            }
        }
    }

    ftp_pasv($conn_id, true);
    $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);

    if ($uploaded)
    {
        unlink($folder . $filename);
    }
    else
    {
        return false;
    }

    fclose($fp);
    
    return true;
}


So we add a guard clause that checks if $r->dayOfWeek falls on a weekend, and we use the continue statement if so.
foreach ($result as $r)
{
    if ($r->dayOfWeek == 0 || $r->dayOfWeek == 6) continue;

    //if ($r->dayOfWeek != 0 && $r->dayOfWeek != 6)
    //{
        if ($r->total > 0)
        {
            $counter++;
            fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
        }
    //}
}


At this point, if the code is still running within the loop, $r->dayOfWeek is definitely a weekday. We can actually eliminate the last If block as well. It seems a little excessive, but sure, let's do it.
foreach ($result as $r)
{
    if ($r->dayOfWeek == 0 || $r->dayOfWeek == 6) continue;
    if ($r->total <= 0) continue;

    //if ($r->total > 0)
    //{
        $counter++;
        fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
    //}
}


This is the final result. We have reduced it to one If-else block. This mostly works for cases where nothing much happens in the Else part of the If-else block, so be cautioned against overusing this.
function deliverDailyFile($result)
{
    $conn_id = ftp_connect('111.222.333.444');
    $login_result = ftp_login($conn_id, 'tthunder', 'thund3r$trike!!!');

    if (!$login_result) return false;

    $folder = ABSPATH . 'cron/01/transactions/daily/';
    $filename =  "transactions_" . date('Y_m_d') . ".csv";
    $fp = fopen($folder . $filename, 'w');
    
    if (!$fp) return false;

    $counter = 0;

    foreach ($result as $r)
    {
        if ($r->dayOfWeek == 0 || $r->dayOfWeek == 6) continue;
        if ($r->total <= 0) continue;
        
        $counter++;
        fputcsv($fp, [$counter, $r->year, $r->month, $r->day, $r->num_transactions, $r->category, $r->total]);
    }

    ftp_pasv($conn_id, true);
    $uploaded = ftp_put($conn_id, 'transactions/' . $filename, $folder . $filename, FTP_BINARY, 0);

    if ($uploaded)
    {
        unlink($folder . $filename);
    }
    else
    {
        return false;
    }

    fclose($fp);
    
    return true;
}


Conclusion

Code works whether or not guard clauses are utilized. But working code is the bare minimum. Code should be easy to read, and intent made clear as much as possible. Guard clauses accomplish this, and consequently, makes code easier to maintain.

if (endPost) return;
T___T