Monday, 27 March 2017

The Myth of Tech Meritocracy

The Android vs iOS debate has been raging on for years, with no end in sight. In each camp, you have devout followers that swear (sometimes very loudly) by their chosen platform.

I have a friend who's a bit of an Apple fanboy, and he absolutely despises Android technology. And I remember one day he was telling me how Android ought to die because its tech is shit.

Die, Android, die!

My opinion was that he wasn't being entirely rational about this - in fact, I was quite positive that he was looking at this from a very emotional point of view. Because things don't work that way in technology. Don't get me wrong - this isn't a debate about which technology is better. Even if, for arguments' sake, I agreed with him that Android technology is shit; Android is not going away. And for his own sake, my buddy should pray that it remains so.

Android, at this point in time, enjoys the largest market share worldwide next to Apple's iOS. What if my friend's wish comes true and Android disappears from the market forever? The void left by Android would not be readily filled, and Apple would reign supreme.

King of the hill

Sounds like a fanboy's wet dream, eh? Just wait - it goes downhill from here.

I think we can all agree that Apple isn't some benevolent mobile tech Santa giving us awesome products for the sheer joy of it. No, like all credible tech companies, Apple is a business, and is motivated by desire for consumer dollars. And its great innovations in recent years was a direct consequence of having to constantly up its game in the face of stiff competition from Android. What happens when that stiff competition is no longer present?

This is what I think happens...

Apple halves its workforce because the amount of talent on their payroll is no longer required.

Apple products slowly become shit, because Apple are now just about the only game in town and if they feel like feeding you shit, you'll eat it and like it.

Apple begins its inevitable decline.

Just a little
competition.

What I'm trying to say is - competition keeps businesses honest. But again, this isn't about Apple or Android - it's about the myth of tech meritocracy.

Let's talk about meritocracy, shall we?

The professional world isn't a perfect meritocracy. Higher management positions could be filled by people who don't deserve to have a job, much less a high-paying one. Less capable and driven folks may get that promotion simply by kissing up to the right people. Pretty girls may get ahead simply because the fellow in charge is a hum sup lou. Less qualified staff could remain employed simply by being better at keeping their jobs rather than actually doing their jobs. The lower echelons of the workforce could be teeming with a serious case of Crouching Tiger, Hidden Dragon.

There's nothing intrinsically wrong with any of that. This is simply how the world works.

Likewise, technology isn't a perfect meritocracy. Tech doesn't die simply because it's inferior. Tech rises to prominence for a variety of reasons, not just quality. And once that tech has entrenched itself as part of the ecosystem, removing it poses several problems, some of which may adversely affect that rival tech you're such a huge sucker for.

More examples

Remember classic ASP, written in VBScript? It was supposed to have been replaced by ASP.NET back in 2003. Where is it now? It's still residing on servers powering legacy systems - huge legacy systems - all over the world. It's not exactly thriving, but until Microsoft's IIS stops supporting classic ASP, no company in its right mind is going to spend money to rewrite a working system to leverage off new and (relatively) untested technology. So classic ASP stays.

Or, hey, look at JavaScript. For many years since Netscape shared it with the world, JavaScript was the only game in town, at least where client-side scripting was concerned. VBScript was client-side scripting too, but it  belonged to Microsoft and ran only on IE. By most yardsticks, JavaScript wasn't a great language. It had its warts, and I'm being generous when I say that. But decades on, it has become so pervasive that its use has expanded to frameworks, libraries and even server-side scripting. Uprooting JavaScript now would be a Herculean task.

For that matter, why are Pascal, FORTRAN and COBOL still around?

In technology, new things catch on fast. But older tech takes a while to go away if it has already entrenched itself - by being introduced at the right time, or filling some niche uncontested.

See how long HTML5 took to become a browser standard? Sure, it's starting to come into its own now, but XHTML is still alive. Because while HTML5 is superior and fixes many of the problems that came with HTML4.0 and XHTML, its introduction cannot undo decades of XHTML-based content overnight.

Such is the tech ecosystem

Tech ceases to grow when people stop using it to make new stuff. But it only truly dies when... well, almost never.

Besides, why should a piece of tech die just because some people (or several) don't like it and think it's rubbish? Technology is a vibrant and multifaceted world because of the variety, not in spite of it.

That's all for now. (ad)iOS!
T___T

Thursday, 23 March 2017

Spot The Bug: Nested JSON Array Trap

Spot The Bug is back, and bigger and meaner than ever! Huzzah!

Woohoo!

Sorry, couldn't resist a bit of melodrama.

Some time ago, I was writing a little mobile application to track my smoking habits (it's a nasty habit, kids - don't start!) and decided to create a little bar graph to tabulate the data. No errors were reported, but somehow the data wouldn't display right.

Here's the data, in JavaScript array format. It's basically an array of 5 days' worth of data. Each day is an array element. This array element, in turn, comprises of a number of objects, each representing the hour (h) and the minute (m) that I smoked that cigarette.
            [
                [
                    {h:6,m:43},
                    {h:7,m:08},
                    {h:7,m:45},
                    {h:9,m:09},
                    {h:10,m:13},
                    {h:13,m:11},
                    {h:14,m:24},
                    {h:16,m:25},
                    {h:17,m:15},
                    {h:21,m:53},
                    {h:23,m:19},
                ],
                [
                    {h:7,m:02},
                    {h:7,m:18},
                    {h:8,m:55},
                    {h:9,m:02},
                    {h:9,m:55},
                    {h:10,m:12},
                    {h:10,m:23},
                    {h:12,m:43},
                    {h:13,m:20},
                    {h:14,m:55},
                    {h:15,m:25},
                    {h:17,m:10},
                    {h:20,m:52},
                    {h:23,m:09},
                ],
                [
                    {h:6,m:57},
                    {h:7,m:38},
                    {h:7,m:43},
                    {h:8,m:08},
                    {h:8,m:45},
                    {h:9,m:09},
                    {h:9,m:13},
                    {h:10,m:43},
                    {h:12,m:11},
                    {h:14,m:24},
                    {h:16,m:25},
                    {h:18,m:15},
                    {h:19,m:53},
                    {h:23,m:19},
                    {h:23,m:19},
                ],
                [
                    {h:6,m:36},
                    {h:6,m:44},
                    {h:7,m:04},
                    {h:7,m:15},
                    {h:7,m:48},
                    {h:9,m:03},
                    {h:10,m:11},
                    {h:12,m:23},
                    {h:13,m:40},
                    {h:14,m:58},
                    {h:16,m:09},
                    {h:17,m:11},
                    {h:21,m:11},
                    {h:23,m:04},
                    {h:23,m:55},
                ],
                [
                    {h:0,m:31},
                    {h:6,m:22},
                    {h:6,m:59},
                    {h:7,m:09},
                    {h:8,m:15},
                    {h:9,m:21},
                    {h:10,m:22},
                    {h:12,m:53},
                    {h:13,m:20},
                    {h:14,m:25},
                    {h:15,m:31},
                    {h:17,m:28},
                    {h:21,m:07},
                    {h:22,m:29},
                    {h:22,m:50},
                    {h:22,m:55},
                    {h:23,m:19},
                ],
            ]


