Friday 28 May 2021

The Sacred and Mysterious Art of GAF

It was perhaps three years ago, in some thread in Facebook, that a friend said to me.

"Dude, you act like you DGAF."


For the sweet-natured prudes who don't know what that acronym means, it's short for Don't Give A Fuck. Which is also modern-speak to say that I don't care about anything.

Before I proceed further, this blogpost centers around the acronym DGAF, so the word "fuck" is going to appear an awful lot. I've been trying to ease off the profanity for a while now, but this piece is going to be balls deep in it. Consider yourselves warned!

So, about "DGAF"...

Now, my friend telling me that I act like I DGAF gave me pause, though at the time I couldn't figure out exactly why it bothered me. In fact, were I still in my twenties, that would have been considered a compliment, because back then, that was the image I worked hard to cultivate. You know the drill: I'm hard, I'm tough, I don't care, I DGAF.

The DGAF look.

My hair was long and all over the place, my clothes a hobo would have been proud of. I dressed like I had nothing to prove... out of sheer arrogance. It was nothing short of a statement of contempt. It was my way of saying to anyone who might be silently judging me - sure, your hair's better than mine. A haircut would take all of twenty minutes, but how long would you have to take to achieve what I'm achieved?

And that was stupid. I was trying too hard to look like I DGAF, when the fact that I was trying at all actually meant I did GAF. What made it doubly stupid was that I hadn't actually achieved anything at that point outside of graduating from University, which basically amounts to jackshit in the software industry.

Twenty years on, though, I'm no longer that guy. I no longer take pride not appearing to give any fucks. I no longer think it's cool, and sometimes I wonder why I ever did. But in the cold light of honest introspection, here's why: I didn't want to appear weak. Giving fucks about things opened up chinks in my emotional armor for people to exploit. I liked being the guy that no one could rattle. I absolutely loved watching people try their darnedest to get a rise out of me and visibly struggling to find ammunition that would hurt me... and inevitably failing.

But in recent years, these little pleasures are fast fading.

Not giving any fucks is pathetically easy if you think about it. Children are notoriously self-centered. They have to grow into understanding that the world doesn't revolve around them. Babies are capable of giving zero fucks. These clamorous little shits don't care about anything other than getting fed and having their diapers changed.

But here's the thing about being an adult - at some point, you have to give some fucks. Just going around and declaring "I DGAF" like some defiant teenager is not only lame, it's self-defeating. Because a defining characteristic of adulthood is the ability to assume responsibility.

Why should we GAF?

Simple answer: because we're brought into this world to interact with it.

We don't have to be a world-changer, but we should, at the very least, be willing to shape our environment in tiny ways, and in turn be shaped by it. It gives life meaning beyond eat, sleep, shit, rinse and repeat until death. If you give totally no fucks about anything or anyone but yourself, you might as well not exist.

For example, when I get into a codebase, I may have to modify and update code, perhaps add new features. If the code is written in a way I consider sub-optimal, I can improve it. If the code is written in a way I recognize as superior to my style, I can learn from it. As a dev alone, my style has plainly evolved over the years because I allowed it to be shaped by the code of other developers. I certainly didn't go, I DGAF what style the others prefer, I'm sticking to mine.

Professionally, I give plenty of fucks. That's only right and natural, because that's what keeps food on the table and the bills paid. And considering how monumentally mediocre I am, the only thing keeping me relevant in the industry is the massive amount of fucks I'm giving.

Putting on my
"I GAF" look.

In fact, years back, instead of acting like I DGAF, I started going the other way. I began to practice acting like I GAF even when I actually don't. Maybe if I fake it long enough, it will eventually come true.

But...!

That said, it's important not to go to extremes. Giving too many fucks is arguably worse than not giving any. You have to be selective about the fucks you give. Some things are worth giving a fuck about. Some things aren't. And as individuals, we should all be able to determine what those things are.

Some things are obviously beneath me. For instance, having the last word in an online argument, responding to every perceived slight and petty revenge. I'm not a vengeful person not because I'm morally superior, but because I'm pragmatic to a fault. I have to judiciously decide what is worthy of my time and attention. And some things just don't offer enough value. There are a thousand and one things screaming for my fucks every given moment, and as such I can't be wasting them on every attention-seeking child online. Or offline, for that matter.

