define([
           'dojo/_base/declare',
           'dojo/_base/array',
           'dojo/has'
       ],
       function( declare, array, has ) {
var FileBlob = declare( null,
/**
 * @lends JBrowse.Model.FileBlob.prototype
 */
{

    /**
     * Blob of binary data fetched from a local file (with FileReader).
     *
     * Adapted by Robert Buels from the BlobFetchable object in the
     * Dalliance Genome Explorer, which was is copyright Thomas Down
     * 2006-2011.
     * @constructs
     */
    constructor: function(b) {
        this.blob = b;
        this.size = b.size;
        this.totalSize = b.size;
    },

    slice: function(start, length) {
        var sliceFunc = this.blob.mozSlice || this.blob.slice || this.blob.webkitSlice;
        return new FileBlob(
            length ? sliceFunc.call( this.blob, start, start + length )
                   : sliceFunc.call( this.blob, start )
        );
    },

    _newlineCode: "\n".charCodeAt(0),


    fetchLines: function( lineCallback, endCallback, failCallback ) {
        var thisB = this;
        this.fetch( function( data ) {
                        data = new Uint8Array(data);

                        // throw away the first (probably incomplete) line
                        var parseState = {
                            data: data,
                            offset: 0
                        };
                        var line;
                        while( parseState.offset < data.length && ( line = thisB._getline( parseState ) )) {
                            if( line.charAt( line.length-1 ) == "\n" )
                                lineCallback( line );
                        }

                        endCallback();
             }, failCallback );
    },

    _getline: function( parseState ) {
        var data = parseState.data;
        var newlineIndex = array.indexOf( data, this._newlineCode, parseState.offset );

        if( newlineIndex == -1 ) // no more lines
            return null;

        var line = '';
        for( var i = parseState.offset; i <= newlineIndex; i++ )
            line += String.fromCharCode( data[i] );
        parseState.offset = newlineIndex+1;
        return line;
    },

    readLines: function( offset, length, lineCallback, endCallback, failCallback ) {
        var start = this.start + offset,
            end   = start + length;
        var skipFirst = offset != 0;
        this.slice( offset, length )
            .fetchLines(
                function() {
                    // skip the first line if we have a
                    // nonzero offset, because it is probably
                    // incomplete
                    if( ! skipFirst )
                        lineCallback();
                    skipFirst = false;
                }, endCallback, failCallback );
    },

    read: function( offset, length, callback, failCallback ) {
        var start = this.start + offset,
            end = start + length;
        this.slice( offset, length )
            .fetch( callback, failCallback );
    },

    fetch: function( callback, failCallback ) {
        var that = this,
            reader = new FileReader();
        reader.onloadend = function(ev) {
            callback( that._stringToBuffer( reader.result ) );
        };
        reader.readAsBinaryString( this.blob );
    },

    _stringToBuffer: function(result) {
        if( ! result || ! has('typed-arrays') )
            return null;

        var ba = new Uint8Array( result.length );
        for ( var i = 0; i < ba.length; i++ ) {
            ba[i] = result.charCodeAt(i);
        }
        return ba.buffer;
    }

});
return FileBlob;
});
@@ -1738,24 +1738,75 @@
  );%0A    %7D,%0A%0A
+    // get a line of text, properly decoding UTF-8%0A
     _getline
@@ -1828,24 +1828,66 @@
 seState ) %7B%0A
+        var newline = this._newlineCode;%0A%0A
         var 
@@ -1926,249 +1926,650 @@
 var 
-newlineIndex
+i
  = 
+p
 ar
-ray.indexOf( data, this._newlineCode, parseState.offset );%0A%0A        if( newlineIndex == -1 ) // no more lines%0A            return null;%0A%0A        var line = '';%0A        for( var i = parseState.offset; i %3C= newlineIndex; i++ )%0A
+seState.offset;%0A%0A        var line = %5B%5D;%0A        while( i %3C data.length ) %7B%0A            var c1 = data%5Bi%5D, c2, c3;%0A            if (c1 %3C 128) %7B%0A                line.push( String.fromCharCode(c1) );%0A                i++;%0A                if( c1 == newline ) %7B%0A                    parseState.offset = i;%0A                    return line.join('');%0A                %7D%0A            %7D else if (c1 %3E 191 && c1 %3C 224) %7B%0A                c2 = data%5Bi + 1%5D;%0A                line.push( String.fromCharCode(((c1 & 31) %3C%3C 6) %7C (c2 & 63)) );%0A                i += 2;%0A            %7D else %7B%0A                c2 = data%5Bi + 1%5D;%0A                c3 = data%5Bi + 2%5D;%0A    
     
@@ -2580,19 +2580,22 @@
     line
- +=
+.push(
  String.
@@ -2611,19 +2611,144 @@
 ode(
- data%5Bi%5D );
+((c1 & 15) %3C%3C 12) %7C ((c2 & 63) %3C%3C 6) %7C (c3 & 63)) );%0A                i += 3;%0A            %7D%0A        %7D%0A%0A        // did not get a full line
 %0A   
@@ -2776,22 +2776,9 @@
 t = 
-newlineIndex+1
+i
 ;%0A  
@@ -2790,20 +2790,20 @@
  return 
-line
+null
 ;%0A    %7D,

make FileBlob line-getting properly decode UTF-8