Posts about javascript

Javascript sort() Gotcha

Javascript pop quiz!

What is the result of this code?
var arr = [12, 3, 24]
arr.sort()
arr
You: [3, 12, 24]
Me: Wrong, You get an F! The correct answer is: [12, 24, 3].
You: WTF?
Me: Array.prototype.sort() uses lexical ordering by default. That is to say, it converts everything to string before comparing them. Thus, '12' comes before '24' comes before '3'.
You: So how do you make it sort numbers?
Me: You pass it a comparator function as an argument:
arr.sort(function(a, b){
  return a - b
})
You: That makes sense, I guess. But still...that is pretty messed up.


Posted by Toby 5 days ago about javascript and programming (0 comments)

Memoize for Javascript

I wrote a memoize function for Javascript today to cache file timestamps. I thought I would share it with the world. Here is the code:
function memoize(f){
    var cache = {}
    return function(){
        var keys = []
        for (var i = 0; i < arguments.length; i++){
            keys.push(typeof(arguments[i]) + ':' + String(arguments[i]))
        }
        var key = keys.join('/')
        if (key in cache){
            return cache[key]   
        }else{
            var val = f.apply(null, arguments)
            cache[key] = val
            return val
        }
    }
}
Let's say you have a function:
function f(x, y){
  return x + y * 2
}
If you memoize it:
f = memoize(f)
The first time you call f with arguments (3, 4):
f(3, 4) => 11
It will compute it, but the second time:
f(3, 4) => 11
It will merely return the cached value computed from the last time.

Note about the implementation: the equality metric used by this memoize function is: 2 objects are equal iff they are of the same type:
typeof(one) == typeof(other)
and, they have the same string represention:
String(one) == String(other)
That is it. Enjoy!
Posted by Toby 5 days ago about javascript and programming (0 comments)

Taking an Average in CouchDB

More fun with CouchDB, this time taking an average of something:
// map
function(doc){
    emit(null, doc.info.size) // in place of doc.info.size, you'd put whatever
                               // value you want averaged here
}
// reduce
function(keys, values, rereduce) {
    if (!rereduce){
        var length = values.length
        return [sum(values) / length, length]
    }else{
        var length = sum(values.map(function(v){return v[1]}))
        var avg = sum(values.map(function(v){
            return v[0] * (v[1] / length)
            }))
        return [avg, length]
    }
}
The end result will be 2 values, the first is the average, the second is the total number of values that we took an average of. Phew! Who woulda thought taking an average would be so involved!
Posted by Toby 5 months ago about couchdb, javascript and programming (0 comments)

Retrieve The Top N Tags in CouchDB

I am reading up on how map/reduce works in CouchDB, think I am getting the hang of it now, thanks to this great tool. I tried the "Retrieve the top N tags" example on this page. It didn't work for me at all. So I wrote my own as an exercise. Here's my code:
// the map function
function(doc){
    for(var i in doc.tags)
    {
        emit(null, doc.tags[i]);
    }
}

// the reduce function
function(key, values, rereduce){
    var hash = {}
    if (!rereduce){
        for (var i in values){
            var tag = values[i]
            hash[tag] = (hash[tag] || 0) + 1
        }
    }else{
        for (var i in values){
            var topN = values[i]
            for (var i in topN){
                var pair = topN[i]
                var tag = pair[0]
                hash[tag] = (hash[tag] || 0) + pair[1]
            }
        }
    }
    var all = []
    for (var key in hash)
        all.push([key, hash[key]])
    return all.sort(function(one, other){
        return other[1] - one[1]
    }).slice(0, 3)
}
The approach I took was different one from the one from the example page, but I believe it to be the more correct one: rather than returning the results keyed by the tag in the map step, I would emit every occurrence of every tag instead. Then in the reduce step, I would calculate the aggregation values grouped by tag using a hash, transform it into an array, sort it, and choose the top 3. For the rereduce case, I would combine a set of top 3 choices and then again pick the top 3 among them.
Posted by Toby 5 months ago about couchdb, javascript and programming (0 comments)

Adding Real Properties to FABridge

