Click here to Skip to main content
11,639,463 members (67,974 online)
Click here to Skip to main content

Beginner's Guide to HTML5 & CSS3 - Server Side Story

, 15 Nov 2014 CPOL 8.9K 262 28
Rate this:
Please Sign up or sign in to vote.
A bridging introduction to server-side scripting.

Introduction

You have learned HTML5 and CSS3 - HTML5 provides the document structure while CSS3 the style. Together, however, they make up only one side of the story, that is the client side. Websites that consists of only client-side components are static in contents and only suitable for information display that does not change frequently.

Today's websites contents are highly dynamic and situation-driven. Just visit the quick answers forum of CodeProject, you will be amazed at the ever updating of new questions on the web page whenever you hit the refresh button. Apparently, it cannot be achieved with HTML5 alone. Then how is it possible? The answer lies on the other side of the story - the server side.

In my article Beginner's Guide to HTML5 & CSS3 - Formidable Forms with HTML5, I have explained the creation of HTML5 forms for sending data to some server-side scripts for processing. However, I had to refrained from going into any actual server-side scripting and used JavaScript to simulate the receiving of form data instead. This article serves as a natural extension to that article by providing a bridging introduction to the server-side scripting through a walk-through tutorial that culminates in the creation of a mini web application. I have chosen PHP as the scripting language and Apache as the web server. At the end of the walk-through, you would gain useful insights into how the client-side and server-side components work together to make a dynamic web application. You can then make use of this knowledge as a stepping stone to further explore more in-depth any server-side technologies.

Setting Up the Stage

Before you can start writing your web application, you have to first set up the stage - the development and testing environment. It is a place equipped with the necessary software and hardware where you write and test your web applications before uploading them to the staging environment and then to the production environment. The most common hardware for such an environment is your own computer. For the mini web application that you are going to create, you will require the following software to set up this environment on your computer:

  • A web server to host the web application.
  • A PHP engine to intepret the PHP scripts.
  • An IDE to help you in the composition and debugging of PHP scripts.

Apache and PHP

For the testing environment, you will install this free all-in-one package software package called XAMPP that you can download from Apache Friends. For Macintosh users, there is also a free package called MAMP. XAMPP stands for Cross-platform, Apache, MySQL, PHP and Perl. Within a few mouse clicks, you can install Apache, MySQL, PHP, and phpMyAdmin on the PC. XAMPP was created primarily as a PHP development environment for Windows computer. MAMP stands for Macintosh, Apache, MySQL and PHP. With just a few mouse-clicks, you can install Apache, MySQL, PHP, and phpMyAdmin on the Macintosh computer. MAMP was created primarily as a PHP development environment for Macintosh computers.

Download the XAMPP for Windows Installer. Close all applications on your computer, double-click the downloaded exe file to start the installation. When prompted, just leave every option to default. By default, the installer will extracts all the necessary files to a new folder at c:\xampp.

If you have an older version of XAMPP on your machine, you have to uninstall it prior to installing the new version. Before the un-installation, however, remember to backup any web projects and databases that reside in the existing XAMPP folder so that you can re-locate them to the new version.

After successful installation, you should find an XAMPP icon on your desktop, double-click it to launch the XAMPP Control Panel as shown in Figure 1. Alternatively, you can locate c:\xampp-control.exe, the control panel program, and drag a shortcut to the desktop.

Figure 1: XAMPP Control Panel

On the XAMPP Control Panel, click the Start buttons for Apache. Messages that it has started successfully are displayed, and the label on its Start buttons changes to Stop. (Figure 2)

Figure 2: Apache Started

Click the Admin button for the Apache server, or alternatively launch a browser and enter http://localhost/ into the address bar. If everything has been correctly set up, you should see the following main admin page on the browser. (Figure 3)

Figure 3: XAMPP Main Admin

The menu on the left of the main admin page gives you web-based access to the various components of XAMPP.

Click the phpinfo() link under the Php section of the menu on the main admin page. You should see a page showing PHP configuration information below. (Figure 4)

Figure 4: PHP Configuration

Integrated Development Environment

You also need software to write the program. Since PHP programs are just text files, you can use a text editor (such as NotePad on Windows or TextEdit on Macintosh). However, there are software tools that offer features that help make program writing easier, they are collectively called Integrated Development Environment or IDE.