I would stringify the data and store it in localStorage, and then when I needed the data, I retrieved it from localStorage and tabulated it with this code.
            var smoke_record=JSON.parse(json_str);
            var tr;
            var td;
            var table=document.getElementById("smoke_graph");

            for (var i=0;i<smoke_record.length;i++)
            {
                tr=document.createElement("tr");
                td=document.createElement("td");
                td.innerHTML=i+1;
                tr.appendChild(td);

                td=document.createElement("td");

                for (var j=0;j<smoke_record.length;j++)
                {
                    td.innerHTML=td.innerHTML+"|";
                }

                tr.appendChild(td);
                table.appendChild(tr);
            }



What went wrong

This, for starters. Every day reported that I had smoked exactly five cigarettes.


The pipe symbol was supposed to represent each stick. I'm pretty sure I didn't smoke exactly five cigarettes a day. No errors were reported in console.

Why it went wrong

Well, the number 5 gave me a clue. What else had five elements? The parent array, of course! I was iterating through the parent array, smoke_record, instead of the children!
                for (var j=0;j<smoke_record.length;j++)
                {
                    td.innerHTML=td.innerHTML+"|";
                }


How I fixed it

Here's the fix. I'm ashamed to report that it was a very easy fix, because this mistake was so amateurish it shouldn't have been made at all.
                for (var j=0;j<smoke_record[i].length;j++)
                {
                    td.innerHTML=td.innerHTML+"|";
                }



This is the correct (and very unhealthy) graphical representation.


Moral of the story

A lot of misery can be found in nested arrays and nested For loops. They're not insurmountable, by any means, but they sure are annoying and treacherous.

Now that's what I call a smokin' hot fix.
T___T

Monday, 20 March 2017

Web Tutorial: Hacker Republic Login Sequence (Part 4/4)

This is the final part, and it may come off as a bit dry. Bear with me - we'll be recycling some functions we previously wrote.

Salander knew it was no empty threat. If she wrote the wrong password three times in a row the site would shut down and the name Wasp would be struck from the membership list. Carefully she wrote the password MonkeyBusiness.

The screen changed again and now had a blue background with the text:

[Welcome to Hacker Republic, citizen Wasp. It has been 56 days since your last visit. There are 11 citizens online. Do you want to (a) Browse the Forum (b) Send a Message (c) Search the Archive (d) Talk (e) Get Laid?]


Here, we're going to implement some click events on the buttons. They will call the login() function again, but with different parameters.

confirm.php
        <div id="speech_wrapper">
            <div id="speech">
                <div id="confirmid">
                    <br />
                    WHO GOES THERE?
                    <br />
                    <input type="text" id="txtConfirmId" maxlength="20">
                    &nbsp;
                    <input type="button" value="go" onclick="login('confirm_form_login');">
                </div>
                <div id="confirmpassword">
                    <br />
                    PROVE IT, OR ELSE....
                    <br />
                    <input type="password" id="txtConfirmPassword" maxlength="20">
                    &nbsp;
                    <input type="button" value="go" onclick="login('confirm_form_password');">
                </div>
            </div>
        </div>


Back to your JavaScript, let's modify the login() function by putting in If blocks for the different cases we've just specified.

tt_millenniumlogin.js
function login(formid)
{
    var parameters;

    if (formid=="login_form")
    {
        document.getElementById("login_form").style.display="none";
        document.getElementById("login_message").style.display="block";

        parameters="type=0";
        parameters=parameters+"&id="+document.getElementById("login_id").value;
        parameters=parameters+"&password="+document.getElementById("login_password").value;

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {
                var logindetails=JSON.parse(xmlhttp_loginform.responseText);

                if (logindetails.valid)
                {
                    var errormessage=document.getElementsByClassName("message_error");
                    errormessage[3].onclick=function(){window.location="confirm.php"};
                }
            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));   
    }

    if (formid=="confirm_form_login")
    {

    }

    if (formid=="confirm_form_password")
    {

    }
}


So if the argument passed in is "confirm_form_login", we use a basic AJAX block. parameters is set to a string where the value of type is 1. The value of id is the string you type into the txtConfirmId textbox. The data is again sent to ajax_login.php, which will then check to see if your id is valid.
tt_millenniumlogin.js
    if (formid=="confirm_form_login")
    {
        parameters="type=1";
        parameters=parameters+"&id="+document.getElementById("txtConfirmId").value;

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {

            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));   
    }

    if (formid=="confirm_form_password")
    {

    }


Remember this code? Now add to it an IF block for the case of the value of type being 1.

ajax_login.php
<?php
session_start();

$type=intval($_POST["type"]);

if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";

    $id=$_POST["id"];
    $password=$_POST["password"];

    if (array_key_exists($id,$logins))
    {
        if ($logins[$id]==$password)
        {
            $_SESSION["loggedin"]=true;
            echo json_encode(array("valid" => true));
        }
        else
        {
            echo json_encode(array("valid" => false));
        }
    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}

if ($type==1)
{

}
?>