Although FABridge is a nice tool, it's got its shortcomings. I am going to fix one of them now, namely that it doesn't give you real properties, but uses Java's getter setter convention instead. In 3 out 4 browsers, the __defineSetter__ and __defineGetter__ methods are already usable, so why not take advantage? It's really easy as it turned out. Change the addPropertyToType method to:
    addPropertyToType: function(ty, propName)
    {
        var c = propName.charAt(0);
        var setterName;
        var getterName;
        if(c >= "a" && c <= "z")
        {
            getterName = "get" + c.toUpperCase() + propName.substr(1);
            setterName = "set" + c.toUpperCase() + propName.substr(1);
        }
        else
        {
            getterName = "get" + propName;
            setterName = "set" + propName;
        }
        function setter(val)
        {
            this.bridge.setPropertyInAS(this.fb_instance_id, propName, val);
        }
        ty[setterName] = setter;
        function getter()
        {
            return this.bridge.deserialize(this.bridge.getPropertyFromAS(this.fb_instance_id, propName));
        }
        ty[getterName] = getter;
        if (ty.__defineGetter__)
            ty.__defineGetter__(propName, getter);
        if (ty.__defineSetter__)
            ty.__defineSetter__(propName, setter);
    },
 And..., voila! You got yourself real properties. Now instead of writing:
app.getTextBox()
You will be much happier writting:
app.textBox
On non-sucky browsers that is.
Posted by Toby 5 months ago about flex, javascript and programming (0 comments)

Typed Deserialization with JSON

JSON is nice, but if you use it extensively, you go against the OO style of object modeling, which says that behaviors should be attached to objects themselves. Even if you are not an OO bigot, the OO way is sometimes just far more convenient and structures your code better, IMHO. Given my past work with JYaml, it can't be that hard to read in some JSON and directly deserialize it into a typed object can it?

Say you have a Person type:
function Person(name){
    this.name = name
}
Person.prototype.sayHi = function(){
    return "Hi! I am " + this.name
}
You create an instance called tommy:
> tommy = new Person('Tommy')
> tommy.sayHi()
Hi! I am Tommy
With a little magic from a toJson function that I whipped up:
> tommyJson = toJson(tommy)
> tommyJson
{__proto__: Person.prototype, name: "Tommy"}
tommyJson is now a string representation of tommy. You can see that it sets the special __proto__ link to Person.prototype, which will do its magic when it's deserialized. Now let's deserialize it back:
> tommyCopy = fromJson(tommyJson)
> tommyCopy.sayHi()
Hi! I am Tommy
> tommyCopy.constructor
function Person(name){
    this.name = name;
}
> tommyCopy === tommy
false
As you can see, the deserialized object tommyCopy is not just a plain object literal, but has all of the behaviors of Person.

The code for toJson and fromJson is so small I don't even bother putting it on github, so I'll just post it here(You may need ecma5array if you want to run it on a sucky browser):
function toJson(obj){
    function keys(o){
        var ret = []
        for (var key in o){
            var val = o[key]
            if (o.hasOwnProperty(key)) // exclude attributes in the parent
                ret.push(key)
        }
        return ret
    }
    function quote(str){
        return '"' + str + '"'
    }
    if (obj === null || obj === undefined)
        return String(obj)
    else if (obj.constructor === String)
        return quote(obj)
    else if (obj.constructor === Array)
        return '[' + obj.map(toJson).join(', ') + ']'
    else if (obj.constructor === Object)
        return '{' + keys(obj).map(function(key){return key + ': ' + toJson(obj[key])}).join(', ') + '}'
    else if (obj.constructor && obj.constructor.name)
        return '{' + ['__proto__: ' + obj.constructor.name + '.prototype'].concat(
            keys(obj).map(function(key){return key + ': ' + toJson(obj[key])})).join(', ') + '}'
    else
        return String(obj)
}
function fromJson(str){
    return eval('(' + str + ')')
}

Posted by Toby 5 months ago about javascript, programming and prototype (0 comments)

HTTP Server in 5 Lines With Webrick

Usually when I am prototyping a web UI - either in Javascript or Flex, I would just write a static html, because that's the simplest thing that works. But, once in a while, it doesn't work because of the security restrictions that the browser imposes on local files. Maybe you want to use ajax calls(which is sometimes problematic on IE), trying to use the google maps api, or the FABridge, whatever the reason may be. Well, you can get around this problem easily using this ruby script:
require 'webrick'
server = WEBrick::HTTPServer.new :Port => 1234
server.mount "/", WEBrick::HTTPServlet::FileHandler, './'
trap('INT') { server.stop }
server.start
This runs a web server at http://localhost:1234/ which mounts the top level directory to your current directory.

Update: Oops, it's not exactly that easy after all. In order to prevent caching - which you will want to do if you are doing development - you will want to write an extra class. The modified script:
require 'webrick'
class NonCachingFileHandler < WEBrick::HTTPServlet::FileHandler
  def prevent_caching(res)
    res['ETag']          = nil
    res['Last-Modified'] = Time.now + 100**4
    res['Cache-Control'] = 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
    res['Pragma']        = 'no-cache'
    res['Expires']       = Time.now - 100**4
  end
  
  def do_GET(req, res)
    super
    prevent_caching(res)
  end

end

