Reverse Geocoding in Javascript

This was a neat thing I learned recently. We've all done geocoding... pass an address to a web service, like geocoder.us or through the Google Maps API, and get back a coordinate in latitude and longitude, which you can use to search for things nearby (using a database of things with lat/long coordinates and the trusty Haversine Distance formula, get directions, put a marker on a map, etc.)

Recently I've needed to find out a zip code for the person viewing the website. This won't be an exact science, for the obvious reason of desktop computers don't typically have GPS, and IP address geolocating is pretty good for many people but there are those cases where the ISP might assign an IP for Camden to a person in Philadelphia. I have no proof but go along with it. Either way, it will be close enough for what I'm doing.

Basically, all you do is use Google's GeoCoder object, and pass in a google.maps.LatLng object instead an address! Here's how:

var latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var coder = new google.maps.Geocoder();
coder.geocode({ 'latLng': latLng }, showLocaleCallback);


Here's all the code:

function initZip() {
if (navigator.geolocation && typeof (navigator.geolocation.getCurrentPosition) == "function") {
navigator.geolocation.getCurrentPosition(geoCodeCallback);
}
}

function geoCodeCallback(position) {
var latLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var coder = new google.maps.Geocoder();
coder.geocode({ 'latLng': latLng }, showLocaleCallback);
}

function showLocaleCallback(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var zip = "";
var res = results[0];
for (var i = 0; i < res.address_components.length; i++) {
if (res.address_components[i].types[0] == "postal_code") {
zip = res.address_components[i].short_name;
}
}

$("[id$=txtZip]").val(zip);
}
}

$(document).ready(initZip);


Be sure to include Google's Map API Javascript and jQuery. These are two cool techs that I really like to work with.

Google's Map API: http://maps.google.com/maps/api/js?sensor=false

Also, if anyone has a pointer on how to more effeciently do this part, I'm all ears:

for (var i = 0; i < res.address_components.length; i++) {
if (res.address_components[i].types[0] == "postal_code") {
zip = res.address_components[i].short_name;
}
}

Experiments in Javascript: Multicast Delegate

There are quite a few blog posts out there about "doing window.onload the right way", albeit from 2007. I looked over them, and took issue with the hard-coded feel that they had.  For instance, a function that returns a function, but takes two functions, and no more, as arguments. I came across an instance where this just wasn't acceptable, according the new dogma of Javascript programmers out there, which is to write as few lines as possible. I explored new ways of accomplishing this.

First and foremost, don't use window.onload :) attachEvent and addEventListener are there for this exact purpose. Let's take a trip back 5 years and pretend this was relevant for window.onload. However it is still relevant for Javascript in general.

The problem of just outright setting window.onload to your function, is that it will overwrite whatever window.onload was set to previously. This can lead to malfunctioning pages that are very difficult to debug. That was the theory of all of the posts about "doing window onload the right way".

function foo(){ alert("foo"); }
window.onload = foo;

// somewhere else on the page, maybe 1000 lines below
function bar() { alert("bar"); }

window.onload = bar;

You can deduce that "foo" will not be alerted. This would be bad if our foo actually did something, like start a video of a cat doing silly things. Devastating. How can they both work?!

The first accepted method is to build a function inline that combines two functions:

window.onload = function(){ foo(); bar(); }
This will work if that's the only time window.onload is set. You can't call window.onload explicitly inside of window.onload unless you like infinite loops. However, storing the referenced function and updating window.onload to the new function is fair. With that knowledge, let's continue our investigation.

The current method out there to get the previous window.onload and the additional function to call correctly looks like this:

function doublecast(fn1, fn2){
return function(){
if (typeof(fn1) == "function")
fn1();
if (typeof(fn2) == "function")
fn2();}
}

You would use this method to add to the window's onload functionality like so:

window.onload = doublecast(window.onload, foo);
If you needed to add more functions, you would do so in successive calls to "doublecast":

window.onload = doublecast(window.onload, foo);
window.onload = doublecast(window.onload, bar);

or through the ugly method of

window.onload = doublecast(window.onload, function(){ foo(); bar(); });
jQuery has made this acceptable, but it's still ugly! And the successive calls are not line-number friendly. Obviously, after reading John Resig's "Learning Advanced JavaScript", I feel I am ready to build a more appropriate and clean function.

function multicast(){
if (arguments == null || arguments.length == 0) return function(){ };

var fns = [], j = 0;
for (var i = 0; i < arguments.length; i++){
if (typeof(arguments[i]) == "function")
fns[j++] = arguments[i];
}

return function(){
for (var i = 0; i < fns.length; i++)
fns[i]();
};
}