Again, we'll enter in some test data in the logins array. In Lisbeth's case, her id is "Wasp" and her password is "MonkeyBusiness", so we definitely should make it one of the datasets. We'll also add in "x3" and "x4" as test data, with passwords identical to the ids. After that, whatever you do is similar to what you already did in the first If block. Check if id exists as a key in the logins array. If so, set the session variable login to id and return a true JSON string. If not, print out a false JSON string.

ajax_login.php
<?php
if ($type==1)
{
    $logins=array();
    $logins["Wasp"]="MonkeyBusiness";
    $logins["x3"]="x3";
    $logins["x4"]="x4";

    $id=$_POST["id"];

    if (array_key_exists($id,$logins))
    {
        $_SESSION["login"]=$id;
        echo json_encode(array("valid" => true));
    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}
?>


Again, back to your JavaScript, handle the output accordingly. If the login id is valid, use the confirm_screen() function to switch the content in the speech bubble and change the picture in the "doorway". If not, do the exact opposite.

tt_millenniumlogin.js
    if (formid=="confirm_form_login")
    {
        parameters="type=1";
        parameters=parameters+"&id="+document.getElementById("txtConfirmId").value;

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {
                var logindetails=JSON.parse(xmlhttp_loginform.responseText);

                if (logindetails.valid)
                {
                    confirm_screen(false,true);
                }
                else
                {
                    confirm_screen(true,false);
                }
            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));   
    }

    if (formid=="confirm_form_password")
    {

    }


Try it! Try entering "Wasp", "x3" or "x4". You should see the text in the speech bubble change to this, and the Lara Croft-like figure cock her gun. With any other input, nothing changes.


And of course, we will now handle the last case in the login() function in your JavaScript, via another AJAX block. This time, the value of type is 2, and we send the password over.

tt_millenniumlogin.js
    if (formid=="confirm_form_password")
    {
        parameters="type=2";
        parameters=parameters+"&password="+document.getElementById("txtConfirmPassword").value;

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {

            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));
    }


In your PHP code, we'll, of course, start off by providing another If block for the case where the value of type is 2.

ajax_login.php
<?php
if ($type==1)
{
    $logins=array();
    $logins["Wasp"]="MonkeyBusiness";
    $logins["x3"]="x3";
    $logins["x4"]="x4";

    $id=$_POST["id"];

    if (array_key_exists($id,$logins))
    {
        $_SESSION["login"]=$id;
        echo json_encode(array("valid" => true));
    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}

if ($type==2)
{

}
?>


The test data is identical to the last If block's. We grab the password and check if the value of the element denoted by the key, which is the current session variable login, is the same. If the values match, the login is successful. The session variable confirmed is set to true, and a true JSON string is printed. If not, the session variable attempts is incremented (if it didn't previously exist, the default value is 0. Not particularly elegant, but it works). Then both a false value and the number of attempts is output in a JSON string.

ajax_login.php
<?php
if ($type==2)
{
    $logins=array();
    $logins["Wasp"]="MonkeyBusiness";
    $logins["x3"]="x3";
    $logins["x4"]="x4";

    $password=$_POST["password"];

    if ($logins[$_SESSION["login"]]==$password)
    {
        $_SESSION["confirmed"]=true;
        echo json_encode(array("valid" => true));
    }
    else
    {
        $_SESSION["attempts"]=$_SESSION["attempts"]+1;
        echo json_encode(array("valid" => false, "attempts" => $_SESSION["attempts"]));
    }
}
?>


So back to your JavaScript, we handle a successful login by sending the user to dashboard.php. If the login is unsuccessful (ie, the password is incorrect), use the confirm_screen() function to ask for the id again. Then check the number of attempts in the decoded JSON string. If the number is greater than 3, output a self-destruct message and redirect back to index.html.

tt_millenniumlogin.js
    if (formid=="confirm_form_password")
    {
        parameters="type=2";
        parameters=parameters+"&password="+document.getElementById("txtConfirmPassword").value;

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {
                var logindetails=JSON.parse(xmlhttp_loginform.responseText);

                if (logindetails.valid)
                {
                    window.location="dashboard.php";
                }
                else
                {
                    confirm_screen(true,false);

                    if (parseInt(logindetails.attempts)>3)
                    {
                        alert("ACTIVATING DESTRUCT SEQUENCE...");
                        window.location="index.html";
                    }
                }
            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));
    }
    }


We'll need dashboard.php. So create it, and use this code. A session is created, and then the server checks if the session variable confirmed is true. This prevents unauthorized access, because you could only have confirmed set to true if you logged in.

dashboard.php
<?php
session_start();

if ($_SESSION["confirmed"])
{
?>

<?php
}
?>


Then add in the HTML and CSS. This is pretty bare. I could add more text to this, but I think you get the idea by now.
<?php
session_start();

if ($_SESSION["confirmed"])
{
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Hacker Republic</title>

        <style>
            body
            {
                background-color:#0000FF;
                color:#FFFFFF;
                font-family: monospace;
            }
        </style>
    </head>
    <body>
        <h1>Welcome to the Hacker Republic, citizen <?php echo $_SESSION["login"];?>.</h1>
    </body>
</html>

<?php
}


Try attempting to log in unsuccessfully more than 3 times.


Now try logging in with a proper id and password...


Conclusion

What a neat login sequence. Certain things could have been done more securely, but I just wanted to simulate the front-end. The look and feel wasn't all that important, though as a matter of pride, I've tried to establish a bare minimum of prettiness.

Welcome to the Hacker Republic!
T___T

Saturday, 18 March 2017

Web Tutorial: Hacker Republic Login Sequence (Part 3/4)

Things are starting to get exciting. We now have the first part of the login sequence down, and it's on to the second part.

The screen went blank. Then an animated door opened and a Lara Croft-like figure stepped out. A speech bubble materialized with the text [WHO GOES THERE?]. She clicked on the bubble and wrote Wasp. She got the instant reply [PROVE IT  -  OR ELSE...] as the animated Lara Croft unlocked the safety catch on her gun.


