JSAwesome

Posted April 22nd, 2008 in Javascript, Programming, Tutorials

Wow, what ever happened to Chris the crazy blogger? I think he wants to make a come back…

Here’s the deal, I left Powerset at the beginning of the year to co-found an amazing company. I’ve been able to create some really cool stuff with this company, the most recent being FaceStat. I’ve also been dating an incredible Serbian, but enough personal life excuses for low blog activity, onto the juicy nerd crap.

A pretty cool project I’ve had a chance to work on recently is a little something I like to call JSAwesome as in JSON + Awesome. What does it do? It likes Rails form helpers written in Javascript but 13.5 times better… Read on for the full scoop.

Read the rest of this entry
Kinda like: | | |

Inside the Croppr frontend

Posted April 16th, 2007 in Javascript, Programming, Tutorials

I learned allot creating croppr. It’s pretty intersting what you can do with the DOM and some CSS. Let’s take a peek under the hood.

The first thing croppr does upon instantiation is create a bunch of DOM elements. Here’s what that looks like.

Toggle Styles

<div style="background: white;
            position: absolute;
            top: 0px; left: 0px;
            width: 100%; height: 545px;
            opacity: 0.8;"/> 
<div id="croppr" style="overflow: hidden;
                        position: absolute;
                        top: 0px; left: 0px;
                        width: 100%; height: 545px;">                  
  <div style="border: 1px solid #000000;
              overflow: hidden;
              position: absolute;
              top: 50%; left: 50%;
              width: 118px; height: 118px;
              margin-top: -60px; margin-left: -60px;">
    <img src="/croppr/images/1" style="cursor: move;
                                       position: absolute;
                                       top: 50%; left: 50%;
                                       margin-top: -272px; margin-left: -357px;
                                       width: 714px; height: 544px;
                                       opacity: 1;"/>
  </div>
  <img src="/croppr/images/1" style="cursor: move;
                                     position: absolute;
                                     top: 50%; left: 50%;
                                     margin-top: -272px; margin-left: -357px;
                                     width: 714px; height: 544px;
                                     opacity: 0.3;"/>
  <a class="exit">
    <span>EXIT</span>
  </a>
  <a class="crop">
    <span>CROP</span>
  </a>
  <div class="track">
    <div class="handle" style="cursor: move;
                               position: relative;
                               left: 154.312px;"/>
  </div>
</div>

Toggle the styles to understand what’s going on here. The first element is the transparency or “light box” that dims whatever else is on the screen (trans). The order of the elements is important. We want the transparency to be all the way in the back. Elements later in the DOM that are absolutely positioned will be infront of previous absolutely positioned elements (unless we set the z-index property).

The next element is our container for all the fun goodies we’re about to add (container). It serves three purposes.

  1. Makes cleanup easy. By deleting this element, all the others will disappear.
  2. Namespaces the rest of our elements. You can target #croppr .exit in your CSS without effecting other elements with the class of exit.
  3. Get’s rid of vertical and horizontal scroll bars. By setting overflow:hidden when the image we are cropping goes outside of the window, the browser doesn’t add scrollbars and screw up our math.

The transparent box is not placed in this div because we want a nice fade back into the original environment after we have deleted all of the cropping tools. More on this in a little bit.

Next we have the mask. The width and height of the mask is set to the width and height of the desired crop - 2. We subtract two because it has a 1 pixel border and the box model includes the border in the width and height. A copy of the main image (ghost) is put inside of the mask. overflow:hidden is set on the mask as well, making the parts outside of it invisible. Both the mask and the ghost are positioned absolutely and have top and left attributes set to 50%. We then give them negative top and left margins. The mask’s margin’s will always be half of its width and height, while the ghost’s margins are based on it’s current position. This puts the work of keeping the items centered if the window resizes in the hands of the browser. All of this is done rather elegantly with mootools:

this.mask = new Element("div").setStyles({
      border: "1px solid #000",
      overflow: "hidden",
      position: "absolute",
      width: this.options.crop[0]-2+"px",
      height: this.options.crop[1]-2+"px",
      top: "50%",
      left: "50%", 
      marginTop: -this.options.crop[1]/2+"px", 
      marginLeft: -this.options.crop[0]/2+"px" 
}).injectInside(this.container);
this.ghost = this.image.clone(false).injectInside(this.mask);

The image itself is next up. She is infront of the mask so we can drag her around without having the mask conflict. We set the opacity to .3 making the ghosting effect. The positioning of this image is identical to the one inside the mask. Infact, as you drag the image, it’s css is copied straight into the css of the image inside of the mask. The only difference is the opacity.