However, back last year, I had the dubious pleasure of working under an asshole CTO who kept making disparaging (though perhaps deserved) remarks about my competency. I let a lot of it slide, even though some of it was really childish; because as mentioned earlier, I don't have the need to respond to every slight, imagined or otherwise. But then he made the mistake of calling my professional integrity into question. Sometimes, there is a line that you should not allow anyone to cross without exacting a price. Professionally, there are some things I will not stand for. In fact, unless you have no integrity to speak of, nobody should stand for this.

I advised him to stop yelling, and that if there was indeed evidence of improper conduct, it was grounds for immediate dismissal. Noisy bitch backed down like a good boy.

Yep, I gave selective fucks. Go, me.

Context is also important. Sure, there are some opinions that matter, such as those of your colleagues and employers regarding the quality of your work. Or the opinions of subject matter experts on the subjects that interest you. But what's relevant is also the source of those opinions. For example, if you're not in the software industry and have never had any experience in said industry, I couldn't be less interested in any career advice you thought you could give me. Ditto for forty-year old virgins who want to give me marriage advice. Or senior citizens who think their experience in the 60s and 70s somehow qualifies them to lecture me on how to live my life in the 2000s.

Fuck that noise.

Some things are noise. Some advice is just so much hot air from people who have never mastered the art of understanding exactly who the fuck it is their ignorant selves are trying to educate. Learn to tell the difference.

Of course you can give fucks. Just don't give indiscriminate fucks.

In summary...

Give a fuck about things that ought to matter. Try to keep that list short.

I like to joke that I'm incapable of giving any fucks because Mrs TeochewThunder took them all. But that's all it is, a joke. Not giving a fuck is cool... if you're young and stupid. I'm no longer young, and I'll be damned if I stay stupid.

Error 404: Fucks Not Found,
T___T

Saturday 22 May 2021

Web Tutorial: The Ruby CrossBox

Some time ago, someone forwarded me this programming joke.



It was a pretty amusing one because I could relate. When I was a student, we would get these practice questions and I would fantasize about getting cheeky like that.

Well, today we will be doing this exercise for real. The above exercise looks to have been done in C++, but my C++ is rusty AF. So I will be using Ruby because it's been a while since I touched her gloriously sexy syntax. (Boy, that came out weird)

Since the question wants us to use a nested For loop, this is what we'll begin with. Obviously, we will deal with rows and columns, thus each For loop we will use variables row and col. The box in the question is a 10 by 10 square, so this is what we will use for the variable size. Each iteration goes from 1 to 10.
size = 10

for row in 1..size do
    for col in 1..size do

    end
end


The variable display, in each iteration, is set to false initially.
size = 10

for row in 1..size do
    for col in 1..size do
        display = false
    end
end


If display is true, then we display "*". If not, we display a space. We use a print statement instead of puts because we don't want to automatically go to the next line.
for row in 1..size do
    for col in 1..size do
        display = false
    end

    if (display)
        print "*"
    else
        print " "
    end

end


We will, however, want output to go to the next line if it's the end of the line. It's the end of the line if col is equal to size.
for col in 1..size do
    display = false;

    if (display)
        print "*"
    else
        print " "
    end

    if (col == size)
        print "\n"
    end

end


Now what we need to do is set a condition where display is true. Note that the box has a border of asterisks. That means that if col or row is 1 or the maximum value, size, display is set to true.
for col in 1..size do
    display = false;

    if (row == 1 || col == 1 || row == size || col == size)
        display = true
    end


    if (display)
        print "*"
    else
        print " "
    end

    if (col == size)
        print "\n"
    end
end


This should net us a nice box of asterisks!


Now, in all other cases, it's still possible for display to be true. Look at the crossbox. Are there any patterns in the output we can exploit? I'll start - the asterisk is displayed if row is equal to col.
if (row == 1 || col == 1 || row == size || col == size)
    display = true
else
    if (row == col)
        display = true
    end

end


Now you get that sweet diagonal slash!


What about the other diagonal? That's a separate pattern. We want a diagonal from the other direction, so now we have to compare col to the current value of row subtracted from the value of size. So if row is for example, 2, that means col has to be at (10 - 2 = 7) for display to be true.
if (row == col || size - row == col)
    display = true