Create a new file and call it confirm.php. This will be where the Lara Croft-like figure appears. You have a basic HTML setup and a PHP snippet at the top to initialize a session. The page will still reference tt_millenniumlogin.js. You also have a div with the id of guard_wrapper to hold the Lara Croft-like figure.

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>

    <body>
        <div id="guard_wrapper">

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


We'll be using these images.


Now style guard_wrapper. We'll make it 250 by 400 pixels, and set it somewhere in the middle of the screen via the margin property. Then we'll set the background to hold the image.

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <style>
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
            }
        </style>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>

    <body>
        <div id="guard_wrapper">

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


So now we have this Lara Croft-like figure in the middle of the screen...


Time to build the door. Let's have another div, this time with the id of guard_door, inside the guard_wrapper div. The styling for the guard_door div is included. Here, we've included a "window" in the door using the before pseudoselector. The width and height are set relative to the guard_door div's size, which is in turn set to 100% of the guard_wrapper div's size. The "window" has its outline property set to 800 pixels and is colored black. Due to the guard_door div's overflow property being set to hidden, it gives the illusion of the entire door being black except for the "window"!

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <style>
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
            }

            #guard_door
            {
                width:100%;
                height:100%;
                margin-left:0;
                overflow:hidden;
            }

            #guard_door:before
            {
                display:block;
                content:'';
                margin:60px auto 0 auto;
                outline:800px solid #000000;
                width:50%;
                height:30%;
            }
        </style>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>

    <body>
        <div id="guard_wrapper">
            <div id="guard_door">

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


Yep, here's what it looks like now.


Next, we're gonna animate the door. To do that, first add a transition property to the guard_door CSS. Then add a PHP block with a conditional block near the bottom of the page. This code should fire off only if the user is logged in. So if someone directly accesses this page without having gone through a proper login, nothing happens.

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <style>
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
            }

            #guard_door
            {
                width:100%;
                height:100%;
                transition:all 5s;
                -webkit-transition:all 5s;
                margin-left:0;
                overflow:hidden;
            }

            #guard_door:before
            {
                display:block;
                content:'';
                margin:20px auto 0 auto;
                outline:800px solid #000000;
                width:50%;
                height:30%;
            }
        </style>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>

    <body>
        <div id="guard_wrapper">
            <div id="guard_door">

            </div>
        </div>
        <?php
        if ($_SESSION["loggedin"])
        {
        ?>

        <?php
        }
        ?>
    </body>
</html>


If the user arrived at this page via a proper login, a JavaScript snippet fires off after a tiny delay. The guard_door div's margin-left property is set to -100%, and the transition takes 5 seconds, giving the appearance that the door is sliding open!

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <style>
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
            }

            #guard_door
            {
                width:100%;
                height:100%;
                transition:all 5s;
                -webkit-transition:all 5s;
                margin-left:0;
                overflow:hidden;
            }

            #guard_door:before
            {
                display:block;
                content:'';
                margin:20px auto 0 auto;
                outline:800px solid #000000;
                width:50%;
                height:30%;
            }
        </style>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>

    <body>
        <div id="guard_wrapper">
            <div id="guard_door">

            </div>
        </div>
        <?php
        if ($_SESSION["loggedin"])
        {
        ?>
            <script>
                setTimeout(function() {
                document.getElementById("guard_door").style.marginLeft="-100%";
                },1000);
            </script>
        <?php
        }
        ?>
    </body>
</html>


Try it now.


If you would rather the door slide behind the "doorway", try this instead.

confirm.php
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
                overflow:hidden;
            }


There you go.


Next, we add the speech bubble. There's a speech_wrapper div which a speech div nested within. The function of the speech_wrapper div is simply to hold the speech div in place. It's just set to 250 by 250 pixels, with a margin of 5 pixels from the top. But speech itself has a black background with white text, and here I've used the before pseudoselector to provided the black triangle at the top.

confirm.php
<?php
session_start();
?>

<!DOCTYPE html>
<html>
    <head>
        <title>Who goes there?</title>

        <style>
            #guard_wrapper
            {
                width:250px;
                height:400px;
                margin:10% auto 0 auto;
                background-image:url(img/laracroft.jpg);
                background-position:center center;
                background-repeat:no-repeat;
                background-size:cover;
                overflow:hidden;
            }

            #guard_door
            {
                width:100%;
                height:100%;
                transition:all 5s;
                -webkit-transition:all 5s;
                margin-left:0;
                overflow:hidden;
            }

            #guard_door:before
            {
                display:block;
                content:'';
                margin:20px auto 0 auto;
                outline:800px solid #000000;
                width:50%;
                height:30%;
            }

            #speech_wrapper
            {
                width:250px;
                height:200px;
                margin:5px auto 0 auto;
            }

            #speech
            {
                width:240px;
                height:80px;
                background:#000000;
                position:relative;
                border-radius:10px;
                text-align:center;
                color:#FFFFFF;
                padding:5px;
            }

            #speech:before
            {
                content:"";
                position:absolute;
                right:50%;
                top:-50px;
                width:0;
                height:0;
                border-bottom:25px solid #000000;
                border-right:20px solid transparent;
                border-top:25px solid transparent;
            }
        </style>

        <script type="text/javascript" src="js/tt_millenniumlogin.js"></script>
    </head>
    <body>
        <div id="guard_wrapper">
            <div id="guard_door">

            </div>
        </div>
        <br />
        <div id="speech_wrapper">
            <div id="speech">

            </div>
        </div>
        <?php
        if ($_SESSION["loggedin"])
        {
        ?>
            <script>
                setTimeout(function() {
                document.getElementById("guard_door").style.marginLeft="-100%";
                },1000);
            </script>
        <?php
        }
        ?>
    </body>
</html>


So far so good?


Next, we'll build two screens into the speech div, using another two divs with the ids of confirmid and confirmpassword respectively. The idea here is that confirmid is shown first, and when the user types in a valid id (like "Wasp" in the example), confirmid disappears and confirmpassword is shown. One says "WHO GOES THERE?" and the other says "PROVE IT, OR ELSE..."

confirm.php
        <div id="speech_wrapper">
            <div id="speech">
                <div id="confirmid">
                    <br />
                    WHO GOES THERE?
                </div>
                <div id="confirmpassword">
                    <br />
                    PROVE IT, OR ELSE....
                </div>
            </div>
        </div>


