Monday, 30 December 2013
JQuery mobile 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
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
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
<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
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
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
[error] An error occurred while building the android project./Users/edpitt/Projects/MyProj/platforms/android/cordova/lib/check_reqs.js:35return target.split('=')[1].replace('\n', '').replace('\r', '').replac^TypeError: Cannot call method 'replace' of undefinedat 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
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
Unexpected value from nativeGetEnabledTags
^(?!.*(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.
Yeah me too! The solution? Restart your machine!'npm' is not recognized as an internal or external command, operable program or batch file.
Wednesday, 4 September 2013
The type or namespace name 'UI' does not exist in the namespace 'EPiServer'
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
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
def robots robots = File.read(Rails.root + "config/robots.#{Rails.env}.txt") render :text => robots, :layout => false, :content_type => "text/plain" endIn 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
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
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
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
Thursday, 30 May 2013
Remove .net styling from GridView
$(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
window.addEventListener("orientationchange", function() { //do whatever you need to do }, false);
Monday, 20 May 2013
Ensure a div is visible in mobile browser
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
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
All you need is this attribute:
autocorrect="off"
Thursday, 14 March 2013
Apply PIE.htc when revealing an element in IE8
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
Friday, 25 January 2013
Latitude and longitude in Rails activerecord
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
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
-webkit-text-size-adjust:none;
Wednesday, 9 January 2013
Twilio for rails - setting the StatusCallback URL
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 )