Articles tagged 'javascript'

Blog Projects

HTML5 Drag and Drop Multiple File Upload

Previously I experimented with drag and drop file upload with Google Gears. Recently FireFox 3.6 (codenamed Namoroka) was the first to implement File API. It enables JavaScript to interact with local files.

Correction: Ionut G. Stan pointed out that File API was actually available already in FireFox 3.0. What Namoroka introduced is the drag and drop interface for the files. Sorry for the confusion.

Here is how you can implement drag and drop multiple file upload with native JavaScript. No plugins needed. Just plain old new HTML5. Again there is a working demo. You will need FireFox 3.6 to test it. Full source code can be at GitHub.

PHP Upload Script

PHP script for receiving the files is the same as last time. It accesses uploaded files using $_FILES superglobal. For this demo we assume users upload only image files.

<?php

$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

$upload_dir  = './tmp/';
$num_files = count($_FILES['user_file']['name']);

for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . basename($_FILES['user_file']['name'][$i]);

    if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['user_file']['name'][$i])) {
        print "I asked for an image
  

Continue reading >>


Drag and Drop File Upload With Google Gears

Google Gears is an extension which adds new features to you browser. It lets your browser to interact with the desktop. You can store data locally to an SQLite based database. Browser will also have a worker pool for running JavaScript code on the background.

Update 20091007: I updated the tutorial and demo to support dragging and dropping multiple files for uploading simultaneously.

Below is how you can do basic drag and drop file upload. Gears, PHP and jQuery are needed. If you want to try there is a working demo. All source code for the demo can be found from GitHub.

PHP for Receiving the Files

PHP has easy way of accessing uploaded files using $_FILES array. For demo’s sake I am assuming we should upload only image files. Nothing special here. Just you plain old upload script.

<?php

$error_message[0] = "Unknown problem with upload.";
$error_message[1] = "Uploaded file too large (load_max_filesize).";
$error_message[2] = "Uploaded file too large (MAX_FILE_SIZE).";
$error_message[3] = "File was only partially uploaded.";
$error_message[4] = "Choose a file to upload.";

$upload_dir  = './tmp/';
$num_files = count($_FILES['user_file']['name']);

for ($i=0; $i < $num_files; $i++) {
    $upload_file = $upload_dir . basename($_FILES['user_file']['name'][$i]);

    if (!preg_match("/(gif|jpg|jpeg|png)$/",$_FILES['user_file']['name'][$i])) {
        print "I asked for an image
  

Continue reading >>


Infowindows With Google Static Maps

Note! Code shown is now included in Simple Static Maps PHP class. This blog entry is still good for understanding how it was done. Demo code can be considered deprecated.

Previously I showed you how to make a Google Static Map with clickable markers. Several people emailed me to ask how to show infowindow (or infobubble) when marker is clicked. Technique I explain below still needs JavaScript. It is used to open the infowindow. I use jQuery library in the examples.

Image above is just a screenshot. There is a separate page for working demo. Full source code is also available.

Infowindow HTML

First thing we need is HTML code for the infowindow. I wanted it look exactly the same as in Google Maps. I opened a random map and copied the HTML using FireBug inspect console.

I removed all unneseccary code and moved CSS to separate file. Resulting HTML is quite simple. Now we have an empty infowindow ready for use.

Infowindow Content

In this example we have three green markers. Green markers should open infowindow when clicked. We could write full HTML code for three infowindows. In our case it is unneseccary. We will have separate content div but share rest of infowindow code between markers. This way there will be less HTML code on the page.

<div id="bubble">
  
  

Continue reading >>


WYSIWYG Inline Edit with Jeditable

Latest custom input for Jeditable uses jWYSIWYG editor by Juan M Martinez. This code has been almost ready for a while. This week I finally had time to fix some outstanding FireFox bugs.

If you are interested in writing your own Jeditable custom inputs...

Continue reading >>


PUT Support for Jeditable

Until now Jeditable only supported submitting edited data using POST method. This was against REST principles. POST should be used for creating new objects. PUT should be used for updating existing objects.

Most browsers do not natively support PUT

Continue reading >>


Jeditable and TinyMCE

I have been working with Sam Curren lately improving Jeditable custom input API. At the same time Sam has been working with his TinyMCE custom input.

Inline editing with TinyMCE has been requested several times in mailinglists and forums. After MarkItUp! and WYSIWYG (beta) it is now third content editor plugin for Jeditable.

TinyMCE input needs Jeditable 1.6.0 to work. Download it as source, minified or packed.

Changes Since 1.5.0

You can now call function when onblur is triggered.

$(".editable").editable("http://www.example.com/save.php", { 
    onblur : function(value, settings) {
        console.log(this);
        console.log(value);
        console.log(settings);
    }
});

You can now use any custom event for triggering Jeditable.

$(".editable").editable("http://www.example.com/save.php", { 
    event : "make_editable"
});

Submit button now defaults to <button type=“submit”> instad of <input type=“submit”>. Cancel button now defaults to <button type=“cancel”> instead of <input type=“button”>. This allows you style buttons with something
like:

.editable button[type=submit] {
  
  

Continue reading >>


Clickable Markers With Google Static Maps

HEADS UP! This article was written in 2008. Information provided might not be valid anymore and should be taken with a grain of salt.
NOTE! Code shown is now included in Simple Static Maps PHP class. This blog entry is still good for understanding how it was done. Demo code can be considered deprecated.

Static map is one big image. Markers are embedded inside the image. You can not use traditional <a href="#"> tags around separate markers. Binding onclick event to separate marker images wont work either. There are no separate markers. Just one large image.

With imagemaps you can specify arbitary areas inside an image which links to given url. Area can be circle, rectangle or polygon. Simple imagemap could look like following:

<map name="marker_map">
  <area shape="circle" coords="75,103,12" href="#">
  <area shape="circle" coords="122,105,12" href="#">
</map>

With imagemaps we can create clickable markers for Google Static Maps. We need to position an imagemap area over each marker. Problem is how to calculate x and y pixel coordinates for each marker.

For this tutorial lets start with with static map you see above. It is created with following PHP code.

require_once 'Net/URL.php';
require_once 'Google/Maps.php';

$map_width  = 512;
$map_height = 300;
$map_size   = $map_width . 'x' . $map_height;
$zoom       = 13;
$markers    = '58.378700,26.731110,green|58.368488,26.768908,green|
               58.379646,26.764090,green';

$api_key = trim(file_get_contents('api_key.txt'));

/* Build the URL's for Static Maps API. */
$url = new Net_URL('http://maps.google.com/staticmap');
$url->addQueryString('center',  $center);
$url->addQueryString('zoom',    $zoom);
$url->addQueryString('markers', $markers);
$url->addQueryString('size',    $map_size);
$url->addQueryString('key',     $api_key);
$demo_map = $url->getUrl();

Calculating Pixel Coordinates for Markers

For imagemap we need to know where markers are located in static map image. To calculate pixel location of marker we need to know the following things:

1. Latitude and longitude of the center of the map.
2. Current zoom level.
3. Size of the static map in pixels.

Static Maps API can automatically calculate needed zoom level and center position of the map. However there is no way of receiving this information back. Instead we have to calculate center latitude and longitude ourselves.

Calculating Map Center

There are several ways of calculating geographic center of two or more points on earth surface. First is to calculate geographic midpoint in cartesian coordinates. Second is to find center of distance. Center of distance is point which has smallest possible distance from all given points. Both these methods give accurate results but require pretty complex calculations.

Easiest way to find map center is to use average latitude and longitude of all given markers. Result is close enough approximation for our needs.

/* Calculate average lat and lon of markers. */
$marker_array = explode('|', $markers);
foreach ($marker_array as $marker) {
    list($lat, $lon, $col) = explode(',', $marker);
    $lat_sum += $lat;
    $lon_sum += $lon;
}
$lat_avg = $lat_sum / count($marker_array);
$lon_avg = $lon_sum / count($marker_array);

/* Set map to calculated center. */
$url->addQueryString('center',  $center);

We also have to convert center latitude and longitude to pixel coordinates in world map. For this we use previously created Google_Maps PHP class.

/* Calculate center as pixel coordinates in world map. */
$center_x = Google_Maps::LonToX($lon_avg);
$center_y = Google_Maps::LatToY($lat_avg);

While were at it lets also calculate center as pixel coordinates in map image. This is really easy. We only need to divide image width and height by two.

/* Calculate center as pixel coordinates in image. */
$center_offset_x = round(512 / 2);
$center_offset_y = round(300 / 2);

Now we know three things about demo map center. Latitude and longitude coordinates are 58.3756113333,26.7547026667. This equals to 308334961,160637460 in world map pixel coordinates. Which in turn equals 256,150 in image pixels coordinates.

To prove our point let’s add a blue marker in our calculated center of map.

$markers .= sprintf('|%s,%s,blue', $lat_avg, $lon_avg);
$url->addQueryString('markers', $markers);

$map_with_center = $url->getUrl();

We still need to make markers clickable…

Calculating Marker Pixel Positions

For each marker we know their latitude and longitude. To find their location on map image following calculations have to be done.

1. Convert latitude and longitude to pixel x and y coordinates.
2. Calculate difference between above and map center x and y coordinates.
3. Convert above difference to match current zoom level.
4. Add above difference to center pixel coordinates in image.

PHP code below does all above. Note how shift right operator >> is used to convert the difference between center and marker pixel coordinates for current zoom. Maximum zoom is 21. Variable $zoom is the current zoom level. For zoom level 13 we would be doing delta >> (21-13) which equals delta >> 8 which equals delta / 2^8 which in turn equals delta / 256.

foreach ($marker_array as $marker) {
   list($lat, $lon, $col) = explode(',', $marker);
   $target_y = Google_Maps::LatToY($lat);
   $target_x = Google_Maps::LonToX($lon);
   $delta_x  = ($target_x - $center_x) >> (21 - $zoom);
   $delta_y  = ($target_y - $center_y) >> (21 - $zoom);
   $marker_x = $center_offset_x + $delta_x;
   $marker_y = $center_offset_y + $delta_y;
}

Generate Imagemap for Markers

Now we know marker pixel locations on static map. This enables us to generate imagemap for them. We will create clickable circles with radius of 12 pixels. Lets modify code we just wrote. Note that calculated marker location points to markers foot. We want the round head of marker be clickable. That is done by adjusting y coordinate with -20 pixels.

$imagemap = '<map name="marker_map">';
foreach ($marker_array as $marker) {
  
  

Continue reading >>


Google Maps Without JavaScript Part 2

HEADS UP! This article was written in 2008. Information provided might not be valid anymore and should be taken with a grain of salt.
NOTE! Code shown is now included in Simple Static Maps PHP class. This blog entry is still good for understanding...

Continue reading >>


Howto Show Markers Within Specific Zoom

Google Maps API offers several nice but relatively unknown shortcuts. One of them is new GMarkerManager class. It enables you to easily control at what zoom levels markers are shown.

In the map above if you zoom in twice a marker appears. Zoom in...

Continue reading >>


Google Maps Without JavaScript

HEADS UP! This article was written in 2008. Information provided might not be valid anymore and should be taken with a grain of salt.

We recently did a small Google Maps application for ERGO insurance. It consisted of submitting all their offices to Google Maps. KML export of office data was used to create map at ERGO autoabi campaign site. First version used all JavaScript approach to create the sidebar and map on the page. I was not happy with it. Map page was empty for browsers with JavaScript disabled. Page also took too long to render. Two problems I could not ignore.

Second version was mixture of PHP, JavaScript and new static maps API. Map now works without JavaScript. It renders much faster too. Check the working demo to see yourself.

Importing KML

Google offers GGeoXML library for KML parsing. That I ditched in the beginning. It was impossible to create sidebar navigation with it. First I ended up using EGeoXML. It got the job done but as I said earlier was too slow.

To speed things up I parsed KML files and outputted sidebar HTML with PHP. Simplified HTML and PHP below. Do not mind about # hrefs. They will be replaced with something meaningfull later.

<h3><a id="cityname" class="city" href="#">ERGO cityname</a></h3>
<ul>
    <li><a  class="office" href="#" id="marker-1">Office 1</a></li>
    <li><a  class="office" href="#" id="marker-2">Office 2</a></li>
    <li><a  class="office" href="#" id="marker-3">Office 3</a></li>
</ul>
$kml = array("tartu.kml", "parnu.kml");

/* Used in id's so we can later bind click to correct marker. */
$counter  = 0;
foreach($kml as $file) {
    $city = str_replace('.kml', '', $file);
    $xml  = $xml = simplexml_load_file($file, null, LIBXML_NOCDATA);

    /* Print cityname as headline. */
    printf('<h3><a id="%s" class="city" href="#">%s</a></h3>',
            $city, $xml->Document->name);
    foreach ($xml->Document->Placemark as $placemark) {
        $coordinates = $placemark->Point->coordinates;
        list($longtitude, $latitude, $discard) = explode(',', $coordinates, 3);

        /* Save parsed KML as simpler array. We output this as JSON later. */
        /* Save also id so we can match clicked link ad marker.            */
        $markers[] = array("latitude"    => $latitude,
                           "longtitude"  => $longtitude,
                           "name"        => (string)$placemark->name,
                           "description" => (string)$placemark->description,
                           "id"          => "marker-$counter");

        /* Print officename as link to single office. */
        printf('<li><a class="office" href="#" id="marker-%d">%s</a></li>',
                $latitude, $longtitude, $counter, $placemark->name);
        print "\n";
        $counter++;
    }
    print "</ul>\n";
}

Putting Markers on Map With JavaScript

I gave the PHP parsed KML array back to JavaScript using JSON. Array is then looped passing data to addMarker() method. This method creates new marker, puts it on map and binds event to open new infowindow when marker is clicked. Simplified code below:

$(function() {

    /* Output parsed KML files as JSON */
    var markers     = <?php print json_encode($markers) ?>;
    var marker_hash = {};

    if (GBrowserIsCompatible()) {
        /* Init and center at Tartu. */
        var map = new GMap2(document.getElementById("map"));
        map.addControl(new GSmallMapControl());
        map.setCenter(new GLatLng(58.38133351447725, 24.516592025756836), 12);

        for (var i=0; i<markers.length; i++) {
            var current = markers[i];
            var marker  = addMarker(current);
            marker_hash[current.id] = {marker : marker};
        }
    }

    function addMarker(current) {
      var marker  = new GMarker(new GLatLng(current.latitude,
                                            current.longtitude));
      map.addOverlay(marker);
      GEvent.addListener(marker, 'click', function() {
          var html = '<h3>' + current.name + '</h3><p>' +
                     current.description + '</p>';
          marker.openInfoWindowHtml(html);
      });
      return marker;
    }

});

Make It Work With JavaScript Challenged Browsers

Inside the map <div> there is an <img> tag which points to Google Static maps api. You can pass needed parameters in URL and Google generates map as static image. For example:

http://maps.google.com/staticmap?&zoom=12&size=688x300
&center=58.378700,26.731110&key=GOOGLE_API_KEY

Generates the following map:

What we do here is really simple. In sidebar links we pass needed parameters in querystring. PHP then writes these parameters into <img> tag pointing to static maps api. Instead of center we pass markers parameter. It contains coordinates to one or more markers. Map api then centers map automatically. This comes especially handy when there is multiple markers.

For example link:

<a href="?markers=58.384933,24.499811,red|58.384731,24.507816,red|">

becomes the following img tag:

<img src="http://maps.google.com/staticmap?size=688x300
&markers=58.384933,24.499811,red|58.384731,24.507816,red|
&key=GOOGLE_API_KEY" />

which looks like this:

What we need to do is to update the code which generates sidebar html. I cut out lots of code for sake of clarity here. Important line is one with printf().

foreach($kml as $file) {
    
  

Continue reading >>


MarkItUp! for Jeditable

In the heels of autogrow textarea comes next custom input for Jeditable. This time we will use markItUp! universal markup editor by Jay Salvat.

If you are not familiar with Jeditable custom inputs read the introduction. Also see autogrow and timepicker tutorials.

If you are just looking for the code download it here.

Creating custom input

Throughout the tutorial we will be using following code to trigger Jeditable. Note variable called markitupHTML . It holds configuration of used markItUp! tagset. In this example I use slightly modified HTML tagset. For more information on tagsets see markItUp! documentation.

$(".edit").editable("http://www.example.com/save.php", { 
    type      : "markitup",
    submit    : 'OK',
    cancel    : 'Cancel',
    tooltip   : "Click to edit.",
    onblur    : "ignore",
    markitup  : markitupHTML
});

Again we start by adding custom input type called markitup.

$.editable.addInputType('markitup', {
});

Right now code does not do much anything. It only creates the buttons and hidden input. Click text below to see it in action.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.

Add textarea element

Next we add texarea element. Unlike in previous tutorials we do not copy and paste code from default textarea element. Instead we use a shortcut:

$.editable.addInputType('markitup', {
    element : $.editable.types.textarea.element
});

Now normal textarea is added. Click text below to see how it works.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.

Attach markItUp! to textarea

We wanted to see pretty textarea with toolbar. First we search textarea input inside Jeditable form. This is done with $(‘textarea’, this) selector. Then we attach markItUp! to it.

$.editable.addInputType('markitup', {
    element : $.editable.types.textarea.element,
    plugin  : function(settings, original) {
        $('textarea', this).markItUp(settings.markitup);
    }
});

Now you can edit code with tag editor. Click text below to test yourself.

Lorem ipsum dolor sit amet, consectetuer adipiscing elit, sed diam nonummy nibh euismod tincidunt ut laoreet dolore magna aliquam erat volutpat.

And we are done. This illustrates how easy it is to customize Jeditable with other plugins. Only six lines of code can create pretty inline tag editor. There is also separate Textile demo. As always, all suggestions and feedback are welcome.