Our new code looks like this:
window.onload = multicast(window.onload, foo, bar);
window.onload could have been set previously, and we don't care. We know we've defined two functions in the current module (say, an ASP.NET control or some other type of view) and it will work, so long as no one after us sets window.onload = myAwesomeLoadFunction; which would overwrite it... I guess no solution is completely future proof.
Discuss in the comments, please.

PromoteJS again

JS Array .sort

Just need to do that occasionally because w3schools sucks.

Eclipse bombed on me

Thankfully, I was able to find the answer to my problem in German! Here is the forum I read. I found a lot of English speaking topics with the error message I was getting, but they were having slightly different problems. Basically, I renamed the file in the ".safetable" folder, reimported my project, and everything's fine. Weird. Good thing, too, I've been getting tons of comment spam on my pictures because I haven't implemented my anti-spam mechanism on there. I will employ the same method I use here.

New Server!

This new server is about 4 times more powerful than my previous server, but for the same price. I will now definitely be putting lots of new stuff up on here to get the full use out of it. I have my subversion server running off of it, and again, an open invitation to anyone who wants all the free awesome code they can handle :P

Be wary of hosting companies installing MySQL for you. They might install the latest version of it (5.x), but in my.cnf specify that it should use the old password hashing method, the insecure one used before MySQL 4.1. This old method only generates a hash 16 bytes long, whereas the new one is 41 bytes (with a leading *). I did notice it but didn't think anything of it until I tried to login to my website here to post a new item. I had just finished getting all of my passwords for my websites input too. This required looking in the config file that I read to connect to the database to get the current password out, then running "grant all privileges on database.* to 'user'@'localhost' identified by 'password'" for each one. Ridiculous. I think I got them all right on the second go, I didn't feel like reading in the config files again. This site works, and so does Jim and Kate's site (which is awful and is long overdue for an upgrade). Vacre Tei works too. Stringed.org might work but not all of the name servers are pointed correctly yet, so I can't see it. It might work for you, it worked at the office.

Also, if you get "Can't connect to MySQL" and it's running, chances are it isn't allowing networking. I just commented out those two lines (old-passwords and skip-networking) and if I got all the passwords right, everything should work fine. Overall, the transition from the old server went without a hitch, but was a pain in the ass. Here's the breakdown:

  1. Zip up the websites on May 24 and promise not to upload any new files because then they'd also have to be pushed over.

  2. Zip up the databases and promise not to make any new posts or anything.

  3. Zip up tomcat since I don't want to have to reconfigure it

  4. Download them (the size of jasontconnell.com gzipped up is currently over 600 MB)

  5. Upload everything to the new server.

  6. Extract everything where it belongs.

  7. Update the MySQL passwords

  8. Set JAVA_HOME

  9. Hope it works


There were some other steps in there and perhaps at a later date I will be willing to tell them, but that's the gist of it. Enjoy!

Fixed my event class

Here's the updated code which should work all the time.

var EventList = new Array();
var g_eventIndex = 0;

function Event(obj, type){
if (obj._eventIndex){
if (EventList[obj._eventIndex][type]) return;
}
else
obj._eventIndex = g_eventIndex++;

if (typeof(EventList[obj._eventIndex]) == "undefined")
EventList[obj._eventIndex] = new Array();

EventList[obj._eventIndex][type] = true;
this.handlers = new Array();
this.type = type;
this.obj = obj;
this.obj._event = new Array();
this.obj._event[type] = this;

if (typeof(this.obj.addCustomEvent) != "function"){
this.obj.addCustomEvent = function(type, fn){
if (typeof(fn) == "function"){
this._event[type].handlers.push(fn);
return true;
}
return false;
}
}

this.raise = function(sender, args){
for(var i = 0; i < this.handlers.length; i++){
this.handlers[i](sender, args);
}
}
}

// addEvent(obj, "event", func);


function addEvent(obj, evType, fn, useCapture){
if (typeof(obj._eventIndex) == "number" && EventList[obj._eventIndex][evType] && obj.addCustomEvent){
var r = obj.addCustomEvent(evType, fn);
return r;
}
else if (obj.addEventListener){
obj.addEventListener(evType, fn, useCapture);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else {
alert("Handler could not be attached");
}
}


Now it will handle events of the same name in different objects. I just didn't want to have to come up with different names for events in different objects that did nearly the exact same thing.

I was reading a bit on the internets about how people do this type of thing. I read a post on Yahoo! that said the YUI event handling mechanism is "only 2KB". This is 55 lines with liberal white spacing. The thing about computer science is that sure, there might be something out there that does what you need it to do, and you can get it for free, but it's gonna do tons of other stuff that you really don't need. Not yet anyway. Same goes for software in general. If you need a simple photo editor, you're not gonna pay $600 for Photoshop when iPhoto will do (part of a $79 package with tons of other neat software, which also is overkill if you don't need that other stuff). So, if I need something very specialized, small, and easy to use, I'll write it. If you need this as well, feel free to use mine directly or for knowledge. It's not big or special, but will be used as part of a big and special project :) That will come soon.