draw: function() {
  this.ghost.style.cssText = this.image.style.cssText;
  this.ghost.setOpacity(1);
}

this.drag = new Drag.Base(this.image, {
  limit: {
    x: [-this.size[0] + this.options.crop[0] / 2, -this.options.crop[0] / 2], 
    y: [-this.size[1] + this.options.crop[1] / 2, -this.options.crop[1] / 2]
  },
  modifiers: {
    x: "margin-left", 
    y: "margin-top"
  },
  onDrag: this.draw.bind(this)
});

Mootool’s awesome Drag class provides a way to constrain the object being drug via the limit parameter in the constructor. This logic is repeated in the scaling function (updateSlide) to insure the image is not scaled outside of the mask. This function is the hairiest of them all… maybe we’ll dig into her another time.

Last but not least we have the CROP and EXIT buttons along with the slider itself. These need to be infront of everything else, which is why they are last. There is no styling done explicitly by the croppr javascript library. Instead, it gives you the freedom to style them however you would like.

When we’re done using croppr, cleanup is as easy as:

this.container.remove();
new Fx.Style(this.trans, 'opacity', {duration: 1000, onComplete: this.trans.remove}).start(0.8, 0);

And that’s that, bending the DOM to do your dirty work turns out not to be so bad…

Kinda like: | | | |

Mootools and my sidebar, doing it

Posted March 11th, 2007 in Design, Javascript, Tutorials

This afternoon I was perusing the web and ended up mining Yelp for something to do in this new and foreign city. I noticed a slick effect that was used to keep the map in the view port as you scrolled. I had done this before with the position:fixed css directive, but it's rather limited especially in a centered layout. You also can't choose your easing equation or delay the effect... Thanks Moo, let's do it.

I added a set of links which link to targets on this page in my sidebar entitled "On this page...". I want the links to scroll the page to the targeted element, and I also want the links to follow me there so I can click on another one if I need to. 8 lines of javascript with the aid of Mootools gets her done.

var slideEffect = new Fx.Style('sidenav', 'margin-top', {wait:false, duration:800, transition:Fx.Transitions.circOut});
  window.addEvent('load', function() {
    var top = $('sidenav').getPosition().y - 10;
    window.addEvent('scroll', function(){
      slideEffect.start.delay(600, slideEffect, Math.max(0, document.documentElement.scrollTop - top));
    });
    new SmoothScroll({duration:700, transition:Fx.Transitions.circOut});
  });

