• Posted on December 27, 2010

Bringing back the image map

I’ve just finished working with an image map for the first time in a while. Now, you may be thinking, “Why in the world would anyone want to touch an image map?”, but they still do serve a purpose. Image maps are still in fact part of the HTML5 specification.

Case Study

So why the image map? Take a look at the following image:

Case Study

So our image above is triangular, and is intended to be clickable. The expectation is that only the triangular area should be clickable. So in order to accomplish this, I’ve decided to move forward with using an image map.

Things get a bit more complicated, though. The image above is actually repeated many times on a page. It acts as a flag to being a favorite of sorts. Furthermore, we are not simply sending the user to a page link, we are looking to use JavaScript events on the image map (and unobtrusively at that). This unobtrusive JavaScript must send an ajax request to the server depending on which instance of the image is actually being clicked. So we have our work cut out for us.

Setting up the image map

Getting the image map set up is pretty simple. Let’s start with defining the map:

<map name="my-map">
    <area id="fav-area" shape=poly, coords="0,0, 46,0, 46,46" href="#fav">
</map>

We’ve defined a map named my-map with some coordinates. Note our shape is set to poly and the coordinates provided. In order to determine these coordinates I simply opened up Photoshop and grabbed the x and y coordinates of the 3 corners of the triangle. Along with that, since we have no intent on passing the user to an actual link we’ve provided a dummy hash on the href.

Setting up the image

So now the the map is defined we’ll create an instance of the image and have it use the map we made:

<img src="/images/map-case-study.gif" usemap="#my-map">

Adding an event handler

Adding an event handler is pretty straight forward as you might expect. I’ll be using jQuery for the demonstration.

$(function() {
    $('map[name=my-map] #fave-area').click(function (e) {
        e.preventDefault();
        alert('Clicked!');
    });
});

So we can still target and add an event as you normally would to the area.

Multiple instances of the same image and image map

Now that we have our image map set up, let’s move forward with the more complicated setup. Take the HTML structure below:

<div id="node-1" class="my-node">
    <img id="study-1" src="/images/map-case-study.gif" usemap="#my-map">
</div>
<div id="node-2" class="my-node">
    <img id="study-2" src="/images/map-case-study.gif" usemap="#my-map">
</div>
<div id="node-3" class="my-node">
    <img id="study-3" src="/images/map-case-study.gif" usemap="#my-map">
</div>

<map name="my-map">
    <area id="fav-area" shape=poly, coords="0,0, 46,0, 46,46" href="#fav">
</map>

We have multiple nodes which aim to make use of the same case-study image and they all use the same image map to define the clickable area. Now the problem is, how do we determine which image was actually clicked?

Well first, we can make use of a DOM method called elementFromPoint. This method allows you to pass in x and y coordinates and retrieve the element that is found at the given coordinates. I never knew that this method existed until today and it’s a pretty interesting find.

Putting elementFromPoint to use

$(function() {
    $('map[name=my-map] #fave-area').click(function (e) {
        e.preventDefault();
        var trigger = document.elementFromPoint(e.clientX, e.clientY);
        alert(trigger.id);
    });
});

So we take the coordinates of the actual click event and pass it to the elementFromPoint function to grab the actual element the user was trying to click. One problem still exists though: This works fine in Firefox and Safari but Chrome (and IE as some have reported) will still return the map area when using elementFromPoint. In order to counter this, I’ve found that disabling the image map before running elementFromPoint will then yield our expected element. Of course, once we’re done finding the element we must re-enable the image map.

So how do we do this? Take a look at the final JavaScript:

$(function() {
    $('map[name=my-map] #fave-area').click(function (e) {
        e.preventDefault();
       
        // Cache all instances of the image
        var $all_instances = $('.my-node img');
       
        // Store the usemap in order to re-enable later
        var usemap = $all_instances.attr('usemap');
       
        // Reset the use of an image map on all instances of it
        $all_instances.attr('usemap', '');
       
        // Grap the actual trigger we were looking for
        var trigger = document.elementFromPoint(e.clientX, e.clientY);
       
        // Re-enable the image map on all the instances we disabled it on
        $all_instances.attr('usemap', usemap);
       
        // Do something useful with the information
        alert(trigger.id);
    });
});
  • Posted on December 22, 2010