end


Well this is nice, but it's just a little bit off. It's in the correct general direction, though, so we just need to adjust slightly...


...like so.
if (row == col || size - row == col - 1)
    display = true
end


Yes!


Improvements to the code

Sure, we met the objectives of the question. We used a nested For loop to accomplish this. But when it that ever enough? We can make this better.

Let's perform some abstraction. First, let's place all that code into a function definition. Call that function generateCrossBox().

def generateCrossBox()
    size = 10

    for row in 1..size do
        for col in 1..size do
            display = false;

            if (row == 1 || col == 1 || row == size || col == size)
                display = true
            else
                if (row == col || size - row == col - 1)
                    display = true
                end
            end

            if (display)
                print "*"
            else
                print " "
            end

            if (col == size)
                print "\n"
            end
        end
    end
end


That function should allow a parameter, size. Remove that first line. size will now be defined by whatever argument is passed in.
def generateCrossBox(size)
    #size = 10

    for row in 1..size do
        for col in 1..size do
            display = false;

            if (row == 1 || col == 1 || row == size || col == size)
                display = true
            else
                if (row == col || size - row == col - 1)
                    display = true
                end
            end

            if (display)
                print "*"
            else
                print " "
            end

            if (col == size)
                print "\n"
            end
        end
    end
end


Then we call that function. Pass in a number, say, 20.
def generateCrossBox(size)
    #size = 10

    for row in 1..size do
        for col in 1..size do
            display = false;

            if (row == 1 || col == 1 || row == size || col == size)
                display = true
            else
                if (row == col || size - row == col - 1)
                    display = true
                end
            end

            if (display)
                print "*"
            else
                print " "
            end

            if (col == size)
                print "\n"
            end
        end
    end
end

generateCrossBox(20)


Hot damn!


Wait... we're not done. The code only works with even numbers, so let's do this. Multiply whatever size is, by 2 and set it to doubleSize.
def generateCrossBox(size)
    #size = 10

    doubleSize = size * 2

    for row in 1..size do
        for col in 1..size do
            display = false;

            if (row == 1 || col == 1 || row == size || col == size)
                display = true
            else
                if (row == col || size - row == col - 1)
                    display = true
                end
            end

            if (display)
                print "*"
            else
                print " "
            end

            if (col == size)
                print "\n"
            end
        end
    end
end


Then within the nested For loop, replace all instances of size with doubleSize. Now the code will always be performed on an even number, because we doubled it.
def generateCrossBox(size)
    #size = 10

    doubleSize = size * 2

    for row in 1..doubleSize do
        for col in 1..doubleSize do
            display = false;

            if (row == 1 || col == 1 || row == doubleSize || col == doubleSize)
                display = true
            else
                if (row == col || doubleSize - row == col - 1)
                    display = true
                end
            end

            if (display)
                print "*"
            else
                print " "
            end

            if (col == doubleSize)
                print "\n"
            end
        end
    end
end


Now you get a much larger crossbox, because the number has been doubled.


One more improvement...

Let's generate this crossbox using user input. Add this to the code. Here, we're printing out a message and then setting a variable, continue, to an empty string.
end

puts "Welcome to TeochewThunder's CrossBox!"

continue = ""


generateCrossBox(20)


We want this to continue for as long as continue is "Y" or "y". Since continue is an empty string, this code is going to run once, then stop.
puts "Welcome to TeochewThunder's CrossBox!"

continue = ""
begin
    generateCrossBox(20)
end while continue == "Y" or continue == "y"


Here, we ask the user to enter a value. This gets assigned to the variable size. We then pass in size instead of 20 to generateCrossBox().
begin
    puts "Please enter the size of your CrossBox:"
    size = gets.chomp    


    generateCrossBox(size)
end while continue == "Y" or continue == "y"

 
In the generateCrossBox() function, set the variable finalSize to size after converting size to an integer using the to_i() method. The next If block ensures that finalSize is at least 5. Finally, modify the code to set doubleSize to double of finalSize instead of size.
def generateCrossBox(size)
    #size = 10

    finalSize = size.to_i

    if (finalSize < 5)
        finalSize = 5
    end


    doubleSize = finalSize * 2

    for row in 1..doubleSize do