Let's analyze the crap out of this code. I first create a new Fx.Style specifying wait:false (so If I start scrolling down the page and than suddenly switch direction, the effect will simply start over in that new direction). I'm modifying the margin-top property of the sidenav element. I also specify a custom transition circOut which starts fast and than slows to a stop. Using window.addEvent('load', I wait for the document to load because the position of my div is relative to the image of my face. You could use the domready event if the position of the div you will be scrolling is not relative to an image... Next I get the position of the top of my div and subtract 10 from it to give it some padding.

Now for the magic. I listen for the scroll event and when it happens I fire the recently created slideEffect. However, because the delay is cool and if I don't delay the effect will look really jumpy, I delay the start of the effect. Moo provides a delay method which can be called on any function. You have to call it on the function, not on the thing the function returns: e.g. myFunction.delay(100) or function(){return 'rad'}.delay(300) instead of myFunction('awesome').delay(30). The second attribute to delay is an object that will become this in the function delay is being called on. In this case we want this to be the effect itself. The third attribute is the attribute(s) that will be passed into the function. For us, this is an integer representing the new margin. We calculate this as the maximum of either 0 or the difference of the current viewport offset and the original offset of the image.

The last thing to do is make a call to SmoothScroll, making all links that point to a target scroll to the element with that targets id (this was detailed in my last post). So there it is, a detailed description of how my sidebar and mootools did it.

Kinda like: | | |

Mootools, a silly Fx tutorial

Posted February 28th, 2007 in Javascript, Tutorials

I’ve been loving mootools lately so I figured I would demonstrate some of it’s awesomeness by explaining a few of the effects generated on this very page.

Awesome bouncy nav tabs (upper right, funny creative projects)

$$('#header ul li a').each(function(el) {
  var bounce = new Fx.Style(el, 'top', {duration:700, transition: Fx.Transitions.elasticOut});
  var colors = new Fx.Styles(el, {wait:false});

  el.addEvent('mouseover', function(){
    el.setStyle('background-color', '#d5e88f');
    bounce.start(15,0);
    colors.start({
        color: '35342e'
    });
  });
  el.addEvent('mouseout', function(){
    colors.start({
        'background-color': '35342e',
        color: 'd5e88f'
    });
  });
});

Allright, so moo gives us a DOM selector much like prototypes. I use it to grab all the a tags in my nav list, and then I iterate over them. Moo has three generic Fx classes, Style, Styles, and Elements. I don’t use Fx.Elements here, but you can read about it via moos excellent documentation. If you are familiar with the scriptaculous Morph effect, these will come natuarlly. They let you tween a list of css attributes, leaving the door of possibilities for amazing custom effects wide open.

First I create two effects. One to deal with the motion, bounce, and the other to deal with font and background color, colors. Moo supplies an optional set of robust transitions based on Robert Penner’s easing equations (the easing demo is extremely helpful). I specify the elasticOut transition upon instantiation of the bounce effect to make it, well bouncy. Setting wait to false in the Fx.Styles call tells the effect to go ahead and run even if there is another effect just like it already running. This let’s the tab fade back to normal if I mouseout before it’s finished.

After the effects are created, all that’s left is attaching them to an event. Voila, crazy cool bouncy tabs.

A silly unobtrusive smooth scrolling effect (click “it’s not what you think” next to the VD logo)

new SmoothScroll({duration:1000});

Another optional effect (thank you kickass building tool), is SmoothScroll. Upon instantiation she finds all of the targets (aka anything with an id) and adds an event to all the a tags referencing that target (aka href=”#name_of_target”), which scrolls the window to that element… just click it.

Opaque image rollover (what the flick)

$$('div#flickr img').each(function(e){
  var fade = new Fx.Style(e, 'opacity', {wait:false});
  fade.set(.5);
  e.addEvent('mouseover', function(){
    fade.start(1);
  });
  e.addEvent('mouseout', function(){
    fade.start(.5);
  });
});

This uses the same techniques as the first effect. The set method allows you to set the initial position of the effect. In this case it makes all of the images 50% opaque when the page loads.

So there you have it. Mootools is silly powerful and fun. It puts the control in your hands, and does so in a lightweight and extremely sexy manner.

Kinda like: | | | |

A mephisto tag cloud

Posted February 7th, 2007 in Design, Javascript, Tutorials

As I continue to customize my new mephisto installation, I wanted a tag cloud for the Folksonomy section at the bottom of the page. At first I thought I would use a mephisto plugin… after further consideration I decided that was a little overkill. JavaScript kicks ass, especially with the recent release of mootools version 1.0, so I decided to let it do the work. I created a small class called Overcast which makes tag clouds easy peasy japaneasy. So, just how easy is it to get a tag cloud in mephisto? Here’s a snipet from my layout.liquid

{{ 'mootools' | javascript }}
{{ 'overcast' | javascript }}
</head>

<div id="overcast">
  {% for tag in site.tags %}
    <a href="/tags/{{ tag }}" class="_{{tag | tagged_articles | size}}" rel="tag">{{ tag }}</a>
  {% endfor %}
</div>
<script type="text/javascript">
  over = new Overcast({min: 1, fuzz: 2, overlay: "/images/gloss.png"});
</script>

A couple sexy things to note are the cool transparent overlays ontop of each tag, and the ability to fuzz tags when your weights aren’t very diverse. Also, if you’ve got Firebug you can type over.update(“amazing”,4) in the console to add a new tag to the cloud with that weight, or over.update(“javascript”, 3) to change the weight of an existing tag. All of the functionality is documented in the source.

Grab the source from the include in this page. I’ll put it under version control soon.

MaxMind GeoLite on Rails with PostGIS

Posted October 15th, 2006 in Mapping, Programming, Rails and or Ruby, Tutorials

Ughhhh, many hours of frustration have been spent trying to push the MaxMind GeoLite City CSV files into a database configured with PostGIS. Anyway, I figured I would document the process that I went through incase someone else wants some free IP based geocoding in there Rails app.

MaxMind gives you two CSV files for the city database. One has an assload (2,783,434) of IP blocks that map to the id of a row in the other table containing location data. The location data is pretty rich. It contains a postal code, area code, dma code, country, region (state in US), and a latitude and longitude. Because I wanted to be cool and more efficient, I decided to store the lat / lon as a point in PostGIS format. Using PostGIS along with Guilhem Vellut’s Spatial Adaptor plugin for Rails, makes doing geo-spatial operations sexier and easier than ever. You can use the Spatial Adaptor with MySQL, but you’ll never be as cool as the guy using it with PostgreSQL.

First off I had to create my tables.

create_table "geo_ip_locations" do |t|
  t.column "country", :string, :limit => 2
  t.column "region", :string, :limit => 2
  t.column "city", :string
  t.column "postal_code", :string, :limit => 7
  t.column "dma_code", :integer, :limit => 3
  t.column "area_code", :integer, :limit => 3
  t.column "geom", :point, :null => false, :srid => 4269, :with_z => false
end

add_index :geo_ip_locations, :geom, :spatial => true

create_table "geo_ips" do |t|
  t.column "start_ip", :integer, :limit => 10
  t.column "end_ip", :integer, :limit => 10
  t.column "geo_ip_location_id", :integer
end

add_index :gep_ips, :start_ip
add_index :geo_ips, :end_ip
add_index :geo_ips, :geo_ip_location_id

A few things to note:

  • The geom column has the srid set to 4269, this threw me for a loop for way too long. Make sure you have the spatial_ref_sys table populated. I’m also assuming that the MaxMind data is mapped to the WGS 84 standard.
  • The limit’s on the start and end ip’s are set to 10. This creates bigint columns in my DB. I’m not sure what version of Rails started doing this (I’m on Edge), but the columns have to be bigints for the IP block information to be imported.
  • Remember to create a couple models for the tables
  • You may want to add an index on postal_code or anything else you’ll be using in a SQL WHERE clause

Now because the CSV file with the blocks of IP addresses in it is so large I went with an importing tool to get it into the table. I was going to use PostgreSQL’s COPY command, but it doesn’t seem to support CSV’s with double quotes. I went with Navicat and took advantage of my 30 day trial. The CSV with the locations in it is another story. We need to convert the lat / lon pairs into points for PostGIS. Not only that, but if we want to be really cool we need to convert the city names into UTF-8. The file that MaxMind gives us has ISO-8859-1 encoding. Iconv comes to the rescue. You can either convert the entire file from the command line, or just use the ruby library to do it in the import script below.

require "#{File.dirname(__FILE__)}/../../config/environment"
require 'fastercsv'
require 'iconv'
ICONV = Iconv.new( 'UTF-8', 'ISO-8859-1' )

FasterCSV::HeaderConverters[:underscore] = lambda { |h| h.underscore }

FasterCSV.foreach('geo_ip_locations.csv', {:headers => :first_row, :col_sep => ",", :header_converters => :underscore}) do |row|
  begin
    row['id'] = row.delete('loc_id')[1]
    row['geom'] = Point.from_x_y(row.delete('longitude')[1].to_f, row.delete('latitude')[1].to_f, 4326)
    row['city'] = ICONV.iconv(row['city'])
    cool = GeoIpLocation.new(row.to_hash)
    cool.id = row['id']
    cool.save!
    $stdout.print '.'
  rescue
    puts $!.message
    $stdout.print 'f'
  end
  $stdout.flush
end

Things to note:

  • You need FasterCSV for this to work gem install fastercsv
  • You need to delete the first line (with the copyright info in it) in the CSV for this script to work.
  • If you converted the file from the command line, comment out the three lines referencing iconv to improve performance.
  • I created a directory called transform in my db directory, renamed and moved the location CSV there, and created a file with the above code in it.
  • Always remember to put the srid in the Point.from_x_y call, otherwise you’ll be very very frustrated.
  • You’ll need to specify encoding: unicode Postgres, encoding: utf8 MySQL in database.yml to use UTF-8 with the DB. See the Rails wiki
  • The id is set this way to preserve it.

That’s it! There you have it, the possibilities are now endless. As I said before, I set this up on PostgreSQL, but this should work the same on MySQL. The one main difference will be the srid’s. MySQL doesn’t support them, so you don’t need to specify them. Goodluck!

Croppr podcast... a little late

Posted September 22nd, 2006 in Javascript, Rails and or Ruby, Tutorials

The SDRuby podcast site is filling up with great episodes. One of which being my little talk on camping and Croppr (Episode 002 on the bottom). The thing has been up for a couple of monthes now, but my posting consistency has been miserable lately. I’ve had more pressing issues, but today is a new day. My next project will be pushing this thing over to Mephisto. I should also spend some time fixing the Rails date_select helper, but that’s another post all together. The next time I speak, the entire back end of this puppy will be different… totally awesome.

RMagick on Mac OS X 10.4 Tiger

Posted June 6th, 2006 in Rails and or Ruby, Tutorials

Recently I needed Rmagick installed on my Mac OS X 10.4 system. This turned out to be nearly impossible, until I got down and dirty and just went for a fresh compile from source. Actually it became more impossible until I found enlightenment on the long and arduous journey. The following is a pretty bare-bones install. It leaves out excess functionality like exporting to TIFF, working with PostScript or PDF files, color management, EXIF headers, SVG images, or MSWord documents. All of this functionality can be added by downloading the source from the links above, and compiling them before you run ./configure for ImageMagick. You can also install GraphicsMagick if you like, it shouldn’t make a difference which one you use.

The following instructions will have you up and running in no time (actually about 30 minutes depending on the speed of your system ImageMagick takes forever). These instructions assume you have installed Ruby according to the instructions at HiveLogic. If you have not, make sure you atleast follow the steps up to the point of installing ruby. This makes sure your path is correct and your build environment is in place.

X11:

10.4 Tiger install DVD or http://www.apple.com/downloads/macosx/apple/x11formacosx.html

Freetype2:

curl -O http://umn.dl.sourceforge.net/sourceforge/freetype/freetype-2.2.1.tar.gz
tar zxvf freetype-2.2.1.tar.gz
cd freetype-2.2.1
./configure && make && sudo make install
cd ..

Jpeg:

curl -O ftp://ftp.uu.net/graphics/jpeg/jpegsrc.v6b.tar.gz
tar zxvf jpegsrc.v6b.tar.gz
cd jpeg-6b
ln -s `which glibtool` ./libtool
export MACOSX_DEPLOYMENT_TARGET=10.4
./configure --enable-shared && make && sudo make install
cd ..

PNG:

curl -O http://umn.dl.sourceforge.net/sourceforge/libpng/libpng-1.2.10-no-config.tar.gz
tar zxvf libpng-1.2.10-no-config.tar.gz
cd libpng-1.2.10
cp scripts/makefile.darwin Makefile
make && sudo make install
cd ..

ImageMagick:

curl -O http://umn.dl.sourceforge.net/sourceforge/imagemagick/ImageMagick-6.2.8-0.tar.gz
tar zxvf ImageMagick-6.2.8-0.tar.gz
cd ImageMagick-6.2.8
./configure && make && sudo make install
cd ..

RMagick (finally and thankfully):

sudo gem install rmagick

That’s it, goto RMagick’s web site for information on using RMagick in your apps. Be on the look out for an incredible little Camping app that uses the power of RMagick to appear on VD soon!

Your own personal Geocoder... for FREE

Posted July 18th, 2005 in Mapping, Tutorials

After the release of the google maps api, I began working on custom applications. Because google doesn't allow address lookups in its api, you have to generate a latitude and longitude yourself for a given address. The immediate problem was the need for geocoding, preferably geocoding within php. This is where the US Census comes in. The easiest solution is to download a mysql dump of all the zips with there corresponding lat/longs. That mysql dump can be found here Well, this is great, but it's not detailed enough. I needed to geocode to the street level. geocoder.us offers a free web service for doing this but you can only make one request every 15 seconds. I have a database of 3500 addresses, so that wasn't going to work. geocoder.us uses the US census's Tiger Data, and the perl Geo::Coder::US module. The problem with this solution is the amount of time it would take compile. You have to download every zip file from every state, and than compile it. I'm not patient enough. Thats when I found Dan Egnor. Dan won the 2002 Google Programing contest with a program that does just what I needed it to. Read on to do it yourself. Ok, so the biggest issue is skipping out of the compile process. Dan offers a free download of a bz2'ed version (~300mb) of the compiled address location data (by the way, the README on his site is good reading material). Dans program does more than I needed it to, so I simplified his source code (Thanks Dan) to do simply what needs to be down. It can be downloaded here. Simply download my source, `bunzip2` the map data from Dans site (assuming Linux here), and tar -jxvf the source, run make on the source and issue a command like `./geo-coder (location of the map data ex. ../../all-map) '(the address you want to find)'`. This returns either SUCCESS or FAILURE. Some simple php code will execute and return the address within your web app:
1
2
3
4
5
6
7
8
9
10

$progress = array();
exec("./geo-coder ../all-map '$address'", $progress);
if(substr($progress[0],0,7) == "SUCCESS"){
 //The below returns an array with, $ll[0] = longitude and $ll[1] = latitude from Dan's program
 $ll = explode(", ",trim(substr(strstr($progress[3], '@'),1)));
}else{
 //use a zipcode (from the query or DB table in my case) to find the lat/long in the mysql zip database
 $zips =& $db->query('SELECT latitude, longitude FROM zipcodes WHERE zip = '.$row['zip']);
}
So there it is. A geocoder in no time at all. The geocoder is about 80% effective (according to Dan). If enough interest is created with this post, I will create a torrent of the most up to date Tiger address information, which should improve the accuracy. Don't hesitate to email me at vanpelt@gmail.com if you have questions or requests.
Kinda like: | | | 4 comments »