CSS Gradient Definitions

I’ve just released an update to the CSS3 Gradient Generator which changes the way the code sample is generated for users to copy and paste.

The sample has moved to using the background-image property instead of the background property alone. This change has been made to resolve issues that occur when defining a background with the CSS based gradient but leaving out the other properties common to a background such as position or repeat. When these attributes are left out, browsers define default values which can yield unexpected results with the CSS gradients.

So, the following:

background: -webkit-gradient(
    linear,
    left bottom,
    left top,
    color-stop(0.33, rgb(99,168,125)),
    color-stop(0.67, rgb(129,202,163)),
    color-stop(0.84, rgb(155,243,196))
);
background: -moz-linear-gradient(
    center bottom,
    rgb(99,168,125) 33%,
    rgb(129,202,163) 67%,
    rgb(155,243,196) 84%
);

Becomes,

background-image: -webkit-gradient(
    linear,
    left bottom,
    left top,
    color-stop(0.33, rgb(99,168,125)),
    color-stop(0.67, rgb(129,202,163)),
    color-stop(0.84, rgb(155,243,196))
);
background-image: -moz-linear-gradient(
    center bottom,
    rgb(99,168,125) 33%,
    rgb(129,202,163) 67%,
    rgb(155,243,196) 84%
);

The live sample on the page also makes use of background-image as well, so you can view that as another reference to this update as well.

Thanks to Avi Mahbubani for pointing out the issue and helping come to a resolution.

  • Posted on December 04, 2010

Gradient Generator Updated!

After about a year since being updated I’ve just released a long awaited update to the CSS3 Gradient Generator. This update includes an updated page design as well as some new features. The biggest of these include the often asked for option of being able to choose HEX colors instead of RGB. A full list of release notes can be found below.

Along with these changes the public github repository has been restructured. Only the important things (the JavaScript source of course) are up in the repo. Meaning, you can go ahead and browse it, fork it, learn how it works or use in your own project. Be warned that a lot of the code is pretty specific to the HTML structure of the actual gradients.glrzad.com website. It was never intended to be used as a plugin, though perhaps in the user this will be done.

Check out the changes live, visit the CSS3 Gradient Generator.

Release Notes

Color Format Selection
Color format selection: Choose how your colors are formatted, either RGB or HEX.
The gradient generator will store your preference for the next time you visit!
Copy the code!
Copies the output code to your clipboard for you so you can quickly grab your generated code.
UI Updates
Stripped down page to the core components of the gradient generator, moving it to the top of the fold.
Moved the color picker next to the swatches so that when working with swatches, the user doesn’t have to travel so far to update the color for their selected swatch.
Larger gradient sample included
Code sample now outputs how you would be expected to use the sample, (includes the background: definition)
Disqus Commenting!
Disqus commenting built onto the page. Like the gradient generator? Want to leave back feedback? You can do so now without having to leave the page to my blog.
Spread the word!
Facebook Like button and Twitter’s own retweet button in place (replacing the old TweetMeme button). Share your love for the gradient generator
CDN
All assets have been moved over to an Amazon S3 bucket and make use of Amazon’s CloudFront
JavaScript and CSS have been minified for enhanced performance for requests.
  • Posted on October 26, 2010

Using Uploadify with Rails 3 – Part 2 – Controller Example

Back in August I posted a walkthrough on working with a Flash based file uploader with Rails 3. After reading some comments I realized that a part was left out. Setting up the controller to work with our upload. So this post is here as part 2 of ‘Using Uploadify with Rails 3′.

This sample is going to be using the paperclip gem by thoughtbot to handle our upload.

The Controller

So we’ve gotten our file uploaded successfully, authentication is passing thanks to our previous work but what now? One of the first issues that you might run into is the content type for the file you uploaded. Our uploadify upload will actually have the content_type set to ‘application/octet-stream’ which is not totally accurate representation of what we’re uploading.