An IDE offers an editing and debugging environment that typically consists of a code editor, a compiler, a debugger, and a graphical user interface (GUI) builder. In this walk-through, we will install Zend Eclipse PHP Development Tools (PDT) as the IDE.

Download Zend Eclipse PHP Development Tools (PDT) and extract it to a location on your PC. You are required to sign up a free account in order to proceed with the download. Locate and double-click the zend-eclipse-php.exe in the extracted folder to launch the PDT. (Figure 5)

Figure 5: Zend Eclipse PDT Launching

You will be prompted to enter the path to the workspace. (Figure 6) Change the workspace to "c:\xampp\htdocs" where your PHP projects will reside.

Figure 6: workspace

The initial screen of the PDT will look like Figure 7. Close the Welcome screen.

Figure 7: Welcome Screen

You have successfully set up a development and testing environment on your computer. You are now ready to create the mini web application.

Getting the Basics Right

You will step through the process of writing your first server-side script in PHP using PDT as the IDE. Make sure that the Apache is started (Figure 2). In the PDT menu,

  • Click File > New > Local PHP Project (Figure 8)
     
    Figure 8: Start a New PHP Project
  • Ener "MyFirstServerSideProject" as Project Name > Finish (Figure 9)
     
    Figure 9: Name the PHP Project
  • A PHP project called "MyFirstServerSideProject" has been created and appears on the PHP Explorer panel. An empty "index.php" file will also be created in the project by default (Figure 10)
     
    Figure 10: An Empty PHP Project
  • Double-click the "index.php" node in the PHP Explorer to open the file in the editor window. Type the highlighted code in the editor window under the "index.php" tab (Figure 11)
     
    Figure 11: Type the Script
  • File > Save > then right-click "index.php" on the PHP Explorer > Run As > PHP Web Application (Figure 12)
     
    Figure 12: Executing the Script
  • A preview window is launched showing the configuration information of PHP (Figure 13)
     
    Figure 13: Outcome of the Script
  • You can view the same outcome on a browser. Launch a browser, type in the address bar this URL "http://localhost/MyFirstServerSideProject/index.php" and press Enter. Do you see it?
     
  • The "localhost" in the URL refers to your computer. You can access this page from another computer that is connected to the same local area network by replacing the "localhost" to the IP address of your computer, say 192.168.1.2.

Congratulation, you have just created a simple 3-line PHP script that returns the configuration information of the PHP engine on your computer. Take note of the following characteristics of PHP:

  • A PHP script consists of a series of commands and statements.
  • A PHP script must starts with <?php and ends with ?>.
  • Each PHP statement must end with a semicolon.
  • PHP commands are NOT case sensitive.
  • PHP scripts usually co-exist with client-side components, such as HTML and CSS, in a file.
  • When a file consists of a mix of PHP scripts and client-side components, it must be saved with a .php extension.

Let's add a new PHP file in the same project,

  • Right-click "MyFirstServerSideProject" in the PHP Explorer > New > PHP File (Figure 14) to open a new PHP file in the editor window. Save it as "hello.php".
     
    Figure 14: Add a New PHP File
  • Type the following code into the "hello.php" file, save and preview it in the PDT and you will see the output as shown in Figure 15. The echo is a PHP command that outputs its parameter as string and HTML elements to the browser, while the date("Y-m-d") is a PHP date function that returns current date in the format of "year-month-day". Here, the first echo command output "<h1>Hello CodeProject!</h1>", while the second one the current date in the format of "year-month-day".
    <!DOCTYPE HTML>
    <html>
    <head>
    <title>Simple PHP Example</title>
    </head>
    <body>
        <?php echo '<h1>Hello CodeProject!</h1>'; ?>
        <p><?php echo date("Y-m-d"); ?></p>
    </body>
    </html>
    Figure 15: Preview of hello.php
  • Right-click on the white space in the preview window in Figure 15, choose "View source" from the context menu, it will reveal the source of the displayed page as show in Figure 16. What do you notice?
     
    Figure 16: View source

    The source of display contains only HTML code. The PHP code that you have typed has noticeably disappeared. What you have just learned is this: the PHP code is not accessible by the clients. In other words, the business logic embedded in the PHP code is kept safe in the server. The PHP code is executed by the PHP engine on the server, the outcome of which is rendered as HTML and sent to the browser for display. In this example, the
    <?php echo '<h1>Hello CodeProject!</h1>'; ?>
    has been output as
    <h1>Hello CodeProject!</h1>
    while
    <?php echo date("Y-m-d"); ?>
    as
    2014-05-10 

