function npGet(cont, parts) {
    return function(retainNull) {
        var curCont = cont;
        var curPart;
        var allSame;
        var out;
        var i;
        var j;

        for(i = 0; i < parts.length - 1; i++) {
            curPart = parts[i];
            if(curPart === -1) {
                allSame = true;
                out = [];
                for(j = 0; j < curCont.length; j++) {
                    out[j] = npGet(curCont[j], parts.slice(i + 1))(retainNull);
                    if(out[j] !== out[0]) allSame = false;
                }
                return allSame ? out[0] : out;
            }
            if(typeof curPart === 'number' && !isArrayOrTypedArray(curCont)) {
                return undefined;
            }
            curCont = curCont[curPart];
            if(typeof curCont !== 'object' || curCont === null) {
                return undefined;
            }
        }

        // only hit this if parts.length === 1
        if(typeof curCont !== 'object' || curCont === null) return undefined;

        out = curCont[parts[i]];
        if(!retainNull && (out === null)) return undefined;
        return out;
    };
}
convert a string s (such as 'xaxis.range[0]')
representing a property of nested object into set and get methods
also return the string and object so we don't have to keep track of them
allows [-1] for an array index, to set a property inside all elements
of an array
eg if obj = {arr: [{a: 1}, {a: 2}]}
you can do p = nestedProperty(obj, 'arr[-1].a')
but you cannot set the array itself this way, to do that
just set the whole array.
eg if obj = {arr: [1, 2, 3]}
you can't do nestedProperty(obj, 'arr[-1]').set(5)
but you can do nestedProperty(obj, 'arr').set([5, 5, 5])