Monday, 30 December 2013

JQuery mobile page event order

When navigating from A to B this is the page event order

page B---pagebeforeload
page B---pageload
page B---pagebeforecreate
page B---pagecreate
page B---pageinit
page A---pagebeforehide
page B---pagebeforeshow
page A---pageremove
page A---pagehide
page B---pageshow

Thursday, 19 December 2013

Distance between two latitude longitude coordinates using javascript

I'm building a smartphone app that enables users to search for holiday homes throughout the UK. To improve the UX I decided that a once-per-session download of the headline JSON data, into local storage, would allow me to perform lightning fast searches and filters without going back to the server each time.

One requirement is to search by radius from the user's location, which effectively means calculating the straight-line distance between two lat-lon coordinates on the surface of sphere. And not not consulting the server means using javascript.

This turned out to be easier than I'd imagined, and as ever thanks go to SO users with superior maths than I. One formula available is called the haversine formual, and the javascript implementation of it looks like this:
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
  var R = 6371; // Radius of the earth in km
  var dLat = deg2rad(lat2-lat1);  // deg2rad below
  var dLon = deg2rad(lon2-lon1); 
  var a = 
    Math.sin(dLat/2) * Math.sin(dLat/2) +
    Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) * 
    Math.sin(dLon/2) * Math.sin(dLon/2)
    ; 
  var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); 
  var d = R * c; // Distance in km
  return d;
}

function deg2rad(deg) {
  return deg * (Math.PI/180)
}
What should be noted about this is the assumption that the Earth is spherical. It isn't, as it's actually fatter at the equator, making it a spheroid I believe. The radius figure of 6371km is a global average. So if you know you're only going to be using this formula for certain regions or countries it may be worth adjusting this figure to the radius appropriate to that particular latitude, thereby reducing the error margin.

Another option is to use a flat-earth model, which would presumably produce more accurate results over shorter distances, and runs faster because there's less computation:
HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/

a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;
I'm rolling with the haversine formula for now, but during testing I'll be comparing my distance results with 'real' ones from Google (who are always right - fact) to see what kind of accuracy I'm getting. It may be that the flat-earth formula suits me better for both speed and accuracy.

Friday, 6 December 2013

Change Active Admin password

Note to self.
    admin = AdminUser.find_by_email("admin@domain.com")
    admin.password = "newPassword"
    admin.save

Thursday, 28 November 2013

JQuery Mobile panels and headers in multiple pages

In a jqm multipage project I often want to use a header, footer or navigation panel across several pages. How do I do this without repeating the markup for each page? Well, my solution is to write the markup only once on the main page, and then on the pagebeforecreate event clone it to all the other pages.

Take a nav panel for example:
<div data-role="page" id="index">
<div data-role="panel" data-position-fixed="true" data-theme="a" id="nav-panel">
            <ul data-role="listview" data-theme="a" class="nav-search">
                <li data-icon="delete"><a href="#" data-rel="close">Close menu</a></li>
                    <li><a href="#" data-destination="#home">Home</a></li>
                    <li><a href="#" data-destination="#search">Search</a></li>
                    <li><a href="#" data-destination="#about">About</a></li>
            </ul>
</div> 
 <!-- etc.... -->
And then in subsequent pages I add a div with a suitable classname to identify it:
<div data-role="page" id="search">
        <div class="navPanelChild"></div>
<!-- etc.... -->
And then on the pagebeforecreate event of the first page of the app I clone the nav panel and replace all the child containers with it:
$(document).delegate("#index", "pagebeforecreate", function () {
      var navpanelCopy = $( "#nav-panel" ).clone();
      $( ".navPanelChild" ).replaceWith(navpanelCopy);
}); 

Panel bug with jQuery Mobile

I was implementing a jqm panel this morning to be used as a navigation menu reveal kinda thing, but I came upon a problem
jquery.mobile-1.3.2.js:10330:
var $theme = $.data( page[0], "mobilePage" ).options.theme
$.data(...) is undefined

There are two fixes for this. One is to downgrade from jQuery 2.x back to 1.9. The other is to slightly modify the jqm file simply by replacing 'mobilePage' with 'mobile-page'. I opted for the latter. 

Friday, 15 November 2013

iOS7 status bar with Phonegap and JQM

Only a few weeks after battling with the new annoying iOS7 status bar for a native app, I'm now faced with the same problem while building a Phonegap with jQuery Mobile.

Some of my app's elements, particularly back buttons in headers, sit directly under the transparent status bar.