I entered "3", but it gives me a box with a size of 5!


But this is what happens when I enter 7!


Changing the message slightly would probably make things clearer for the user.
puts "Please enter the size of your CrossBox (min 5):"


Add another message asking for user input. This time, it's to ask the user if they want to continue. Set the variable continue to the value of the user input.
continue = ""
begin
    puts "Please enter the size of your CrossBox (min 5):"
    size = gets.chomp    

    generateCrossBox(size)

    puts "Do you wish to continue? (Y/N)"
    continue = gets.chomp

end while continue == "Y" or continue == "y"


So now you can go on for as long as you like!


Well, that was fun...

...but yes, kind of basic. Sure takes me back to my days of being a wide-eyed IT student, though!

May you live in e⛝citing times,
T___T

Saturday 15 May 2021

Yes I'm a Software Developer; no I won't fix your WiFi

Few things get my goat like being asked to fix someone's WiFi connection, repair someone's laptop or generally perform some kind of hardware maintenance. Normally, I'm a really chill kind of guy, but this is one of the things that is almost guaranteed to fill me with white-hot rage. Admittedly, that reaction is mostly due to some personal issues, but there are plenty of software developers who detest that kind of thing as well.

And today, I will explain why.

We're not hardware people

Web and desktop applications. Application programmer interfaces. Algorithms. We work with software. Wires, circuit boards and the lower levels of the seven OSI layers are not in our purview. Software is a totally different animal from hardware; and in this day and age there's absolutely no excuse for not knowing that elementary fact unless you're really young, really old, or just not the sharpest tool in the box.

Race car drivers are not
vehicle mechanics.

Expecting software developers to understand hardware just because "you work with computers" is like expecting a race car driver to be able to fix a car engine because "you work with cars". Sure, a race car driver could know enough to fix a car engine, but there's no reason why he (or she) has to know. It's a totally different skillset.

I don't consider this work beneath me; in fact I have nothing but respect for hardware professionals. Which really underscores the fact that where hardware repair is concerned, I'm not at all qualified.

Of degrees and qualifications

The argument often used is - you work with computers, even if Wifi isn't really your area, you have a better idea of how to do this than me. And that's not totally untrue. But it's not for the reason most people who say this, think.

You see, years of attending school and working in the software industry have taught us one skill that everybody else really should be learning - if you don't know something, look it up on the internet. It's 2021, folks. Google has been a thing for more than a decade!

Learn to search the internet!

Yes. Search engines. That's really all our so-called expertise amounts to. And if you can't even be bothered to search for information on your own problem, why the hell do you think you deserve our help? Entitled much?

It's not our job

Obviously, it's not a software developer's job to fix hardware issues. I think we've established as much. But for the sake of argument, let's say it was our job.

So what?

It's after hours. Just because something happens to be our day job doesn't mean we want to continue doing it after the workday is over. It's called work for a reason. We're in no way obliged to fix your hardware problems for you because you're not a paying customer. And if you were a customer, you can get in line like everyone else.

This is what we do.

Now, I happen to love writing code and I would write code even outside of office hours... for myself. For my own projects. Not for yours. But anyone who knows me, knows how much I hate working on hardware. I took a friggin' pay cut and restarted my career doing something else, so that I would never have to do it again. That's how much I hate it.

So if you were someone who knows just how much I detest fixing hardware because it reminds me of all the years I wasted in Desktop Support, asking me to do it just so you can save a few bucks is a totally dick move. Seriously, how do you sleep at night?!

In conclusion

Admittedly, if this post was written ten years ago, it would probably have been a lot more entertaining due to a constant stream of profanities. In the decade that has followed, much of my annoyance has abated.

Don't get me wrong; I still think arguments like "you work with computers, therefore you should know" or "you have a Bachelor's Degree in IT, therefore you can fix my WiFi" are really stupid. But I've also made peace with with the fact that people are stupid, and that's really their cross to bear, not mine.

Fix your own goddamn WiFi!
T___T

Sunday 9 May 2021

Your AJAX Starter Kit, WordPress edition (Part 2/2)

I'm back!

