From 2ea0cdaea18026028461ab82703e4b1da1731e48 Mon Sep 17 00:00:00 2001 From: Ben Hsieh Date: Tue, 26 Jul 2016 16:49:56 +0800 Subject: [PATCH] Fix XMLHttpRequest module bugs #44 Add Event and ProgressEvent class #44 Add timeout support #44 --- .../com/RNFetchBlob/RNFetchBlobConfig.java | 4 + .../java/com/RNFetchBlob/RNFetchBlobReq.java | 75 ++++---- src/index.js | 29 ++- src/ios/RNFetchBlobNetwork.m | 25 ++- src/ios/RNFetchBlobReqBuilder.h | 2 + src/polyfill/Event.js | 11 ++ src/polyfill/EventTarget.js | 29 ++- src/polyfill/ProgressEvent.js | 32 ++++ src/polyfill/XMLHttpRequest.js | 178 +++++++++--------- src/polyfill/XMLHttpRequestEventTarget.js | 42 ++++- src/polyfill/index.js | 3 +- 11 files changed, 280 insertions(+), 150 deletions(-) create mode 100644 src/polyfill/Event.js create mode 100644 src/polyfill/ProgressEvent.js diff --git a/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java b/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java index e90feb819..07c099e96 100644 --- a/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java +++ b/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobConfig.java @@ -17,6 +17,7 @@ public class RNFetchBlobConfig { public String key; public String mime; public Boolean auto; + public long timeout = -1; RNFetchBlobConfig(ReadableMap options) { if(options == null) @@ -31,6 +32,9 @@ public class RNFetchBlobConfig { this.key = options.hasKey("key") ? options.getString("key") : null; this.mime = options.hasKey("contentType") ? options.getString("contentType") : null; this.auto = options.hasKey("auto") ? options.getBoolean("auto") : false; + if(options.hasKey("timeout")) { + this.timeout = options.getInt("timeout"); + } } } diff --git a/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java b/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java index 39fd0091b..2c63a7a07 100644 --- a/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java +++ b/src/android/src/main/java/com/RNFetchBlob/RNFetchBlobReq.java @@ -28,8 +28,10 @@ import java.io.IOException; import java.io.InputStream; import java.net.MalformedURLException; +import java.net.SocketTimeoutException; import java.net.URL; import java.util.HashMap; +import java.util.concurrent.TimeUnit; import okhttp3.Call; import okhttp3.Headers; @@ -79,6 +81,7 @@ enum ResponseType { long downloadManagerId; RequestType requestType; ResponseType responseType; + boolean timeout = false; public RNFetchBlobReq(ReadableMap options, String taskId, String method, String url, ReadableMap headers, String body, ReadableArray arrayBody, final Callback callback) { this.method = method; @@ -257,32 +260,41 @@ else if(this.options.fileCache == true) clientBuilder.addInterceptor(new Interceptor() { @Override public Response intercept(Chain chain) throws IOException { - Response originalResponse = chain.proceed(req); - ResponseBody extended; - switch (responseType) { - case KeepInMemory: - extended = new RNFetchBlobDefaultResp( - RNFetchBlob.RCTContext, - taskId, - originalResponse.body()); - break; - case FileStorage: - extended = new RNFetchBlobFileResp( - RNFetchBlob.RCTContext, - taskId, - originalResponse.body(), - destPath); - break; - default: - extended = new RNFetchBlobDefaultResp( - RNFetchBlob.RCTContext, - taskId, - originalResponse.body()); - break; - } - return originalResponse.newBuilder().body(extended).build(); + try { + Response originalResponse = chain.proceed(req); + ResponseBody extended; + switch (responseType) { + case KeepInMemory: + extended = new RNFetchBlobDefaultResp( + RNFetchBlob.RCTContext, + taskId, + originalResponse.body()); + break; + case FileStorage: + extended = new RNFetchBlobFileResp( + RNFetchBlob.RCTContext, + taskId, + originalResponse.body(), + destPath); + break; + default: + extended = new RNFetchBlobDefaultResp( + RNFetchBlob.RCTContext, + taskId, + originalResponse.body()); + break; + } + return originalResponse.newBuilder().body(extended).build(); + } catch(SocketTimeoutException ex) { + timeout = true; + } + return chain.proceed(chain.request()); } }); + + if(options.timeout != -1) { + clientBuilder.connectTimeout(options.timeout, TimeUnit.SECONDS); + } OkHttpClient client = clientBuilder.build(); Call call = client.newCall(req); @@ -386,26 +398,23 @@ private WritableMap getResponseInfo(Response resp) { info.putInt("status", resp.code()); info.putString("state", "2"); info.putString("taskId", this.taskId); + info.putBoolean("timeout", timeout); WritableMap headers = Arguments.createMap(); for(int i =0;i< resp.headers().size();i++) { headers.putString(resp.headers().name(i), resp.headers().value(i)); } info.putMap("headers", headers); Headers h = resp.headers(); - if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/plain")) - { + if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/plain")) { info.putString("respType", "text"); } - else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json")) - { + else if(getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json")) { info.putString("respType", "json"); } - else if(getHeaderIgnoreCases(h, "content-type").length() < 1) - { + else if(getHeaderIgnoreCases(h, "content-type").length() < 1) { info.putString("respType", "blob"); } - else - { + else { info.putString("respType", "text"); } return info; @@ -413,7 +422,7 @@ else if(getHeaderIgnoreCases(h, "content-type").length() < 1) private boolean isBlobResponse(Response resp) { Headers h = resp.headers(); - boolean isText = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/plain"); + boolean isText = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("text/"); boolean isJSON = !getHeaderIgnoreCases(h, "content-type").equalsIgnoreCase("application/json"); return !(isJSON || isText); } diff --git a/src/index.js b/src/index.js index b6dec1dcf..9f7854bb1 100644 --- a/src/index.js +++ b/src/index.js @@ -19,6 +19,7 @@ import type { import fs from './fs' import getUUID from './utils/uuid' import base64 from 'base-64' +import polyfill from './polyfill' const { RNFetchBlobSession, readStream, @@ -34,7 +35,6 @@ const { mv, cp } = fs -import polyfill from './polyfill' const Blob = polyfill.Blob const emitter = DeviceEventEmitter @@ -151,7 +151,7 @@ function fetch(...args:any):Promise { subscriptionUpload.remove() stateEvent.remove() if(err) - reject(new Error(err, data)) + reject(new Error(err, info)) else { let rnfbEncode = 'base64' // response data is saved to storage @@ -163,9 +163,9 @@ function fetch(...args:any):Promise { } info = info || {} info.rnfbEncode = rnfbEncode - resolve(new FetchBlobResponse(taskId, info, data)) } + }) }) @@ -228,14 +228,23 @@ class FetchBlobResponse { return this.respInfo } /** - * Convert result to javascript Blob object. - * @param {string} contentType MIME type of the blob object. - * @param {number} sliceSize Slice size. - * @return {blob} Return Blob object. + * Convert result to javascript RNFetchBlob object. + * @return {Promise} Return a promise resolves Blob object. */ - this.blob = (contentType:string, sliceSize:number) => { - console.warn('FetchBlobResponse.blob() is deprecated and has no funtionality.') - return this + this.blob = ():Promise => { + return new Promise((resolve, reject) => { + if(this.type === 'base64') { + try { + let b = new polyfill.Blob(this.data, 'application/octet-stream;BASE64') + b.onCreated(() => { + console.log('####', b) + resolve(b) + }) + } catch(err) { + reject(err) + } + } + }) } /** * Convert result to text. diff --git a/src/ios/RNFetchBlobNetwork.m b/src/ios/RNFetchBlobNetwork.m index 2db3ee904..5bd23bfb7 100644 --- a/src/ios/RNFetchBlobNetwork.m +++ b/src/ios/RNFetchBlobNetwork.m @@ -14,6 +14,7 @@ #import "RNFetchBlobFS.h" #import "RNFetchBlobNetwork.h" #import "RNFetchBlobConst.h" +#import "RNFetchBlobReqBuilder.h" #import //////////////////////////////////////// @@ -113,8 +114,12 @@ - (void) sendRequest:(NSDictionary * _Nullable )options // the session trust any SSL certification NSURLSessionConfiguration *defaultConfigObject = [NSURLSessionConfiguration defaultSessionConfiguration]; + if([options valueForKey:@"timeout"] != nil) + { + defaultConfigObject.timeoutIntervalForRequest = [[options valueForKey:@"timeout"] floatValue]; + } session = [NSURLSession sessionWithConfiguration:defaultConfigObject delegate:self delegateQueue:taskQueue]; - + if(path != nil || [self.options valueForKey:CONFIG_USE_TEMP]!= nil) { respFile = YES; @@ -171,10 +176,12 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat if ([response respondsToSelector:@selector(allHeaderFields)]) { NSDictionary *headers = [httpResponse allHeaderFields]; - NSString * respType = [[headers valueForKey:@"Content-Type"] lowercaseString]; + NSString * respType = [[RNFetchBlobReqBuilder getHeaderIgnoreCases:@"content-type" + fromHeaders:headers] + lowercaseString]; if([headers valueForKey:@"Content-Type"] != nil) { - if([respType containsString:@"text/plain"]) + if([respType containsString:@"text/"]) { respType = @"text"; } @@ -199,6 +206,7 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat @"state": @"2", @"headers": headers, @"respType" : respType, + @"timeout" : @NO, @"status": [NSString stringWithFormat:@"%d", statusCode ] }; [self.bridge.eventDispatcher @@ -253,22 +261,22 @@ - (void) URLSession:(NSURLSession *)session dataTask:(NSURLSessionDataTask *)dat } - (void) URLSession:(NSURLSession *)session task:(NSURLSessionTask *)task didCompleteWithError:(NSError *)error { - NSLog([error localizedDescription]); + self.error = error; [[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO]; NSString * respType = [respInfo valueForKey:@"respType"]; + if(error != nil) { + NSLog([error localizedDescription]); + } if(respFile == YES) { - if(error != nil) { - NSLog([error localizedDescription]); - } [writeStream close]; callback(@[error == nil ? [NSNull null] : [error localizedDescription], respInfo == nil ? [NSNull null] : respInfo, destPath - ]); + ]); } // base64 response else { @@ -347,4 +355,5 @@ - (void) URLSession:(NSURLSession *)session didReceiveChallenge:(NSURLAuthentica } } + @end diff --git a/src/ios/RNFetchBlobReqBuilder.h b/src/ios/RNFetchBlobReqBuilder.h index 57cd84fd3..2ec48793a 100644 --- a/src/ios/RNFetchBlobReqBuilder.h +++ b/src/ios/RNFetchBlobReqBuilder.h @@ -37,6 +37,8 @@ form:(NSString *)body onComplete:(void(^)(NSURLRequest * req, long bodyLength))onComplete; ++(NSString *) getHeaderIgnoreCases:(NSString *)field fromHeaders:(NSMutableArray *) headers; + @end diff --git a/src/polyfill/Event.js b/src/polyfill/Event.js new file mode 100644 index 000000000..770bc5436 --- /dev/null +++ b/src/polyfill/Event.js @@ -0,0 +1,11 @@ +// Copyright 2016 wkh237@github. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +export default class Event { + + constructor() { + + } + +} diff --git a/src/polyfill/EventTarget.js b/src/polyfill/EventTarget.js index 016c1ce4a..f1f090cb9 100644 --- a/src/polyfill/EventTarget.js +++ b/src/polyfill/EventTarget.js @@ -1,6 +1,7 @@ // Copyright 2016 wkh237@github. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. + import Log from '../utils/log.js' const log = new Log('EventTarget') @@ -14,6 +15,11 @@ export default class EventTarget { this.listeners = {} } + /** + * Add an event listener to given event type + * @param {string} type Event type string + * @param {(Event) => void} cb Event handler function + */ addEventListener(type:string, cb : () => void) { log.info('add event listener', type, cb) if(!(type in this.listeners)) { @@ -22,7 +28,13 @@ export default class EventTarget { this.listeners[type].push(cb) } - removeEventListener(type:string, cb:() => any) { + /** + * Remove an event listener + * @param {string} type Type of the event listener + * @param {()=>void} cb Event listener function. + * @return {[type]} [description] + */ + removeEventListener(type:string, cb:() => void) { log.info('remove event listener', type, cb) if(!(type in this.listeners)) return @@ -35,6 +47,10 @@ export default class EventTarget { } } + /** + * Dispatch an event + * @param {Evnet} event Event data payload. + */ dispatchEvent(event:Event) { log.info('dispatch event', event) if(!(event.type in this.listeners)) @@ -46,4 +62,15 @@ export default class EventTarget { } + /** + * Remove all registered listeners from this object. + * @nonstandard + * @return {[type]} [description] + */ + clearEventListeners() { + for(let i in this.listeners) { + delete listeners[i] + } + } + } diff --git a/src/polyfill/ProgressEvent.js b/src/polyfill/ProgressEvent.js new file mode 100644 index 000000000..5286d6096 --- /dev/null +++ b/src/polyfill/ProgressEvent.js @@ -0,0 +1,32 @@ +// Copyright 2016 wkh237@github. All rights reserved. +// Use of this source code is governed by a MIT-style license that can be +// found in the LICENSE file. + +import Event from './Event' + +export default class ProgressEvent extends Event { + + _lengthComputable : boolean = false; + _loaded : number = -1; + _total : numver = -1; + + constructor(lengthComputable, loaded, total) { + super() + this._lengthComputable = lengthComputable; + this._loaded = loaded + this._total = total + } + + get lengthComputable() { + return this._lengthComputable + } + + get loaded() { + return this._loaded + } + + get total() { + return this._total + } + +} diff --git a/src/polyfill/XMLHttpRequest.js b/src/polyfill/XMLHttpRequest.js index 6bfdd18f2..4e9d47391 100644 --- a/src/polyfill/XMLHttpRequest.js +++ b/src/polyfill/XMLHttpRequest.js @@ -6,6 +6,7 @@ import RNFetchBlob from '../index.js' import XMLHttpRequestEventTarget from './XMLHttpRequestEventTarget.js' import Log from '../utils/log.js' import Blob from './Blob.js' +import ProgressEvent from './ProgressEvent.js' const log = new Log('XMLHttpRequest') @@ -21,31 +22,43 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ _onreadystatechange : () => void; + upload : XMLHttpRequestEventTarget = new XMLHttpRequestEventTarget(); + + // readonly _readyState : number = UNSENT; _response : any = ''; - _responseText : any = ''; + _responseText : any = null; _responseHeaders : any = {}; _responseType : '' | 'arraybuffer' | 'blob' | 'document' | 'json' | 'text' = ''; - // TODO : not suppoted for now + // TODO : not suppoted ATM _responseURL : null = ''; _responseXML : null = ''; _status : number = 0; _statusText : string = ''; _timeout : number = 0; - _upload : XMLHttpRequestEventTarget; _sendFlag : boolean = false; + _uploadStarted : boolean = false; // RNFetchBlob compatible data structure - _config : RNFetchBlobConfig; + _config : RNFetchBlobConfig = {}; _url : any; _method : string; - _headers: any; + _headers: any = { + 'Content-Type' : 'text/plain' + }; _body: any; // RNFetchBlob promise object, which has `progress`, `uploadProgress`, and // `cancel` methods. _task: any; + // constants + get UNSENT() { return UNSENT } + get OPENED() { return OPENED } + get HEADERS_RECEIVED() { return HEADERS_RECEIVED } + get LOADING() { return LOADING } + get DONE() { return DONE } + static get UNSENT() { return UNSENT } @@ -66,12 +79,9 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ return DONE } - constructor(...args) { + constructor() { super() - log.verbose('XMLHttpRequest constructor called', args) - this._config = {} - this._args = {} - this._headers = {} + log.verbose('XMLHttpRequest constructor called') } @@ -89,7 +99,7 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this._method = method this._url = url this._headers = {} - this.readyState = XMLHttpRequest.OPENED + this._dispatchReadStateChange(XMLHttpRequest.OPENED) } /** @@ -105,23 +115,22 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ log.verbose('XMLHttpRequest send ', body) let {_method, _url, _headers } = this log.verbose('sending request with args', _method, _url, _headers, body) - - this._upload = new XMLHttpRequestEventTarget() log.verbose(typeof body, body instanceof FormData) if(body instanceof Blob) { body = RNFetchBlob.wrap(body.getRNFetchBlobRef()) } + else if(typeof body === 'object') { + body = JSON.stringify(body) + } - this.dispatchEvent('loadstart') - if(this.onloadstart) - this.onloadstart() - - this._task = RNFetchBlob.config({ auto: true }) - .fetch(_method, _url, _headers, body) + this._task = RNFetchBlob + .config({ auto: true, timeout : this._timeout }) + .fetch(_method, _url, _headers, body) + this.dispatchEvent('load') this._task .stateChange(this._headerReceived.bind(this)) - .uploadProgress(this._progressEvent.bind(this)) + .uploadProgress(this._uploadProgressEvent.bind(this)) .progress(this._progressEvent.bind(this)) .catch(this._onError.bind(this)) .then(this._onDone.bind(this)) @@ -129,12 +138,12 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ overrideMimeType(mime:string) { log.verbose('XMLHttpRequest overrideMimeType', mime) - this._headers['content-type'] = mime + this._headers['Content-Type'] = mime } setRequestHeader(name, value) { log.verbose('XMLHttpRequest set header', name, value) - if(this._readyState !== OPENED && this._sendFlag) { + if(this._readyState !== OPENED || this._sendFlag) { throw `InvalidStateError : Calling setRequestHeader in wrong state ${this._readyState}` } // UNICODE SHOULD NOT PASS @@ -199,29 +208,36 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ _headerReceived(e) { log.verbose('header received ', this._task.taskId, e) this.responseURL = this._url + this.upload.dispatchEvent('loadend') + this.dispatchEvent('load') if(e.state === "2") { - this._readyState = XMLHttpRequest.HEADERS_RECEIVED this._responseHeaders = e.headers - this._responseText = e.status + this._statusText = e.status this._responseType = e.respType || '' this._status = Math.floor(e.status) + this._dispatchReadStateChange(XMLHttpRequest.HEADERS_RECEIVED) } } + _uploadProgressEvent(send:number, total:number) { + console.log('_upload', this.upload) + if(!this._uploadStarted) { + this.upload.dispatchEvent('loadstart') + this._uploadStarted = true + } + if(send >= total) + this.upload.dispatchEvent('load') + this.upload.dispatchEvent('progress', new ProgressEvent(true, send, total)) + } + _progressEvent(send:number, total:number) { log.verbose(this.readyState) - if(this.readyState === XMLHttpRequest.HEADERS_RECEIVED) - this.readyState = XMLHttpRequest.LOADING + if(this._readyState === XMLHttpRequest.HEADERS_RECEIVED) + this._dispatchReadStateChange(XMLHttpRequest.LOADING) let lengthComputable = false - let e = { lengthComputable } if(total && total >= 0) - e.lengthComputable = true - else { - Object.assign(e, { loaded : send, total }) - } - - if(this.onprogress) - this.onprogress(e) + lengthComputable = true + let e = new ProgressEvent(lengthComputable, send, total) this.dispatchEvent('progress', e) } @@ -230,46 +246,51 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this._statusText = err this._status = String(err).match(/\d+/) this._status = this._status ? Math.floor(this.status) : 404 - this._readyState = XMLHttpRequest.DONE - if(String(err).match('timeout') !== null) { + this._dispatchReadStateChange(XMLHttpRequest.DONE) + if(err && String(err.message).match(/(timed\sout|timedout)/)) { this.dispatchEvent('timeout') - if(this.ontimeout) - this.ontimeout() - } - else if(this.onerror) { - this.dispatchEvent('error') - this.onerror({ - type : 'error', - detail : err - }) } + this.dispatchEvent('loadend') + this.dispatchEvent('error', { + type : 'error', + detail : err + }) + this.clearEventListeners() } _onDone(resp) { - log.verbose('XMLHttpRequest done', this._task.taskId, this) - this.statusText = '200 OK' - switch(resp.type) { - case 'base64' : - if(this.responseType === 'json') { + log.verbose('XMLHttpRequest done', this._url, resp) + this._statusText = this._status + if(resp) { + switch(resp.type) { + case 'base64' : + if(this._responseType === 'json') { + this._responseText = resp.text() + this._response = resp.json() + } + else { this._responseText = resp.text() - this._response = resp.json() - } - else { + this._response = this.responseText + } + break; + case 'path' : + this.response = resp.blob() + break; + default : this._responseText = resp.text() this._response = this.responseText - } - break; - case 'path' : - this.response = resp.blob() - break; + break; + } + this.dispatchEvent('loadend') + this._dispatchReadStateChange(XMLHttpRequest.DONE) } - this.dispatchEvent('loadend') - if(this.onloadend) - this.onloadend() - this.dispatchEvent('load') - if(this._onload) - this._onload() - this.readyState = XMLHttpRequest.DONE + this.clearEventListeners() + } + + _dispatchReadStateChange(state) { + this._readyState = state + if(typeof this._onreadystatechange === 'function') + this._onreadystatechange() } set onreadystatechange(fn:() => void) { @@ -277,17 +298,8 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ this._onreadystatechange = fn } - set readyState(val:number) { - - log.verbose('XMLHttpRequest ready state changed to ', val) - this._readyState = val - if(this._onreadystatechange) { - log.verbose('trigger onreadystatechange event', this._readyState) - log.verbose(this._onreadystatechange) - this.dispatchEvent('readystatechange', ) - if(this._onreadystatechange) - this._onreadystatechange() - } + get onreadystatechange(fn:() => void) { + return this._onreadystatechange } get readyState() { @@ -300,20 +312,11 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ return this._status } - set statusText(val) { - this._statusText = val - } - get statusText() { log.verbose('get statusText', this._statusText) return this._statusText } - set response(val) { - log.verbose('set response', val) - this._response = val - } - get response() { log.verbose('get response', this._response) return this._response @@ -344,11 +347,6 @@ export default class XMLHttpRequest extends XMLHttpRequestEventTarget{ return this._timeout } - get upload() { - log.verbose('get upload', this._upload) - return this._upload - } - get responseType() { log.verbose('get response type', this._responseType) return this._responseType diff --git a/src/polyfill/XMLHttpRequestEventTarget.js b/src/polyfill/XMLHttpRequestEventTarget.js index 06da83d72..bac96be7a 100644 --- a/src/polyfill/XMLHttpRequestEventTarget.js +++ b/src/polyfill/XMLHttpRequestEventTarget.js @@ -1,6 +1,7 @@ // Copyright 2016 wkh237@github. All rights reserved. // Use of this source code is governed by a MIT-style license that can be // found in the LICENSE file. + import EventTarget from './EventTarget.js' import Log from '../utils/log.js' @@ -10,19 +11,46 @@ log.disable() export default class XMLHttpRequestEventTarget extends EventTarget { - _onabort : (e:Event) => void; - _onerror : (e:Event) => void; - _onload : (e:Event) => void; - _onloadstart : (e:Event) => void; - _onprogress : (e:Event) => void; - _ontimeout : (e:Event) => void; - _onloadend : (e:Event) => void; + _onabort : (e:Event) => void = () => {}; + _onerror : (e:Event) => void = () => {}; + _onload : (e:Event) => void = () => {}; + _onloadstart : (e:Event) => void = () => {}; + _onprogress : (e:Event) => void = () => {}; + _ontimeout : (e:Event) => void = () => {}; + _onloadend : (e:Event) => void = () => {}; constructor() { super() log.info('constructor called') } + dispatchEvent(event:string, e:Event) { + super.dispatchEvent(event, e) + switch(event) { + case 'abort' : + this._onabort(e) + break; + case 'error' : + this._onerror(e) + break; + case 'load' : + this._onload(e) + break; + case 'loadstart' : + this._onloadstart(e) + break; + case 'loadend' : + this._onloadend(e) + break; + case 'progress' : + this._onprogress(e) + break; + case 'timeout' : + this._ontimeout(e) + break; + } + } + set onabort(fn:(e:Event) => void) { log.info('set onabort') this._onabort = fn diff --git a/src/polyfill/index.js b/src/polyfill/index.js index ebe7a0150..f911a9db4 100644 --- a/src/polyfill/index.js +++ b/src/polyfill/index.js @@ -2,7 +2,8 @@ import Blob from './Blob.js' import File from './File.js' import XMLHttpRequest from './XMLHttpRequest.js' import FormData from './FormData.js' +import ProgressEvent from './ProgressEvent' export default { - Blob, File, XMLHttpRequest, FormData + Blob, File, XMLHttpRequest, FormData, ProgressEvent }