You are now ready to work on the creation of the mini web application.

Looking Ahead...

The mini web application will consist of 2 pages - a registration page (Figure 17) that captures user's particulars and portrait and submit them to an acknowledgement page (Figure 18) for validation and display.

Figure 17: Registration Page

 

Figure 18: Acknowledgment Page

 

Importing the Resources

Download and extract the "ServerSideStory.zip" to your computer. Among other things, you should find a PHP file, a CSS file, and an images folder inside the extracted "ServerSideStory":

  • index.html
  • style.css
  • images folder

Move the "ServerSideStory" folder to "c:\xampp\htdocs\" which is the default root location for storing web folders. When you type "http://localhost" on the address bar of a browser, it will automatically be mapped to this location.

To work on these files using the PDT IDE, you have to first import the "ServerSideStory" as a project into the PDT workspace. On the menu, choose File > Import > Existing Projects into Workspace,

Figure 19: Importing Existing PHP Project

You would have imported "ServerSideStory" into the PDT as shown in Figure 20.

Figure 20: ServerSideStory Imported

 

Registration Page

 

Double-click the "index.html" file in the PHP Explorer and you should find the following code page being opened on the editor window.

<!DOCTYPE html>
<html>
<head>
<title>Registration Form</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>Registration Form</h1>
        </header>
        <div class="form">
            <form id="particularsform" action="acknowledge.php" method="post"
                enctype="multipart/form-data">
                <p class="particulars">
                    <label for="name">Name</label>
                </p>
                <input type="text" id="name" name="fullname"
                    placeholder="First name, last name" required autofocus>
                <p class="particulars">
                    <label for="email">Email</label>
                </p>
                <input type="email" id="email" name="email"
                    placeholder="someone@gmail.com" required>
                <p class="particulars">
                    <label for="password">Create a password</label>
                </p>
                <input type="password" id="password" name="password" required
                    pattern="[\w]{8,}">
                <p class="particulars">
                    <label for="confirmpassword">Confirm your password</label>
                </p>
                <input type="password" id="confirmpassword" name="confirmpassword"
                    required>
                <fieldset>
                    <legend>Gender</legend>
                    <label for="male">Male</label> 
                      <input type="radio" id="male" name="gender" value="Male" checked> 
                  <label for="female">Female</label>
                        <input type="radio" id="female" name="gender" value="Female">
                </fieldset>
                <p class="particulars">
                    <label for="dateofbirth">Date of Birth</label>
                </p>
                <input type="date" id="dateofbirth" name="dateofbirth">
                <p class="particulars">
                    <label for="hobbies">Hobbies</label>
                </p>
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Coding">Coding<br>
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Reading">Reading<br>
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Swimming">Swimming<br>
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Jogging">Jogging<br> 
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Eating">Eating<br> 
                    <input type="checkbox" id="hobbies" name="hobbies[]" value="Others">Others<br>
                <p class="particulars">
                    <label for="favoritecolor">Favorite Color</label>
                </p>
                <input type="color" id="favoritecolor" name="favoritecolor">
                <p class="particulars">
                    <label for="zodiac">Zodiac</label>
                </p>
                <input list="zodiac" name="zodiac">
                <datalist id="zodiac">
                    <option value="Aries"></option>
                    <option value="Taurus"></option>
                    <option value="Gemini"></option>
                    <option value="Cancer"></option>
                    <option value="Leo"></option>
                    <option value="Virgo"></option>
                    <option value="Libra"></option>
                    <option value="Scorpio"></option>
                    <option value="Sagittarius"></option>
                    <option value="Capricorn"></option>
                    <option value="Aquarius"></option>
                    <option value="Pisces"></option>
                </datalist>
                <p class="particulars">
                    <label for="portrait">Portrait</label>
                </p>
                <input type="file" id="portrait" name="portrait"> 
              <input type="submit" class="bottom" name="submit" id="submit" value="Sign up">
            </form>
        </div>
    </div>