Implementing AJAX, of course, requires a lot more than just JavaScript. We've done the front-end, now we need a back-end. AJAX in WordPress has its own implementation, via admin-ajax.php. To do this, we need to pass the URL of that location, like so.

plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */

function tt_ajax_listener() {
    wp_register_script( 'ajax_listener', plugin_dir_url( __FILE__ ) . '/js/tt_ajax_listener.js', ['jquery'] );
    wp_localize_script( 'ajax_listener', 'objAjax', ['ajaxurl' => admin_url( 'admin-ajax.php' )]);        

    wp_enqueue_script( 'ajax_listener' );
}

add_action( 'wp_enqueue_scripts', 'tt_ajax_listener' );


Then have some back-end code, defined in a function. In this case, it will take a string passed in via a POST, and use it as the format for a date() function. And most importantly, encode the output using json_encode(), then echo it.

plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */

function tt_ajax_listener() {
    wp_register_script( 'ajax_listener', plugin_dir_url( __FILE__ ) . '/js/tt_ajax_listener.js', ['jquery'] );
    wp_localize_script( 'ajax_listener', 'objAjax', ['ajaxurl' => admin_url( 'admin-ajax.php' )]);        

    wp_enqueue_script( 'ajax_listener' );
}

add_action( 'wp_enqueue_scripts', 'tt_ajax_listener' );

function tt_ajax_call() {
    $date = date($_POST['format'], strtotime('now'));
    echo json_encode(['date' => $date]);
    die();
}


After that,we use add_action() to add this function to the hooks wp_ajax_tt_ajax_call and wp_ajax_nopriv_tt_ajax_call, for logged in and anonymous access respectively. What happens is that every AJAX call is registered in a named variable at runtime, and we just did it for tt_ajax_call(). I'm aware that this isn't a great way of explaining it, but you get my drift...

plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */

function tt_ajax_listener() {
    wp_register_script( 'ajax_listener', plugin_dir_url( __FILE__ ) . '/js/tt_ajax_listener.js', ['jquery'] );
    wp_localize_script( 'ajax_listener', 'objAjax', ['ajaxurl' => admin_url( 'admin-ajax.php' )]);        

    wp_enqueue_script( 'ajax_listener' );
}

add_action( 'wp_enqueue_scripts', 'tt_ajax_listener' );

function tt_ajax_call() {
    $date = date($_POST['format'], strtotime('now'));
    echo json_encode(['date' => $date]);
    die();
}

add_action("wp_ajax_nopriv_tt_ajax_call", "tt_ajax_call");
add_action("wp_ajax_tt_ajax_call", "tt_ajax_call");


The AJAX call is pretty much what was presented in Your AJAX Start Kit, jQuery edition. For added security, we could implement a nonce, but I'm going with the bare minimum here. In here, we pass in a test string, to this URL. This references the function I just created in the backend. For data, I will pass in "j M Y H:i:s".

plugins/tt_ajax_test/js/ajax_listener.js
jQuery(document).ready(function(){
    if (window.location.href.indexOf("ajax-test") > -1) {
        jQuery('#btnAjax').click(()=>{
            jQuery.ajax({
                type : "post",
                dataType : "json",
                url : objAjax.ajaxurl,
                data : {action: "tt_ajax_call", format: "j M Y H:i:s"},
                success: function(data, textStatus, jqXHR) {

                },
                error: function(err) {
                    alert('Error');
                }
            });        
        });

    
        alert('test js');
    }
});


Once the data is returned, I populate the placeholder with the results. Oh yes, and I'll remove the earlier test command to remove the annoying pop-up.

plugins/tt_ajax_test/js/ajax_listener.js
jQuery(document).ready(function(){
    if (window.location.href.indexOf("ajax-test") > -1) {
        jQuery('#btnAjax').click(()=>{
            jQuery.ajax({
                type : "post",
                dataType : "json",
                url : objAjax.ajaxurl,
                data : {action: "tt_ajax_call", format: "j M Y H:i:s"},
                success: function(data, textStatus, jqXHR) {
                    jQuery('.tt_placeholder').html(data.date);
                },
                error: function(err) {
                    alert('Error');
                }
            });        
        });
    
        //alert('test js');
    }
});