This is what you should see. Both confirmid and confirmpassword are visible.
16


Here's the content for these divs.
confirm.php
        <div id="speech_wrapper">
            <div id="speech">
                <div id="confirmid">
                    <br />
                    WHO GOES THERE?
                    <br />
                    <input type="text" id="txtConfirmId" maxlength="20">
                    &nbsp;
                    <input type="button" value="go">
                </div>
                <div id="confirmpassword">
                    <br />
                    PROVE IT, OR ELSE....
                    <br />
                    <input type="password" id="txtConfirmPassword" maxlength="20">
                    &nbsp;
                    <input type="button">
                </div>
            </div>
        </div>


This is what you should see now. The content overflows the bubble, but that's all right. We'll take care of that soon.


Now add this line just after the code for opening the door. This fires off the confirm_screen() function which we'll write next, in tt_millenniumlogin.js.

confirm.php
            <script>
                setTimeout(function() {
                document.getElementById("guard_door").style.marginLeft="-100%";
                },1000);

                confirm_screen(true,false);
            </script>


The confirm_screen() function basically decides which div to display and which to hide. In this case, the confirmid div is shown first, remember?

It also changes the background image of the guard_wrapper div accordingly.

tt_millenniumlogin.js
function close_overlay(ovid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="none";
}

function confirm_screen(id,password)
{
    document.getElementById("confirmid").style.display=(id?"block":"none");
    document.getElementById("confirmpassword").style.display=(password?"block":"none");

    document.getElementById("guard_wrapper").style.backgroundImage="url(img/laracroft"+(id?"":"_proveit")+".jpg)";

}


So running the code for confirm.php again, this is what you should see now.


Next

We'll handle the second login process.

Thursday, 16 March 2017

Web Tutorial: Hacker Republic Login Sequence (Part 2/4)

Now that you have that gallery up, it's time to interact with it. Here's how Lisbeth Salander does it in the story.

She put her cursor on the spire of the church tower and clicked. She instantly got a pop-up dialog box that asked for her I.D. and password. She took out her stylus and wrote the word Remarkable on the screen as her I.D. and A(89)Cx#magnolia as the password. She got a dialog box with the text [ERROR  -  you have the wrong password] and a button that said [OK  -  Try again]. Lisbeth knew that if she clicked on [OK  -  Try again] and tried a different password, she would get the same dialog box again  -  for years and years, for as long as she kept trying. Instead she clicked on the [O] in [ERROR].

So this it how it goes. You have to click on the spire of the church to get the pop-up dialogue box. Anything else, something harmless happens. To do this, we're going to divide the image into several clickable squares, like so. Look at the spire of the church. It's right at the 9th square of the 1st row. For your reference, I've numbered the squares for you in x,y format, where x is the row and y is the column.
0,0
0,1
0,2
0,3
0,4
0,5
0,6
0,7
0,8
0,9
1,0
1,1
1,2
1,3
1,4
1,5
1,6
1,7
1,8
1,9
2,0
2,1
2,2
2,3
2,4
2,5
2,6
2,7
2,8
2,9
3,0
3,1
3,2
3,3
3,4
3,5
3,6
3,7
3,8
3,9
4,0
4,1
4,2
4,3
4,4
4,5
4,6
4,7
4,8
4,9
5,0
5,1
5,2
5,3
5,4
5,5
5,6
5,7
5,8
5,9
6,0
6,1
6,2
6,3
6,4
6,5
6,6
6,7
6,8
6,9
7,0
7,1
7,2
7,3
7,4
7,5
7,6
7,7
7,8
7,9
8,0
8,1
8,2
8,3
8,4
8,5
8,6
8,7
8,8
8,9
9,0
9,1
9,2
9,3
9,4
9,5
9,6
9,7
9,8
9,9


Back to your code...

Clear the div by setting its innerHTML property to an empty string. You'll see why in a minute. Next, declare the variable grid, and set up a nested For loop. We're going to create a grid of 10 squares by 10 squares.

tt_millenniumlogin.js
function open_overlay(ovid,imgid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="block";

    if (ovid=="gallery_overlay")
    {
        var img_actual=document.getElementById("gallery_actual");
        img_actual.style.backgroundImage="url(img/"+imgid+".jpg)";
        img_actual.innerHTML="";

        var grid;

        for (var i=0;i<10;i++)
        {
            for (var j=0;j<10;j++)
            {

            }
        }
    }
}


Here, we create div elements and set grid to reference them, then set the class to gallery_grid.

tt_millenniumlogin.js
function open_overlay(ovid,imgid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="block";

    if (ovid=="gallery_overlay")
    {
        var img_actual=document.getElementById("gallery_actual");
        img_actual.style.backgroundImage="url(img/"+imgid+".jpg)";
        img_actual.innerHTML="";

        var grid;

        for (var i=0;i<10;i++)
        {
            for (var j=0;j<10;j++)
            {
                grid=document.createElement("div");
                grid.className="gallery_grid";
                grid.id=imgid+"_"+i+"_"+j;
                img_actual.appendChild(grid);
            }
        }
    }
}


Now, in your HTML file, create the CSS class gallery_grid. Each one has the float property set to left, and has a height and width of 50 pixels. It's a 10x10 grid, and (10x50=500) pixels, which is the width and height of your image! Of course, the squares are invisible, so you won't see them.

index.html
        <style>

            body, html {height:100%;}

            #gallery
            {
                width:100%;
                height:200%;
            }

            .gallery_row
            {
                width:100%;
                height:5%;
                margin-top:10px;
            }

            .gallery_row .thumbnail
            {
                width:9.5%;
                height:100%;
                float:left;
                margin-left:5px;
                background-size:cover;
                background-position:center center;
                cursor:pointer;
            }

            #gallery_overlay
            {
                position:fixed;
                left:0;
                top:0;
                width:100%;
                height:100%;
                display:none;
                z-index:100;
                background-color:rgba(0,0,0,0.9);
            }

            #gallery_actual
            {
                width:500px;
                height:500px;
                margin-top:5%;
                margin-left:auto;
                margin-right:auto;
                background-size:cover;
                background-position:left top;
                cursor:pointer;
            }

            .gallery_grid
            {
                width:50px;
                height:50px;
                float:left;
            }
        </style>