</body>
</html>

These are code for constructing a HTML5 form which you would have already learned in my article Beginner's Guide to HTML5 & CSS3 - Formidable Forms with HTML5.

Pay particular attention to the following <form> attributes:

  • action="acknowledge.php" specifies the PHP script to which the form data will be sent.
  • method="post" transfers data via HTTP headers that is invisible to others and is the method for uploading file.
  • enctype="multipart/form-data" allows files to be included in the data stream. This will enable a user to upload a portrait file to the server through the <input type="file"> element.

Open a (Chrome) browser, type in the address bar "http://localhost/ServerSideStory/index.html" followed by the Enter key, you should see a registration page as shown in Figure 17. We will use Chrome for subsequent testing of the pages as the preview window in the PDT does not fully support HTML5 yet.

Acknowledgment Page

You will create the acknowledgement page to receive, validate, and display the information and portrait submitted by a user via the "index.html". Right-click "ServerSideStory" in the PHP Explorer > New > PHP File (Figure 14) to open a new PHP file in the editor window. Save it as "acknowledge.php". Open this file in the editor window, enter the following code:

<!DOCTYPE html>
<html>
<head>
<title>Acknowledgement</title>
<link rel="stylesheet" type="text/css" href="style.css">
</head>
<body>
    <div class="container">
        <header>
            <h1>Acknowledgement</h1>
        </header>
        <div class="form">

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

Let's start by capturing the name data sent over from the "index.html". Recall this piece of code in "index.html":

<input type="text" id="name" name="fullname" placeholder="First name, last name" required autofocus>

Note the name="fullname" of the <input> tag, to access its value in the receiving PHP script, we pass the value of the name attribute to the PHP's $_POST function like this:

$_POST["fullname"]

Add the following code between the <div class="form"> tag and the closing </div> tag.

<div class="form">
    <p>
        <label>Name: <?php echo $_POST["fullname"] ?></label>
    </p>
</div>

Note the embedding of PHP code inside HTML code. You can place PHP code anywhere in the file as long as they are properly enclosed in <?php and ?>. This line of PHP code will output the value of the name sent over from "index.html" as label.

Similarly, add the following code to capture and display email, gender, dateofbirth, and zodiac after the Name label.

    <p>
        <label>Name: <?php echo $_POST["fullname"] ?></label>
    </p>
    <p>
        <label>Email: <?php echo $_POST["email"] ?></label>
    </p>
    <p>
        <label>Gender: <?php echo $_POST["gender"] ?></label>
    </p>
    <p>
    <label>Date of Birth: <?php echo $_POST["dateofbirth"] ?></label>
    </p>
    <p>
        <label>Zodiac: <?php echo $_POST["zodiac"] ?></label>
    </p>

Add the following code to capture hobbies in between the labels of Date of Birth and Zodiac.

<label>Date of Birth: <?php echo $_POST["dateofbirth"] ?></label>
</p>
<p>
    <label>Hobbies: 
        <?php
            if (isset ($_POST["hobbies"] )) {
                $hobbies = implode(", ", $_POST ["hobbies"]);
                echo $hobbies;
            }
        ?>
</label>
</p>
<p>
<label>Zodiac: <?php echo $_POST["zodiac"] ?></label>

Unlike the previous form items each of which carries only a single value, hobbies can take multiple values. How to capture them? The answer is by array. Recall the following code in "index.html":

<input type="checkbox" id="hobbies" name="hobbies[]" value="Coding">Coding<br>
<input type="checkbox" id="hobbies" name="hobbies[]" value="Reading">Reading<br>
<input type="checkbox" id="hobbies" name="hobbies[]" value="Swimming">Swimming<br>
<input type="checkbox" id="hobbies" name="hobbies[]" value="Jogging">Jogging<br> 
<input type="checkbox" id="hobbies" name="hobbies[]" value="Eating">Eating<br> 
<input type="checkbox" id="hobbies" name="hobbies[]" value="Others">Others<br> 

