Tuesday, January 31, 2012

A simple Echo Machine to demonstrate basic $.ajax on Jquery Mobile page

This example uses the Jquery and Jquery Mobile application with HTML5 to send an asynchronous request to php on the server. 

<!DOCTYPE HTML>
<html>
<head>
    <title>Echo Machine</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.css" />
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.6.4.min.js"></script>
    <script type="text/javascript" src="http://code.jquery.com/mobile/1.0/jquery.mobile-1.0.min.js"></script>
</head>

<body>

<div data-role="page" id="easyloader" data-theme="e" data-add-back-btn="true">
    <div data-role="header">
        <h1>Ajax Echo Machine</h1>
    </div>
   
    <div data-role="content">   
        <p> Test Jquery $.get function.  Send an asynchronous request to the server and show results. </p>
        <label for="echo_term"> Enter a phrase (and then press tab or click outside of box): </label>
        <input type="text" name="echo_term" id="echo_term" onchange="makeEcho()"/>
    </div>
   
    <div id="echo_results" style="margin-left:50px" > Echo here. </div>

</div>

<script type="text/javascript">

    function makeEcho() {
        var echo_val=$("#echo_term").val();
        $.get("scripts.php", {echo_term : echo_val}, function(data){
            if (data.length>0){
                $("#echo_results").html("<p> " + data + " </p>");
               
            } else $("#echo_results").html('<p>no data returned! </p>');
        });
    }
</script>
</body>
</html>
 uses the php file called scripts.php

<?php
//a simple echo machine the returns what you sent it.
$term = strip_tags(substr($_GET['echo_term'],0, 100));
$term = mysql_escape_string($term);
$term = "Response from php: " . $term;
echo $term;
?>

Wednesday, August 3, 2011

Update web pages using the Asynchronous HTTP Request with jQuery in CodeIgniter

1. Create a web page that has in input box for the guest to enter their email address.