Add this line to your JavaScript. It'll set a click event which will call the function process_grid(), with a string parameter. You'll pass in the id of the current square as an argument.

tt_millenniumlogin.js
function open_overlay(ovid,imgid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="block";

    if (ovid=="gallery_overlay")
    {
        var img_actual=document.getElementById("gallery_actual");
        img_actual.style.backgroundImage="url(img/"+imgid+".jpg)";
        img_actual.innerHTML="";

        var grid;

        for (var i=0;i<10;i++)
        {
            for (var j=0;j<10;j++)
            {
                grid=document.createElement("div");
                grid.className="gallery_grid";
                grid.id=imgid+"_"+i+"_"+j;
                grid.onclick=function(){process_grid(this.id)};
                img_actual.appendChild(grid);
            }
        }
    }
}


So declare process_grid().

tt_millenniumlogin.js
function open_overlay(ovid,imgid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="block";

    if (ovid=="gallery_overlay")
    {
        var img_actual=document.getElementById("gallery_actual");
        img_actual.style.backgroundImage="url(img/"+imgid+".jpg)";
        img_actual.innerHTML="";

        var grid;

        for (var i=0;i<10;i++)
        {
            for (var j=0;j<10;j++)
            {
                grid=document.createElement("div");
                grid.className="gallery_grid";
                grid.id=imgid+"_"+i+"_"+j;
                grid.onclick=function(){process_grid(this.id)};
                img_actual.appendChild(grid);
            }
        }
    }
}

function process_grid(gridid)
{

}