I used this solution, only slightly modified from one I found online:
$(document).delegate("div[data-role='page']", "pageinit", function () {
           
            if (navigator.userAgent.match(/(iPad|iPhone);.*CPU.*OS 7_\d/i)) {
                $("body").addClass("ios7");
                $('body').append('<div id="ios7statusbar"/>');
            }


        })

#ios7statusbar
{
    width:100%;
    height:20px;
    background-color:white;
    position:fixed;
    z-index:100;
}
 
.ios7 .ui-page
{
    margin-top: 20px;
}

Tuesday, 5 November 2013

phonegap build android - An error occurred while building the android project

When building my Phonegap Android project I got this error during compile:

[error] An error occurred while building the android project. 
/Users/edpitt/Projects/MyProj/platforms/android/cordova/lib/check_reqs.js:35
        return target.split('=')[1].replace('\n', '').replace('\r', '').replac
                                    ^
TypeError: Cannot call method 'replace' of undefined
    at Object.module.exports.get_target (/Users/edpitt/Projects/MyProj/platforms/android/cordova/lib/check_reqs.js:35:37)

The only way I could work out a fix for this was by switching workspace in Eclipse, and then importing the code into that new workspace.


AndroidManifest.xml: Resource is out of sync with the file system

Today when starting up my Android project (which worked yesterday) I got the following error:

com.android.ide.eclipse.adt.internal.project.AndroidManifestHelper] Unable to read /Users/edpitt/Projects/MyProject/platforms/android/AndroidManifest.xml: org.eclipse.core.internal.resources.ResourceException: Resource is out of sync with the file system: '/MyProject/AndroidManifest.xml'.

 I fixed this by refreshing the project (click the project and hit F5) and rebuilding it (Projects > Clean).

Android Logcat - Unexpected value from nativeGetEnabledTags

I've recently started using Phonegap for a new project as we're targeting iOS, Android and WinPhone simultaneously. It's early days but I've had very few problems so far.

After running 'phonegap platform add android' and starting up the basic project for the first time in Eclipse I was met with this error, hundreds of times, in Logcat:
Unexpected value from nativeGetEnabledTags
The workaround for this is to filter out the message in Logcat by using this:

^(?!.*(nativeGetEnabledTags)).*$

Goto your Logcat, and in the Saved Filters part, click on Edit selected logcat filter. There in the by Log Message field enter ^(?!.*(nativeGetEnabledTags)).*$. If Saved Filters is not visible then click on Display saved filters view in the Logcat.

Friday, 25 October 2013

'npm' is not recognized as an internal or external command, operable program or batch file.

Get this error after installing Node.js on your Windows machine?
'npm' is not recognized as an internal or external command, operable program or batch file.
Yeah me too! The solution? Restart your machine!

Wednesday, 4 September 2013

The type or namespace name 'UI' does not exist in the namespace 'EPiServer'

This morning I updated an EpiServer project from Visual Studio 2008 to 2012. The only problem I had was this error when building:

The type or namespace name 'UI' does not exist in the namespace 'EPiServer' (are you missing an assembly reference?)

I discovered that Episerver.UI.dll has some dependencies that I didn't have. The solution was install the Microsoft Chart Controls on my machine

Monday, 12 August 2013

Use Tokn app to stay safe while you work, play, or travel alone

I'm pleased to announce a long-term project has finally gone live. The concept was pitched to me by my very good friend @RichieJones in early 2012 and I liked it immediately - it had legs.

After many hours of hard work and hundreds of iterations, and even doing design and artwork, I think we've got something that's pretty damn good for a first release.



Tokn is to be used by surfers, hikers, snow-boarders, skiers, mountain bikers, horse riders, cyclists, nurses, health visitors, social workers, real estate agents, teachers, mental health workers, lone workers, teenagers, anyone walking home alone late at night, and many other people. Anyone who is doing something risky or is vulnerable can make use of Tokn.

Simply enable Tokn just like you'd check in on Facebook or FourSquare. Leave a note with us and go and do whatever it is you're doing - and if you don't get back to us when you said you would, we'll send a text message to your nominated buddy that you might need assistance. We'll pass on your note, and show them your location.

A technical tag list of the project would look something like this: XCode, Cocoa, Ruby on Rails, Rubymine, JSON, Heroku, Github, Facebook Graph API. Some of this posed a steep learning curve but it's been a great experience.

Here's hoping that Tokn serves thousands of users well and has the opportunity to grow with its user-base and offer more and more great functionality.