2. The page should load the javascript file, ready.js (which initializes jQuery.
When the onchange event is fired, it will send the contents of the input box to the ajaxCheck function.

3. This function will use the ajax $.post function provided through jQuery to send the str value to the server using asynchronious http calls. (note: you can write this yourself or use jQuery). Notice how the controller registration is called and routed to the checkID function. The checkID function queries the database and then returns a value that indicates what was found. These values are:
Current (the email is registered), Invalid (the email is not valid email account), True (user is not registered but in our contact list), False (user is not registered).

4. Finally it puts these values into a json array structure and sends it back to the javascript function that called it.

echo json_encode(array("returnValue"=>$value,"idValue"=>$id));

5. The callback function will have access to these two variables: data.returnValue and data.idValue;

With this information it can update the web page as appropriate. Use jQuery functions to rewrite elements and the page will not need to do a complete refresh.

The view file:

<html>
<head>
<script src="<?=base_url()?>/_lib/js/ready.js" type="text/javascript"></script>
</head>

<script >
function ajaxCheck(str) {
    //called by email input box
   
    $.post(
        "registration/checkID",
        { sendValue: str },

        function(data) {
            //this is the callback function that will receive the results from the php function on the server
            //alert(data.returnValue);
            $success = data.returnValue;
            $newid = data.idValue;
            switch ($success ) {
                case 'current':
                    //user is registered for the year
                    $('#displayMsg').text('You are already registered for 2011. Sign in to edit');
                    var href = jQuery(location).attr('href');
                    $('#signinbox').show();
                   
                    $('input[name="email"]').val(str);
                    //var tmpString = '<p><a href="'+href +'/unregister/'+$newid +'"> Unregister </a> for the workshop.</p>';
                    //$('#unregisterbox').html(tmpString);
                    //$('#unregisterbox').show();
                    break;
                case 'true':
                    //user is not registered yet, but they are in the rolls
                    $('#displayMsg').text('');
                    $('#oldUser').show();
                    $('#regForm2').show();
                    $('#regForm3').show();
                    break;
                case 'false':
                    //user is not registered and is new
                    $('#displayMsg').text(' Registration requires your name, zip code and organization');
                    $('#regForm2').show();
                    $('#regForm3').show();
                    break;
                case 'invalid':
                    $('#displayMsg').text(' oops...not a valid form of email');
                    break;
                default:
                    $('#displayMsg').text(' please register');
                    $('#regForm2').show();
                    $('#regForm3').show();
            }
        },
        "json"
    );
}

</script>
<body>

   
                    <label for="in_email" style="display:inline">Email </label>
                    <input type="text" id="in_email" name="in_email" onchange="ajaxCheck(this.value)" size=40 value="" />
               
                   
                   

</body>
</html>





And finally, some php for the controller and the server:

function checkID() {
        //this is called by the browser using ajax techniques via jQuery
        //IF USER IS REGISTERED FOR 2011, DROP OUT.
        //FIND USER IN DATABASE
        // returns true if it finds the email
        //returns false if it doesn't find the email
        //returns invalid if the email is bad
        //returns current if the email is registered for 2011
        $id = '';
        $email = strtolower($_POST['sendValue']);
        $this->load->helper('email');
        $this->load->database();

        if ($email != '' && valid_email($email) ) {       
            //CHECK if email is in use
            $this->db->where('active', '1');
            $this->db->where('email', $email);
            $attendees = $this->db->get('attendees');
            $attendee = $attendees->result();
            $count = count($attendee);

            if ( $count >= 1 ) {
                $value = 'true';
                //it is in use, see if //check if registered
                $id = $attendee[0]->id;
                $this->db->where('attendeeId', $id);
                $regged = $this->db->get('registrations');
                $reg = $regged->result();
                if ( count($reg ) >=1 ) {
                    //it is registered, see what year

                    $year = $reg[0]->workshopYear;
                    if ( $year == '2011') {
                        //crash and burn.  don't let them reregister. without signing in first
                        $value = 'current';
                    }
                }
            }  else  { $value = 'false'; }
        } else { $value = 'invalid'; }
        echo json_encode(array("returnValue"=>$value,"idValue"=>$id));
    }

Wednesday, October 14, 2009

Encoded Polylines for Static Google Maps


The Google Static Map has a size limitation of 2400 characters which is because of IE's url size limit. Since the static map is created by sending the polyline endpoints through the URL, it becomes easy to reach that limit with a curvey path on the map...for example...gpx paths for hiking paths. Using the ecoded polyline feature, which calculates the path segments by their relative changes, and converting to a more efficient binary representation, the data set can be greatly compressed in the URL.


The Urban Sensing Wiki at UCLA about "How-to use Google Maps polyline encoding to compact data size" and gives the following Python functions to use.




# Encode a signed number into the google maps polyline encode format.
# from: http://wiki.urban.cens.ucla.edu
def encodeSignedNumber(num):
sgn_num = num << sgn_num =" ~(sgn_num)" encodestring = "">= 0x20:
encodeString += chr((0x20 | (num & 0x1f)) + 63)
num >>= 5
encodeString += chr(num + 63)
return encodeString

# Create the encoded polyline and level strings.
# @points: [{'lat':100, 'lng':50}, {'lat':102, 'lng':49.8}]
# This function takes a list of dictionaries (each dictionary contains a
# single 'lat','lng' pair) and returns a dictionary:
# {'encoded_points': string, 'encoded_levels': string}.
# from: http://wiki.urban.cens.ucla.edu
def encodePolyPts(points):
i = 0
plat = 0
plng = 0
encoded_points = ""
encoded_levels = ""
for i in range(0, len(points)):
point = points[i]
lat = point['lat']
lng = point['lng']
level = 3 - i%4
late5 = int(math.floor(lat * 1e5))
lnge5 = int(math.floor(lng * 1e5))
dlat = late5 - plat
dlng = lnge5 - plng
plat = late5
plng = lnge5

encoded_points += encodeSignedNumber(dlat) + encodeSignedNumber(dlng)
encoded_levels += encodeNumber(level)

return {'encoded_points': encoded_points, 'encoded_levels': encoded_levels}


To use this encoding capability to draw a static Google map, the (points) list needs to be built. The variable is
expected to be a list of dictionaries. The documentation shows us it is expecting the format to be as follows:


@points: [{'lat':100, 'lng':50}, {'lat':102, 'lng':49.8}]


The following function retrieves the datastore item and parses the lat and lng values
to create a single dictionary which is then appended to the list.


Note: in this example the "newStr" variable is a JSON string from the datastore which is loaded into a python object using the
simplejson utility. It can then be iterated to find all the stored lat and lng points.



def mapShow(request, route_key):
"""creates a static google map of the entire route """
"""written by Thea Ganoe, October 2009"""
#http://maps.google.com/maps/api/staticmap?size=400x400&path=weight:3|color:orange|enc:polyline_data
user = users.get_current_user()
route = models.Route.get(route_key)
pathList = []
newStr = route.way
#'{"description":"'+ route.routeName +'", "img":"'+route.img+'", "getPic": "' + route.img +'", "zoom":"'+ zoomstring +'" , "longitude":"'+ route.longitude +'", "latitude":"'+ route.latitude +'", "position": "0", "heading": "" }'
ways_obj = simplejson.loads(newStr)
sizeofWay = len(ways_obj['way'])
for way in ways_obj['way']:
pathDic = { 'lat': float(way['latitude']), 'lng': float( way['longitude']) }
pathList.append(pathDic)
# points: [{'lat':100, 'lng':50}, {'lat':102, 'lng':49.8}]
polyPts = encodePolyPts(pathList)
return respond(request, user, 'staticMap', {'enc':polyPts } )


Since the Django template system is used by this Google App Engine application, the return value will merge the staticMap.html template with the server response. To use the enc value, it is necessary to specify which list is needed, enc.encoded_points or enc.encoded_levels. This template looks like this:


<image src="http://maps.google.com/maps/api/staticmap?size=512x512&path=weight:2|color:red|enc:{{ enc.encoded_points }}&sensor=false&key=myveryownbiglongkeyfromGoogle"/>



To create a static map, the levels data is not needed. If a live Google Map was being displayed, the levels values would be used by it, because the enc list contains dictionaries of . The above function, Showmap() will return the data needed for either type of map. This is because the return value enc actually contains list of points and a list of levels.

'encoded_points': encoded_points, 'encoded_levels': encoded_levels

Tuesday, May 26, 2009

Dynamic HTML SELECT forms for AppEngine

AppEngine
dynamically created HTML select forms for Django python templates

I didn't see this particular solution anywhere so I'll post it here, since it something that may come up for anyone trying to do a XMLHttpRequest() in Google AppEngine. Normally I use the Django forms in App Engine but I wanted to quickly add some query filters without having to create a model for them.
This could happen when the web app needs to interface with the user. In this example, I simply wanted to filter the dataset by the location. So I have one select box with all the states listed. I do have a Django model but it is used for creating new records, not filtering lists.

First step, put the forms in the html template (this uses built-in Django template tags ):


<form method="post" id="frmSearchBox">

<table>
<tr><td>
<SELECT name="states" id="states" onChange="selectState(this)">
{% for stt in states %}
<option name="op" value="{{ stt }}" >

{% endfor %}
</SELECT>
</td>
<table>
<input type="button" name="btnSearch" value="Submit" onclick="javascript:searchStates();">
<form>


Notice this form is a post. Because I want to add AJAX to only update a portion of the page.

function searchRoutes() {
//the submit button on the searchbox form triggers this function
//this will do an ajax post to the server and filter the routes according to the user selection

var states = document.getElementById('states')[document.getElementById('states').selectedIndex].innerHTML;

var body ="states=" + states ;
var req = createXMLHttpRequest();
req.open('POST', '/rpcRouteSearch/', true);
req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
req.setRequestHeader("Content-length", body.length);
req.setRequestHeader("Connection", "close");
req.onreadystatechange = onAddSuccess;
req.send(body);

function onAddSuccess(response) {
//alert("in searchRoutes callback: " + req.readyState);
//replaces the #_nodes with the slideshow
if (req.readyState != 4) {return; }
var serverResponse = req.responseText;
var mg = document.getElementById("leftImages");
mg.innerHTML = serverResponse;

}
}



================
Now a peek at the python in views.py, our callback is waiting for the line that starts with request.method == 'POST':


def intro(request):
user = users.get_current_user()
statelist = ['All']
if request.method == 'GET':
routes = db.GqlQuery("SELECT * FROM Route" )
numroutes = routes.count()
"""Next sections prepare data for the search box forms """
for route in routes:
statelist.append(route.state)
"""this next trick removes duplicate values! """
statelist = list(set(statelist))
return respond(request, user, 'intro', {'routes' : routes, 'numroutes' : numroutes, 'states' : statelist })
if request.method == 'POST':
"""AJAX call, use post when filtering with the search box form """
post= request.POST.copy()
statefilter = post['states']
statefilter = statefilter.strip()
# The Query interface prepares a query using instance methods.
q = db.Query(models.Route)
"""q = models.Route.all()"""
q.filter('state = ',statefilter)
# The query is not executed until results are accessed.
routes = q.fetch(25)
numroutes = q.count()
statelist = ['All']
return respond(request, user, 'introRoutes', {'routes' : routes, 'numroutes' : numroutes, 'states' : statelist})


====

The lines that were nonstandard, are these

statefilter = post['states']
statefilter = statefilter.strip()

It was necessary to use the string function strip to get the results to work properly in the filter function. All it does is remove whitespace from the ends of the string (not inside) and now the variable is ready to be used by the filter.

======

The python code is going to use a template called "introRoutes.html" to build it's serverResponse that gets sent to the waiting Callback function in the javascript.

Since we are working with an HTML form select box, it would be nice to set the SELECTED value, to make sure the form is reset for the user, moreso important when there are many of these select options...otherwise the user has to reset them all manually. We want to set the form back to 'All' after every use, the 'All' setting is added at the beginning of the Python function,

statelist = ['All']

and then the other states available are added to it. To make sure ALL is always SELECTED, the attribute must be written to the form, in this case we are in an asychronous call to the server, but it works the same way.

Here is an extra $2 trick..use Django’s built-in template tags and filters! Anything in the {% isPython %} commands and the bracket tags {{ areVariables }}. These are more fully described at http://docs.djangoproject.com/en/dev/ref/templates/builtins/#ref-templates-builtins

Example of dynamically created HTML select forms for Django python templates.


<em>
<select name="states" id="states" style="width: 6em;"> <option name="op" value="{{ stt }}" ifequal="" stt="" all="" selected="selected" endifequal="">{{ stt }} {% endfor %} </option></select>
</table></form></em>

Sunday, February 15, 2009

Configuring Ubuntu, Eclipse w/phpEclipse plugin, and LAMPP running on a web server

Developing with Eclipse. I've always been a notepad IDE person (obviously hence the blog name but....real work has required me to use Eclipse and CVS).

This covers Ubuntu/8.04 (hardy), Eclipse Ganymede with phpEclipse plugin, and LAMPP. LAMPP is a quick way to get an Apache2 web server up and running for development purposes, with mySQL and PHP automatically set up. But the ultimate solution and purpose of this post, getting Eclipse to output files that run on a web server, will work on any web server. In addition configure phpEclipse and Eclipse workspace folders which requires a few additional steps which are detailed here.

If you don't have LAMPP, you can get xampp-linux-1.7.tar.gz from:
Linux http://www.apachefriends.org/en/xampp-linux.html

Note: If apache2 is installed somewhere else on the system, LAMPP can coexist, but the other Apache daemons will have to be stopped before LAMPP will start (system will check and warn).

Eclipse Ganymede was downloaded and installed from the Eclipse.org site since an older Eclipse was available from the Syntaptic Package Manager and I wanted Ganymede, but really you can use whichever you want, it will not affect these instructions.

Once LAMPP is running and localhost is showing the XAMPP page and Eclipse is configured and loaded with phpEclipse and all the web tools, the Eclipse workspace will normally be placed in your home folder. However, if you're writing PHP files with Eclipse (you installed phpEclipse, afterall) obviously both the web server and Eclipse needs to know where to find the folders. The secret is to put those Eclipse php folders on the webserver. Then put symlinks in the Eclipse workspace that point to the those folders.

Demonstration:

1. make a folder called 'projectX', or whatever you want to call it, in htdocs (the default server document root which can be found at /opt/lampp ) and give it permissions so it is owned by the user:

chown $user /opt/lampp/htdocs/projectX

(in all these examples, $user should be replaced by your Ubuntu username in the commands)

2. make a symbolic link to this to file, and put it (the symlink) in the workspace to be used by Eclipse to hold projects. When Eclipse saves to the folder, the files will actually be written to the folder that was created in the web server's document root (in htdocs).

The rule for creating a symbolic link:

ln -s /path/to/real/file /path/to/non-existant/file

modified for this demo:

ln -s /opt/lampp/htdocs/projectX /home/$user/workspace

Which will result in a folder with an arrow on it in the users eclipse workspace!! It will have zero items in it until files are download from Eclipse CVS or the phpEclipse IDE is used to create or edit files. Most importantly, the files can be browsed and read by the development web server.

Friday, January 16, 2009

Python Preview

In order to do much with Google App Engine, Python server programming awaits with its strange mix of being an old programmer's program and being of the future with a direction that will carry us away from everything as we've known it. Python is different from programs like PHP or Perl or ASP because it isn't a scripting language. It's a real meat and potatoes programming language that can go toe to toe (maybe, almost) with the likes of C++. To work with Python, server side developers will spend the most time figuring out how to put data in a list or a dictionary or some combination of the two. The Python book is 5 inches thick but I spend most of my time around page 20, preview about lists and dictionaries because to get my data processed, this was the only way. (My data being strings of json-like recordsets)

And there is a good reason for that. It is not necessary to name fields or columns or components. Python doesn't care. Just stuff the data into a lists, put the list in a dictionary and follow a few rules to iterate through it. It's not even necessary to declare the data types. Python does that invisibly at run time based on what it finds in the containers.

It works beautifully with Google's Big Table. Formal database records with their keys and nomalized relations are out the window. Goodbye to recordsets and table joins. Who needs a table schema? The process of defining the list and dictionary in Python creates dynamic objects. Which are all any reasonable server needs to manipulate the data before sending it off to any of its possible destinations.

Sunday, December 21, 2008

Reading Visual Basic Binary encoding for screen coordinates

Reading Visual Basic Binary encoding for screen positions, X and y.



This is a slight departure from Ajax-related topics, but since I've been heavily involved with transforming older binary files to modern XML, there was one challenge that took some unexpected calculating that I want to document here.



The older binary files were created by Visual Basic 6, apparently it writes the screen coordinate positions in big endian formats.



In my binary files, I knew these eight bytes contain the screen position and size of a block. Another way to describe this would be a the top left corner and lower right corner, the coordinates (x1, y1) and (x2, y2).



Here are some bytes that describe 4 boxes in a row, with varying widths, but all with the same height,effectively making a table.



4 rows of 8 bytes in hex code format:



00 8C 00 08 01 31 00 69

00 8C 00 69 01 31 00 C5

00 8C 01 C4 01 31 02 7F

00 8C 02 7F 01 31 03 16







In each row, the first two bytes are x1. Next two are y1. Then x2, and last is y2. Little endian doesn't work, the numbers get too big. Using big endian:



ypos1, xpos1, ypos2, xpos2



140, 8, 305, 105

140, 105, 305, 453

140, 452, 305, 639

140, 639, 305, 790





Since we know the y values are the same, column 1 would be y1 and column 3 would be y2.

This would leave column 2 as x1 and column 4 as x2. This works since each row starts where the previous row left off. Note that to find the width, just subtract xpos1 from xpos2 and to find the height, subtract ypos1 from ypos2.



The only reason this was any problem, is that the Intel machines are little endian, the built-in readUint16() gave results that were way too high to be screen positions. Therefore, an extra processing function was needed to make the calculation.



Here is an example from the internet (devx.com) showing a conversion between little endian and big endian.





int SHORT_little_endian_TO_big_endian(int i)

{

return ((i>>8)&0xff)+((i << 8)&0xff00);

}






Here was my own coded solution that reads one byte at a time while parsing through the binary file:



highb = inf.ReadByte()

lowb = inf.ReadByte()

highb = (highb * &H100&)

ypos1 = highb + lowb