Now click the button, and we should see the current date in the specified format. Every time you click, the format should change!


Finally!

That seemed simple enough.

AJAX isn't really that straightforward in WordPress. But now that I've documented which hoops to jump through, hopefully it's a lot easier from this point forward.

(Word)Press on,
T___T

Thursday 6 May 2021

Your AJAX Starter Kit, WordPress edition (Part 1/2)

AJAX, or if you want to be unnecessarily verbose, Asynchronous JavaScript And XHTML, is simple enough to implement on its own. In the past, I've performed a few basic demos on how to implement AJAX, and even done a jQuery version. Today's post is analogous to the other two; except that there's a little complication.

Because we'll be doing it in WordPress.

For those of you who are blissfully unaware of what WordPress is, it's an open-source Content Management System used to develop websites and applications. However, the applications part would be pretty hard if a developer had no idea how to wrangle some AJAX out of WordPress. Having gone through the process myself, it's my opinion that this is not at all straightforward for the uninitiated. And what I would like to do today, is take you through the process of creating a plugin in WordPress which we can then use to implement AJAX.

This is going to have a few moving parts.

To begin, I will create a plugin in WordPress. This is really nothing more than creating a folder in the plugins folder of the wp-content directory, then creating a PHP file. I'm not going to elaborate on this; because this isn't a web tutorial and even if it were, that's not what it's about.

plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */


JavaScript

Now, before you can have AJAX, you first need to be able to run JavaScript. We will be creating a JavaScript file in the folder we just created. I'd prefer, as a matter of good practice, to put that file in a sub-folder, perhaps named js. Call it anything you like. We'll use jQuery for this. Use a ready() method to run our test command, which will simply pop up an alert message.

plugins/tt_ajax_test/js/ajax_listener.js
jQuery(document).ready(function(){
    alert('test js');
});


Then in the PHP file, we use add_action() to add this newly-created function tt_ajax_listener().
plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */

function tt_ajax_listener() {

}

add_action( 'wp_enqueue_scripts', 'tt_ajax_listener' );


This function basically calls wp_enqueue_script() to ensure that the JS file we just wrote, is included in the pages of the WordPress site. Before that, we also have to run wp_register_script() using the URL of the JavaScript file, and because we're going to use jQuery, we pass that in as an argument.

plugins/tt_ajax_test/tt_ajax.php
<?php
/**
 * Plugin Name: AJAX Test
 * Plugin URI: http://www.teochewthunder.com
 * Description: This plugin implements and tests AJAX functionality
 * Version: 1.0.0
 * Author: TeochewThunder
 * Author URI: http://www.teochewthunder.com
 * License: GPL2
 */

function tt_ajax_listener() {
    wp_register_script( 'ajax_listener', plugin_dir_url( __FILE__ ) . '/js/tt_ajax_listener.js', ['jquery'] );

    wp_enqueue_script( 'ajax_listener' );

}

add_action( 'wp_enqueue_scripts', 'tt_ajax_listener' );


Now when you refresh, this should come up. That's how you know it's working.

Of course, there's a possibility that we don't want this script to fire off just anywhere. So here's a really cheap way of making sure that it only fires off on specific pages. Basically, check for the URL and make sure it confirms to certain requirements. Now if you run the page again, you won't see anything.

Be warned that this is really not a great way to implement things, but this is just an example. A very clumsy example.

plugins/tt_ajax_test/js/ajax_listener.js
jQuery(document).ready(function(){
    if (window.location.href.indexOf("ajax-test") > -1) {    
        alert('test js');
    }
});


I'm creating a page here. This one is titled ajax-test, and in the screenshot, you can see the URL. And you can also see that the popup appears here because "ajax-test" is in the URL.

For this page, I added the following code via the built-in CMS.
<div>
    <div class="tt_placeholder">This is a placeholder</div>
    <input type="button" id="btnAjax" value="AJAX Call">
</div>



This ensures that now we have a button and a placeholder, with id and class attributes we can leverage on later.

All good? Moving on...

Now we have working JavaScript, and a test page from which to launch our AJAX call. All we're lacking now is more JavaScript to make that AJAX call, and back-end code to execute the command.

Next

Don't quit on me just yet! We will be implementing an AJAX call.