Friday, 12 July 2013

Make robots.txt environment-aware in Rails

In home_controller.rb:
  def robots
    robots = File.read(Rails.root + "config/robots.#{Rails.env}.txt")
    render :text => robots, :layout => false, :content_type => "text/plain"
  end
In routes.rb:
  get "/robots.txt" => "home#robots"
And create robots.production.txt, robots.development.txt in your config directory as you wish

SPF records on Fasthosts with Mandrill

I'm using Mandrill for sending mail from my Heroku-based rails app, and my domain and dns is hosted with Fasthosts. To ensure deliverability and high reputation Mandrill recommend you add DKIM and SPF records to your domain, and they provide instructions on how to do this.

Unfortunately I had trouble getting this right with Fasthosts, until I found this in a Mandrill troubleshooting guide:

If your domain has an SPF type record, it's best to add a matching TXT record for wider compatibility.

This solved my problems. Here's how it looks in my Fasthosts control panel:

Friday, 7 June 2013

Cross-domain or 3rd party cookies through an iFrame in Safari 6

In Safari 6 setting a 3rd party cookie has become even more difficult. Previously problems like this could be overcome with P3P headers and the like (and here's a previous post of mine where it caught me out again with IE), and for Safari 5 and earlier there were other techniques.

Well now the user needs to have interacted directly with the domain for a cookie to be set, and there are several ways of acheiving this described in this SO post, which also has the following notable comment from one user:

"it is becoming pretty obvious that 3rd party cookies are now the devil, even when used in appropriate ways"

This is particularly pertinent for Facebook app developers when we need to display a once-only welcome message and remember that is has been presented, or for those who choose an obstructive opt-in cookie message (rather than implied consent) and need to remember that this has been accepted. 

Login dialog popup not working within app when not logged onto Facebook

Came across a weird problem today with a Facebook app we're building. When viewing the app in its canvas or a page tab environment, clicking a button that should trigger the javascript SDK login popup actually resulted in brief flash of a waiting animation and then nothing.

Some investigation revealed this bug logged by Facebook but only replicated by 9 people (as of today) in about three weeks. Sure enough, checking an existing live Facebook app which I built a few months ago, and which gets about 1000 users a day, displayed the same behaviour - you couldn't use the app if you weren't alreay logged in.

It's interesting that Facebook appear to have broken something quite critical but seem to have done little to fix it, and it's also interesting how so few devs have contributed a replication report to the bug. Surely 1000's of apps are affected by this?

I think what this shows us, and the fact that I've had no complaints about accessing my other live app that are nonetheless affected by this bug, is how few users visit a Facebook app without actually being logged in.

Anyway, a temporary and quick fix while this remains a problem is to simply use javascript to detect the scenario of being in a canvas or a tab, and the user being logged out, and instead of prompting the broken Fbook login popup simply alert the user that they need to be logged in. This seems better than nothing happening.

In the meantime subscribe to the bug and when you get notice that its fixed then put your code back to how it was.

Saturday, 1 June 2013

Publicación Companias Marcas Internacionales - Misleading invoices from Spain

I recently applied for a Community trade mark from the Office for Harmonization in the Internal Market   (OHIM) in Spain, in relation to an iPhone app I'm working on.

Soon after this I received an invoice from Spain for a similar amount (€1050), from Publicación Companias Marcas Internacionales, and it mentioned OHIM clearly in the first paragraph, although it became apparent after re-reading it that they have absolutely nothing to do with OHIM.

If you get one of these you should ignore it (in my opinion) - it's basically a con (in my opinion), clearly designed (in my opinion) to confuse the recipient into paying what they think is their OHIM invoice. If what they're doing is legal, it shouldn't be (in my opinion).

Thursday, 30 May 2013

Remove .net styling from GridView

Don't you just hate the way .net webforms insists on adding style and border attributes to the tables produced by Gridviews and other controls? Well I came up with a pretty reliable way of getting rid of them so my css (specified in the 'CssClass' attribute of the gridview) can take over.
$(document).ready(function () {
    $('table').filter(function (index) {
        $(this).removeAttr("style");
        $(this).removeAttr("rules");
        $(this).removeAttr("border");
        $(this).removeAttr("cellspacing");
        $(this).removeAttr("cellpadding");
    });
});

Tuesday, 21 May 2013

Detect change of browser orientation with javascript

Today I needed to detect a change from landscape to portrait or vice versa on mobiles and tablets. So I did this:
window.addEventListener("orientationchange", function() {
  //do whatever you need to do
}, false);

Monday, 20 May 2013

Ensure a div is visible in mobile browser

I'm currently working on a responsive site that has a high predicted mobile user base. There are a few areas in this site where divs are revealed or hidden using jQuery slideDown(), slideUp(), hide(), show(), and sometimes modal overlays are presented to the user with a tinted blocked UI.

Sometimes on mobile devices presentation of these can be problematic - the reveal of a new div may happen fully or partially off-screen, seriously damaging the user experience. To counteract this I built a javascript function which I use after each new reveal to check if the div is viewable in the browser:

function checkIfDivIsViewable(div) {

   
    var $window = $(window);           
    var myTop = div.offset().top;  // top of element
    var windowTop = $window.scrollTop();           // top of window
    var windowBottom = windowTop + $window.height();  // bottom of window

    if (myTop >= windowTop && myTop <= windowBottom) {
        // div is viewable in browser, do nothing
    } else {
        // div is not viewable in browser, so scroll it
        $('html,body').animate({ scrollTop: myTop }, 300);
    }


}

I tried calling this on completion of reveals, like this:
$('#divToShow').slideDown(300,checkIfDivIsViewable($('#divToShow')));
but this didn't prove too reliable. I don't know why, but it's posssibly because the div is still in motion when we're asking jQuery to assess it's position. So at the risk of appearing a bit hacky I check the display after a small delay. This seems to work fine:
$('#divToShow').slideDown(300);

//now check that the div we just revealed is visible
setTimeout(function () {
            checkIfDivIsViewable($('#divToShow'));
        }, 500);

Tuesday, 7 May 2013

Rails Activerecord - common performance pitfalls

I just found this great blog entry on Engineyard on the common pitfalls using Activerecord - when an innocent bit of code works great in development but in the wild, when faced with 1000's of records, it'll grind to halt. Here's a summary:

1. Model.find(:all)

In versions of Rails before 2.3, this is a memory killer. The most common form in the wild is:

Comment.find(:all).each{ |record| do_something_with_each(record) 1125;

If you have 100,000 Comments, this will load and instantiate all 100k records in memory, then go through each one. In Rails 2.3, the .each will paginate through the results, so you’ll only load in small batches, but this won’t save you from the following variations:

@records = Comment.all
@records = Comment.find(:all)
@record_ids = Comment.find(:all).collect{|record| record.id }

Each of these will load up all Comment records into an instance variable, regardless if you have 100 or 100,000 and regardless if you are on Rails 2.1 or 2.3

2. :includes are Including Too Much

Article.find(:all, :include => [:user => [:posts => :comments]])

This is a variant of the above, intensified by the one or multiple joins on other tables. If you only have 1000 articles you may have thought loading them in is not a big deal. But when you multiply 1000 that by the number of users, the posts they have and the comments that they have… it adds up.

3. :includes on a has_many

@articles.users.find(:all, :include => [:posts => :comments]])

Variation on the above, but through a has_many.

4. @model_instance.relationship

Referring to a has_many relationship directly like so:

@authors.comments

is a shortcut to the potentially bloated:

@authors.comments.find(:all)

Be sure that you don’t have thousands of related records, because you will be loading them all up.

5. Filtering Records with Ruby Instead of SQL

This is also fairly common, especially as requirements change or when folks are in a hurry to just get the results they want:

Model.find(:all).detect{ |record| record.attribute == "some_value" }

ActiveRecord almost always has the ability to efficiently give you what you need:

Model.find(:all, :conditions => {:attribute => "some_value"})

This is a simple example to make the point clear, but I’ve seen more convoluted chunks of code where detect or reject is using some non-attribute model method to determine inclusion. Almost always, these queries can be written with ActiveRecord, and if not, with SQL.

6. Evil Callbacks in the Model

I’ve helped a couple of customers track down memory issues where their controller action looked perfectly reasonable:

def update
  @model = Model.find_by_id(params991;:id])
