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: | | | |

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!

QuickPresent

Posted October 6th, 2006 in Programming, Projects, Rails and or Ruby

I threw together a quick and simple way to make a presentation on a mac using Quicksilver and Camping a while back. Well, I finally got around to refining it and putting it into a both tiny and awesome package. This puppy is made of two files, and makes presenting dead simple. You just outline your thoughts in a text file, and QuickPresent smacks it onto your screen using Quicksilver’s largetype feature. Not only that, but it also gives you a slick little web interface to show you what you’ve said, and what you’re about to say. Let me show you…

A few other slick features include automatically opening URL’s and Files from your hard drive. All you need is Camping and Quicksilver (with the command line tool (qs) plugin installed). Just type camping quickpresent.rb from the command line and your up running. If you want to try something cheaper than Keynote or Powerpoint, play with QuickPresent. Let me know what you think.

Get it from my SVN Repo: http://svn.vandev.com/presenter

Introducing AjaxSpy

Posted May 4th, 2006 in Javascript, Programming, Rails and or Ruby

While playing with the new RJS templates in rails, I found it difficult to know what was actually being returned from my requests. Thus, AjaxSpy was born. It’s a simple JavaScript and CSS file. All you need to do is include the js file in the header of your document, the css is automagically included (must be in the stylesheets directory). The script relies on Prototype 1.5.0rc0, and for it to be sexy you need the Scriptaculous effects library. You can include it conditionaly based on params in the query string, or do some fancy session stuff. That’s all up to you. This is what I’ve been doing

<%= javascript_include_tag "prototype", "effects" %>
<%= javascript_include_tag "debugger" if params["debug"] %>

The script is currently only working with Firefox and Safari, I’ll work on the bastard child that is IE down the road. Here’s what it looks like, syntax highlighting and all:

AjaxSpy Preview

I present to you, AjaxSpy:

Again, to install simply include the js in the layout, it automagically includes the css (as long as it’s in a directory named “stylesheets” relative to the site route). Let me know if you have any problems.

Seperating presentation and application logic

Posted March 23rd, 2005 in Programming

I was browsing around for different web application development solutions. I'm well acquainted with PHP and I have done some separation of logic with Smarty. Smarty only does so much, and basically is just a cleaner way of presenting data. Almost a hybrid PHP. After a few articles dealing with J2EE and struts I found some php solutions for making a Model View Controller based web app. I also learned a little about Java based web design, which is interesting as well. As far as the Java solution goes there are really two decent ways of doing it from what I understand. You could use Struts or Tapestry. Struts functions as a controller object for the web application realm. If you want to know more about the Model View Controller paradigm check out wikipedia's answer. Basically through the use of an XML configuration file you tell struts what to do with actions from the UI. The view and model stay the same as any web application would. In this case the view is generally written in JSP and the model done with servlets. I am by know means an expert so if you want to know more check out there website. After surfing around for a little while I found allot of complaint about the learning curve for Struts. I also read some complaints about the functionality. There was an interesting explanation of Tapestry on slashdot. Tapestry gives a level of abstraction to the web application. You know longer refer to URI's or add query parameters, you think of everything in terms of objects, methods, and parameters. This seems like a very helpful way of programming a large project. It is just one more step in making a web application more like a desktop app. Well, I'm a PHP guy, and I don't see my self migrating to Java anytime soon. There are two good solutions out there that I know of currently: WACT and PHPTAL. Both seem like good solutions. WACT looks a little more feature rich, but PHPTAL is based on Zope. Zope a very mature application server. It too is an option for a large scale project. I'll be sure to experiment with the PHP implementations in all of my free time. I'm just looking for the cleanest and most extensible way to create a web application. At the very least looking at all of these techniques gets the mind into learning mode.
Kinda like: | | | 0 comments »