mapbox.markers.csv_to_geojson = function(x) {
    // Extracted from d3
    function csv_parse(text) {
        var header;
        return csv_parseRows(text, function(row, i) {
            if (i) {
                var o = {}, j = -1, m = header.length;
                while (++j < m) o[header[j]] = row[j];
                return o;
            } else {
                header = row;
                return null;
            }
        });
    }

    function csv_parseRows (text, f) {
        var EOL = {}, // sentinel value for end-of-line
        EOF = {}, // sentinel value for end-of-file
        rows = [], // output rows
        re = /\r\n|[,\r\n]/g, // field separator regex
        n = 0, // the current line number
        t, // the current token
        eol; // is the current token followed by EOL?

        re.lastIndex = 0; // work-around bug in FF 3.6

        /** @private Returns the next token. */
        function token() {
            if (re.lastIndex >= text.length) return EOF; // special case: end of file
            if (eol) { eol = false; return EOL; } // special case: end of line

            // special case: quotes
            var j = re.lastIndex;
            if (text.charCodeAt(j) === 34) {
                var i = j;
                while (i++ < text.length) {
                    if (text.charCodeAt(i) === 34) {
                        if (text.charCodeAt(i + 1) !== 34) break;
                        i++;
                    }
                }
                re.lastIndex = i + 2;
                var c = text.charCodeAt(i + 1);
                if (c === 13) {
                    eol = true;
                    if (text.charCodeAt(i + 2) === 10) re.lastIndex++;
                } else if (c === 10) {
                    eol = true;
                }
                return text.substring(j + 1, i).replace(/""/g, "\"");
            }

            // common case
            var m = re.exec(text);
            if (m) {
                eol = m[0].charCodeAt(0) !== 44;
                return text.substring(j, m.index);
            }
            re.lastIndex = text.length;
            return text.substring(j);
        }

        while ((t = token()) !== EOF) {
            var a = [];
            while ((t !== EOL) && (t !== EOF)) {
                a.push(t);
                t = token();
            }
            if (f && !(a = f(a, n++))) continue;
            rows.push(a);
        }

        return rows;
    }

    var features = [];
    var parsed = csv_parse(x);
    if (!parsed.length) return features;

    var latfield = '',
        lonfield = '';

    for (var f in parsed[0]) {
        if (f.match(/^Lat/i)) latfield = f;
        if (f.match(/^Lon/i)) lonfield = f;
    }

    if (!latfield || !lonfield) {
        throw 'CSV: Could not find latitude or longitude field';
    }

    for (var i = 0; i < parsed.length; i++) {
        if (parsed[i][lonfield] !== undefined &&
            parsed[i][lonfield] !== undefined) {
            features.push({
                type: 'Feature',
                properties: parsed[i],
                geometry: {
                    type: 'Point',
                    coordinates: [
                        parseFloat(parsed[i][lonfield]),
                        parseFloat(parsed[i][latfield])]
                }
            });
        }
    }
    return features;
};