end

However, a look at the filters on the model showed something like this:

after_save :update_something_on_related_model
.
.
def update_something_on_related_model
  self.relationship.each do |instance|
    instance.update_attribute(:status, self.status)
  end
end

7. Named scopes, default scopes, and has_many relationships that specify :include Where Inappropriate

Remember the first time you setup your model’s relationships? Maybe you were thinking smartly and did something like this:

class User
  has_many :posts, :include => :comments
end

So, by default, posts includes :comments. Which is great for when you are displaying posts and comments on the same page together. But lets say you are doing something in a migration which has something to do with all posts and nothing to do with comments:

@posts = User.find(:all, :conditions => {:activated => true}).posts

This could feel ‘safe’ to you, because you only have 50 users and maybe a total of 1000 posts, but the include specified on the has_many will load in all related comments – something you probably weren’t expecting.

8. Use :select When You Must Instantiate Large Quantities of Records

Sometimes, in the reality of running a real production site, you need to have a query return a large data set, and no, you can’t paginate. In that case, the first question you should ask is “Do I need to instantiate all of the attributes?”

Maybe you need all the comment_ids in an Array for some reason.

@comment_ids = Comment.find(:all).collect{|comment| comment.id }

In this case, you are looking for an array of ids. Maybe you will be delivering them via JSON, maybe you need to cache them in memcached, maybe they are the first step of some calculation you need. Whatever the need, this is a much more efficient query:

@comment_ids = Comment.find(:all, :select => 'comments.id').collect{|comment| comment.id }

9. Overfed Feeds

Check all the places you are making XML sandwiches. Often these controllers are written early on and don’t scale well. Maybe you have a sitemap XML feed that delivers every record under the sun to Google, or are rending some large amount of data for an API.

10. Monster Migrations

Finally, watch out for your Migrations, as this is a common place where you need to do things like iterate over every record of a Model, or instantiate and save a ton of records. Watch the process size on the server with top or with “watch ‘ps aux | grep migrate’”.

Thursday, 11 April 2013

Disable autocorrection in HTML text input field

Autocorrection is a pain when typing into HTML text input fields for user names and other kinds of data that shouldn't be auto-corrected. As web devs we should be disabling this functionality to make mobile users' lives better

All you need is this attribute:
autocorrect="off"

Thursday, 14 March 2013

Apply PIE.htc when revealing an element in IE8

I discovered today that PIE.htc only redraws elements that are visible when the page is loaded. Kinda seems obvious now, but I hadn't planned for this and the testers picked up on some horrible looking buttons on a newly revealed div.

So I had to work out how to apply PIE to an element once it had been revealed. The answer is thus, thanks to a bit of googling and good old SO:
function applyPie(elem) {
    if (window.PIE) {
        elem.css('behavior', 'none');
        elem.css('behavior', 'url("/includes/css/PIE.htc")');
    }
}

Monday, 18 February 2013

Adding a facebook app to a page tab

https://www.facebook.com/dialog/pagetab?app_id=YOUR_APP_ID&display=popup&next=YOUR_URL

Friday, 25 January 2013

Latitude and longitude in Rails activerecord

I needed to store latitude and longitude in my Postgres db in a way that maintained accuracy of data but also allowed fast efficient searching of this spatial data using BETWEEN queries. The PostGIS extension was not available to me.

The suitable data type for this is a decimal with precision of 9 and scale of 6 [decimal(9,6)]. The activerecord migration code for this turned out to be thus:
add_column :livetokens, :latitude, :decimal, :precision => 9, :scale => 6

Tuesday, 22 January 2013

Sniff iPhone HTTP traffic with mitmproxy

I always forget how to use mitmproxy, so here's a reminder for myself:
Download and install mitmproxy, if you haven't done so already.
Go to a terminal and run mitmproxy.
Get the network IP address of your mac by running ifconfig en1.
Set the proxy on your iPhone by going to wifi settings. Set HTTP Proxy to “Manual”, and enter the IP of your Mac and port 8080.
Start browsing on the iPhone, or run an app. Press ‘?’ to bring up the list of commands.

Wednesday, 16 January 2013

Text size bigger in iPhone Safari landscape

Is your text size being unpredictable when in landscape on an iPhone? Mine was. Here's the fix, just add this to your body class:

-webkit-text-size-adjust:none;

Wednesday, 9 January 2013

Twilio for rails - setting the StatusCallback URL

I've been using Twilio with Rails to send SMS, and needed Twilio to notify my Rails app if a send had been successful or not.

To acheive this I added a StatusCallback parameter when calling message.create. This contains a URL with a unique identifier of the message I sent.

 # send an sms
    @client.account.sms.messages.create(
        :from => '+44207XXXXXX',
        :to => phoneNumber,
        :body => body,
        :StatusCallback => ENV['app_url']+ENV['twilio_sms_postback']+'?smsId='+textmessageId.to_s
    )