We have named all the checkboxes for hobbies as "hobbies[]", where the [] denotes array. In "acknowledge.php", we can use $_POST["hobbies"] to capture the array of values that the user has checked. Here, I have used another PHP function implode() to concatenate these values into a string delimited by commas.

Add the following code to capture the favoritecolor in between the labels of Hobbies and Zodiac. As usual, $_POST is used to capture the color code. Instead of just displaying the plain color code (<?php echo $_POST["favoritecolor"] ?>) which makes no sense, I have placed this value in a <span> tag and assigned the color code to the foreground color of the text as well as the background color of the <span> area through an inline CSS (style='color:<?php echo $_POST["favoritecolor"] ?>; background-color:<?php echo $_POST["favoritecolor"] ?>'). The result is a color bar of the color code.

        <?php
              if (isset ($_POST["hobbies"] )) {
                  $hobbies = implode(", ", $_POST ["hobbies"]);
                  echo $hobbies;
              }
        ?>
      </label>
</p>
<p>
    <label>Favorite Color: 
       <span style='color:<?php echo $_POST["favoritecolor"] ?>; background-color:<?php echo $_POST["favoritecolor"] ?>'>
              <?php echo $_POST["favoritecolor"] ?>
          </span>
      </label>
</p>
<p>
    <label>Zodiac: <?php echo $_POST["zodiac"] ?></label>

Add the following code to capture and save the uploaded portrait file after the Zodiac label.

    <label>Zodiac: <?php echo $_POST["zodiac"] ?></label>
</p>
<br>
<p>
    <?php
        if ($_FILES["portrait"]["error"] == 0) {
              move_uploaded_file($_FILES["portrait"]["tmp_name"], "images/".$_FILES ["portrait"]["name"]);
        }
    ?>
</p>
</div> 

Here, we are dealing with a file object, not primitive values as before. However, the starting point is always the same, look for the name attribute in the <input> tag. Recall this piece of code in "index.html":

<input type="file" id="portrait" name="portrait">

You have found it, name="portrait". To access the file data, we pass the value of the name attribute to a PHP's $_FILE array object like this:

$_FILES["portrait"]

The code will first check for any file errors before proceeding further:

if ($_FILES["portrait"]["error"] == 0)

If there are no errors, it will save the uploaded file in its original file name to the "images" folder:

move_uploaded_file($_FILES["portrait"]["tmp_name"], "images/".$_FILES ["portrait"]["name"]);

By default, PHP will store any upload files at some temporary location specified by $_FILES["portrait"]["tmp_name"]. These files will vanish when the script ends. Therefore, it is important to remember to save them to a permanent location using the PHP function move_uploaded_file() before the script ends.

You will add the code to retrieve and display the uploaded file that you have just saved:

    <br>
    <p>
        <?php
              if ($_FILES["portrait"]["error"] == 0) {
                  move_uploaded_file($_FILES["portrait"]["tmp_name"], "images/" . $_FILES ["portrait"]["name"]);
              }
                    
            // Open images directory
              $dir = dir("images");
                    
              // List files in images directory
            while (($file = $dir->read()) != false) {
                  if ($file != '.' and $file != '..') {
                      echo "<img src='images/" . $file . "' class='thumbnail'>";
                  }
             }                
             $dir->close();
        ?>
    </p>
</div>

The code will first assign the "image" folder to a variable called $dir. It will then loop through this folder object of $dir (using PHP while) to retrieve any files present inside ($dir->read()) and display them as images (echo "<img src='images/" . $file . "' class='thumbnail'>";).

Testing 1, 2, 3...

Are you done? Let's test out your mini web application on a browser. Enter "http://localhost/ServerSideStory/index.html" in the browser address bar and you should see a registration form as shown in Figure 17. Enter some data, pick an image, and click the "Sign up" button, voila you are redirected to the acknowledgement page at "http://localhost/ServerSideStory/acknowledge.php" (as shown in Figure 18) which you have just created in the preceding session. The data that you have entered and the image that you have picked in the first page have appeared here.

Wait a minute! Go back to the registration page, enter different values for the "Create a password" and "Confirm your password" fields, then click the "Sign up" button, it will still faithfully redirect you to the acknowledgement page. That is not right! You would have expected it to scream at you with some message like "Passwords not matched!" Apparently, there is still some more work to be done.