As stated earlier, this example is using paperclip, which will need the true content_type to be set to accurately continue uploading the file. To resolve the ‘application/octet-stream’ issue we can use the mime-type gem and set the content_type of the file afterwards in the controller before trying to save the upload. Add the following to your gem file and run a bundle install:

gem 'mime-types', :require => 'mime/types'

With this in place we can force our controller to set the content type of the upload to the proper value.

We can do this with the following code:

params[:Filedata].content_type = MIME::Types.type_for(params[:Filedata].original_filename).to_s

Below is a full code sample of our Paperclip based model, and the full create controller method for handling our upload. I hope you’ve found this post useful and please feel free to comment below with any questions, as I’ll try to get to them as soon as possible.

GalleryImage Model

class GalleryImage < ActiveRecord::Base
   
    belongs_to :gallery
    delegate :venue, :to => :gallery

    # Storing photos in /galleries/:venue_name/:gallery_id/...
    has_attached_file :photo,
        :styles => { :thumb => "70x70", :iphone_thumb => "30x30#", :iphone_full => "320x480" },
        :url => '/galleries/:venue/:gallery/:id/:attachment/:style/:basename.:extension',
        :path => ':rails_root/public/galleries/:venue/:gallery/:id/:attachment/:style/:basename.:extension'
end

GalleryImagesController

def create 
       
    @gallery = Gallery.find(params[:gallery_id])
    @gallery_image = GalleryImage.new
    @gallery_image.gallery = @gallery
       
    # Associate the correct MIME type for the file since Flash will change it
    params[:Filedata].content_type = MIME::Types.type_for(params[:Filedata].original_filename).to_s
       
    @gallery_image.photo = params[:Filedata]
    if @gallery_image.save
        render :json => { 'status' => 'success'  }
    else
        render :json => { 'status' => 'error' }    
    end
end
  • Posted on August 24, 2010

Using Uploadify with Rails 3

I recently worked on a couple of Rails projects which both implemented the ability to upload photos to the server. One of these projects required the ability to upload multiple files at once and since HTML5 multi-file uploads aren’t ready yet I needed another solution. So I decided to move forward using a Flash based uploader, specifically Uploadify. Uploadify is a jQuery plugin which allows you to upload multiple files through a Flash based interface which provides helpful functionality like progress bars and multi-upload queuing, along with this, it’s super easy to use.

Well that is until you’re trying to work with a Rails application. There are lots of resources around that go into the details of how to get this to work but the real issue that I had was that they were mostly for Rails 2.3.8 and my application is now using Rails 3 and the methods described in all of these articles don’t completely apply to Rails 3.

Why is this extra work needed?

Before moving forward on how to get everything working let’s take a look at why it’s not working in the first place to better understand what we’re dealing with and how we’re fixing the problem.

Requests made via POST, PUT, DELETE all require a valid authentication token and session information in order to complete the request successfully in order to protect against Cross-site request forgery. When building out forms using Rails form helpers, a hidden input is created which contains the authenticity token to prove the authenticity of the request along with your session cookie data.

This poses 2 problems for us using Flash to upload files.

First, Uploadify is not going to automatically send the authenticity token with the POST request. Second, Flash based requests do not send our session information along with the request. The first situation is pretty easy to fix while the session information requires a little more work on our part.

Sending the Authentication_Token

Let’s start by sending the authenticity token along with the request. In previous versions of Rails we would have had to found a way to get the authenticity token to send but as part of Rails 3 usage of Unobtrusive JavaScript, it now includes a new helper method called csrf_meta_tag. This helper method will provide us with 2 meta tags, csrf-param and csrf-token

<meta name="csrf-param" content="authenticity_token"/>
<meta name="csrf-token" content="s2yuw7u24N9fni1m+Wynr595kTphEeMrNSWPAnMroLM="/>

Once we have the CSRF meta tags in place, we can move forward and add the authenticity to the parameters which are sent with Uploadify’s request. The JavaScript snippet below is an example of pulling the CSRF token information and passing it along with our Uploadify requests using the scriptData configuration parameter.