server = WEBrick::HTTPServer.new :Port => 1234
server.mount "/", NonCachingFileHandler , './'
trap('INT') { server.stop }
server.start
Not 5 lines anymore, bummer! The code for NonCachingFileHandler was stolen  from unittest_js.
Posted by Toby 5 months ago about html, javascript, programming and ruby (0 comments)

Describing "with"

The with statement of javascript got a lot of hate from Douglas Crockford in his famous Javascript lecture videos, as well as in this blog post. Now it gets hated on by the Ecmascript 5 spec too, where its use is disallowed in strict mode. The general consensus is that, you should never use with, ever.

But, it is so very tempting - once in a while - to make use of this forbidden feature. So I decided to write a spec for with which will uncover exactly what all the pitfalls of using it are. Then, you will decide for yourself whether you want to use it or not. Without further ado(comments are inlined with the code):
describe('with')
  .beforeEach(function(){
    this.name = 'Houston'
    this.age = 18
    this.eyes = null
    this.lips = undefined
    window.name = "window's name"
  })
  .should('get attr', function(){
    with(this){
      expect(name).toBe('Houston')
      expect(age).toBe(18)
      expect(eyes).toBe(null)
      expect(lips).toBe(undefined)
    }
  })
  .should('raise if doesn\'t exist', function(){
    with(this){
      expect(function(){namee}).toRaise()
      this.namee // but write "this." and doesn't raise
    }
  })
  .should('reference variable from outside with scope', function(){
    var hair = 'dark'
    with(this){
      expect(hair).toBe('dark')
    }
  })
  .should('do assignment', function(){
    with(this){
      name = 'Kenny'
    }
    expect(this.name).toBe('Kenny')
  })
  .should('not do assignment if attr does exist yet', function(){
    /*
    when you make an assignment to an attribute of the object in 
    question(the one you are working *with*), that attribute must 
    already exist(been assigned some value), otherwise, it will 
    behave like it would outside the with scope and assign the 
    attribute to the window object.
    */
    with(this){
      hair = 'dark'
    }
    expect(this.hair).toBe(undefined)
    expect(hair).toBe('dark')
    expect(window.hair).toBe('dark')
  })
  .should('do assignment if attr is defined to be undefined', function(){
    /*
    I had always thought that assigning undefined to an attribute 
    is the same as it having never been assigned a value at all. 
    I was wrong. But deleting the attribute will revert it to the 
    state of non-existence, as the next test shows.
    */
    with(this){
      lips = 'pink'
    }
    expect(this.lips).toBe('pink')
  })
  .should('not do assignment if attr has been ' +
    'deleted(deleting makes it "not exist")', function(){
    /*
    Deleting the attribute will revert it to the 
    state of non-existence, which makes unassignable from within
    the 'with' scope again
    */
    delete this.name
    with(this){
      name = 'tony' // this sets window.name to 'tony'
    }
    expect(this.name).toBe(undefined)
    expect(window.name).toBe('tony')
  })
  .should('delete', function(){
    with(this){
      delete name
    }
    expect(this.name).toBe(undefined)
  })
  .should('deleting makes the attribute go away, and you are' +
    'again accessing window\'s attrs', function(){
    with(this){
      expect(name).toBe('Houston')
      delete name
      expect(name).toBe("window's name")
    }
  })
  .should('be silent if deleting non-existing attr', function(){
    with(this){
      delete hands
    }
    expect(this.hands).toBe(undefined)
  })
  .should('do assignment if attr is null', function(){
    with(this){
      eyes = 'brown'
    }
    expect(this.eyes).toBe('brown')
  })
  .should('do assignment using this if attr does not exist', function(){
    with(this){
      this.hair = 'dark'
    }
    expect(this.hair).toBe('dark')
  })
  .should('var outside with scope does not override', function(){
    /*
    variables defined outside the with scope has lower precedence 
    than attributes of the object in question.
    */
    var name = 'Jen'
    with(this){
      expect(name).toBe('Houston')
    }
  })
  .should('var outside with scope does not override (2)', function(){
    this.window = 'Not window'
    with(this){
      expect(window).toBe('Not window')
    }
  })
  .should('var inside scope overrides', function(){
    with(this){
      var name = 'Jen'
      expect(name).toBe('Jen')
    }
  })
  .should('have no lexical scoping', function(){
    var instrument = 'sax'
    with(this){
      var instrument = 'trumpet'
      expect(instrument).toBe('trumpet')
    }
    expect(instrument).toBe('trumpet')
  })
  .should('erase vars after the with scope closes', function(){
    var name = 'blah'
    with(this){
      expect(name).toBe('Houston')
    }
    expect(name).toBe('blah')
  })
Full source is at github.
Posted by Toby 7 months ago about javascript and programming (0 comments)