Back to the coding board. Add the following PHP code to the beginning of the "acknowledge.php" page.

<?php 
    if ($_POST["password"] != $_POST["confirmpassword"]){
        header('Location: index.php?msg=01');
    }
?>
<!DOCTYPE html>
<html> 

The code will check that if the received values of "password" and "confirmpassword" do not match (if ($_POST["password"] != $_POST["confirmpassword"])), then redirect it back to the sending page accompanied by a name-value pair of "msg=01" where "msg" is the name and "01" is its value (header('Location: index.php?msg=01');). You may noticed that the sending page has been written as "index.php" instead of "index.html". It is not a mistake, you will find out the reason soon, so read on...

You will switch to the "index.html" page, add the following PHP code to the beginning of this page.

<?php 
    if (isset($_GET["msg"])){
        if ($_GET["msg"] == '01'){
            echo "<script>alert('Password and Confirm Password do not match, please try again')</script>";
        }
    }
?>
<!DOCTYPE html>
<html>

The code will look out for an incoming string by the name of "msg" ($_GET["msg"]). If it exists (if (isset($_GET["msg"]))), that means it has been sent back from the "acknowledge.php" page, it will then check the value of this "msg". If the value equals "01" which is the code indicating that the values of "password" and "confirmpassword" in the previous submission did not match, it will pop up an message box telling the user about this mistake (echo "<script>alert('Password and Confirm Password do not match, please try again')</script>"). The alert box is created in JavaScript.

Since you have added the PHP code into the "index.html", it cannot remain as a pure HTML page anymore, you have to change its file extension to .php, i.e. "index.php". That would have answered your question above. In the PDT menu, choose File > Save As... to save "index.html" as "index.php"; or in the PHP Explorer, right-click "index.html" > Refactor > Rename... to rename it.

Test it out again. This time you should get an alert message box screaming at you if you provide different values for the "Create a password" and "Confirm your password" fields again. (Figure 21)

Figure 21: Alert Message

You may think you have done it. Wait a second! Try visiting the acknowledgement page directly by typing "http://localhost/ServerSideStory/acknowledge.php" in the address bar and hit Enter. What will happen? You will receive a page full of error messages as shown in Figure 22.

Figure 22: Direct Entry Denied

The acknowledgement page was expecting data in the various $_POSTs which are missing as the visit was not initiated from the "index.php". The solution is to redirect any visits to "acknowledge.php" that are not a redirection from the "index.php" to the "index.php". So go back to the coding board again. Add the following code to the PHP block at the beginning of "acknowledge.php".

<?php 
    if (!isset($_POST["submit"])){
        header('Location: index.php');
    }

    if ($_POST["password"] != $_POST["confirmpassword"]){
        header('Location: index.php?msg=01');
    }
?> 

The code will look out for this piece of data named "submit" ($_POST["submit"]) that is supposed to accompanied every visit to "acknowledge.php". Recall this piece of code in "index.php":

<input type="submit" class="bottom" name="submit" id="submit" value="Sign up">

So "submit" is the value of "name" attribute of the "Sign up" button. If this piece of data does not exists (if (!isset($_POST["submit"]))), that means the visit is not a redirection from "index.php", then redirect it to "index.php" (header('Location: index.php');).

Test it out again, it should be working perfect now.

Mission Accomplished

We have touched down. From this walk-through, you would have gained fundamental knowledge on the working of a server-side scripting and the role and functionality that it plays in making a dynamic web application. This should make your learning on HTML5 and CSS3 more complete. More importantly, I hope it will motivate you to embark on another learning journey, that is the server-side technologies. As the saying goes, "The end of a journey is the beginning of another". Of course, you may do so after you have taken a well-deserved break. I shall take my leave now.

References

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Peter Leow
Instructor / Trainer
Singapore Singapore
子曰:"三人行,必有我师焉;择其善者而从之,其不善者而改之

"There is always something we can learn from another person. Choose to follow his strengths while use his shortcomings to reflect upon ourselves."
― Confucius

“Live as if you were to die tomorrow. Learn as if you were to live forever.”
― Mahatma Gandhi

You may also be interested in...

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150728.1 | Last Updated 15 Nov 2014
Article Copyright 2014 by Peter Leow
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid