Creating An IP Geolocation Popfly Block

There is a new shared block on Popfly called Locust IP Locator courtesy of mois. I thought I'd detail how I built it.

First of all, download the block SDK, read the Building Blocks guide, and look at the samples once you start going. It's really helpful to see what the XML looks like, and how it maps over to the JavaScript.

The JavaScript

First thing to do is "declare your block class" which is also known as "declaring a function:"

function LocustClass()

{

     this.__getLocationbyIP = new Location();

}

That wasn't so bad, right? The purpose of the one line of code there is to set the return type of a method called getLocationByIP to Location.

Next, let's declare a custom object (also known as "declaring a function"). This is the type of object that we'll return to the Popfly block.

function IPLocation(ip, cityState, country, latitude, longitude)

{

     this.IPAddress = ip;

     this.CityState = cityState;

     this.Country = country;

     this.Latitude = latitude;

     this.Longitude = longitude;

}

Straightforward, right?

... Right?

Okay, moving on. Now we have to do some actual work! Let's write the function that goes to my Locust web service and parses the XML data it gets back (using the fancy-shmancy ternary operator):

LocustClass.prototype.getLocationByIP = function(ip)

{

     var url = "https://locust.danwaters.com/locust.asmx/GetLocationByIP?ipAddress=" + ip;

     var resultXml = environment.getXml(url);

    

     var country = resultXml.getElementsByTagName("Country").length == 1 ? resultXml.getElementsByTagName("Country")[0].text : "";

     var cityState= resultXml.getElementsByTagName("CityState").length == 1 ? resultXml.getElementsByTagName("CityState")[0].text : "";

     var longitude= resultXml.getElementsByTagName("Longitude").length == 1 ? resultXml.getElementsByTagName("Longitude")[0].text : "";

     var latitude= resultXml.getElementsByTagName("Latitude").length == 1 ? resultXml.getElementsByTagName("Latitude")[0].text : "";

     var myIP= resultXml.getElementsByTagName("IPAddress").length == 1 ? resultXml.getElementsByTagName("IPAddress")[0].text : "";

     var resultLocation = new IPLocation(myIP, cityState, country, latitude, longitude);

     return resultLocation;

}

Why did I declare that function as a prototype of LocustClass? Because I didn't want to declare it in the global namespace, which is not a good thing in the context of Popfly.

The environment.getXml command is used to navigate to an external URL that provides XML and load it into an XML object. From there, you can use the getElementsByTagName function to return a collection of elements matching that name. My object has no arrays (and is not an array) so I only have to check that the length is 1 before I reference the text insdie it.

I instantiate a new Location object and return it, and that is all.

There is one more function we need to look at: the toString function which is a prototype of the Location class. We do this because some other Popfly blocks will want to query it to get a description of its data, and if we don't implement this method we'll just get [object Object] or something else that's very nondescript.

Location.prototype.toString = function()

{

     return "IP: " + this.IPAddress + "<br>" +

          "Location: " + this.CityState + "<br>" +

          "Country: " + this.Country + "<br>" +

          "Lat/Long: " + this.Latitude + ", " + this.Longitude;

}

Voila! Javascript is done. Now we have to map it somehow to the XML description of the block.

First element is the XML declaration.

Then comes the block element, the root of the document. block has one attribute, class, which is the same thing as your block class (LocustClass).

Under the block element are some block and provider metadata nodes: provderName, providerUrl, providerLogoUrl, blockIconUrl, and keys (keys is used when you need to identify yourself to the provider before using it). Then comes the operations node, which tells you what the block can do. Each subsequent operation node describes the inputs and outputs of a block operation.

After the operations node is the objects node, which Popfly uses to figure out what the objects in your javascript look like (and what their data types are). Here is the XML in its entirety:

<?xml version="1.0" encoding="UTF-8"?>

<block class="LocustClass">

  <providerName>Locust IP Geolocator</providerName>

  <providerUrl>https://locust.danwaters.com</providerUrl>

  <providerLogoUrl></providerLogoUrl>

  <blockIconUrl></blockIconUrl>

  <keys/>

  <operations>

    <operation name="getLocationByIP">

      <description>Returns location information for an IP address via the hostip.info project.</description>

      <inputs>

        <input name="ipAddress" required="true" type="ipAddress">

          <description>Source IP address</description>

          <defaultValue>207.46.193.254</defaultValue>

          <constraints/>

        </input>

      </inputs>

      <outputs>

        <output isArray="false" type="custom" object="Location"/>

      </outputs>

    </operation>

  </operations>

  <objects>

    <object name="Location">

      <field name="IPAddress" type="ipAddress" isArray="false"/>

      <field name="CityState" type="description" isArray="false"/>

      <field name="Country" type="description" isArray="false"/>

      <field name="Latitude" type="latitude" isArray="false"/>

      <field name="Longitude" type="longitude" isArray="false"/>

    </object>

  </objects>

</block>

See how that works? Once you understand the structure and how it maps directly to the JavaScript, it's a piece of cake!

I used this block immediately in a mashup that takes input from a User Input box, calls into the IP Locator to get latitude and longitude, and adds a pushpin to an instance of Live Maps!

That's the power and extensibility of Popfly, and we're only in Alpha. I can't wait to see what it's like down the road!