commit b7c6cbfadcc72c9ee1d5048087c840398422a72c
parent 863de76fb6cb97d6b908a63d57f956c2da13fa0f
Author: Sergej Orlov <wladimirych@gmail.com>
Date:   Fri, 16 Dec 2016 18:17:19 +0300
[tracks] read binary text from xhr using arraybuffer instead of overriding mime-type; strip Byte Order Mark when parsing gpx and kml
Chrome always overrides encoding when file contains BOM
Diffstat:
4 files changed, 29 insertions(+), 3 deletions(-)
diff --git a/src/lib/leaflet.control.track-list/lib/geo_file_formats.js b/src/lib/leaflet.control.track-list/lib/geo_file_formats.js
@@ -1,5 +1,6 @@
 import JSUnzip from 'vendored/github.com/augustl/js-unzip/js-unzip';
 import RawDeflate from 'vendored/github.com/dankogai/js-deflate/rawinflate';
+import stripBom from 'lib/stripBom';
 
 import {decode as utf8_decode} from 'utf8';
 
@@ -65,6 +66,7 @@ function parseGpx(txt, name) {
         return waypoints;
     };
 
+    txt = stripBom(txt);
     // remove namespaces
     txt = txt.replace(/<([^ >]+):([^ >]+)/g, '<$1_$2');
     var dom = (new DOMParser()).parseFromString(txt, "text/xml");
@@ -248,6 +250,7 @@ function parseKml(txt, name) {
         return points;
     }
 
+    txt = stripBom(txt);
     txt = txt.replace(/<([^ >]+):([^ >]+)/g, '<$1_$2');
     var dom = (new DOMParser()).parseFromString(txt, "text/xml");
     if (dom.documentElement.nodeName === 'parsererror') {
diff --git a/src/lib/leaflet.control.track-list/track-list.js b/src/lib/leaflet.control.track-list/track-list.js
@@ -241,7 +241,7 @@ L.Control.TrackList = L.Control.extend({
                         .pop();
                     fetch(url_for_request, {responseType: 'binarystring'})
                         .then(function(xhr) {
-                                var geodata = parseGeoFile(name, xhr.response);
+                                var geodata = parseGeoFile(name, xhr.responseBinaryText);
                                 this.addTracksFromGeodataArray(geodata);
                             }.bind(this),
                             function() {
diff --git a/src/lib/stripBom/index.js b/src/lib/stripBom/index.js
@@ -0,0 +1,6 @@
+export default function stripBom(s) {
+    if (s.substr(0, 3) === '\xef\xbb\xbf') {
+        s = s.substr(3);
+    }
+    return s;
+}
diff --git a/src/lib/xhr-promise/index.js b/src/lib/xhr-promise/index.js
@@ -6,6 +6,20 @@ function retryIfNetworkErrorOrServerError(xhr) {
     return (xhr.status === 0 || xhr.status >= 500);
 }
 
+
+function arrayBufferToString(arBuf) {
+    const result = [];
+    const arr = new Uint8Array(arBuf);
+    let chunk;
+    for (let i = 0; i < arr.length; i += 4096) {
+        chunk = arr.subarray(i, i + 4096);
+        chunk = String.fromCharCode.apply(null, chunk);
+        result.push(chunk);
+    }
+    return result.join('');
+}
+
+
 class XMLHttpRequestPromise {
     constructor(
         url, {method='GET', data=null, responseType='', timeout=30000, maxTries=3, retryTimeWait=1000,
@@ -20,6 +34,7 @@ class XMLHttpRequestPromise {
         this.catch = promise.catch.bind(promise);
         this.method = method;
         this.url = url;
+        this.responseType = responseType;
         this.postData = data;
         this._isResponseSuccess = isResponseSuccess;
         this._responseNeedsRetry = responseNeedsRetry;
@@ -31,8 +46,7 @@ class XMLHttpRequestPromise {
         this._open();
         xhr.timeout = timeout;
         if (responseType === 'binarystring') {
-            xhr.responseType = 'text';
-            xhr.overrideMimeType('text/plain; charset=x-user-defined');
+            xhr.responseType = 'arraybuffer';
         } else {
             xhr.responseType = responseType;
         }
@@ -47,6 +61,9 @@ class XMLHttpRequestPromise {
         const xhr = this.xhr;
         if (xhr.readyState === 4 && !this._aborted) {
             // console.log('ready state 4', this.url);
+            if (this.responseType === 'binarystring' && xhr.response && xhr.response.byteLength) {
+                xhr.responseBinaryText = arrayBufferToString(xhr.response);
+            }
             if (this._isResponseSuccess(xhr)) {
                 // console.log('success', this.url);
                 this._resolve(xhr);