ThinAir: Create DOM Elements Without Sweating

Announcing yet another tiny javascript library: ThinAir.

ThinAir is a DSL for creating DOM elements on-the-fly. Example, if you want to create a text input:
var elm = thinair.input({type: 'text', name: 'name', value: 'Bobby'})
But, having to write "thinair." everywhere makes the code pretty cluttered as soon as you start doing some complicated stuff. One way to avoid having to write the namespace is using the with keyword:
with(thinair){
  var elm = input({type: 'text', name: 'name', value: 'Bobby'})
}
Within the scope of the with statement you can use all the tag-named methods unadorned. Now, let's try something else... A paragraph containing a checkbox and a label? You got it:
var elm =
  p(
    input({type: 'checkbox', name: 'checkme'}),
    label('Check me, please!')
  )
At this point elm is a jQuery wrapped element, so you can add it to the DOM by using one of jQuery's standard ways of inserting it, for example:
$(document.body).append(elm)
To generate a unordered list out of an array of elements:
ul(['bobby', 'timmy', 'barry'].map(function(name){
  return li(name)
})
A more elaborate example is here. That's really about all I wanted to show. The code/project page is on github.
Posted by Toby 7 months ago about javascript, jquery and programming (0 comments)

First Encounter with IE8

If you are like me, you have been just ignoring IE8 all this time. What you didn't know is that, chances are, your app(or javascript library) doesn't work on it. Sure, I had heard about how it breaks sites all over the place and they worked around it by using an database of sites for which they override the browser to use the IE7 compatibility mode. That's right, before rendering any web page, IE8 actually makes a request back to Microsoft headquarters to find out whether it should render the page in regular mode, or IE7 compatibility mode. Wow, kill me now...

Anyway, so I didn't think too much of that. Until today, when I had the pleasure of "porting" some code to work in IE8. To be fair, this app hasn't been tested much on IE period, before today. So, here's me dutifully reporting my findings.

First, of all, if you are using a non-core(unlike jQuery, prototype, mootools, etc., which would be core) third party Javascript library, it probably does not work with IE8. The Google Visualization API is one example. Because of this, we ended up explicitly telling it to use IE7 compat mode. The way you do this is to put this meta tag inside of your <head> element:
<meta http-equiv="X-UA-Compatible" content="IE=7" />

Second, make sure you specify the doctype. We used html 4.01 strict. If you don't specify it, standard things like transparencies won't work. This should be obvious.

Third, even if you do all the right things, transparencies still may not work... See this gem to read all about it. In my situation, I had some semi-transparent pngs inside some div that had its opacity value set. It turns out that in IE8, this causes the png transparency to not work at all. You could fix it the hard way in javascript as described in Bb's article, or you could just use IE7 compat mode, which is what I did, sigh...

One more thing that was interesting was that forgetting to write var in front of a new variable caused an error to be throw. That looks a lot like strict mode behavior. I'll have to take a second look at this to verify, but it is worth mentioning.

Oh, almost forgot: on the positive side, MS did a great job with the built-in developer tool, which opens in a separate window via F12. I used it to inspect the DOM and change css attributes on the fly. I am sure it can do more that I haven't tried. Kudos to the IE team for that.
Posted by Toby 7 months ago about ie8, javascript and programming (0 comments)

Radio Buttons Simplified

Working with radio buttons in javascript has always been a pain, even with the help of jQuery. For example, if you have something like:
<label>Yes: </label>
<input type="radio" name="yes_no" value="yes"><br>
<label>No: </label>
<input type="radio" name="yes_no" value="no">
First, it is tedious, bordering non-trivial to get the get the current value of this radio selection group: is it yes or no?
Second, it is also tedious, bordering non-trivial to listen for changes to the value, especially given the different behaviors of different browsers(IE does not emit a change event when you click on a radio button to change it until you blur from it).

I wrote a tiny jQuery library to make this easier, with which you can do:
> $.radio('yes_no').val()
'yes'
to get the selected value, and to listen for changes:
> $.radio('yes_no').change(function(ui){
    console.log('yes_no value changed to ' + ui.val());
})
That's as simple as it gets. Code is as usual, on github.
Posted by Toby 7 months ago about javascript, jquery, programming and radiobutton (0 comments)

ColorCode Lineup - A Color-Code Scheme Editor

For my work I had the need to dynamically color code many objects, and I have to make sure that all the colors used are both distinguishable and the text inside them legible. So, I whipped up a little jQuery and built a tool for making and editing color-code schemes: ColorCode Lineup. You can view source to see the code, but it's also hosted on github.
Posted by Toby 8 months ago about colorcoding, javascript, jquery, programming and tools (0 comments)