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