<script type="text/javascript">
$(function () {
  // Create an empty object to store our custom script data
  var uploadify_script_data = {};
 
  // Fetch the CSRF meta tag data
  var csrf_token = $('meta[name=csrf-token]').attr('content');
  var csrf_param = $('meta[name=csrf-param]').attr('content');
 
  // Now associate the data in the config, encoding the data safely
  uploadify_script_data[csrf_token] = encodeURI(csrf_param);

   // Configure Uploadify
  $('#photo-upload').uploadify({
    'uploader' : '/assets/uploadify.swf',
    'script' : '/photos/',
    'scriptData' : uploadify_script_data
  });
});
</script>

Uploadify allows you to pass extra data along with it’s request using the scriptData parameter which accepts an object of properties to send with the request. In our case we are fetching the CSRF and creating a new property in the scriptData object with the name of the CSRF token and setting it’s value to the CSRF param. Using this, we can pass along the authenticity token with the request. It’s important to note that we are encoding the authentication token value using encodeURI to escape any special characters so the data gets sent along to the server correctly.

I actually ran into an issue where even using encodeURI once was not enough and I needed to double encode because + signs were being converted into spaces. So if you are continuing to run into InvalidAuthenticityToken error, check your log to see that the authenticity token is being correctly sent along. While trying to debug the problem a quick look into the log showed that I was receiving

authenticity_token"=>"3WB Cj0h7y4St5ZyvlYU xIEVT7yIIr6IRAhOOLCb5w=

The spaces in the above authenticity token should have been + signs instead. In order to resolve this issue I ended up having to double encode the authenticity token before sending it along like so:

uploadify_script_data[csrf_token] = encodeURI(encodeURI(csrf_param));

Now that we have the authenticity token being sent by Uploadify, let’s move forward and get our session information passed along as well.

Passing the Session Cookie

One of the first steps to passing the session cookie along with our Uploadify requests is to add it to the scriptData like we did with the authentication_token. Unfortunately there is no quick way to handle this through JavaScript alone, we need to include it using some ERB to apply it.

In earlier versions of Rails the session cookie key could be accessed via: ActionController::Base.session_options[:key]

As of Rails 3 though we can access the session cookie key using: Rails.application.config.session_options[:key]

Below is a simple example of how to send the session cookie information in the scriptData.

<script type="text/javascript">
<%- session_key = Rails.application.config.session_options[:key] -%>
$(function () {
   
  var uploadify_script_data = {};
 
  // Fetch the CSRF meta tag data
  var csrf_token = $('meta[name=csrf-token]').attr('content');
  var csrf_param = $('meta[name=csrf-param]').attr('content');
 
  // Now associate the data in the config, encoding the data safely
  uploadify_script_data[csrf_token] = encodeURI(csrf_param)
 
  // Associate the session information
  uploadify_script_data['<%= session_key %>'] = '<%= cookies[session_key] %>';
 
  $('#upload-photo').uploadify({
    script : '/photos/',
    scriptData : uploadify_script_data
  });  
});
</script>

Now that we have the session cookie data being passed with the request we’ll need to move forward and have that information set with the headers of the Flash request, enter Middleware.

Rack Middleware

In order to get the session information to be associated with the request correctly we’ll need to create some custom middleware which will intercept the requests from Flash and inject the cookie information into the header.

Some middleware has been around for accomplishing this but most are not compatible with Rails 3. I’ve posted a Gist on Github which has a working middleware for Rails 3 which I found on metautonomo.us which we can use to accomplish our task.

Create a file in app/middleware/flash_session_cookie_middleware.rb creating the middleware directory if it doesn’t already exist and add the following code in it:

Once you’ve created that file we’ll need to add the middleware directory to our autoload’s path if it isn’t already. You can do this by modifying you’re config/application.rb file and add the following in the autoload section:

# Custom directories with classes and modules you want to be autoloadable.
# config.autoload_paths += %W(#{config.root}/
config.autoload_paths += %W(#{config.root}/app/middleware/)

Once that’s done we have one more step to do, inserting the middleware so that it will go to work when we need it.

Place the following code in config/initializers/session_store.rb

With all this in place you should now be ready to successfully upload files via Flash with your Rails project.

Good luck!

*** Updates ***

<

ul>

  • 2010-08-26 Resolved issue in code sample where session_key_name was being reference instead of session_key. Original sample updated.

    uploadify_script_data['<%= session_key %>'] = '<%= cookies[session_key] %>';

    • Posted on September 13, 2009

    CSS3 Webkit Gradient Generator

    Visit the CSS3 Webkit Gradient Generator

    CSS gradients are quite powerful and very useful, but when taking a first look at the markup for writing a code based gradient it can appear daunting. This is why I’ve decided to create the CSS3 Webkit Gradient Generator. Please note that the generator is intended to be viewed in a webkit browser. Live sampling of the gradient will not be available without a webkit browser.

    The gradient generator has 2 goals. The first is to provide a showcase for the power of CSS gradients. The second, is to provide a graphical user interface in which a user can visually create gradient code to use in their CSS where they see fit. In order to do this, I tried to create a UI that appears familiar, such as that of a graphic editing tool like Adobe Photoshop. Providing color swatches to make up the gradient and a slider for each to determine the color’s position in the gradient.

    Color Swatch Control

    Color Swatch Control

    Once a color swatch is selected the user can use the color picker found on the right to adjust the swatch’s color. The color picker is provided by the ColorPicker jQuery plugin from eyecon.ro.

    jQuery ColorPicker Plugin

    Gradient Direction Control

    Along with this control, I have added the ability to set the direction of the gradient, allowing the user to take advantage of the keywords that are available (left, right, bottom, top) or use custom point locations.

    Gradient Direction Control

    Live Sampling

    The generator provides live sampling, meaning any changes that are made to the controls on the page will immediately update the color sample and the CSS code output.

    Note: Live sampling of the gradient colors requires a webkit browser.

    Live Sampling

    Coming Soon

    There are still more features to be implemented. Currently, the gradient generator only supports linear gradients. I’m working to provide radial gradient support in the near future. Also, currently the generator will only accept hex color values, I am planning on adding RGB support soon as well.

    Let me know what you think of the generator so far as well as any features you may like to see.

    Links

    • Posted on July 27, 2009

    About iPhone Web Applications

    Let me start by this post by specifying that it actually originated from another post that I’m working on right now, but while writing it I went off on quite a tangent that I decided it was time to make it into a completely separate post, and start off with it. So this is part one of an iPhone web application piece that I am working on. While I am it, let me point out what the scope of this article is. This article is not about how to write an iPhone web application. It does not go into details on the syntax or use of technologies for the iPhone. It does explain some of the misconceptions between iPhone applications, iPhone targeted websites, and finally, iPhone Web Applications. I do plan to write more about the iPhone web application technologies and provide some examples and working code, but that is all currently beyond the scope of this article.

    While researching on the topic over the last few weeks, I discovered what I believe is a misconception of an iPhone web application, and my goal is to hopefully, clear things up.

    Let’s start by defining some of the possible ways of targeting an iPhone user, which actually diverges into two separate categories of its own. The first and probably the most popular way of targeting an iPhone user is through an iPhone app, sold through the iTunes App store. Coding and compiling Objective C/Cocoa based native iPhone applications.

    Yet there are still other ways of creating a custom user experience for a user on the iPhone.

    • Posted on July 26, 2009

    Welcome to glrzad.com

    Welcome to glrzad.com, formerly rad-ev.com. It’s been quite a while since I’ve maintained a blog but I feel that now is a good time to get back to blogging. I wish I had some of my old articles/tutorials from rad-ev.com but I think their long gone, maybe I’ll do some digging to see if I can find them.

    Things are still a little under construction here, but feel free to poke around. I have quite a few ideas floating around in my head to write, some I’ve already begun working on while others are just in early stages. So stay tuned for updates.