Thursday, December 13, 2012
A little iPad app for Santa
I had a bit of downtime recently, so I slapped together a little toy app using a few technologies I've been meaning to play around with. The original idea was to create a proof of concept for bringing Telus' (or Rogers or whoever) technician appointment system onto iPads. A customer rep (or a customer via some web interface) would book appointments to the system, and a technician could then see which ones are close by.
That sounds a bit dry, so in the spirit of Christmas, I decided to populated the appointments database with some tasks for Santa.
P.S: Dear Santa. I've been a good kid and I'd like a flamethrower.
If you have an iPad, you can see the app here. Change the radius input to see more pins on the map and feel free to add more if you want to. It works on desktop too, if your browser supports Geolocation, but it might not pinpoint you as accurately.
Technologies: Angular.js, Geolocation, Google Maps, Mongo on the cloud
One thing I wanted to explore was organizing front end code a little better than the common one-giant-file-full-of-random-jQuery-scripts. So, I looked into Angular.js
It's essentially a client-side MVC framework. It does routing:
$routeProvider.when('/', { controller: ListCtrl, templateUrl: 'list.html' })
Controllers are just functions, but they have magic arguments: in the example below, the $scope object magically makes its members available as variables in the view. This feature is also used to import modules, making it very terse and elegant to manage dependencies in controllers.
function ListCtrl($scope) { $scope.greeting = "hello" }
Views use plain HTML with extra attributes to provide functionality:
<-- this would say hello --> <input ng-model="greeting" />
Models make it super easy to integrate to RESTful JSON services, which helps keeping server-side and client-side logic separated.
Check the Appointment module for an out-of-the-box example of connecting to a mongo db RESTful web interface (via mongolab.com, hosted on the EC2 cloud). The Geolocation module connects to the HTML5 Geolocation API, and to MapQuest's Nominatim service to map GPS points to addresses.
One of the neat things about Angular.js is that it does a lot of the heavy lifting for data-binding on the UI. Check the ng-repeat directive in the list.html file and $scope.nearby function in controller.js for an example of filtering the data set by distance to current location.
Google Maps is something I added mostly for eye candy. The code for it is in the controller.js file and it's fairly straightforward.
As always, feedback welcome.
Wednesday, November 28, 2012
HTML 5 iOS App Development
I wrote a presentation on HTML5 application development for iOS. There are also some supporting files up on GitHub for those who would like to play around.
Feedback welcome.
Tuesday, November 13, 2012
Tuesday, October 16, 2012
Wednesday, October 10, 2012
Tuesday, July 10, 2012
getUserMedia - camera / microphone APIs landing in Chromium
The Chromium team just announced the latest beta now has user media APIs. Javascript is quickly becoming more and more interesting.
Monday, July 9, 2012
Friday, July 6, 2012
Friday, June 8, 2012
Tuesday, May 29, 2012
(ab)using labels in javascript
Something that always felt a bit messy in unobtrusive javascript coding was how we normally use class names for both styling and binding functionality to an element. For example:
<-- HTML --> <a class="expander" href="javascript:void(0)"></a> /* CSS */ .expander {/* some arrow styles */} /* javascript */ $(".expander").click(toggleAccordion)
There are two problems with this:
- If I have another type of expander button somewhere else that needs to do something different (e.g. maybe somewhere I need to ajax content in first), then I need to clutter the code with more stuff in order to allows the different buttons to do different things
- I have no indication of what said button does by inspecting it w/ Firebug/dev tools, let alone by just looking at it. I have to slog through the code to figure out what code might get attached to this element
Stepping back for a moment
As it turns out, there's a third, minor issue: the href attribute of the element doesn't serve any purpose. It doesn't even tell the user where clicking on the element might go. It's just a clunky way of telling the browser to not do its default action when you click on the element. It's a code smell that can be refactored to something like the this:
$(".expander").click(false)
As it also turns out, browsers as old as IE8 support document.querySelectorAll, so finding elements by href nowadays is not particularly slower than searching by class name. So why not repurpose that useless href back to identifying what a user should expect when clicking on the element?
What's javascript:void(0) anyways?
javascript:void(0) is an antique from a decade ago that most people just blindly use without really thinking about what it might mean. All this line does is run some javascript that returns undefined. Returning undefined in this context is equivalent to e.preventDefault(), which in this case means "prevent going to a URL".
There are many ways of returning undefined, the shortest of which is to simply... do nothing.
<a href="javascript:;"></a>
But we can do better: do nothing in a descriptive way, by using javascript labels:
<a class="expander" href="javascript:expand:;"></a>
And we can target that via jQuery:
$("[href='javascript:expand:;']").click(toggleAccordion)
We can attach different behaviors to different ".expander" elements simply by using a different href:
<a class="expander" href="javascript:expandData:;"></a>
$("[href='javascript:expandData:;']").click(ajaxAccordion)
So now we have a clean way of separating styling from behavior on an element, while making "javascript:void(0)" more informative for users and future maintainer selves.
Wednesday, May 16, 2012
On learning how to code, or whose life is it anyways?
So this drama started by Jeff Atwood about whether people should learn how to code or not still hasn't died.
The trigger for his post seems to be that some mayor somewhere ought to be spending his time doing mayor work and not learning things that don't necessarily help w/ his day job.
The thing is, even if he gets paid by tax payers to do his job, it doesn't mean he must do it at all times of the day. Lots of coders go home after work to learn about things like photography or martial arts. And not because they are actually trying to get photos published in a magazine, or actually going into real combat.
Some are advocating everyone should learn how to code, but that's a bit like saying everyone should learn English or Mandarin under the logic that it opens up a whole lot of business opportunities because of the whole global economy thing. Sure, there are many things that are probably worth pursuing if we had infinite time, but since we don't, just let people pursue whatever the heck they want to.
Monday, May 14, 2012
Saturday, March 31, 2012
Friday, March 30, 2012
scaling wordpress on the cheap
How to handle 10 million hits a day. He claims to handle ~7000 hits per minute. Doesn't necessarily handle spikes though.
Thursday, March 29, 2012
jQuery Trigger custom parameters
So I just found out that you can pass custom parameters to jQuery's trigger()
, which greatly simplifies automating UI actions.
Say you're building a really basic carousel:
<ul class="carousel"> <li><img src="slide1.jpg" /></li> <li><img src="slide2.jpg" /></li> <li><img src="slide3.jpg" /></li> </ul> <button id="next">Next</button> $.fn.carousel = function(options) { //hide all the slides, except the first one this.children().hide().first().show() var self = this $(options.next).click(function(e) { e.preventDefault() //move first slide to end of list and hide it self.children().first().appendTo(self).hide() //show new first slide self.children().first().show() }) } $(".carousel").carousel({next: "#next"})
And say you want to make it rotate automatically every 10 seconds.
var animation = setInterval(function() { $("#next").trigger("click") }, 10000)
BUT, you want to make it stop once a user clicks the button to go next. What now?
Custom parameters
You basically just want to differentiate between a real user clicking, and the click event that is fired programmatically from the setInterval. We can use custom parameters to do this fairly elegantly:
var animation = setInterval(function() { $("#next").trigger("click", [true]) }, 10000) $.fn.carousel = function(options) { //hide all the slides, except the first one this.children().hide().first().show() var self = this $(options.next).click(function(e, continueAnimation) { if (!continueAnimation) clearInterval(animation) e.preventDefault() //move first slide to end of list and hide it self.children().first().appendTo(self).hide() //show new first slide self.children().first().show() }) } $(".carousel").carousel({next: "#next"})
A regular click event only passes the event object as a parameter to the callback function, so the continueAnimation
variable is undefined in that case. When triggering the event programmatically, we can pass the value true
and run code conditionally based on that.
So that's it.
Tuesday, March 27, 2012
Thursday, March 22, 2012
Monday, March 12, 2012
Friday, March 9, 2012
Monday, March 5, 2012
Using jQuery custom events to organize code better
One problem when creating complex user interfaces comes when you have lots of plugins interacting asynchronously. For example, you might have a modal popup that ajaxes an image carousel in. And maybe you use the same modal code to play videos (probably using HTML5 with Flash fallback.) Or maybe you're using collapsible side panels and cool eye-catching widgets like Isotope side-by-side.
The naive way to structure code like that would typically go along these lines:
//let's suppose we're loading a carousel $("#carousel").load("/carousel/", function() { //but we have an Isotope panel //on the page and we need to hide it hideIsotopePanel() //and we also need to hide modal popups killModals() //and make sure videos/audio //aren't playing in the background killVideos() //get the idea? })
The problem, of course, is that hiding the Isotope panel, killing videos, modal popups and God knows what else are not part of the carousel logic. It just so happens that when including this carousel on the page, you need to de-clutter your page so that your user isn't overwhelmed.
The headache starts when you have to worry about the permutations of what needs to hide when: now there's a carousel on the page. When is it supposed to hide? Where do I put the code for that? What else exists in the page that needs to hide when the carousel loads?
Ideally, we'd like to organize the code for doing all of this showing and hiding in their own modules: Isotope hiding code goes where the rest of the Isotope code is, modal hiding code goes where the rest of modal code is, and so on. But how can we do that?
jQuery custom events
One way is to use jQuery custom events. We could rewrite the carousel loading function above like this:
$("#carousel").load("/carousel/", function() { //fire a custom event we just made up $(document).trigger("carousel-loaded") })
This is basically the same thing the browser does behind the scenes when you click on an element, and an event handler magically fires. The difference here is that you can define when the event gets fired, and what it's called.
Now we can go into the Isotope module and define there when it's appropriate to hide the component:
//in the file where we're doing all //the Isotope heavy-lifting $(document).on("carousel-loaded", function() { hideIsotopePanel() })
What's the advantage of refactoring code around like that?
The main advantage is that now it's easier to manage orthogonality (i.e. hiding something on one corner case, but forgetting to on another):
$("#isotope").load("/isotope/", function() { $(document).trigger("isotope-loaded") }) $(document).on("carousel-loaded modal-loaded", function() { hideIsotopePanel() })
The snippet above is just an extension of the refactoring idea above. It allows me to fire Isotope-specific events, and ensures the Isotope panel hides when modal popups load (in addition to hiding when the carousel loads). By refactoring all my code to use custom event like this, I can set the groundwork to make other permutations also easy to maintain (e.g. I would be able to go into the code dealing with modals, and, without plowing through thousands of lines of code, I could easily make the modal hide on video load, on carousel load, and so on and so on.)
Saturday, February 25, 2012
A curious module pattern (in javascript)
So one thing that mildly bugs me about Javascript is that creating a project often requires a pattern like this:
;(function(root) { var Project = {} root.Project = Project Project.someMethod = ... Project.anotherMethod = ... })(this)
What bugs me in the code above is that I have to come up with a name for the project, and then this name will eventually end up sprinkled all over the codebase. But the thing is: at heart, I'm a coder, not a marketing person. So coming up with a catchy name for my project is not something I generally want to do before I start typing away. And going back and renaming dozens of variable names after the fact is kinda kludgy.
So I was playing around to see if there's a neater way to write my module patterns. Lo and behold, here's what came out:
with ({project: "MyProject", root: this}) new function() { var shadow = root[project], self = root[project] = this; self.noConflict = function() { root[project] = shadow; return self; } self.someMethod = ... self.anotherMethod = ... };
Basically this allows me to declare the name of the library only once without polluting the global namespace, while supporting a jQuery/underscore.js-like noConflict() method.
Now, if anyone is getting ego massage urges at the sight of that with
statement at the beginning: yes, I'm perfectly aware it's been called evil by just about every js expert, and quite frankly, I don't think it matters in this case:
- There's no huge performance hit - it's a module pattern, not an expression deep inside nested for loops.
- There's no searching for unknown variable names up the scope tree - the only reason you'd want to do that in a module pattern would be to access the global object, and this object is conveniently accessible from the
root
variable. As it turns out, accessing any other magically global variable would actually be a code smell: you can't expectdocument
to exist in, say, Node.js for example, so assuming that is a global variable breaks portability. - There's no unexpected variable shadowing. I know exactly what and where the variables are - they're
project
androot
and they're used exactly 3 times each on the first 3 lines of the constructor function. It's fairly easy to tell I'm not accidentally clobbering hypothetical global variablesproject
androot
, if there were such things to begin with.
Generally, it seems like a pretty neat idiom, and it cleanly delimits the boundaries of the project. I don't need to go scrolling down to double check what the exact capitalization of the project namespace is, or whether it's imported into window
, this
, module.exports
, or what. Also, it reminds me a bit of the local
keyword in Standard ML. Anyways. Enough babbling for today.
Friday, February 24, 2012
Tiny C
A compiler for an ultra-stripped-down subset of C, written in a few hundred lines of C. Code is designed to be educational, so it's very minimalistic (i.e. no out-of-bound checks, etc), and thus pretty readable.
Not to be confused with TinyCC, which, while a small-ish project (compared to GCC or Clang, anyways), already implements most of C99.
Friday, February 17, 2012
Thursday, February 16, 2012
Monday, February 13, 2012
Friday, February 10, 2012
Orphion - iPad musical instrument
Pretty interesting. There appears to be some amount of thought given to the geometry of the layouts as it relates to musical theory. And it's a new platform that I'm sure will gather a community around it.
Thursday, February 2, 2012
Tuesday, January 31, 2012
Friday, January 27, 2012
Wednesday, January 25, 2012
Friday, January 13, 2012
What the bagel man saw
Interesting story, but the conclusion might suffer from survival bias - is the number of dropped customers statistically significant?