
```import { ICache } from "./js-dos-cache";
import CacheNoop from "./js-dos-cache-noop";

```

# Xhr
`Xhr` is small wrapper over XMLHttpRequest, that provides some
handy methods

You can configre Xhr with XhrOptions object:

```interface XhrOptions {
    cache: ICache;
    method?: string;
    success?: (response: any) => void;
    progress?: (total: number, loaded: number) => void;
    fail?: (url: string, status: number, message: string) => void;
    data?: string;
    responseType?: XMLHttpRequestResponseType;
}
```

* `method` - "GET" | "POST"
* `success` - callback when resource is downloaded
* `progress` - callback for progress
* `fail` - fail callback
* `data` - data for POST request, should typeof `application/x-www-form-urlencoded`
* `responseType` - XMLHttpRequestResponseType

Class Xhr does not have any public methods

```export class Xhr {
    private cache: ICache;
    private resource: string;
    private options: XhrOptions;
    private xhr: XMLHttpRequest | null = null;
    private total: number = 0;
    private loaded: number = 0;

    constructor(url: string, options: XhrOptions) {
        this.resource = url;
        this.options = options;
        this.options.method = options.method || "GET";
        this.cache = options.cache || new CacheNoop();

        if (this.options.method  === "GET") {
            this.cache.get(this.resource, (data) => {
                if (this.options.success !== undefined) {
                    this.options.success(data);
                }
            }, () => {
                this.makeHttpRequest();
            });
        }
    }

    private makeHttpRequest() {
        this.xhr = new XMLHttpRequest();
        this.xhr.open(this.options.method || "GET", this.resource, true);
        if (this.options.method === "POST") {
            this.xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
        }
        this.xhr.overrideMimeType("text/plain; charset=x-user-defined");

        let progressListner;
        if (typeof (progressListner = this.xhr).addEventListener === "function") {
            progressListner.addEventListener("progress", (evt) => {
                this.total = evt.total;
                this.loaded = evt.loaded;
                if (this.options.progress) {
                    return this.options.progress(evt.total, evt.loaded);
                }
            });
        }

        let errorListener;
        if (typeof (errorListener = this.xhr).addEventListener === "function") {
            errorListener.addEventListener("error", (evt) => {
                if (this.options.fail) {
                    this.options.fail(this.resource, (this.xhr as XMLHttpRequest).status, "connection problem");
                    return delete this.options.fail;
                }
            });
        }
        this.xhr.onreadystatechange = () => {
            return this.onReadyStateChange();
        };
        if (this.options.responseType) {
            this.xhr.responseType = this.options.responseType;
        }
        this.xhr.send(this.options.data);
    }

    private onReadyStateChange() {
        const xhr = (this.xhr as XMLHttpRequest);
        if (xhr.readyState === 4) {
            if (xhr.status === 200) {
                if (this.options.success) {
                    const total = Math.max(this.total, this.loaded);
                    if (this.options.progress !== undefined) {
                        this.options.progress(total, total);
                    }

                    if (this.options.method === "GET" && this.resource.indexOf("?") < 0) {
                        this.cache.put(this.resource, xhr.response, () => { /**/ });
                    }

                    return this.options.success(xhr.response);
                }
            } else if (this.options.fail) {
                this.options.fail(this.resource, xhr.status, "connection problem");
                return delete this.options.fail;
            }
        }
    }

}

```


