Categories
Webinars

Webinar On Understanding How WordPress Works

By Lon Hosford
I am often asked in my Web Development Courses about WordPress. For many students who want to get started on the Internet quickly, this could be a good approach. wordpress_icon_violet_14_02_22_600x600So I did a series of webinars on WordPress.

The goal was to over some general topics to provide an overview. Here is the topic list.
[ad name=”Google Adsense”]

  • WordPress Session 1 Introduction
  • WordPress DIY Installation and Hosting
  • WordPress Administrative Panel and Posting Overview
  • WordPress Session 1 Q&A
  • WordPress Session 2 Introduction
  • WordPress User Accounts Roles and Capabilities
  • WordPress Create and Edit Posts
  • WordPress Categories and Tags
  • WordPress Permalinks
  • WordPress Post Formats
  • WordPress Polls Ratings

[iframe http:///www.youtube.com/embed/videoseries?list=PL1Edo5Vc75aQ7vf6YGge_5ammnQmkzE0Y 640 360]

There is a page filled with research links;

Understanding WordPress Research Links

And if you are looking for a full how to do it course you might look at

Categories
Articles

How to Recursively Traverse File Directories with PHP RecursiveDirectoryIterator


By Lon Hosford
You can avoid writing recursive function to traverse through tree structures like you server file system. PHP has several Iterator classes starting with version 5. PHP Logo with  RecursiveDirectoryIterator

In this article we will look at the RecursiveDirectoryIterator class. We will build a utility function to use the RecursiveDirectoryIterator class to provide a text listing of the path and file names in one or more directories.

Although we are just displaying paths to the files, you can also access the file information such as modification date, creation date, size, permissions and a variety of properties through the parent classes of RecursiveDirectoryIterator. Those classes include in order of inheritance: FilesystemIterator, DirectoryIterator and SplFileInfo. SplFileInfo provides many of the global file functions in PHP such as isDir, is_readable is_writeable and is_real for example.

Source Files: Download

Video Tutorial:

[iframe https://www.youtube.com/embed/jtqVJ3-95m4 640 360]

The User Interface – test_debug_dir_list.php

There is a simple hybrid HTML PHP script, test_debug_dir_list.php, to demonstrate. It has five tests using a customized function for RecursiveDirectoryIterator.

test_debug_dir_list.php in browser no links chosen
This is the first test showing all the file and directories in the folder that test_debug_dir_list.php is in.

test_debug_current_dir in browser first test link chosen

[ad name=”Google Adsense”]

The second test showing all the file and directories in the folder that test_debug_dir_list.php and of all its child directories.

test_debug_current_dir_1 in browser second test link chosen

The third links shows the files and directories of the parent directory for test_debug_dir_list.php.

test_debug_parent_dir in browser third test link chosen

This fourth test link is like the third but includes the first level of children directories for the parent directory.

test_debug_parent_dir_1 in browser third test link chosen

This fifth link test one more child directory level than the fourth.

test_debug_parent_dir_2 in browser third test link chosen

Custom Function debug_dir_list Using RecursiveDirectoryIterator

The function parameters on line 17 are the depth levels for recursing file system directories and the starting file system directory.

The default file system directory is the running script that might include debug_utils.inc.php contain our function. You can use the standard file system notation to express parent directories and paths.

The depth levels are the exact values for the RecursiveIteratorIterator class instance created on line 23. A negative one recurses to the last lowest level. Be careful with that on a big file system where the second argument is the root or near it. The debug_dir_list default value is zero which confines the RecursiveIteratorIterator instance to the starting files system directory.

debug_utils.inc.php – parameters

function debug_dir_list($dir_recurse_depth = 0, $dir_list_root = '.'){

Lines 19 to 21 creates the RecursiveDirectoryIterator instance. It has to arguments. The first, on line 20, is the starting directory and we use the debug_dir_list function’s second parameter without change. The second RecursiveDirectoryIterator instance parameter is a set of flags. We are using the flag to suppress showing the single and double dot files.

debug_utils.inc.php – The RecursiveDirectoryIterator Instance

	// Create a recursive file system directory iterator.
	$dir_iter = new RecursiveDirectoryIterator(
		$dir_list_root,
		RecursiveDirectoryIterator::SKIP_DOTS) ;// Skips dot files (. and ..)

Lines 23 to 27 create the RecursiveIteratorIterator class instance. Its first parameter on line 24 requires a class with a traversal iterator class and RecursiveDirectoryIterator implements the RecursiveIterator interface to meet that requirement.

The second argument on line 25 for the RecursiveIteratorIterator constructor is called mode. There are three modes that are constants to the class.

  • RecursiveIteratorIterator::LEAVES_ONLY – The default. Lists only leaves in iteration.
  • RecursiveIteratorIterator::SELF_FIRST – Lists leaves and parents in iteration with parents coming first.
  • RecursiveIteratorIterator::CHILD_FIRST – Lists leaves and parents in iteration with leaves coming first.

The RecursiveIteratorIterator constructor’s third argument on line 26 is called modes. It is optional and currently only has its own RecursiveIteratorIterator::CATCH_GET_CHILD as a possible value which will then ignore exceptions thrown such as denied file permissions.

Line 29 passed the recursion depth through the RecursiveIteratorIterator class setMaxDepth method. Here to the debug_dir_list function’s parameter is passed unchanged.

debug_utils.inc.php – The RecursiveIteratorIterator Instance

	// Create a recursive iterator.
	$iter = new RecursiveIteratorIterator(
		$dir_iter,
		RecursiveIteratorIterator::SELF_FIRST, // Lists leaves and parents in iteration with parents coming first.
		RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore exceptions such as "Permission denied"
		);
	// The maximum recursive path.
	$iter->setMaxDepth($dir_recurse_depth);

The rest of the function uses the RecursiveIteratorIterator to iterate its objects which have a base class of SplFileInfo. These objects resolve as the path string. But also you can see on line 33, they have the method isDir which is like the standalone is_file function. If you explore the SplFileInfo class you can see all the other methods for file system objects. Line 31 is really just adding for visual purposes a trailing slash to objects that are a directory and not the starting directory.

Line 34 pushes that $path string onto the $paths array which is returned from the debug_dir_list function.

debug_utils.inc.php

	// List of paths Include current paths
	$path = array($dir_list_root);
	foreach ($iter as $path => $dir) {
		if ($dir_recurse_depth == 0 && $dir->isDir()) $path .= "/";
		$paths[] = substr($path,2);
	}
	return $paths;

debug_utils.inc.php – full listing

<?php
/**
 *	Utilitites to help in debugging.
 *
 *	@author Lon Hosford
 *	@link www.lonhosford.com
*/
/**
 *	Create an array of files and directory names, Requires PHP 5 >= 5.3.1.
 *
 *	Directories without contents have a slash appended or at the $dir_recurse_depth regardless if they have contents. Hidden files and folders are included.
 *	@link http://stackoverflow.com/questions/14304935/php-listing-all-directories-and-sub-directories-recursively-in-drop-down-menu This code is based on this Stackoverflow post.
 *	@param int $dir_recurse_depth recurse depth. 0 for $dir_list_root. Add 1 for each child level.  -1 is used for any depth.
 *	@param string $dir_list_root recurse depth. Starting folder path. Files in this directory are included. Default is current directory.
 *	@return string[] List of folders and files found.
 */
function debug_dir_list($dir_recurse_depth = 0, $dir_list_root = '.'){
	// Create a recursive file system directory iterator.
	$dir_iter = new RecursiveDirectoryIterator(
		$dir_list_root,
		RecursiveDirectoryIterator::SKIP_DOTS) ;// Skips dot files (. and ..)
	// Create a recursive iterator.
	$iter = new RecursiveIteratorIterator(
		$dir_iter,
		RecursiveIteratorIterator::SELF_FIRST, // Lists leaves and parents in iteration with parents coming first.
		RecursiveIteratorIterator::CATCH_GET_CHILD // Ignore exceptions such as "Permission denied"
		);
	// The maximum recursive path.
	$iter->setMaxDepth($dir_recurse_depth);
	// List of paths Include current paths
	$path = array($dir_list_root);
	foreach ($iter as $path => $dir) {
		if ($dir_recurse_depth == 0 && $dir->isDir()) $path .= "/";
		$paths[] = substr($path,2);
	}
	return $paths;
}
?>
The User Interface – Exploring the source of test_debug_dir_list.php

For the UI script line 10 imports our debug_dir_list function.

test_debug_dir_list.php

include_once "debug_utils.inc.php";

Line 32 and lines 35 to 40 provide a url link back to test_debug_dir_list.php. Lines 35 to 40 provide a NVP (Name Value Pair) for the URL query and line 32 omits that. The NVP name is debug-action and the values current_dir, current_dir_1, parent_dir, parent_dir_1 and parent_dir_2.

test_debug_dir_list.php

<h3>debug_dir_list($dir_recurse_depth = 0, $dir_list_root = '.')</h3>
<ol class = 'mono-space'>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=current_dir';?>">Run</a> - List Current Directory - debug_dir_list()</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=current_dir_1';?>">Run</a> - List Current Directory + Children(1) - debug_dir_list(1)</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir';?>">Run</a> - List Parent Directory  + Children(0) - debug_dir_list(0, "../")</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir_1';?>">Run</a> - List Parent Directory + Children(1) - debug_dir_list(1, "../")</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir_2';?>">Run</a> - List Parent Directory + Children(2) - debug_dir_list(2, "../")</li>
</ol>

Lines 44 to 48 interrogate the super global $_GET variable for the debug-action key and sets the $get_action variable either as an empty string or the value in the $_GET['debug-action'] variable. There is no need to sanitize here as this is for internal development and testing and not a production script.

test_debug_dir_list.php

<?php
if ( !isset($_GET["debug-action"]) ){
	$get_action = "";
}else{
	$get_action = $_GET["debug-action"];
}

Lines 52 to 68 a switch block sets out each of the debug-action values and echoes the results of the debug_dir_list function with parameters to meet the desired results.

test_debug_dir_list.php

switch($get_action){
	case 'current_dir':
		echo "File list: " . print_r(debug_dir_list(), true);
	break;
	case 'current_dir_1':
		echo "File list: " . print_r(debug_dir_list(1), true);
	break;
	case 'parent_dir':
		echo "File list : " . print_r(debug_dir_list(0, "../"), true);
	break;
	case 'parent_dir_1':
		echo "File list: " . print_r(debug_dir_list(1, "../"), true);
	break;
	case 'parent_dir_2':
		echo "File list: " . print_r(debug_dir_list(2, "../"), true);
	break;
}

Worth a mention are lines 49 to 51. They help debug the debugging. The file name is helpful on line 49 when you are bleary eyed and not sure which testing script you are running. The basename function passed the magic constant __FILE__ provides the file name of this script. In face you might want to add a line use display the magic constant __FILE__. Testing scripts can get spread around and copied when the coding battle to finish gets wild.

Along with that heat of the battle information is the PHP version in front of your eyes. Line 50 does that. There is always the situation that a “temporary” server is needed to run some tests and no one bothers to check the PHP version installed. The phpversion function is just the ticket.

Then to check this debugging program is passing the expected debug-action value, we throw that out on line 51.

test_debug_dir_list.php

echo basename(__FILE__) . "\n";
echo "PHP version: " . phpversion() . "\n";
echo "debug-action=" . $get_action . "\n";

test_debug_dir_list.php – full listing

<?php
/**
 *	The debugging dashboard for testing and development.
 *
 *  @author Lon Hosford
 *  @link www.lonhosford.com
 *	@copyright 2014 Alonzo Hosford
 *  @license GPL
*/
include_once "debug_utils.inc.php";
?>
<!doctype html>
<html>
<head>
	<meta charset="UTF-8">
	<title>Testing and Debugging Dashboard | lonhosford.com</title>
	<style>
	body{ font-family:"Gill Sans", "Gill Sans MT", "Myriad Pro", "DejaVu Sans Condensed", Helvetica, Arial, sans-serif}
	pre {
	 white-space: pre-wrap;       /* css-3 */
	 white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
	 white-space: -pre-wrap;      /* Opera 4-6 */
	 white-space: -o-pre-wrap;    /* Opera 7 */
	 word-wrap: break-word;       /* Internet Explorer 5.5+ */
	}
	.mono-space{font-family:monospace;}
	table,td {border:solid 1px #000;}
	</style>
</head>
<body>
<h2>Testing and Debugging Dashboard</h2>
<h4><a href="<?php echo $_SERVER['PHP_SELF'];?>"><?php echo basename(__FILE__);?></a></h4>
<h3>debug_dir_list($dir_recurse_depth = 0, $dir_list_root = '.')</h3>
<ol class = 'mono-space'>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=current_dir';?>">Run</a> - List Current Directory - debug_dir_list()</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=current_dir_1';?>">Run</a> - List Current Directory + Children(1) - debug_dir_list(1)</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir';?>">Run</a> - List Parent Directory  + Children(0) - debug_dir_list(0, "../")</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir_1';?>">Run</a> - List Parent Directory + Children(1) - debug_dir_list(1, "../")</li>
	<li><a href="<?php echo $_SERVER['PHP_SELF'] . '?debug-action=parent_dir_2';?>">Run</a> - List Parent Directory + Children(2) - debug_dir_list(2, "../")</li>
</ol>
<hr>
<pre>
<?php
if ( !isset($_GET["debug-action"]) ){
	$get_action = "";
}else{
	$get_action = $_GET["debug-action"];
}
echo basename(__FILE__) . "\n";
echo "PHP version: " . phpversion() . "\n";
echo "debug-action=" . $get_action . "\n";
switch($get_action){
	case 'current_dir':
		echo "File list: " . print_r(debug_dir_list(), true);
	break;
	case 'current_dir_1':
		echo "File list: " . print_r(debug_dir_list(1), true);
	break;
	case 'parent_dir':
		echo "File list : " . print_r(debug_dir_list(0, "../"), true);
	break;
	case 'parent_dir_1':
		echo "File list: " . print_r(debug_dir_list(1, "../"), true);
	break;
	case 'parent_dir_2':
		echo "File list: " . print_r(debug_dir_list(2, "../"), true);
	break;
}
?>
</pre>
</body>
</html>

[ad name=”Google Adsense”]


Categories
Articles

JQuery Ajax PHP Dynamic Content Loading


By Lon Hosford
This example shows how to dynamically load HTML content from PHP into an HTML element using JQuery and AJAX.jq_php_logos_900_544

You might want to do this when the main page content has all the needed SEO material but there is a lot of additional content such as course or code files that can be loaded on visitor demand. For example a course page or tutorial page where you have all the summary and details about the course or tutorial that would satisfy SEO. Then you can have the user load the particular items they want.

This example provides the basic elements in JQuery, HTML, AJAX and PHP for a potential architecture to do that and then build upon further.

[ad name=”Google Adsense”]

Source Files: Download

[iframe https://www.youtube.com/embed/GhDZpphiDII 640 360]

The User Interface

This example has three buttons.

UI Before Buttons Are Clicked

Two are for loading content and one for clearing so we can play a bit while testing. Do not expect the user will need to clear the data.

UI After  Buttons Are Clicked

The User Interface Code

The first file to look at is the HTML document.

Full listing of ui_view_content.php

<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="ajax.js">
</script><link rel="stylesheet" type="text/css" href="site.css">
<title>JQ Dynamic Load Content | lonhosford.com</title>
<script>
	$(document).ready(function(e) {
        // Click handler for content_button class
		$(".content-button").click(function(e) {
            //console.log(".content-button.click");
            //console.log($(this).attr("data-content-id"));
			contentId = $(this).attr("data-content-id");
			sendServiceRequest(
				"content-id=" + contentId,
				function(data, status){
					//console.log("SUCCESS!");
					//console.log(contentId);
					//console.log(data);
					ele = "#" + contentId;
					$(ele).html(data);
				},
				function(data, status, error){
					//console.log("FAIL!");				
				});			
        });
		 // Click handler clear button
		$("#clear-content-button").click(function(e) {
			//console.log("#clear-content_button.click");
			$(".content").html("");
		});
   });
</script>
</head>
<body>
<div id ="wrapper">
    <header>
		<h2>JQ Dynamic Load Content</h2>
    </header>
    <div>
        <p><input id="clear-content-button" type="button" value="Clear All"></p>
        <div>
            <p><input class="content-button" type="button" data-content-id="content01" value="Load 1"></p>
			<p id="content01" class="content"></p>
		</div>
        <div>
            <p><input class="content-button" type="button" data-content-id="content02" value="Load 2"></p>
     		<p id="content02" class="content"></p>
		</div>
    </div>
</div>
</body>
</html>

Examine lines 45 and 49. You see they share the class attribute content-button.

            <p><input class="content-button" type="button" data-content-id="content01" value="Load 1"></p>
			<p id="content01" class="content"></p>
		</div>
        <div>
            <p><input class="content-button" type="button" data-content-id="content02" value="Load2"></p> 

We have on line 12 the JQuery click method calling a function for the content-button class. So both buttons call this same function when clicked.

	$(document).ready(function(e) {
        // Click handler for content_button class
		$(".content-button").click(function(e) {

Lines 45 and 49 are using the element attribute data-content-id. The values of the data-content-id are content01 and content02 respectively.

            <p><input class="content-button" type="button" data-content-id="content01" value="Load 1"></p>
			<p id="content01" class="content"></p>
		</div>
        <div>
            <p><input class="content-button" type="button" data-content-id="content02" value="Load2"></p> 

On line 15 we extract the data-content-id values and they are then sent on to PHP to return the needed content. All that is handled in the external ajax.js file we will look at when we get to the ajax.js code listing.

	$(document).ready(function(e) {
        // Click handler for content_button class
		$(".content-button").click(function(e) {
            //console.log(".content-button.click");
            //console.log($(this).attr("data-content-id"));
			contentId = $(this).attr("data-content-id");
			sendServiceRequest(
				"content-id=" + contentId,
				function(data, status){
					//console.log("SUCCESS!");
					//console.log(contentId);
					//console.log(data);
					ele = "#" + contentId;
					$(ele).html(data);
				},
				function(data, status, error){
					//console.log("FAIL!");				
				});			
        });

The ajax.js has a sendServiceRequest function which accepts a NVP (Name Value Pair). The server is expecting the name to be content-id.

Its second argument is the call back function for successful network send and receive. The third argument is if something horribly goes wrong on the network or the server and it is either not reached, never responds or it returns unexpected data formats. The data format returned is simple text containing HTML markup.

In the success function starting on line 18 we take the contentId variable that is available via closure and prepend the hash character on line 22 to create an id for JQuery to find. We thus get the same values for the two button data-content-id attributes which will be content01 and content02. On line 23 we call the JQuery html function to replace the content of the element having that id.

Lines 46 and 50 have the two possible elements with the ids of content01 and content02.

            <p><input class="content-button" type="button" data-content-id="content01" value="Load 1"></p>
			<p id="content01" class="content"></p>
		</div>
        <div>
            <p><input class="content-button" type="button" data-content-id="content02" value="Load 2"></p>
     		<p id="content02" class="content"></p>

So they are updated in line the Javascript on line 18.

Then so we can play a bit a clear button was added on line 43.

        <p><input id="clear-content-button" type="button" value="Clear All"></p>

And the click handler is on line 30. On line 32 all elements with the class attribute value of content have their HTML removed.

		 // Click handler clear button
		$("#clear-content-button").click(function(e) {
			console.log("#clear-content_button.click");
			$(".content").html("");
		});

And the two elements are the same we are loading from the server seen here on lines 46 and 50.

        <div>
            <p><input class="content-button" type="button" data-content-id="content01" value="Load 1"></p>
			<p id="content01" class="content"></p>
		</div>
        <div>
            <p><input class="content-button" type="button" data-content-id="content02" value="Load 2"></p>
     		<p id="content02" class="content"></p>
		</div>

The article is not about styling so you see it is simple and designed to fit this blog width.

These are the CSS files if you need to explore. The site.css file has the style selectors particular to the web page. We first import the Meyer reset.css to create the basic uniform styling to build up.

You could get more elegant with JQuery how the content opens with saw a collapsible approach making the buttons full width and having them slide the content down once it is loaded.

site.css

/* Meyer CSS Reset */
@import 'reset.css';
body{
	font-family:"Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", "DejaVu Sans", Verdana, sans-serif
	}
div{
	padding: 5px;	
}
header > h2 {
	font-size:2em;
	text-align:center;}
/* Content container */
#wrapper{
	margin:0 auto;
	width:590px;
}
/* Content containers */
.content{
	border:solid 1px; #000000;
	height:70px;
	margin-bottom:5px;
	min-height:25px;
	overflow:auto;
	padding:7px;
}

reset.css

/* http://meyerweb.com/eric/tools/css/reset/ 
   v2.0 | 20110126
   License: none (public domain)
*/

html, body, div, span, applet, object, iframe,
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
a, abbr, acronym, address, big, cite, code,
del, dfn, em, img, ins, kbd, q, s, samp,
small, strike, strong, sub, sup, tt, var,
b, u, i, center,
dl, dt, dd, ol, ul, li,
fieldset, form, label, legend,
table, caption, tbody, tfoot, thead, tr, th, td,
article, aside, canvas, details, embed, 
figure, figcaption, footer, header, hgroup, 
menu, nav, output, ruby, section, summary,
time, mark, audio, video {
	margin: 0;
	padding: 0;
	border: 0;
	font-size: 100%;
	font: inherit;
	vertical-align: baseline;
}
/* HTML5 display-role reset for older browsers */
article, aside, details, figcaption, figure, 
footer, header, hgroup, menu, nav, section {
	display: block;
}
body {
	line-height: 1;
}
ol, ul {
	list-style: none;
}
blockquote, q {
	quotes: none;
}
blockquote:before, blockquote:after,
q:before, q:after {
	content: '';
	content: none;
}
table {
	border-collapse: collapse;
	border-spacing: 0;
}

[ad name=”Google Adsense”]

Client Side Network Handling

Nothing particularly special about using the JQuery ajax method. It is just modified to sent a NVP, receive HTML and call a success or fail function.

The ajax.js file has the sendServiceRequest generic function makes on lines 5 to 10 an ajax call to the get_content.php script. It passes in its nvpSendData parameter to the get_content.php script using POST and expects and HTML return.

Lines 11 to 16 have the successful network handler which simply called the passed in function reference.

Same for the network failure on lines 17 to 22.

ajax.js

// Generic AJAX call to POST NVP formatted data to server PHP and receive HTML
function sendServiceRequest(nvpSendData, successCallback, failCallBack){
	//console.log("sendServiceRequest");
	//console.log(JSON.stringify(nvpSendData));
	$.ajax({
		type: "POST",
		url: "get_content.php",
		data : nvpSendData,
		dataType : 'html'
	})
	.success(function(data, status){
		//console.log(".done");
		//console.log("success data: " + JSON.stringify(data));
		//console.log("Return AJAX status: " + status);
		successCallback(data, status);
	})
	.fail(function(data, status, error) {
		//console.log(".fail");
		//console.log("Return data: " + JSON.stringify(data));
		//console.log("Return AJAX status: " + status);
		failCallBack(data, status, error);	
	}); 
}
Server Side Network Response

The first file is a stripped down controller for PHP that only returns HTML. You see the headers on lines 10 through 13 which are depending on how you want caching handled.

Line 17 sanitizes the POST content-id value we are receiving and places it into the $content_id variable. If the sanitzing fails then $content_id is false and we are returning a paragraph with a hard space on line 23. This is all we need for the demo. We could also return nothing.

If the $content_id variable is a string then we include the content.inc.php file.

get_content.php

<?php
/**
 *  Process network data received and return html content matching content-id.
 *  @author Lon Hosford 
 *  @link www.lonhosford.com 
*/
/**
 *  Standard headers to avoid caching
*/
	header('Cache-Control: no-cache, must-revalidate');
	header('Expires: Mon, 26 Jul 1997 05:00:00 GMT');
	header('Pragma: no-cache'); // HTTP 1.0.
	header('Expires: 0'); // Proxies.	
/**
 *  Validate and capture content-id 
*/
$content_id = filter_var($_POST['content-id'], FILTER_SANITIZE_STRING);
/**
 *  Return content based on content-id
*/
// Invalid content-id format received
if ($content_id == false){
	echo '<p>&nbsp;</p>';
// Valid content-id format received
}else{
	include_once "content.inc.php";
}
?>

The content.inc.php file is designed to return to standard out. It uses a switch statement to choose the $content_id variable on l one 7 which has the value passed in from the client. Lines 8 and and 11 have the corresponding content to return. Line 13 handles an invalid $content_id value.

There are many ways to return HTML from the server with PHP including storing it in a database. So you can do as you wish and even use another data format such as JSON or XML for the standard in and standard out.

content.inc.php

<?php
/**
 *  Std-out html content matching content-id from client.
 *  @author Lon Hosford 
 *  @link www.lonhosford.com 
*/
switch ($content_id){?>
<?php case 'content01':?>
<p>Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut <strong>laoreet dolore magna aliquam</strong> erat volutpat. Ut wisi enim ad minim veniam, quis nostrud exerci tation ullamcorper suscipit lobortis nisl ut aliquip ex ea commodo consequat. Duis autem vel eum iriure dolor in hendrerit in vulputate velit esse molestie consequat, vel illum dolore eu feugiat nulla facilisis at vero eros et accumsan et iusto odio dignissim qui blandit praesent luptatum zzril delenit augue duis dolore te feugait nulla facilisi. Nam liber tempor cum soluta nobis eleifend option congue nihil imperdiet doming id quod mazim placerat facer possim assum. </p>
	
<?php break;case 'content02':?>
<p>Claritas est etiam processus dynamicus, qui sequitur mutationem consuetudium lectorum. Mirum est notare quam littera gothica, quam nunc putamus parum claram, anteposuerit litterarum formas humanitatis per seacula quarta decima et quinta decima. Eodem modo typi, qui nunc nobis videntur parum clari, fiant sollemnes in futurum.</p>
<?php break;default:?>
	
<p>&nbsp;</p>
	
<?php break;
}?>

[ad name=”Google Adsense”]


Categories
Articles

Best 3 Personal LAMP Web Servers


By Lon Hosford
Static web pages can be loaded from your local hard drive and viewed in a web browser.

xampp wamp mamp logosHowever to send and receive data from a web server such as in processing a form, you need to place your files on a web server. Actually you might find some plugins that require user authentication may require an http web browser address line. Placing your files on a web server allows you to test exactly using http urls and to use a server database.

However using a public web server like BlueHost is just that public. Generally this is your production version of your web site and you do not want to be experimenting with changes that could render your site unusable. You could create a practice domain and host it but still you have the inconvenience of having to FTP your file changes to the server each change you make. It is more convenient to see the changes as soon as you save them without the step to move them to the web server.

For beginners all you need is a private web server. When you are ready to go live, then you need a public web server such as BlueHost.

Popular web servers are called the LAMP stack or Linux web hosting. LAMP stands for Linux (Operating System), Apache, MySQL and PHP. You can install the AMP part of LAMP on most computers. They are generally intended for local development work and not for public web hosting. But security and performance is constantly improved making them useful for in house web servers. Here are popular free versions.

XAMPP (Windows, Mac, Linux and Solaris)
xampp logo
XAMPP is an easy to install Apache distribution containing MySQL, PHP and Perl. It has versions for Windows, MacOS Solaris and Linux. XAMPP is really very easy to install and to use – just download, extract and start. Free of Charge. It comes with PHPMyAdmin and ProFTPD.

MAMP (Mac OS only)
mamp logo
MAMP installs a local server environment in a matter of seconds on your Mac OS X computer, be it MacBook or iMac. MAMP is free of charge however there is a PRO version that has a fee.

WampServer
wampp logo
WampServer is a Windows web development environment. It allows you to create web applications with Apache2, PHP and a MySQL database. Alongside, PhpMyAdmin allows you to manage easily your databases.

[ad name=”Google Adsense”]


Categories
Articles

How to Send Email From XAMPP using localhost on a Mac and your Gmail Account


By Lon Hosford

This is sample code for using SMTP and your Google GMail account with XAMPP installed on a Mac and PHPMailer.xampp_in_envelope_150x96
Often you want to send mail from your web site to yourself from a user form or even to others. But you may be using XAMPP as a local host and want to test that without having to upload to a public server. This script shows how you can do that with PHPMailer.

Step 1: Install XAMPP MacOS

Step 2: Download PHPMailer [ad name=”Google Adsense”]

Step 3: Create a testing php file shown below. Be sure you have the correct paths to class.phpmailer.php and class.smtp.php on lines 3 and 4 that you downloaded with PHPMailer.

<!--?php 
include("class.phpmailer.php"); 
include("class.smtp.php"); 
function test_gmail_smtp_basic() {
 	// Uncomment as needed for debugging
 	//error_reporting(E_ALL);
  	//error_reporting(E_STRICT);
 	// Set as needed
	date_default_timezone_set('America/New_York');
	$mail = new PHPMailer(); 
	// Optionally get email body from external file
 	$body = file_get_contents('contents.html');
 	$body = eregi_replace("[\]",'',$body);
  	$mail->IsSMTP();                            // telling the class to use SMTP
	$mail->Host       = "smtp.gmail.com";       // SMTP server
	$mail->SMTPDebug  = 2;                      // enables SMTP debug information (for testing)
                                                    // 0 default no debugging messages
                                                    // 1 = errors and messages
                                                    // 2 = messages only
	$mail->SMTPAuth   = true;                   // enable SMTP authentication
	//$mail->SMTPSecure = 'ssl';                // Not supported
	$mail->SMTPSecure = 'tls';                  // Supported
	$mail->Host       = "smtp.gmail.com";       // sets the SMTP server
	$mail->Port       = 587;                    // set the SMTP port for the GMAIL server
	$mail->Username   = "me@gmail.com";         // SMTP account username (how you login at gmail)
	$mail->Password   = "mygmailpassword";      // SMTP account password (how you login at gmail)

	$mail->setFrom('mememe@gmail.com', 'Joe Dirt');

	$mail->addReplyTo('mememe@gmail.com', 'Joe Dirt");

	$mail->Subject    = "PHPMailer Test Subject via smtp, basic with authentication";

	$mail->AltBody    = "To view the message, please use an HTML compatible email viewer!"; // optional, comment out and test

	$mail->msgHTML($body);

	$address = "someone-else@their-email-domain.com";
	$mail->addAddress($address, "Miss Piggy");
	// if you have attachments
	$mail->addAttachment("phpmailer.gif");      // attachment 
	$mail->addAttachment("phpmailer_mini.gif"); // attachment

	if(!$mail->Send()) {
	  echo "Mailer Error: " . $mail->ErrorInfo;
	} else {
	  echo "Message sent!";
	}
}
// Test the connection
test_gmail_smtp_basic();
?>

What you may find is an issue with many examples heretofore is that SMTPSecure shown on line 25 needs to be TLS (Transport Layer Security) instead of SSL (Secure Sockets Layer). The other item is the port number is 587 as you see on line 29. You can disable line 29 once you have this fully debugged. Use with care. 

These images come with PHPMailer and are included here for your convenience.

phpmailer.gif

phpmailer.gif

Optional external html file for the body content. See line 12 in the code above.

<body style="margin: 10px;">
<div style="width: 640px; font-family: Arial, Helvetica, sans-serif; font-size: 11px;">
<div align="center"><img style="height: 90px; width: 340px;" alt="" src="phpmailer.gif" /></div><br>
<br>
&nbsp;This is a test of PHPMailer.<br>
<br>
This particular example uses <strong>HTML</strong>, with a <div> tag and inline<br>
styles.<br>
<br>
Also note the use of the PHPMailer logo above with no specific code to handle
including it.<br />
Included are two attachments:<br />
phpmailer.gif is an attachment and used inline as a graphic (above)<br />
phpmailer_mini.gif is an attachment<br />
<br />
PHPMailer:<br />
Author: Lon Hosford (somebody@no.net)
</div>
</body>

[ad name=”Google Adsense”]


Categories
Articles

PhoneGap Cordova Accelerometer HTML5 Canvas Animation XCode Example


This is a template example using the accelerometer with Cordova or Phonegap and animating a sprite on the HTML5 canvas.blog_image400x545

Learn cocos2D Game Development
PhoneGap Essentials: Building Cross-Platform Mobile Apps

I was working on a game using the html canvas and the accelerometer. I developed a way to combine both in IOS using Objective C and the UIWebView in the UIKit. That approach limits distribution to the IOS platform but allows you to have all of the native UI items like screen navigation and just use the canvas for animations.

Then I became curious about distributing on multiple devices containing an accelerometer since the animation is being done in html canvas. The solution was a hybrid mobile platform like Cordova. UI in Cordova is really up to you although you can find UI libraries written in HTML, JavaScript and JQuery optimized for mobile.

Here are screen shots for the app.

There is only one screen and these are just showing the animated red circle in different positions. The functionality is simple in that the red circle moves in the direction of the accelerometer’s x and y values – well with some adjustments but we will cover them in a bit.

When it reaches the boundaries of the canvas for either direction, it stop moving in that one direction. Thus you can move it to any corner and it will stay fixed and you can move it around the edges of the canvas.

You can also tap the canvas to start and stop the animation.

The Project Files

The project uses Cordova 1.6, XCode 4.3.2 and was tested using iOS 5.1 on a IPhone 4.

I had started the example using Cordova 1.5. Cordova 1.6 was released before completing this article and Cordova 1.6 changed the accelerometer values. The release notes included “Rewrite of accelerometer code and removed DeviceInfo old init approach” and “Added unification of accelerometer values on ios (based on android values)”. Boiling those entries down we get that the values are the same across platform, but if you are using IOS you better divide by 10. I commented in the code in case you are still using 1.5 or earlier.

I added a simple html console area to display debugging messages from the Javascript.

I also stripped out most of the extra Cordova comments that were not relevant to this example.

Download XCode Project Files

Step 1 – Install Cordova for XCode

Instructions for setting up your development environment are located at the PhoneGap site Getting Started with iOS.

Step 2 – Create New Cordova-based Application

Step 3 – Set XCode Project Options


Set the XCode project options as follows:

  • Product name: Here I used CordovaAccelerometerCanvas as the project name. You can use a name of your own choosing.
  • Company identifier: Provide your own reverse domain name.
  • Use Automatic Reference Counting: Uncheck

Step 4 – Choose A File Location

Once you select a file location on your computer you will have a folder structure as follows:

And in XCode you will see a project window as follows:

You may notice the project has a warning. Ignore this for now.

Step 5 – Create and Add the www Folder and Files to the Project

These are the normal setup instructions for a Cordova alias PhoneGap XCode project. You can skip this step if you are used to creating Cordova XCode projects.

The process is creating a www folder by running the app once in the IPhone simulator and then add to the project explorer.

First run the app in the IPhone simulator.

The app runs but complains of the missing index.html file.

A www folder with this file and one other js file were created on your file system as this the app launched in the simulator. Here you see them and you need to drag the www folder into the Project Explorer. Do not drag to or copy to the XCode folders outside of XCode.

Fill out the “Choose options for adding these files” dialog as follows.

  • Destination: Unchecked
  • Folders: “Created folder references for added folders” selected.
  • Add to targets: CordovaAccelerometerCanvas checked.

And here are the final results you see in the Project Explorer window.

Run the app in the IPhone simulator one more time.

This time you will be greeted with an Alert dialog with the “Cordova is working” message.

Step 7a – The Completed index.html File

Here is the full index.html file completed for your copy convenience. Just replace your index.html file with this and you can test on your device.

I removed Cordova code comments and commented code not related to our needs that is included in the index.html file.

<!DOCTYPE html>
<html>
    <head>
        <title></title>
        <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
        <meta charset="utf-8">
        <script type="text/javascript" charset="utf-8" src="cordova-1.6.0.js"></script>
        <script type="text/javascript">
            var console_log;                // Debugging div on device
            var canvas_ctx;                 // HTML canvas 2d context
            var SPEED = 10;                 // Canvas redraw speed in milliseconds and accelerometer frequency
            var DISTANCE_FACTOR = .1;       // Factor to adjust accelerometer values to screen distance. Cordova 1.6 changed to values to be Android compatible which appeared to be * 10. For Cordova 1.5 make this value 1.
            var ax = 0;                     // Acceleration x axis (Accelerometer value adjusted for direction)
            var ay = 0;                     // Acceleration y axis (Accelerometer value adjusted for direction)
            var x;                          // Circle x position
            var y;                          // Circle y position
            var vx = 0;                     // Velocity x axis
            var vy = 0;                     // Velocity y axis
            var WIDTH = 320;                // Width of canvas
            var HEIGHT = 300;               // Height of canvas
            var RADIUS = 50;                // Width of circle object
            var CIRCLE_COLOR = "#f00";      // Circle color
            var CANVAS_COLOR = "#FAF7F8";   // Color of canvas background
            var watchID;                    // Accelerometer.watchAcceleration return value. 
            var drawID;                     // Draw time interval. 
            var playing = true;             // Boolean if animation is playing.
            /* DOM body onload event callback */
            function onBodyLoad()
            {		
                document.addEventListener("deviceready", onDeviceReady, false);
            }
            /* Cordova has been initialized and is ready to roll */
            function onDeviceReady()
            {
                console.log('console_div');
                console_log = document.getElementById('console_div');
                console_log.innerHTML += "onDeviceReady()<br/>";
                init();
            }
            /* Initialize canvas and animation */
            function init() 
            {
                var canvas = document.getElementById("canvas");
                canvas_ctx = canvas.getContext("2d");
                // Center 
                x = WIDTH / 2 ;
                y = HEIGHT/ 2 ;
                startPlay();
            }
            /* Start watching the accelerometer */
            function startWatch() 
            {
                var options = { frequency: SPEED };
                watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
            }   
            // Stop watching the accelerometer
            function stopWatch() 
            {
                if (watchID) {
                    navigator.accelerometer.clearWatch(watchID);
                    watchID = null;
                }
            }
            /* Accelerometer data callback */
            function onSuccess( acceleration )
            {
                // Set drawing acceleration values
                ax = acceleration.x * DISTANCE_FACTOR * -1; // -1 to change direction for Cordova 1.6. Removed for Cordova 1.5.
                ay = acceleration.y * DISTANCE_FACTOR ;// Add * -1 for Cordova 1.5;
                // Optional ouput for understanding accelerator values.
                console_log.innerHTML = 
                'Acceleration X: ' + acceleration.x + '<br />' +
                'Acceleration Y: ' + acceleration.y + '<br />' +
                'Acceleration Z: ' + acceleration.z + '<br />' +
                'Timestamp: '      + acceleration.timestamp ;
            }
            /*  Accelerometer error callback */
            function onError()
            {
                alert("Accelerometer Error");
            }
            /* Steps to start animation play */
            function startPlay()
            {
                playing = true;
                vx = 0;
                vy = 0;
                startWatch();
                drawID = setInterval(draw, SPEED);
            }
            /* Steps to stop animation play */
            function stopPlay()
            {
                clearInterval(drawID);
                stopWatch();
                playing = false;
            }
            /* Draw circle */   
            function circle( x, y, r ) 
            {
                canvas_ctx.beginPath();
                canvas_ctx.arc(x, y, r, 0, Math.PI*2, true);
                canvas_ctx.fill();
            }
            /* Draw rectangle */
            function rect( x, y, w, h ) 
            {
                canvas_ctx.beginPath();
                canvas_ctx.rect(x,y,w,h);
                canvas_ctx.closePath();
                canvas_ctx.fill();
            }
            /* Clear canvas */
            function clear() 
            {
                canvas_ctx.clearRect(0, 0, WIDTH, HEIGHT);
            }
            /* Compute drawing metrics and draw frame */    
            function draw() 
            {
                // Increase velocity by acceleration
                vx += ax;
                vy += ay;
                // Update circle drawing position.
                x += vx;
                y += vy;
                /* Boundaries testing */
                // Right boundary
                if ( x + RADIUS > WIDTH  )
                {
                    x = WIDTH - RADIUS ;
                    vx = 0;
                }
                // Left boundary
                if (x - RADIUS  <= 0)
                {
                    x = RADIUS   ;
                    vx = 0;
                }
                // Bottom boundary
                if (y +  RADIUS  > HEIGHT)
                {
                    y = HEIGHT - RADIUS ;
                    vy = 0;
                }
                // Top boundary
                if (y - RADIUS  <= 0)
                {
                    y = RADIUS  ;
                    vy = 0;
                }
                
                // Debugging info.
                //console_log.innerHTML = 
                //'x: ' + x + '<br />' +
                //'y: ' + y + '<br />' +
                //'vx: ' + vx + '<br />' +
                //'vy: ' + vy + '<br />' +
                //'ax: ' + ax + '<br />' +
                //'ay: ' + ay + '<br />' ;    
                
                /* Draw frame */
                // Clear canvas
                clear();
                // Draw canvas background
                canvas_ctx.fillStyle = CANVAS_COLOR;
                rect( 0, 0, WIDTH, HEIGHT );
                /* Draw circle */
                canvas_ctx.fillStyle = CIRCLE_COLOR;
                circle( x, y, RADIUS );
            }
            /* Canvas tag touch end event handler */
            function canvasTouchEnd()
            {
                if (playing)
                {
                    stopPlay();
                }
                else
                {
                    startPlay();
                }
            }
        </script>
    </head>
    <body onload="onBodyLoad()" style = "text-align:center;background-color:#ccc;padding:0px;margin:0px;">
        <div>
            <h1 style = "font-size:20px;margin-bottom:0px;margin-top:0px;padding-top:0px;">Accelerometer + HTML5 Canvas</h1>
            <canvas id="canvas" width="320" height="300" ontouchend = "canvasTouchEnd();" >
                This text is displayed if your browser 
                does not support HTML5 Canvas.
            </canvas>
            <div id = "console_div" style = "position:absolute;text-align:left;margin:2px;border:1px solid black;background-color:#fff;top:330px;left:0px;width:314px;height:118px;overflow:auto;" 
                >
            </div>
        </div>
    </body>
</html>

The remaining part of this article will explain the parts of the index.html file by topic.

Step 7a – The index.html body Section

Key in this section is the canvas tag on line 189. I am using the ontouchend event versus the onclick event to prevent a canvas flicker when the screen is touched. The action is to start and stop the animation.

Line 193 is my own html debugging console for simple examples. You should see the accelerometer values displayed in here when testing on a device.

 <body onload="onBodyLoad()" style = "text-align:center;background-color:#ccc;padding:0px;margin:0px;">
        <div>
            <h1 style = "font-size:20px;margin-bottom:0px;margin-top:0px;padding-top:0px;">Accelerometer + HTML5 Canvas</h1>
            <canvas id="canvas" width="320" height="300" ontouchend = "canvasTouchEnd();" >
                This text is displayed if your browser 
                does not support HTML5 Canvas.
            </canvas>
            <div id = "console_div" style = "position:absolute;text-align:left;margin:2px;border:1px solid black;background-color:#fff;top:330px;left:0px;width:314px;height:118px;overflow:auto;" 
                >
            </div>
        </div>
    </body>

[ad name=”Google Adsense”]
Step 7b – The index.html Accelerometer Code

Cordova provides an api for the accelerometer. We use a good portion of it with this example.

The variables on lines 11 to 14 are helpers for the animation. We do not need them for raw access to the accelerometer. You can see their explanations and we will look at them applied further along.

            var SPEED = 10;                 // Canvas redraw speed in milliseconds and accelerometer frequency
            var DISTANCE_FACTOR = .1;       // Factor to adjust accelerometer values to screen distance. Cordova 1.6 changed to values to be Android compatible which appeared to be * 10. For Cordova 1.5 make this value 1.
            var ax = 0;                     // Acceleration x axis (Accelerometer value adjusted for direction)
            var ay = 0;                     // Acceleration y axis (Accelerometer value adjusted for direction)

Line 24 has an id for the accelerometer watch event. This can be used to clear the watch event when you do not need the accelerometer anymore.

            var watchID;                    // Accelerometer.watchAcceleration return value. 

I created the startWatch function for this example to call when we need to start collecting accelerometer data.

The accelerometer works by calling your own event handler functions using the watchAcceleration method you see on line 54.
The watchAcceleration take a reference to your success and error handler as the first two arguments.

It also takes an options object for the third argument. Currently the only option object key is frequency measured in milliseconds. I am using the SPEED variable set to 10 milliseconds for the frequency.

The watchAcceleration returns an id so we can refer to it later.

            /* Start watching the accelerometer */
            function startWatch() 
            {
                var options = { frequency: SPEED };
                watchID = navigator.accelerometer.watchAcceleration(onSuccess, onError, options);
            }   

The stopWatch function is also created for the example to stop requesting accelerometer data anywhere in the code. Line 60 uses the clearWatch method to stop watching the accelerometer. The watchID variable from line 54 identifies the watch activity to end.

To prevent throwing errors, logic is included to test for the watchID.

            // Stop watching the accelerometer
            function stopWatch() 
            {
                if (watchID) {
                    navigator.accelerometer.clearWatch(watchID);
                    watchID = null;
                }
            }

The onSuccess function on line 64 is called from the watchAcceleration on line 54.

The onSuccess function passes an acceleration object. The acceleration object currently has four values and they are all displayed in our html console on line 71.

The other code in the onSuccess method prepares variables for the animation.

The ax and ay variables on line 68 and 69 are accelerometer x and y values respectively. They represent acceleration in our animation.

For the x-axis value we need to reverse the value sign so the tilt of the device represents the direction of the x-axis animation. Interesting this is for Cordova 1.6. In Cordova 1.5 this is not needed.

For the y-axis value we need to reverse the value sign if you are using Cordova 1.5. Again to represent the direction by the tilt of the device.

Finally the x and y acceleration values were adjusted with Cordova 1.6. As I had mentioned I started the example in Cordova 1.5 and was able to use the raw values. But with Cordova 1.6 the values are 10x so I added the DISTANCE_FACTOR variable set to .1 to compensate. Perhaps I should have called it a “VERSION_FACTOR”.

I had no need for z and timestamp properties in this example, but they are displayed for interest. You may get updates from the device due to your frequency of request, but the device has not sensed new values, so you can use the timestamp property in case you are using the accelerometer updates for a cpu intensive function that without a change is unnecessary.

            /* Accelerometer data callback */
            function onSuccess( acceleration )
            {
                // Set drawing acceleration values
                ax = acceleration.x * DISTANCE_FACTOR * -1; // -1 to change direction for Cordova 1.6. Removed for Cordova 1.5.
                ay = acceleration.y * DISTANCE_FACTOR ;// Add * -1 for Cordova 1.5;
                // Optional ouput for understanding accelerator values.
                console_log.innerHTML = 
                'Acceleration X: ' + acceleration.x + '<br />' +
                'Acceleration Y: ' + acceleration.y + '<br />' +
                'Acceleration Z: ' + acceleration.z + '<br />' +
                'Timestamp: '      + acceleration.timestamp ;
            }

This final code snippet is simply the error handler for watching the accelerometer. Apparently there is no information provided for the error, so you will need to improvise.


            /*  Accelerometer error callback */
            function onError()
            {
                alert("Accelerometer Error");
            }

Step 7c – User Start and Stop Interaction

Touching the canvas starts and stops the animation. This starts on line 189 with the canvas tag ontouchend event calling the canvasTouchEnd() function on line 172.

           <canvas id="canvas" width="320" height="300" ontouchend = "canvasTouchEnd();" >
                This text is displayed if your browser 
                does not support HTML5 Canvas.
            </canvas>

There is the playing variable set on line 26 that retains the state of animation playing.

            var playing = true;             // Boolean if animation is playing.

The canvasTouchEnd function uses the playing boolean variable to determine starting or stopping animation and calls function to handle changing the animation playing state.

            /* Canvas tag touch end event handler */
            function canvasTouchEnd()
            {
                if (playing)
                {
                    stopPlay();
                }
                else
                {
                    startPlay();
                }
            }

The startPlay function first sets the playing state to true.

To start playing the velocity variables are set to zero. This prevents any timing issues that may cause the animation move in a direction not indicative of the tilt of the device when the animation is restarted.

Receiving events from the accelerometer is started with the call to the startWatch function we reviewed earlier.

Handing the canvas redraw is done on line 89 with a JavaScript timer. The timing of the of the canvas redraw is the same as the updates from the accelerometer. You could consider a different design where the accelerometer updates perform the canvas redraw. However the approach we are using allows animations to occur that not dependent on the accelerometer changes should you need to add them.

            /* Steps to start animation play */
            function startPlay()
            {
                playing = true;
                vx = 0;
                vy = 0;
                startWatch();
                drawID = setInterval(draw, SPEED);
            }

Stopping animation is done by clearing the timer interval, stopping the accelerometer watching and setting the playing state to false.

            /* Steps to stop animation play */
            function stopPlay()
            {
                clearInterval(drawID);
                stopWatch();
                playing = false;
            }

Step 7d – The index.html Canvas Animation

The canvas animation does not require Cordova. It is just an interesting way to demonstrate using the accelerometer. All the animation in the canvas could be done in any HTML5 web browser.

First is the canvas tag which has a predefined width and height. We could create the canvas tag dynamically in JavaScript, but I left it out to keep code simpler.

           <canvas id="canvas" width="320" height="300" ontouchend = "canvasTouchEnd();" >
                This text is displayed if your browser 
                does not support HTML5 Canvas.
            </canvas>

Next there are some variables that impact the canvas animation work.

Line 10 has a variable to reference the canvas 2d context to allow drawing. This will refer back to the canvas tag.

The SPEED variable on line 11 is for the JavaScript timer that will call the draw function. The draw function does all the work on the canvas.

Line 13 and 14 are acceleration values that are updated using the accelerometer.

The x and y on lines 14 and 15 are the position of the circle sprite we are animating.

The velocity variables are initialized to zero on lines 16 and 17.

Height and width of the canvas are repeated here for computing the boundaries for our animated circle sprite.

The diameter of the circle sprite is configurable on line 21

Adding some color on lines 22 and 23 for the circle sprite and background respectively.

Then we have on line 25 the JavaScript timer interval id for redrawing the canvas.

            var console_log;                // Debugging div on device
            var canvas_ctx;                 // HTML canvas 2d context
            var SPEED = 10;                 // Canvas redraw speed in milliseconds and accelerometer frequency
            var DISTANCE_FACTOR = .1;       // Factor to adjust accelerometer values to screen distance. Cordova 1.6 changed to values to be Android compatible which appeared to be * 10. For Cordova 1.5 make this value 1.
            var ax = 0;                     // Acceleration x axis (Accelerometer value adjusted for direction)
            var ay = 0;                     // Acceleration y axis (Accelerometer value adjusted for direction)
            var x;                          // Circle x position
            var y;                          // Circle y position
            var vx = 0;                     // Velocity x axis
            var vy = 0;                     // Velocity y axis
            var WIDTH = 320;                // Width of canvas
            var HEIGHT = 300;               // Height of canvas
            var RADIUS = 50;                // Width of circle object
            var CIRCLE_COLOR = "#f00";      // Circle color
            var CANVAS_COLOR = "#FAF7F8";   // Color of canvas background
            var watchID;                    // Accelerometer.watchAcceleration return value. 
            var drawID;                     // Draw time interval. 
            var playing = true;             // Boolean if animation is playing.

The init() function is called when the app boots up and here you see on line 43 and 44 the standard way to get the drawing context to the canvas tag.

Also the starting position of the circle sprite is placed at the center of the screen.

            /* Initialize canvas and animation */
            function init() 
            {
                var canvas = document.getElementById("canvas");
                canvas_ctx = canvas.getContext("2d");
                // Center 
                x = WIDTH / 2 ;
                y = HEIGHT/ 2 ;
                startPlay();
            }

There are some utility functions for drawing shapes in the html canvas. First we have the circle method which demonstrates how to draw a circle given x, y and radius values.

Discussing the values for drawing on the canvas are beyond the scope of this article but I strongly suggest you get a copy of Foundation HTML5 Animation with JavaScript.

The values for the canvas 2d context arc method are centerX, centerY, radius, startingAngle in radians, endingAngle in radians and the boolean antiClockwise.

The startingAngle and endingAngle have radian values to draw a complete circle.

Note you will need to compensate for x and y because the values for x and y reference the center. You will see this offset in the boundary testing code.

            /* Draw circle */   
            function circle( x, y, r ) 
            {
                canvas_ctx.beginPath();
                canvas_ctx.arc(x, y, r, 0, Math.PI*2, true);
                canvas_ctx.fill();
            }

Next is the rect function on line 106 to draw a rectangle. The canvas 2d context rect method is straightforward drawing from top left corner of the rectangle.

            /* Draw rectangle */
            function rect( x, y, w, h ) 
            {
                canvas_ctx.beginPath();
                canvas_ctx.rect(x,y,w,h);
                canvas_ctx.closePath();
                canvas_ctx.fill();
            }

In drawing for animation, you will need to clear the canvas, so this clear function allows that to happen. The one line is clearRect method of the canvas 2d context. The clearRect method clears a rectangular area and here we have the entire canvas covered.


            /* Clear canvas */
            function clear() 
            {
                canvas_ctx.clearRect(0, 0, WIDTH, HEIGHT);
            }

The drawing position computations and actual drawing are both done in the draw function. Some animation applications you may want to split the drawing from the computations, but in this case putting them together meets our needs.

The velocity in the x and y directions are computed on lines 122 and 123. Simply they are increased or decreased by the change in acceleration values from the accelerometer. Acceleration values contain direction as well as acceleration.

Then we recompute the circle sprite position on lines 125 and 126.

Lines 127 – 151 are computing the boundaries for the canvas in relation to the proposed position of the center point, the x and y values, of the circle sprite. If any boundary is reached, then the velocity for that direction is set to zero and the position value is computed to keep the circle sprite in view at the edge.

I left the debugging information lines in but commented. If you use them, then comment lines 71 – 75.

Final work is to draw and this starts on line 164 where the canvas is cleared of all drawings.

Then lines 166 and 167 get our canvas background drawn.

The circle sprite is drawn on lines 169 and 170.

            /* Compute drawing metrics and draw frame */    
            function draw() 
            {
                // Increase velocity by acceleration
                vx += ax;
                vy += ay;
                // Update circle drawing position.
                x += vx;
                y += vy;
                /* Boundaries testing */
                // Right boundary
                if ( x + RADIUS > WIDTH  )
                {
                    x = WIDTH - RADIUS ;
                    vx = 0;
                }
                // Left boundary
                if (x - RADIUS  <= 0)
                {
                    x = RADIUS   ;
                    vx = 0;
                }
                // Bottom boundary
                if (y +  RADIUS  > HEIGHT)
                {
                    y = HEIGHT - RADIUS ;
                    vy = 0;
                }
                // Top boundary
                if (y - RADIUS  <= 0)
                {
                    y = RADIUS  ;
                    vy = 0;
                }
                
                // Debugging info.
                //console_log.innerHTML = 
                //'x: ' + x + '<br />' +
                //'y: ' + y + '<br />' +
                //'vx: ' + vx + '<br />' +
                //'vy: ' + vy + '<br />' +
                //'ax: ' + ax + '<br />' +
                //'ay: ' + ay + '<br />' ;    
                
                /* Draw frame */
                // Clear canvas
                clear();
                // Draw canvas background
                canvas_ctx.fillStyle = CANVAS_COLOR;
                rect( 0, 0, WIDTH, HEIGHT );
                /* Draw circle */
                canvas_ctx.fillStyle = CIRCLE_COLOR;
                circle( x, y, RADIUS );
            }

Good luck!

[ad name=”Google Adsense”]

Categories
Articles

PhoneGap Cordova Camera XCode Example


This is a bare bones example of using Cordova or Phonegap to take a photo with your IPhone or IPad camera.

Learn cocos2D Game Development
PhoneGap Essentials: Building Cross-Platform Mobile Apps

The project uses XCode 4.3.2 and was tested using iOS 5.1 on a IPhone 4. There are no frills. I did make the button large enough for IPhone human guideline standards. I also added a simple console area to display debugging messages from the Javascript.

I also stripped out most of the extra Cordova comments that were not relevant.

Each time you take a picture another file is added. This example does not provide a way to delete the files. If you remove the app from the phone, the images are deleted. You may need to study the Cordova File API for how to manage your app’s local storage.

Here are the screens for the app.

The left screen is the app before any photo has been taken. It shows the integrated debugging console with the logged message that the Cordova onDeviceReady() method was called.

The middle screen is the IPhone Camera app with a photo just taken.

The right screen shows the app with a photo taken. You also see in the integrated console messages logged from the Javascript. The second line is the method called by touching the “Take Picture” button. The final message is the callback method from the Cordova getPicture camera method for a successful photo take. In particular you can see the file path and name of the photo.

Download XCode Project Files

Step 1 – Install Cordova for XCode

Instructions for setting up your development environment are located at the PhoneGap site Getting Started with iOS.

Step 2 – Create New Cordova-based Application

Step 3 – Set XCode Project Options


Set the XCode project options as follows:

  • Product name: Here I used CordovaCamera as the project name. You can use a name of your own choosing.
  • Company identifier: Provide your own reverse domain.
  • Use Automatic Reference Counting: Uncheck

Step 4 – Choose A File Location

Once you select a file location on your computer you will have a folder structure as follows:

And in XCode you will see a project window as follows:

You may notice the project has a warning. Ignore this for now.

Step 5 – Create and Add the www Folder and Files to the Project

These are the normal setup instructions for a Cordova alias PhoneGap XCode project. You can skip this step if you are used to creating Cordova XCode projects.

The process is creating a www folder by running the app once in the IPhone simulator and then add to the project explorer.

First run the app in the IPhone simulator.

The app runs but complains of the missing index.html file.

A www folder with this file and one other js file were created on your file system as this the app launched in the simulator. Here you see them and you need to drag the www folder into the Project Explorer. Do not drag to or copy to the XCode folders outside of XCode.

Fill out the “Choose options for adding these files” dialog as follows.

  • Destination: Unchecked
  • Folders: “Created folder references for added folders” selected.
  • Add to targets: CordovaCamera checked.

And this is the final results you see in the Project Explorer window.

Run the app in the IPhone simulator one more time.

This time you will be greeted with an Alert dialog with the “Cordova is working” message.

[ad name=”Google Adsense”]

Step 7 – Code the index.html File

I removed code comments and commented code that is included in the index.html file.

Here is the full index.html file completed for your copy convenience.

<!DOCTYPE html>
<html>
  <head>
  <title>Camera</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
	<meta charset="utf-8">
	<script type="text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
    <script type="text/javascript">
    // Debugging div on device
    var console_log;
	
	function onBodyLoad()
	{		
		document.addEventListener("deviceready", onDeviceReady, false);
	}
	
	/*  Cordova has been initialized and is ready to roll */
	function onDeviceReady()
	{
        console_log = document.getElementById('console_div');
        console_log.innerHTML += "onDeviceReady()<br/>";
	}
        
    /*  Open the device camera app */
    function capturePhoto() 
    {
        console_log.innerHTML += "capturePhoto()<br/>";
        // navigator.camera.getPicture( cameraSuccess, cameraError, [ cameraOptions ] );
        navigator.camera.getPicture(getPhoto, 
                                    onFail, 
                                    { 
                                    quality: 50,
                                    destinationType: Camera.DestinationType.FILE_URI, // // Return image file URI
                                    sourceType:Camera.PictureSourceType.CAMERA,
                                    targetWidth:120,  // Width in pixels to scale image. Aspect ratio is maintained. Required targetHeight.
                                    targetHeight:180  // Height in pixels to scale image. Aspect ratio is maintained. Required targetWidth.
                                    });
    }
   /* navigator.camera.getPicture success function */
    function getPhoto(imageData) 
    {
        var cameraImage = document.getElementById('cameraImage');
        cameraImage.src = imageData;
        console_log.innerHTML += "getPhoto() - cameraImage.src: " + cameraImage.src + "<br/>";
    }
    /* navigator.camera.getPicture fail function */
    function onFail(message) 
    {
        alert('Failed because: ' + message);
    }       
    </script>
  </head>
  <body onload="onBodyLoad()" style = "text-align:center;background-color:#ccc;padding:0px;">
      <div>
          <h1 style = "margin-bottom:0px;">Cordova Camera</h1>
          <button style = "font-size:20px;width:200px;height:44px;;margin-bottom:5px;" onclick="capturePhoto();">Take Picture</button> 
          <br>
          <img style="width:120px;height:180px;;background-color:#fff;" id="cameraImage" src="" />
          <div id="console_div" style = "text-align:left;border:1px solid black;background-color:#fff;height:150px;overflow:auto;"></div>
      </div>
  </body>
</html>

First look at the html starting on line 53. To simplify, the css is placed inline with tags versus the better approach of using an external css file.

  <body onload="onBodyLoad()" style = "text-align:center;background-color:#ccc;padding:0px;">
      <div>
          <h1 style = "margin-bottom:0px;">Cordova Camera</h1>
          <button style = "font-size:20px;width:200px;height:44px;;margin-bottom:5px;" onclick="capturePhoto();">Take Picture</button> 
          <br>
          <img style="width:120px;height:180px;;background-color:#fff;" id="cameraImage" src="" />
          <div id="console_div" style = "text-align:left;border:1px solid black;background-color:#fff;height:150px;overflow:auto;"></div>
      </div>
  </body>

Line 54 is the container div.

Line 55 provides a simple page heading.

Line 56 is the “Take Picture” button. It calls the capturePhoto() method when touched. Notice that you use the onClick handler, Cordova does the translation to touch up event for you.

Line 57 is the img tag for the photo once we take a picture.

Line 58 is a debugging div for output from your javascript. There are other debugging console solutions that may better serve your needs.

Back to the top of index.html, line 10 declares the console_log variable. It will globally reference the console_div div tag in the html. You can then see on lines 20 and 21 getting the reference from the DOM for console_div div tag.

<!DOCTYPE html>
<html>
  <head>
  <title>Camera</title>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no;" />
	<meta charset="utf-8">
	<script type="text/javascript" charset="utf-8" src="cordova-1.5.0.js"></script>
    <script type="text/javascript">
    // Debugging div on device
    var console_log;
	
	function onBodyLoad()
	{		
		document.addEventListener("deviceready", onDeviceReady, false);
	}
	
	/*  Cordova has been initialized and is ready to roll */
	function onDeviceReady()
	{
        console_log = document.getElementById('console_div');
        console_log.innerHTML += "onDeviceReady()<br/>";
	}

The onBodyLoad function on line 12 is called from the DOM body tag onload event. It then provides a listener to the deviceready event from Cordova.

The deviceready event calls the onDeviceReady function to get the ball rolling.

This boot strapping is all standard for Cordova apps as it has since it was called PhoneGap.

Moving down to line 24 the capturePhoto function was added and is called by the “Take Picture” button in the html.

The function logs to our makeshift console on line 27.

The key item is the Cordova Camera API being used on line 29.

You see the Cordova API is attached to the DOM navigator object.

In this case we are using the camera.getPicture method.

The first argument is the function to call for a successful use of the device camera and the second is for failed usage of the device camera.

    /*  Open the device camera app */
    function capturePhoto() 
    {
        console_log.innerHTML += "capturePhoto()<br/>";
        // navigator.camera.getPicture( cameraSuccess, cameraError, [ cameraOptions ] );
        navigator.camera.getPicture(getPhoto, 
                                    onFail, 
                                    { 
                                    quality: 50,
                                    destinationType: Camera.DestinationType.FILE_URI, // // Return image file URI
                                    sourceType:Camera.PictureSourceType.CAMERA,
                                    targetWidth:120,  // Width in pixels to scale image. Aspect ratio is maintained. Required targetHeight.
                                    targetHeight:180  // Height in pixels to scale image. Aspect ratio is maintained. Required targetWidth.
                                    });
    }

The details are in the third argument which is an array of properties called cameraOptions.

For the quality option on line 32 I choose the value of 50. This is a blatant copy from the documentation. But also I read that values over 50 may become a factor in memory issues. Something for you to research if you need a quality value over 50.

On line 33 the destinationType option takes values defined by Camera.DestinationType.

For the destinationType option I choose Camera.DestinationType.FILE_URI. The Camera.DestinationType.FILE_URI sends a file url of the picture taken to the success function. The only other choice for the destinationType property as of this writing is Camera.DestinationType.DATA_URL which returns an image as base64 encoded string to the success function.

Line 34 sets the sourceType option. It uses values from Camera.PictureSourceType. The official documentation leaves the sourceType option values up to your interpretation based on naming. The sourceType option I used is Camera.PictureSourceType.CAMERA since my plan is to use the device camera. The other two values are Camera.PictureSourceType.PHOTOLIBRARY and Camera.PictureSourceType.SAVEDPHOTOALBUM.

Finally on lines 35 and 36 I included the targetWidth and targetHeight options.

I found if you omit the targetWidth and targetHeight options you may have aspect ratio issues with displaying correct scaling. I discovered this when taking a picture using portrait and using landscape orientations in the camera app. The landscape orientation would have a distorted aspect ratio.

The html I set the img tag height and width to 120px and 180px respectively. As you see I used the same values for targetWidth and targetHeight respectively and that resolved my scaling issue. You see I copied the API documentation comments for these two options and added my own interpretation.

The last part of the javascript contains the navigator.camera.getPicture method success and fail functions named getPhoto and onFail respectively.

   /* navigator.camera.getPicture success function */
    function getPhoto(imageData) 
    {
        var cameraImage = document.getElementById('cameraImage');
        cameraImage.src = imageData;
        console_log.innerHTML += "getPhoto() - cameraImage.src: " + cameraImage.src + "<br/>";
    }
    /* navigator.camera.getPicture fail function */
    function onFail(message) 
    {
        alert('Failed because: ' + message);
    }       

For getPhoto the image url is passed in the imageData argument in line 40. All is needed is to assign the url to img tag src property. In the html the img tag has the id of cameraImage and on line 42 we get a reference from the DOM and on line 43 the url assignment to the img tag is complete.

Line 44 displays the url in our makeshift console window for observation.

The navigator.camera.getPicture fail callback is the onFail function on line 47. Line 49 displays feedback using the argument which has a message. I have not yet hit a problem and do not know how to simulate one.

Step 8 – Run the App

When you run the app, you need to use your IPhone or IPad. The camera cannot be simulated. Here is the first screen. At the beginning of the blog are all the screens for your reference.

Good luck!

[ad name=”Google Adsense”]

Categories
Articles

Tap, Move, Shake: Turning Your Game Ideas into iPhone & iPad Apps


Todd Moore’s new book, Tap, Move, Shake: Turning Your Game Ideas into iPhone & iPad Apps, is currently the best choice for getting started with writing and publishing IOS games.

A key feature is that the apps in the book are IN THE APP STORE. Never saw that trick before and I think is a standard for anyone writing IOS books. I actually followed the steps of creating an app I could download suffice it had a tad more flash in the store.

Tap, Move, Shake: Turning Your Game Ideas into iPhone & iPad Apps
Tap, Move, Shake: Turning Your Game Ideas into iPhone & iPad Apps

This is the first book I have seen in XCode/ObjectiveC that takes the submission to the App store as important a learning step as is the process of writing code. I loved the give and take App store rejection emails that provided a lot of insight for anyone who is going to submit an app the first time.

Also insightful is the special effort in giving the coder who is light on media creation great chapters on creating graphics and sound. Links provided are well researched.

On the wish list for me was not to dismiss ARC (Automatic Reference Counting) at the onset. Gee the book kinda assumes the beginner and ARC leans to that. But I suspect a case of examples completed at a time Apple was busy upsetting the cart for writers in progress.

The chapters are extremely well thought out especially the progression of development with the sections in each chapter.

I think you need a basic skill in XCode and Objective C to follow the book. XCode is introduced nicely for beginners and Objective C is learned more by example and less by explanation. I could not have solved some bugs without a basic skill in Objective C. The architecture of IOS app is explained well. I loved the clarity of explaining the bootstrap of an IOS app. I finally got it!

The book covers EVERYTHING you need from setting up for development, coding, basic testing, resource creation, app submission and even app marketing. All at a very clear to the point approach. The book examples make you feel you are starting at the beginning because they are from the gaming industry beginnings tuned to the phone.

This is 254 page book that has a good number of images taking up pages. Compared to 3 and 4 inch opus magnum IOS books out there that are OMGs difficult to hold open on the desk or in a lounge chair, Todd gets a great deal done as a focused writer. Cutting content is key. I rather buy more books than have big monsters.

Want to get started in IOS gaming without a gaming engine or better understand your gaming engine, then do this book.

[ad name=”Google Adsense”]