Create an If conditional. The pop-up dialogue box will only appear if the id of the square, passed into the function, is "img_166_0_8". That is to say, image number 167, 1st row, 9th column! So if you click on the correct square, the function open_overlay() is called (we're re-using the function we created earlier) but this time with arguments "login_overlay" and an integer. An integer needs to be passed in because that's the signature of the function, but it doesn't matter what integer because we won't be using it. Just pass in a 0 and be done with it.

tt_millenniumlogin.js
function process_grid(gridid)
{
    if (gridid=="img_166_0_8")
    {
        open_overlay("login_overlay",0);
    }
    else
    {

    }
}


If you click on any other square, we open the image. We do this by using the split() method and obtaining the second element in the resultant array. This gives you the image number, which we'll then pass into the getURL() function, to obtain the URL of that image. Then we'll just open a new window to display the image.

tt_millenniumlogin.js
function process_grid(gridid)
{
    if (gridid=="img_166_0_8")
    {
        open_overlay("login_overlay",0);
    }
    else
    {
        var params=gridid.split("_");
        var url=getUrl(params[1]);
        window.open(url);
    }
}


As mentioned in the previous parts, all the images (save for the church) were random images taken off the internet. However, I don't want to remote-link them to the gallery (which is legally questionable and, well, just not very nice) and I don't want to not reference them in any way. So I've decided to link the images to the original URLs where I took the images from. If this were a real login page, you would want to do differently, of course.

Here, I've created the getURL() function which returns the URL based on the number passed in. urls is an array which holds all the URLs, and the final line simply returns the element at the index which you passed in as an argument. So if you passed in "2", you would get the URL "http://cdn.visitjacksonville.com/pages/4185/offshore_fishing__flexslider.jpg".

tt_millenniumlogin.js
function process_grid(gridid)
{
    if (gridid=="img_166_0_8")
    {
        open_overlay("login_overlay",0);
    }
    else
    {
        var params=gridid.split("_");
        var url=getUrl(params[1]);
        window.open(url);
    }
}

function getUrl(imgindex)
{
    var urls=[
        "http://www.atlantisbahamas.com/media/Things%20To%20Do/Water%20Park/Beaches/Gallery/waterpark_beaches_sexy.jpg",
        "http://cutedogandcat.xyz/wp-content/uploads/2016/06/cool-seeing-eye-dog-for-blind-dog-plus-a-brief-history-of-guide-dogs-the-best-dog-blog-dogpacer.jpg",
        "http://cdn.visitjacksonville.com/pages/4185/offshore_fishing__flexslider.jpg",
        "http://midcenturymotels.com/wp-content/uploads/2016/03/Blue-Swallow-Motel.jpg",
        "https://static-ssl.businessinsider.com/image/57e41098077dcc1c008b7964-2048/14066261_2122557997968462_5010985606905929631_o.jpg",
        "https://static01.nyt.com/images/2015/12/21/dining/21COOKING_DEVILSFOODCAKE/21COOKING_DEVILSFOODCAKE-superJumbo.jpg",
        "http://kingofwallpapers.com/race/race-002.jpg",
   
 (Click here for the rest of the code)
    ];

    return urls[imgindex];
}


Try it! Click on any of the images. You should get a new window that shows the original image. Of course, things on the internet being as fluid as they are, it's entirely possible that you'll get a different image or a 404 error.




Of course, if you click on the spire of image #167, nothing should happen. That's because you haven't defined the case of "login_overlay" being passed into open_overlay(). So let's do it here. This basically hides the div element login_message and displays the div element login_form.

tt_millenniumlogin.js
function open_overlay(ovid,imgid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="block";

    if (ovid=="login_overlay")
    {
        document.getElementById("login_form").style.display="block";
        document.getElementById("login_message").style.display="none";
    }

    if (ovid=="gallery_overlay")
    {
        var img_actual=document.getElementById("gallery_actual");
        img_actual.style.backgroundImage="url(img/"+imgid+".jpg)";
        img_actual.innerHTML="";

        var grid;

        for (var i=0;i<10;i++)
        {
            for (var j=0;j<10;j++)
            {
                grid=document.createElement("div");
                grid.className="gallery_grid";
                grid.id=imgid+"_"+i+"_"+j;
                grid.onclick=function(){process_grid(this.id)};
                img_actual.appendChild(grid);
            }
        }
    }
}

But there are no such elements!

Of course. You haven't created them yet! Let's do that right now. Back to your HTML file, create a div with the id login_overlay.

index.html
    <body onload="load_gallery();">
        <div id="gallery_overlay" onclick="close_overlay(this.id);">
            <div id="gallery_actual">

            </div>
        </div>

        <div id="login_overlay">

        </div>

        <div id="gallery">

        </div>
    </body>


This time, you only want the overlay to close if you click on a "close" button, so add this code.

index.html
    <body onload="load_gallery();">
        <div id="gallery_overlay" onclick="close_overlay(this.id);">
            <div id="gallery_actual">

            </div>
        </div>

        <div id="login_overlay">
            <div id="login_x">
                <span onclick="close_overlay('login_overlay');">close x</span>
            </div>
        </div>

        <div id="gallery">

        </div>
    </body>


Now style login_overlay and login_x. login_overlay has almost the same styling as gallery_overlay, except that it only covers 50% of the screen's height, and appears somewhere center of screen.

For login_x, the styling is as follows.
width:80% - to give it a bit of spacing at the sides.
margin: 0 auto 0 auto - to place the div center of the login_overlay div.
color:#FFFFFF - to make the text stand out.
text-align:right - to set the text flush right of the login_x div.
cursor:pointer - to make it damn obvious that this is clickable.

index.html
        <style>

            body, html {height:100%;}

            #gallery
            {
                width:100%;
                height:200%;
            }

            .gallery_row
            {
                width:100%;
                height:5%;
                margin-top:10px;
            }

            .gallery_row .thumbnail
            {
                width:9.5%;
                height:100%;
                float:left;
                margin-left:5px;
                background-size:cover;
                background-position:center center;
                cursor:pointer;
            }

            #gallery_overlay
            {
                position:fixed;
                left:0;
                top:0;
                width:100%;
                height:100%;
                display:none;
                z-index:100;
                background-color:rgba(0,0,0,0.9);
            }

            #gallery_actual
            {
                width:500px;
                height:500px;
                margin-top:5%;
                margin-left:auto;
                margin-right:auto;
                background-size:cover;
                background-position:left top;
                cursor:pointer;
            }

            .gallery_grid
            {
                width:50px;
                height:50px;
                float:left;
            }

            #login_overlay
            {
                position:fixed;
                left:0;
                top:20%;
                width:100%;
                height:50%;
                display:none;
                z-index:100;
                background-color:rgba(0,0,0,0.9);
            }

            #login_x
            {
                width:80%;
                margin: 0 auto 0 auto;
                color:#FFFFFF;
                text-align:right;
                cursor:pointer;
            }
        </style>


Try your code. Click on thumbnail #167, then click on the spire. You should see the gallery_overlay disappear and the login_overlay appear with the "close x". Click on "close x" and the login_overlay div should disappear as well. It's pretty hard to see since the text is in black, so I've highlighted it for you in the screenshot.


Then add the login_form div with text boxes for login id, password and a login button. We'll impose a maximum length of 20 characters for the text boxes, as a good practice.

index.html
    <body onload="load_gallery();">
        <div id="gallery_overlay" onclick="close_overlay(this.id);">
            <div id="gallery_actual">

            </div>
        </div>

        <div id="login_overlay">
            <div id="login_x">
                <span onclick="close_overlay('login_overlay');">close x</span>
            </div>

            <div id="login_form">
                <input type="text" id="login_id" placeholder="login" maxlength="20"><br />
                <input type="password" id="login_password" placeholder="password" maxlength="20"><br />
                <input type="button" value="LOGIN">
            </div>
        </div>

        <div id="gallery">

        </div>
    </body>


Next, we have a div for login_message which holds the error message which is going to appear. The word "ERROR" is divided into a series of span elements, with each character occupying one span. Note that each span is styled using the CSS class message_error. The "OK - Try again" button only has one function - to rerun the open_overlay() function using the arguments "login_overlay" and an empty string.

index.html
    <body onload="load_gallery();">
        <div id="gallery_overlay" onclick="close_overlay(this.id);">
            <div id="gallery_actual">

            </div>
        </div>

        <div id="login_overlay">
            <div id="login_x">
                <span onclick="close_overlay('login_overlay');">close x</span>
            </div>

            <div id="login_form">
                <input type="text" id="login_id" placeholder="login" maxlength="20"><br />
                <input type="password" id="login_password" placeholder="password" maxlength="20"><br />
                <input type="button" value="LOGIN">
            </div>

            <div id="login_message">           
                <span class="message_error">E</span>
                <span class="message_error">R</span>
                <span class="message_error">R</span>
                <span class="message_error">O</span>
                <span class="message_error">R</span>
                 - You have the wrong password
                <br />
                <input type="button" value="OK - Try again">
            </div>
        </div>

        <div id="gallery">

        </div>
    </body>


OK, this is pretty ugly, so just spice it up a little with some CSS. Again, I've had to highlight it for you, for better visibility.


index.html
            #login_overlay
            {
                position:fixed;
                left:0;
                top:20%;
                width:100%;
                height:50%;
                display:none;
                z-index:100;
                background-color:rgba(0,0,0,0.9);
            }

            #login_form,#login_message
            {
                width:40%;
                margin: 10% auto 0 auto;
                color:#FFFFFF;
            }

            #login_message
            {
                font-size:10px;
                font-weight:bold;
            }

            #login_x
            {
                width:80%;
                margin: 0 auto 0 auto;
                color:#FFFFFF;
                text-align:right;
                cursor:pointer;
            }


Better now. It's still not very pretty, but I'm depending on your good asthetic sense to take care of that. For now, it's at least functional and that's enough.

Note that the error message and "Try again" button have fallen outside the overlay now. That's OK, it should be invisible anyway. We'll take care of that later.


Our next step is to write code that will process the login. So alter your HTML. The button should run the login() function with the argument "login_form" when clicked.

index.html
<input type="button" value="LOGIN" onclick="login('login_form');">


And in your JavaScript file, write the function login(). We first declare the variable parameters, then add an If block for the string "login_form" You guessed it, we'll be reusing the function later. I'm lazy that way.

tt_millenniumlogin.js
function process_grid(gridid)
{
    if (gridid=="img_166_0_8")
    {
        open_overlay("login_overlay",0);
    }
    else
    {
        var params=gridid.split("_");
        var url=getUrl(params[1]);
        window.open(url);
    }
}

function login(formid)
{
    var parameters;

    if (formid=="login_form")
    {
   
    }
}

function close_overlay(ovid)
{
    var overlay=document.getElementById(ovid);
    overlay.style.display="none";
}


First, make the login_form div disappear and login_message div appear.

tt_millenniumlogin.js
function login(formid)
{
    var parameters;

    if (formid=="login_form")
    {
        document.getElementById("login_form").style.display="none";
        document.getElementById("login_message").style.display="block";   
    }
}


Next, set the parameters string to pass in the values from the form. The first will be a parameter type with a value of 0. This will be increasingly relevant later.

tt_millenniumlogin.js
function login(formid)
{
    var parameters;

    if (formid=="login_form")
    {
        document.getElementById("login_form").style.display="none";
        document.getElementById("login_message").style.display="block";

        parameters="type=0";
        parameters=parameters+"&id="+document.getElementById("login_id").value;
        parameters=parameters+"&password="+document.getElementById("login_password").value;   
    }
}


Next is a standard AJAX block that sends the data to ajax_login.php via POST. We'll come back to this later.

tt_millenniumlogin.js
    var parameters;

    if (formid=="login_form")
    {
        document.getElementById("login_form").style.display="none";
        document.getElementById("login_message").style.display="block";

        parameters="type=0";
        parameters=parameters+"&id="+document.getElementById("login_id").value;
        parameters=parameters+"&password="+document.getElementById("login_password").value;   

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {

            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));
    }