More awesome free code!

I just have to ask that you send $5 to me via email. Thanks.

It turns out that I needed a way to raise events in my pager from the last post. Like when the page changed, I wanted to display "Page 3 of 5 (9 elements)" (for my test size of 2 elements per page (not likely to happen)). But also, for future reference, if say, to save on load times, only load X pages, we'll call it 5, at a time, so 10 elements, so then when they got to page 4 or 5, the non-pager code can load 10 more elements asynchronously, and call refresh on the pager to update the page count etc. Here's the event object:

var EventList = new Array();

function Event(obj, type){
if (EventList[type]) return;
EventList[type] = true;
this.handlers = new Array();
this.type = type;
this.obj = obj;
this.obj._event = new Array();
this.obj._event[type] = this;

if (typeof(this.obj.addCustomEvent) != "function"){
this.obj.addCustomEvent = function(type, fn){
if (typeof(fn) == "function"){
this._event[type].handlers.push(fn);
return true;
}
return false;
}
}

this.raise = function(sender, args){
for(var i = 0; i < this.handlers.length; i++){
this.handlers[i](sender, args);
}
}
}

// addEvent(obj, "event", func);


function addEvent(obj, evType, fn, useCapture){
if (EventList[evType] && obj.addCustomEvent){
var r = obj.addCustomEvent(evType, fn);
return r;
}
else if (obj.addEventListener){
obj.addEventListener(evType, fn, useCapture);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else {
alert("Handler could not be attached");
}
}


Yup, it only handles on event per object right now. That's easily remedied if I use something other than "obj._event". Like this.obj._event = new Array(); this.obj._event[type] = this;. Will do that soon. UPDATE: Fixed that, and used the EventList array to keep track of custom events, and modified the addEvent function to use that and see if it's a custom event first. This way, if I add custom events to a "div" html element, it will work, whereas before it would have evaluated one of the other ones (attachEvent or addEventListener) before it would have added the event where I wanted it. UPDATE 2: Damn, that still has a shortcoming. To be fixed later, like Tuesday. I'm booked this weekend. If you can point out the shortcoming, I'll give YOU $5 :P A hint would be that you don't have to worry about "click" events, only custom events... but damn near everything has a "click" event.

Notice that I modified the "addEvent" function that I shamelessly stole from QuirksMode.org to check if the object that we're adding the event to has an "addCustomEvent" function on it. If it does, call it, which is added to the object when it creates a new event. This is how the pager uses it:

function Pager(){
this.init = function(divid){
...
this.textspan = null;

this.onPageChanged = new Event(this, "pageChanged");
addEvent(this, "pageChanged", this.internalOnPageChanged);
this.initialized = true;
}
...

this.pageChanged = function(pageNumber){
// change page functionality here...

this.onPageChanged.raise(this, { page : this.currentPage, pages: this.pageCount, total: this.elementCount});
}

...

this.internalOnPageChanged = function(pager, args){
if (pager.textspan){
clearChildren(pager.textspan);
pager.textspan.appendChild(document.createTextNode("Page " + (args.page+1) + " of " + args.pages + " (" + args.total + " elements)"));
}
}



I removed unnecessary code but you get the idea. As always, I found code online to do it, but it was too complicated (KISS = Keep it simple stupid). If I have to add to it I will but it doesn't have to solve all of my problems right now. In my html page that is apart from the pager, I add another handler for "pageChanged" to just test it out. Integrating it seamlessly with the "addEvent" function is crucial, so now I just use the same exact syntax for every event, whether it's custom or not. So this works perfectly:

addEvent(window, "load", loaded);
var pager = new Pager();



function loaded(){
pager.init("pager");
addEvent(pager, "pageChanged", pagerPageChanged);
loadNotes();
}

function pagerPageChanged(p, args){
var i = args.page;
//alert("page changed : " + args.page + " of " + args.pages + " pages, " + args.total + " elements in all");
}


That's from "index.html" because I don't need any kind of server side scripting language with my new Ajax framework. And it's loading data, saving data, deleting data, even will begin to authenticate users soon, on the server. This keeps the custom code on the server down to 0 lines. Yup, no lines of Java pertaining to a "Notes" application (basic and without user authentication or authorization). I like writing Java but this will make things quicker :) When I incorporate YUI! into it, it'll even be beautiful. Ahh, until that day.

Sunburned and brain fried

I wrote a javascript pager tonight. It'll be in use on my new Javascript front end, hopefully coming here soon. It's all AJAX-y. I like it. Here's the code:

function Pager(){
this.init = function(divid){
this.div = document.getElementById(divid);
this.divToPage = document.getElementById(this.div.getAttribute("divtopage"));
this.pageSize = parseInt(this.div.getAttribute("pageSize"));
this.elementCount = this.divToPage.childNodes.length;
this.currentPage = (params(divid+"_page") != null ? parseInt(params(divid+"_page")) : 0);
this.pageCount = this.elementCount == 0 ? 0 : Math.ceil(this.pageSize / this.elementCount);
this.initialized = true;
}

this.refresh = function(){
if (this.initialized){
this.elementCount = this.divToPage.childNodes.length;
this.pageCount = this.pageSize == 0 ? 20 : Math.ceil(this.elementCount/this.pageSize);
if (this.elementCount < this.pageSize){
this.div.style.display = "none";
}
else {
this.div.style.display = "block";
clearChildren(this.div);
this.showPager();
}
}
}

this.showPager = function(){
var firstlink = new PagerLink("<<< first", this, function() { this.pageChanged(0); });
var nextlink = new PagerLink("next >>", this, function() { this.pageChanged(this.pager.currentPage+1); }) ;
var prevlink = new PagerLink("<< prev", this, function() { this.pageChanged(this.pager.currentPage-1); }) ;
var lastlink = new PagerLink("last >>>", this, function() { this.pageChanged(this.pager.pageCount); });

var pagelinks = Array();
for (var i = 0; i < this.pageCount; i++){
var pl = new PagerLink("" + (i+1), this, function(){ this.pageChanged(this.pageIndex); });
pl.setPageIndex(i);
pagelinks.push(pl);
}

if (this.pageCount > 10) this.div.appendChild(firstlink.link);

this.div.appendChild(prevlink.link);
for (var i = 0; i < pagelinks.length; i++){
this.div.appendChild(pagelinks[i].link);
}
this.div.appendChild(nextlink.link);

if (this.pageCount > 10) this.div.appendChild(lastlink.link);

this.pageChanged(this.currentPage);
}

this.pageChanged = function(pageNumber){
if (pageNumber >= this.pageCount) pageNumber = this.pageCount-1;
if (pageNumber < 0) pageNumber = 0;

var pageRange = [pageNumber*this.pageSize, (pageNumber+1)*this.pageSize];
for (var i = 0; i < this.elementCount; i++){
if (i >= pageRange[0] && i < pageRange[1]){
this.divToPage.childNodes[i].style.display = "block";
}
else this.divToPage.childNodes[i].style.display = "none";
}
this.currentPage = pageNumber;
}
}

function PagerLink(text, pager, clickEvent){
this.link = getLink(text, clickEvent);
this.pager = pager;
this.text = text;
this.link.pager = pager;
this.link.pageIndex = -1; // for page number links

this.link.pageChanged = function(pageNumber){
this.pager.pageChanged(pageNumber);
}

this.setPageIndex = function(i){
this.link.pageIndex = i;
}


}


It's used like this:

addEvent(window, "load", loaded);
var pager = new Pager();

function loaded(){
pager.init("pager");
}


You initialize the pager with the id of the element you want to hold the pager links.

<div id="pager" divtopage="existingNotes" pageSize="2">

</div>



Then when you add or remove elements from the element you are paging (I hard-coded a div), you refresh the pager, in case the number of pages has changed:

function saveNote(){
var note = getNote();
saveObject(note, noteSaved);
}

function noteSaved(note){
appendNote(note);
clearForm();

pager.refresh();
}
function deleteNote(note){
if (confirm("Are you sure you want to delete the note '" + note.Title + "'?")){
deleteObject(note, noteDeleted);
clearForm();
}
}
function noteDeleted(note){
removeNote(note.NoteId);

pager.refresh();
}


The code I'm writing uses AJAX, JSON and regular old javascript to talk to my ORM. So I can call stuff like "deleteObject" and it'll create the XML (parsing XML on the server is easier than parsing JSON) to make the call, call it, and get back a response. It's super simplistic right now, there's nothing returned except for some easy things.

The pager uses some common functions that have to be linked somewhere (which were either shamelessly stolen from quirksmode.org or I wrote them myself)

Array.prototype.clear = function(){
while (this.length >= 1){
this.shift();
}
}

function addEvent(obj, evType, fn, useCapture){
if (obj.addEventListener){
obj.addEventListener(evType, fn, useCapture);
return true;
} else if (obj.attachEvent){
var r = obj.attachEvent("on"+evType, fn);
return r;
} else {
alert("Handler could not be attached");
}
}

function clearChildren(node){
while ( node.childNodes.length >= 1 )
{
node.removeChild( node.firstChild );
}
}

function params(name){
var qs = document.location.search;
if (qs && qs.length > 1){
var start = qs.indexOf(name);
var end = start != -1 ? (qs.indexOf("&", start) != -1 ? qs.indexOf("&", start) : qs.length) : -1;
if (start != -1 && end != -1){
var value = unescape(qs.substring(start+1+name.length, end));
return value;
}
else return null;
}
else return null;

}

function getLink(text, clickEvent){
var jsl = document.createElement("a");
jsl.setAttribute("href", "javascript:void(0)");
if (text) jsl.appendChild(document.createTextNode(text));
if (clickEvent) addEvent(jsl, "click", clickEvent);
return jsl;
}

Subversion server up

If you want access, let me know, I'll create you an account and let you access all the code on all of my websites! There's lots of neat stuff up there.

Subversion is a code versioning system that is heavily used by developers on teams who want to keep their source code in a single place, let people edit or view it, and keep versions of it. Branches are often made when projects are going to be upgraded from, for example, 1.0 to 2.0. The 1.0 branch is kept so that any bugs that come up for 1.0 users can be fixed without them having to upgrade to 2.0. Patching is also very easy with a good versioning system. There are many reasons to use them, and hardly any not to.

But why am I using one? Frankly because I had to set up a Subversion server at work (svn for short), so I knew somewhat how to do it. Also, because it's a good backup system. Like, hmm, this code used to work, I wonder what I did. Then I can just look back at the history of a file and see what changed, and revert back to what worked. Also, for backup, if I ever want to work on it anywhere, I can, because it's on my server that's on the internet 24/7 with the same IP address. Also if something were to happen to my computer and I haven't backed up recently, I'm screwed.

I've set up SVN about 5 times now, so I'm pretty much an expert. One thing that I am not an expert at is the authz file. The other stuff is simple though. Here's a rundown:

yum install subversion (or apt-get install subversion or emerge subversion) (or download it and run the exe or dmg on Windows or Mac)

svnadmin create /var/data/svndata

edit the configuration
vi /var/data/svndata/conf/svnserve.conf

edit the passwd file (add your user)
echo "jason = jason123" > /var/data/svndata/conf/passwd

(I actually haven't tested that exact syntax... it may put it on the same line as a commented line, which wouldn't work)

svnserve -d -r /var/data/svndata

This will create the repository and start the server daemon.

Then you can test it out.

svn mkdir svn://localhost/dev

It might ask you for a username and password then hopefully spit out the message

Committed Revision 1.

Then you can use your favorite code editor plugin for svn and start sharing! Simple as that.

Is it hypocritical of me?

I am a software developer, but I will not buy most of the software that I use. Don't worry, I don't steal it. But I find that there's tons of software out there that's "free" but also good. I'm not talking about "Lite" versions of software, or "Shareware" trial versions that you can hack to never have to register / buy... I'm talking about FREE SOFTWARE that the authors wrote out of the kindness of their heart (and a huge curiosity and desperate search for knowledge) that they put on websites somewhere, put the code out there, and said "Here. Enjoy."

I know I couldn't have a job right now if all software was free. But custom development is another story. If someone needs an online scheduling system, then I (we) will write them one. I'm sure they could find one online and tweak it, but that's usually a shot in the dark. There's also somewhat of a difference between working out of curiosity and working to put food on the table. I've done an online scheduling system, it wouldn't be fun or illuminating since I've already done it.

The thing with software is that if you're making it for fun or out of curiosity, the cost of reproducing it today is so cheap. The same thing everyone's figuring out about music and movies. You produce it once, and everyone in the world can have a copy if you can get it to them. It's kind of crappy that we all still have to pay $0.99 per song at most places we can get digital music. Some software, however, can cost millions of dollars to produce, and these should charge hefty sums. But if you're just doing it out of curiosity and to learn something new, putting it on the web, even just code in html or something, gives that knowledge to everyone who happens to come across it. Then they can be inspired to try something new with it, and if they learn something out of it, they would share it with you since you sparked their interest and gave them that initial knowledge to light that fire under their bums.

Software today is generally in good shape this way. There are sites dedicated to sharing code (sourceforge.net to name one). There are sites set up to provide legal assistance to those looking to open source their code in a way that if someone uses it, they can't sue them if it goes bad. These licenses generally also state that if the next person makes changes, (s)he's free to either send the changes back to the original author to improve that software, or put his or her changes up on their own website and pass it on to people under the same license (free and open source). You don't see this type of stuff in business, only in SCIENCE. Science is the pursuit of knowledge. That's what it's all about baby.

Sorry for the Atom Spam

There's probably two subscribers to my feed, and one of them yelled at me :p But Google Reader should be back to business now, shortly after showing 400+ new items from this website. Atom has an ID that it uses, and Google Reader apparently keeps track of your read items by that ID and doesn't care about the date at all, evidently. So, new items showed up back to the beginning of this website.

So, will there be 400+ new items every time I post? No. It has all those IDs now, which are new and changed, and unless I change them, they won't show up again.

I can't just write a post apologizing though. I have to talk about the new cool thing that I'm working on here at home. I've talked about it before but apparently, wasn't able to carry through with it. Reasons were mainly that my laptop proved to be way to slow after the initial "wow" factor of installing Ubuntu. (My reaction now would be more "Wow, FreeBSD on a ThinkPad RULES!") The thing I'm talking about is my seamless integration between front end Javascript and backend Java. I wrote about it here. But I've made a few changes.

First, instead of the backend generating XML, it'll just read XML and spit out results in JSON. I'm kind of partial to that technology because of the name... it just kicks ass. I got my first foray into JSON last week at work. Initially, our backend PHP was just returning a few things, like 3, for the front end Javascript to parse and spit into HTML on the website. What the final requirements called for was way more complex, and I didn't want to have to come up with some naming scheme, because it would just get so ugly. Some things were related to each other but not to everything. It would be something like this:

"What is my name?:Jason,Steve,Albert:Jason:True: Jason,80%:Steve,5%,Albert:15%,2000,Elite"

This is like a quiz or trivia, where the question was "What is my name?", the possible answers "Jason,Steve,Albert" comma separated, the correct answer "Jason", whether that person got it write "True", and finally the percentages that other people guessed, the total points for that user "2000", and their ranking "Elite". And that's not even all of it. It has to include the next question, and all of those possible answers. Painful. So instead, this is what it looks like:

{
"LastQuestion" : {
"Text" : "What is my name?",
etc
},
"User" : {
"Points" : "2000",
etc
},
"AnswerPercentages" : {
"Jason" : "80",
etc
}
}


Much easier to maintain, much easier to read, much MUCH easier for the front end to parse! This can be parsed like this... assume your callback function is called "callback" and is passed whatever is returned from the server, e.g. the JSON code above:

function callback(data){
var obj = eval(data);
alert(obj.LastQuestion.Text); // will alert "What is my name?"
}


Yup, it's that easy.

Next affair... I'll need, and will build over time, a large Javascript library for building out websites with the data returned from the server. Tables, forms, etc. This will generally be a pain in the ass and is attributed to me delaying this project for as long as possible. Ugh.

If I get tired of that, then something's gotta give, and I'll either stop this crazy experiment or go crazy writing Javascript for hours and hours. Thank God for FireBug.

More code for your convenience

I had this log file from my server that's just been sitting there collecting errors for about 2 years. It got pretty big, upwards of 60 MB. So, I wrote this program to break it into chunks. I looked over the FreeBSD documentation to see if there's a program that just reads the last few KB or so. I was getting no output on my new "Browse By Month" code, and when I wrote this program, I was able to split the file into chunks of about 976 KB each (1,000,000 bytes).

package filebreaker;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;

public class FileBreaker {
public static void main(String []args){
String dir = "/usr/home/jason/Downloads/";
String filename = dir + "catalina.out";
int chunk = 1000000;

try {
BufferedInputStream fs = new BufferedInputStream( new FileInputStream(filename) );
BufferedOutputStream os = null;
int itr = 0;
byte []buf = new byte[chunk];
int status = 0;
while (status != -1){
status = fs.read(buf, 0, chunk);
String chunkFilename = dir + "/out/catalina-" + itr + ".txt";
os = new BufferedOutputStream(new FileOutputStream(chunkFilename));
os.write(buf);
os.flush();
os.close();
itr++;
}
fs.close();
}
catch (Exception e){
e.printStackTrace();
}
}
}


I found out the error. I'm using Java 1.6.0 on my local machine, and have yet to install it on my server. I used a method (Calendar.getDisplayName(int, int, Locale)) that wasn't part of Java 1.5.0, which is running on my server. DOH!

I also now like putting code on my website since I have new css for handling <code /> tags. It looks like this:

code {
display: block;
overflow: auto;
width: 600px;
white-space: pre;
}

code br {
display: none;
}


:D My text formatter takes line breaks and replaces them with "<br />", and I wanted code to be preformatted so I don't have to replace tabs with spaces, and also I didn't have to check if I was inside a "code" tag and not insert <br /> for line breaks. It's just a very convenient way to handle it that might look like crap in another browser that's not FireFox. I'll add lots more code now :)

Hello URL Rewriting!

After about an hour of programming, I have my URLs in a search engine friendly format! This has many other benefits as well:

For instance, I don't have to know the ID of the news I want to link to, just the title. So, I can make a link to a news item I have previously written by typing in the title in a format that it expects. For the news item titled "The Guitar Player", the link will be "the-guitar-player". Click here to test.

Also, if I want, I can modify my "text writer" component in JSF, to search for strings like "[[The Guitar Player]]", and to write out the url, namely "/news/the-guitar-player". Or I'd have to make it "[[News|The Guitar Player]]" if I want to add more dynamic linking, say for photos, downloads, comments, etc. This is much like a Wiki works. It can be a "news wiki" or a "wiki blog" or something ridiculous like that.

Also, after I save this, it will probably write out all of the news in my atom.xml file... it might take a while, there'll be 454 news items! That's ok though. I'll probably have to delegate that task to a new thread, so I can get to adding more news right away.

Categories are changed for rewriting purposes. "Labels" have not been updated yet. I have to make a new page because a "Label" object can pertain to Downloads and Pictures, so I have to list results for each, or modify the URL rewrite filter to accept "/picture/label/me" and "/download/label/music" to list pictures of me and my music downloads, respectively.

I don't run Apache HTTPD, so I had to write the URL rewriter all in Java for Tomcat. It wasn't too bad. Regular Expressions are neat.

The meat:

String url = ((HttpServletRequest)req).getRequestURL().toString().replace(HttpUtil.getBaseUrl((HttpServletRequest)req), "");

if (rewriteConfig.ignoreUrl(url)){
chain.doFilter(req, resp);
}
else {
String location = rewriteConfig.getLocation(url);
String rewriteUrl = rewriteConfig.getUrl(location, url.replaceAll("/" + location + "/", ""));

if (rewriteUrl != null){
RequestDispatcher dispatcher = req.getRequestDispatcher(rewriteUrl);
dispatcher.forward(req, resp); // forward the request
}
else {
chain.doFilter(req, resp);
}
}


url is the requestUrl stripped of the host. This is so I can make it work on dev (http://localhost:8080/jtccom) and production (/).

This filter is called for every request, so I have an "ignore" map set up. This tests if the URL contains a ".", since every rewritten url will only contain numbers, letters, and the occasional "/" or "-". This is a HashMap for quick lookups. So, it won't attempt to rewrite calls to "/js/someFile.js".

The "location" part of the request is "/x/", so for news it will be "/news/". The location is "news". This is so I only have to loop through rewrite patterns for news, not for everything.

The pattern is written as "([0-9a-z-]+)/?" so if I pass in the whole URL (without the host), "/news/my-news-title", it won't match, so that's what the next line does, removes "/news/" so it's just "my-news-title". The getURL function returns the rewritten URL, so like "/news.jsf?title=my-news-title". I created a new unique column for News, and my "DaoWeb" interface has a "ChangeListener" property, so if I create a news or change it's title, it updates the "TitleKey" in this case. Pictures are just forwarded by their picture id for now, so
"/picture/23" as an example.

Coming soon: News by date span! /news/2008 for all news in 2008. /news/2008/03 for all news in March 2008! /news/2008/03/23 for all news on my birthday this year!!

Tune in for more updates. I'm cranking now, and Java is like riding a bike. I also fixed a few bugs.

Laptop Shopping...

Blows monkey snot (or insert your own, more vulgar version of "sucks" here). When you're going to look at a store instead of online, of course you have to deal with salespeople who know less about laptops than you do. You can't tell them exactly what you're going to be using it for, because it won't help them decide what model you need, and you already have in mind that you want a "business" laptop. Not something for games, or something with eighteen card readers on it. Definitely not something that comes preinstalled with Windows Vista.

I'm trying to tell the guy, "I don't need HDMI, card readers, Bluetooth, photo album software, MS Office, built in web cam... I have a Mac" :D I just need a good CPU, a fast hard drive, and decent amount of RAM. Wireless is optional. Before I let him in on the little secret that I know a slow mother f@#%@$ing hard drive when I see it, he was trying to push 4800 RPM drives on me.

When he was trying to sell me anti-virus software and MS Office, I sorta just smiled at him and said "I won't be running Windows", in a smart ass type of way that only a true geek can do.

What I ended up with was a Lenovo ThinkPad T61 (as "business" as it gets, and what I have been eyeing online for weeks), with a 14.1" screen (which is running at 1280x800 on Gnome on my FreeBSD, so I'm fine with it), 2.1 GHz Core 2 Duo, 1 GB RAM (but I put in another GB when I got it home), a 100GB 7200 RPM hard drive, and an NVidia basic video card, and tons of ThinkPad goodness (which means nothing!). Minimal but is absolutely awesome. It's 5.3 lbs. It kicks the f%#@ out of my previous laptop's ass. So fast.

Compare to my other laptop (which I'm fuzzy on now):
1.5 GHz single core Centrino, I upped it to 1GB ram, 4800 RPM 60GB hard drive, ATI video card.

A hard drive's speed will make or break productivity in programming. I also just needed an upgrade in CPU speed, and RAM never hurts :)

There's just one thing though that I wish I had noticed about this laptop before I bought it, but I think I can deal with it. Most laptops today have a touch version of a mouse's "scroll wheel" on their pads. My previous laptop has it. This one doesn't :( I can use page up / down, or go old school and use the scroll bar. I'm used to it though, since I had Gentoo Linux on my other laptop for so long, nothing f%@#$ing worked anyway :)

So, I'm installing FreeBSD, getting my development environment set up, and will be creating features for this website, and new websites and software in the near future.

Open Source is so inexpensive

Keeping with my 'money' theme from the last few posts, here we have something on the complete opposite side of the spectrum. Not including donations, I've paid $0 for all of the software on my other laptop, and it does everything.

Now, I've been thinking about doing something drastic. Since I installed Ubuntu Linux, my development time on that laptop has actually diminished about 100%. I haven't done any development on it. This is because it's so f@#$@#ing slow. I do a lot of ANT builds and they just take forever. The drastic thing I've been thinking of is upgrading my laptop to a new one. Preferably a Lenovo ThinkPad or something. Customizing one of those for a Linux installation (everything except picking Linux, since they only offer Vista Home Basic) is a lot cheaper than configuring it for even Vista Home Basic. All I need is 2GHz Core 2 Duo (the minimum CPU option), 1GB RAM (although I've been bumping it up to 2GB in the "Customize" section), 1Gbps LAN, and integrated graphics. I could opt to have Wireless, but that's extra. It's not that expensive, although I can't afford it now.

So, instead of getting a beefier laptop to run a slow operating system (Gentoo ran super fast on the same exact old crappy laptop), I was looking into some other operating system that's just as powerful and fast as Gentoo, but not the hardest thing to set up. In my search, I decided to go with something that I've been eyeing ever since I got into free software. FreeBSD.

Having this Mac, and having nothing to work on when I got a side job doing .NET with SQL Server for one of the local schools (Episcopal Academy, which isn't exactly 'one of the local schools'), and the Mac being Intel, I found out about that program called "Parallels", which can run virtual machines of multiple operating systems. I have a Windows one installed for all of that development I did for EA, and I have an Ubuntu Linux one for when I tried it out (much like now) before I installed it on my laptop. So, I did the same thing with FreeBSD. I think I'm going to give it a shot on the laptop.

It's a lot easier than Gentoo, that's a plus. It's as well documented as Gentoo (just visit freebsd.org and see for yourself), it's *almost* as fast as Gentoo, and it runs Gnome. The only thing that I was wondering was "Will it run everything?" Yes it will.

FreeBSD is Unix, not Linux. While they are similar in some areas, there's others where they are just very very different. Like, FreeBSD and Linux use different threading models, which Sun depends on in its Linux implementation of Java and Sun does not provide a native implementation of Java for FreeBSD, yet. However, you can get "Linux compatibility mode" running on FreeBSD which will make the Linux implementation work on FreeBSD. The down side of that is that FreeBSD has to build the source for Java to install it, and the source is released under an incompatible license. So, when you install it, it fails, and asks you to download certain files before it can continue. And still, after you do that, you're contractually obligated (through the license) to NOT distribute the resulting built Java binaries. It's weird. There is hope, though, as most believe that Java 7 (it's at 6 right now) will be released under the GPL, since Sun completely open sourced Java recently.

So, I'll give FreeBSD a shot, and if it doesn't work out, then I'll download and try out another one!

Interesting side story. FreeBSD offers its ISOs in "torrent" format. I've known what BitTorrent was since about 7 years ago, but never got into it. It's a giant legal mess since it's also used to distribute "pirated" material. But there are very legal, morally good ways of using BitTorrent. Anyway, I downloaded it through that (a BitTorrent client called "Transmission" for Mac, also free open source software), and it's just neat seeing all the people you're downloading the same file from simultaneously. It's really brilliant software, BitTorrent is.