For now, let's set up the file ajax_login.php.  First, we start a session using the function session_start(). Next, we declare a variable, type, and set it to the type value sent via POST. In this case, it would be 0. But to be absolutely sure you get an integer, we use the intval() function.

Then we set a conditional block for type 0.

ajax_login.php
<?php
session_start();

$type=intval($_POST["type"]);

if ($type==0)
{

}
?>


First, we provide dummy data. In a genuine scenario, the data (suitably encrypted, of course) would be provided by a database; however this is a simulation and as such, data will be provided in the form of arrays. In this case, we use the array logins. The data provided in the first element reflects Lisbeth Saander entering "Remarkable" as the id and "A(89)Cx#magnolia" as the password. We also have two other login ids, "x1" and "x2", prepared for testing. Because typing in "x1" or "x2" is way less troublesome than typing in "Remarkable" or "A(89)Cx#magnolia".
ajax_login.php
if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";
}


Next, we obtain the values for the variables id and password from the POST data.

ajax_login.php
if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";

    $id=$_POST["id"];
    $password=$_POST["password"];
}


Next, we use the array_exists() function in a conditional block. Basically, if the key id exists in the logins array, we process the information. If not, we print out a JSON string for the AJAX code to get.

ajax_login.php
if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";

    $id=$_POST["id"];
    $password=$_POST["password"];

    if (array_key_exists($id,$logins))
    {

    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}


So if the id is valid, we use another conditional block to check if the password is correct. If it's not, again, we output a JSON string to store a false value.

ajax_login.php
if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";

    $id=$_POST["id"];
    $password=$_POST["password"];

    if (array_key_exists($id,$logins))
    {
        if ($logins[$id]==$password)
        {

        }
        else
        {
            echo json_encode(array("valid" => false));
        }
    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}


If the password is correct, we set a session variable, loggedin, to true. And then we output a JSON string with a true value for the AJAX code to get.

ajax_login.php
if ($type==0)
{
    $logins=array();
    $logins["Remarkable"]="A(89)Cx#magnolia";
    $logins["x1"]="x1";
    $logins["x2"]="x2";

    $id=$_POST["id"];
    $password=$_POST["password"];

    if (array_key_exists($id,$logins))
    {
        if ($logins[$id]==$password)
        {
            $_SESSION["loggedin"]=true;
            echo json_encode(array("valid" => true));
        }
        else
        {
            echo json_encode(array("valid" => false));
        }
    }
    else
    {
        echo json_encode(array("valid" => false));
    }
}


Back to the AJAX code, we declare the variable logindetails and set it to grab the output from ajax_login.php, parsing it for a JSON value. We then take the resultant object and retrieve the data for the property valid. If it's false, do nothing. If the login and password were both correct, however, we grab all elements in the current HTML document with the class of message_error, and then set a click event on the fourth element in the resultant array, ie. the character "O". This means that the error message appears no matter what, but if id and password are correct, the letter "O" is now clickable. Clicking on it will send the user to the page confirm.php.

tt_millenniumlogin.js
    var parameters;

    if (formid=="login_form")
    {
        parameters="type=0";
        parameters=parameters+"&id="+document.getElementById("login_id").value;
        parameters=parameters+"&password="+document.getElementById("login_password").value;   

        if (window.XMLHttpRequest)
        {// code for IE7+, Firefox, Chrome, Opera, Safari
            xmlhttp_loginform=new XMLHttpRequest();
        }
        else
        {// code for IE6, IE5
            xmlhttp_loginform=new ActiveXObject("Microsoft.XMLHTTP");
        }
       
        xmlhttp_loginform.onreadystatechange=function()
        {
            if (xmlhttp_loginform.readyState==4 && xmlhttp_loginform.status==200)
            {
                var logindetails=JSON.parse(xmlhttp_loginform.responseText);

                if (logindetails.valid)
                {
                    var errormessage=document.getElementsByClassName("message_error");
                    errormessage[3].onclick=function(){window.location="confirm.php"};
                }
            }
        }

        xmlhttp_loginform.open("POST","ajax_login.php",true);
        xmlhttp_loginform.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        xmlhttp_loginform.send(encodeURI(parameters));
    }


Try it. Type in an id other than "Remarkable", "x1" or "x2", and/or an incorrect password. It should do nothing except show you the error message. However, if you type in a correct id/password combination, the "O" in "ERROR" should be clickable.




Click on it and it should show you this. That's because you currently don't have confirm.php created.


Next

Creating confirm.php