Skip to content

Commit

Permalink
Merge pull request #8 from Amarok24/relook
Browse files Browse the repository at this point in the history
hotfix for 1.2.0
  • Loading branch information
Amarok24 authored Feb 11, 2022
2 parents aa5fabb + 0a0391d commit 8e74e9c
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 68 deletions.
43 changes: 20 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
# Oxhr v1.2.0
An object-oriented XHR (XMLHttpRequest) wrapper/library.
An object-oriented and asynchronous XHR (XMLHttpRequest) wrapper.

### Modern programmers use fetch, others prefer Oxhr 🐮

<img width="180" alt="Oxhr logo" src="./oxhr-logo.svg" />

### Why Oxhr?
- Written in TypeScript
- No dependencies
- Promise-based (asynchronous)
- Modular (JavaScript modules)
- Tiny (under 5kb minified)
- Robust and simple
- No dependencies
- Robust and simple, useful for most tasks
- An alternative to _XMLHttpRequest_ and _fetch_
- An alternative to feature-rich libraries like [_axios_](https://github.com/axios/axios)

A small demo is included, see `index.html`.
- An alternative to feature-rich and complex libraries like [_axios_](https://github.com/axios/axios)

If you are not familiar with ES modules have a look [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Modules).

There is also an old version of the library called jXhr which was not written using OOP, see [here](https://github.com/Amarok24/Oxhr/tree/non-oop-version).
A small demo is included, see `index.html`. If you prefer a plain .js file instead of a modular system you can use a bundler to combine all modules into one file; for such a task I recommend [Deno](https://deno.land/) which has a bundle feature.

---
## API
Expand All @@ -42,7 +39,7 @@ async function fetchDataExample()

// Send request to the server and output response data to console.
response = await myConnection.send();
console.log(result);
console.log(response);
}
```

Expand All @@ -57,11 +54,11 @@ interface IOxhrParams
data?: XMLHttpRequestBodyInit | Document | null;
requestHeaders?: IRequestHeader[];
timeoutMs?: number;
consoleInfo?: string;
consoleMessage?: string;
debug?: boolean;
onLoadEnd?: () => void;
onProgress?: (percent: number, bytes: number) => void;
onTimeOut?: () => void;
onTimeout?: () => void;
onAbort?: () => void;
}
```
Expand All @@ -72,11 +69,11 @@ interface IOxhrParams
| :------------- | :----------------------- | :-----: | :----: | :--- |
| url | URL for http request | x | | string |
| method | HTTP request method | - | "GET" | string ("GET" \| "POST" \| etc...)) |
| responseType | A valid response type | - | "" | "", "arraybuffer", "blob", "document", "json", "text" |
| responseType | One of response types | - | "" | "", "arraybuffer", "blob", "document", "json", "text" |
| data | Data to send | - | null | Blob, BufferSource, FormData, URLSearchParams, ReadableStream, string, Document, null |
| requestHeaders | Array of IRequestHeader | - | | IRequestHeader[] |
| timeoutMs | Timeout in milliseconds | - | 60'000 | number |
| consoleInfo | Description for console output after loading is done. | - | | string |
| timeoutMs | Timeout in milliseconds. If the request takes longer, it will be terminated. | - | 0 | number |
| consoleMessage | Description for console output after loading is done. | - | | string |
| debug | Additional console output | - | false | boolean |


Expand All @@ -86,10 +83,10 @@ interface IOxhrParams
| :-- | :-- | :-- |
| onLoadEnd | Called after success, timeout, abort or error | -- |
| onProgress | Ongoing loading progress in percent and bytes | (percent: number, bytes: number) |
| onTimeOut | Time-out callback (see timeoutMs parameter) | -- |
| onTimeout | Timeout callback (see timeoutMs parameter) | -- |
| onAbort | When an open connection gets aborted | -- |

Please note that progress in % must not always work because it depends on server settings (not all connections give you the total data/file size).
Please note that progress in % must not always work because it depends on server settings (not all connections give you the total data/file size in advance).

### Methods

Expand All @@ -106,9 +103,9 @@ Please note that progress in % must not always work because it depends on server
| instanceId | A unique ID of the Oxhr instance (UUID). |
| readyState | The XHR readyState code. |
| status | The XHR status code. |
| success | Returns _true_ if transfer was successful (readyState is DONE and status is OK). |
| success | Returns _true_ if transfer was successful (readyState is DONE and status is OK). |
| isProcessed | Returns _true_ if a request is already being processed. |

There is also a simple getter for reading the "readyState" of current XMLHttpRequest connection.

## FAQs

Expand All @@ -121,13 +118,13 @@ __Can I open multiple connections at once?__

## Changelog
### _v1.2.0_
- Many code improvements, now the library is more robust and handles all kind of wrong usage in a nice way
- Added 4 new getters: instanceId, status, success, isProcessed
- Major code refactoring
- Previous version didn't compile in TypeScript 4.5.4 because of an error, this update fixes it
- Added new "debug" parameter for more verbose console log
- Several small bugfixes, one big bugfix for custom onLoadEnd method which didn't get triggered in TypeScript 4.5.4
- Added 3 new getters: instanceId, status, success
- Added new "debug" parameter for more verbose console log
- Demo in index.html changed significantly
- Many code improvements, now the library is more robust and handles all kind of wrong usage in a nice way
- Major code refactoring

---

Expand Down
11 changes: 5 additions & 6 deletions dist/demo.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { Oxhr } from './oxhr.js';
import { ResourcesType } from './swapi-schema.js';
import { OxhrError } from './oxhr-error.js';
import { XhrReadyState } from './xhr-codes.js';
const startButton = document.querySelector('#startButton');
const abortButton = document.querySelector('#abortButton');
const swButton = document.querySelector('#swButton');
Expand All @@ -12,7 +11,7 @@ async function fetchRandomStarWarsData() {
const random = Math.floor(Math.random() * 10 + 1);
const swOptions = {
url: `https://swapi.dev/api/${ResourcesType.People}/${random}`,
consoleInfo: 'StarWars connection finished, some details below.',
consoleMessage: 'StarWars connection finished, some details below.',
onLoadEnd: () => {
console.info('Opening browser pop-up message with character data...');
alert(JSON.stringify(peopleResponse));
Expand All @@ -38,9 +37,9 @@ const myOptions = {
requestHeaders: myRequestHeaders,
timeoutMs: 20000,
debug: true,
consoleInfo: 'My test connection finished, some details below.',
consoleMessage: 'My test connection finished, some details below.',
onLoadEnd: onLoadEnd,
onTimeOut: onTimeOut,
onTimeout: onTimeout,
onProgress: onProgress,
onAbort: onAbort
};
Expand All @@ -52,7 +51,7 @@ async function tryToSendData() {
console.log('try-block of tryToSendData');
console.log(`myConnection.instanceId = ${myConnection.instanceId}`);
console.log(`XHR status of myConnection is ${myConnection.status}`);
if ((myConnection.readyState !== XhrReadyState.DONE) && (myConnection.readyState !== XhrReadyState.UNSENT)) {
if (myConnection.isProcessed) {
console.log('You have probably clicked on that button while a connection is being processed.');
return;
}
Expand Down Expand Up @@ -93,7 +92,7 @@ function onLoadEnd() {
alert('Success!');
}
}
function onTimeOut() {
function onTimeout() {
console.log('demo: custom timeout handler');
alert('Connection time out!');
}
Expand Down
26 changes: 15 additions & 11 deletions dist/xhr-handler.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,10 @@ class XhrHandler {
if (this.xhr.status >= XhrStatus.LOADING &&
this.xhr.status < XhrStatus.MULTIPLE_CHOICE) {
resolve(this.xhr.response);
if (this.params.consoleInfo)
console.group(this.params.consoleInfo);
if (this.params.consoleMessage)
console.group(this.params.consoleMessage);
console.log(`${ev.loaded} bytes loaded.`);
if (this.params.consoleInfo)
if (this.params.consoleMessage)
console.groupEnd();
}
else {
Expand All @@ -24,11 +24,11 @@ class XhrHandler {
const handleError = (ev) => {
this.debugMessage(`handleError()`);
reject(new OxhrError('Failed to send request!'));
if (this.params.consoleInfo)
console.group(this.params.consoleInfo);
if (this.params.consoleMessage)
console.group(this.params.consoleMessage);
console.log(ev);
console.error(`xhr status: ${this.xhr.status}`);
if (this.params.consoleInfo)
if (this.params.consoleMessage)
console.groupEnd();
};
const handleProgress = (ev) => {
Expand All @@ -54,8 +54,8 @@ class XhrHandler {
this.xhr.removeEventListener('progress', handleProgress);
if (this.params.onAbort)
this.xhr.removeEventListener('abort', this.params.onAbort);
if (this.params.onTimeOut) {
this.xhr.removeEventListener('timeout', this.params.onTimeOut);
if (this.params.onTimeout) {
this.xhr.removeEventListener('timeout', this.params.onTimeout);
}
else {
this.xhr.removeEventListener('timeout', handleTimeout);
Expand All @@ -72,7 +72,7 @@ class XhrHandler {
}
});
}
this.xhr.timeout = this.params.timeoutMs ?? 60000;
this.xhr.timeout = this.params.timeoutMs ?? 0;
this.xhr.responseType = this.responseType;
if (!this._eventHandlersAssigned) {
this._eventHandlersAssigned = true;
Expand All @@ -83,8 +83,8 @@ class XhrHandler {
this.xhr.addEventListener('progress', handleProgress);
if (this.params.onAbort)
this.xhr.addEventListener('abort', this.params.onAbort);
if (this.params.onTimeOut) {
this.xhr.addEventListener('timeout', this.params.onTimeOut);
if (this.params.onTimeout) {
this.xhr.addEventListener('timeout', this.params.onTimeout);
}
else {
this.xhr.addEventListener('timeout', handleTimeout);
Expand Down Expand Up @@ -114,4 +114,8 @@ class XhrHandler {
return (this.xhr.readyState === XhrReadyState.DONE &&
this.xhr.status === XhrStatus.OK);
}
get isProcessed() {
return (this.xhr.readyState !== XhrReadyState.DONE &&
this.xhr.readyState !== XhrReadyState.UNSENT);
}
}
5 changes: 0 additions & 5 deletions minify.sh

This file was deleted.

11 changes: 5 additions & 6 deletions src/demo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ https://github.com/Amarok24/Oxhr
import {Oxhr} from './oxhr.js';
import {IPeople, ResourcesType} from './swapi-schema.js';
import {OxhrError} from './oxhr-error.js';
import {XhrReadyState} from './xhr-codes.js';
import type {IOxhrParams, IRequestHeader} from './oxhr-types.js';

const startButton = document.querySelector<HTMLButtonElement>('#startButton');
Expand All @@ -26,7 +25,7 @@ async function fetchRandomStarWarsData(): Promise<void>
const swOptions: IOxhrParams = {
// I also recommend this free API for testing: https://webhook.site/
url: `https://swapi.dev/api/${ ResourcesType.People }/${ random }`,
consoleInfo: 'StarWars connection finished, some details below.',
consoleMessage: 'StarWars connection finished, some details below.',
onLoadEnd: () =>
{
console.info('Opening browser pop-up message with character data...');
Expand Down Expand Up @@ -76,9 +75,9 @@ const myOptions: IOxhrParams = {
requestHeaders: myRequestHeaders,
timeoutMs: 20000,
debug: true,
consoleInfo: 'My test connection finished, some details below.',
consoleMessage: 'My test connection finished, some details below.',
onLoadEnd: onLoadEnd,
onTimeOut: onTimeOut,
onTimeout: onTimeout,
onProgress: onProgress,
onAbort: onAbort
};
Expand All @@ -103,7 +102,7 @@ async function tryToSendData(): Promise<void>
console.log(`myConnection.instanceId = ${ myConnection.instanceId }`);
console.log(`XHR status of myConnection is ${ myConnection.status }`);

if ((myConnection.readyState !== XhrReadyState.DONE) && (myConnection.readyState !== XhrReadyState.UNSENT))
if (myConnection.isProcessed)
{
console.log('You have probably clicked on that button while a connection is being processed.');
return;
Expand Down Expand Up @@ -164,7 +163,7 @@ function onLoadEnd(): void
}
}

function onTimeOut(): void
function onTimeout(): void
{
console.log('demo: custom timeout handler');
alert('Connection time out!');
Expand Down
10 changes: 5 additions & 5 deletions src/oxhr-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,12 @@ export type {
* @param data Data to send with request. Supports all valid data types. Default: null.
* @param responseType A valid response type. Default: "".
* @param requestHeaders Custom HTTP headers. Array of IRequestHeader.
* @param timeoutMs Timeout in milliseconds after which connection will be interrupted.
* @param consoleInfo Description of console.group for console output.
* @param timeoutMs Timeout in milliseconds. After that time the request will be terminated.
* @param consoleMessage Description for console output after loading is done.
* @param debug Output additional info to the browser console (debug mode).
* @param onLoadEnd Called after load success, timeout, abort or error.
* @param onProgress Callback function to which the loading progress in % shall be passed.
* @param onTimeOut Callback function for timeout.
* @param onTimeout The ionTimeout callback is fired when progression is terminated due to preset time expiring (see timeoutMs).
* @param onAbort Callback function when an open connection is aborted.
*/
interface IOxhrParams
Expand All @@ -36,11 +36,11 @@ interface IOxhrParams
data?: CombinedDataType;
requestHeaders?: IRequestHeader[];
timeoutMs?: number;
consoleInfo?: string;
consoleMessage?: string;
debug?: boolean;
onLoadEnd?: () => void;
onProgress?: (percent: number, bytes: number) => void;
onTimeOut?: () => void;
onTimeout?: () => void;
onAbort?: () => void;
}

Expand Down
35 changes: 23 additions & 12 deletions src/xhr-handler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,9 @@ class XhrHandler<T>
this.xhr.status < XhrStatus.MULTIPLE_CHOICE)
{
resolve(this.xhr.response);
if (this.params.consoleInfo) console.group(this.params.consoleInfo);
if (this.params.consoleMessage) console.group(this.params.consoleMessage);
console.log(`${ ev.loaded } bytes loaded.`);
if (this.params.consoleInfo) console.groupEnd();
if (this.params.consoleMessage) console.groupEnd();
}
else
{
Expand All @@ -63,10 +63,10 @@ class XhrHandler<T>
{
this.debugMessage(`handleError()`);
reject(new OxhrError('Failed to send request!'));
if (this.params.consoleInfo) console.group(this.params.consoleInfo);
if (this.params.consoleMessage) console.group(this.params.consoleMessage);
console.log(ev);
console.error(`xhr status: ${ this.xhr.status }`);
if (this.params.consoleInfo) console.groupEnd();
if (this.params.consoleMessage) console.groupEnd();
};

const handleProgress = (ev: ProgressEvent<XMLHttpRequestEventTarget>): void =>
Expand All @@ -79,7 +79,7 @@ class XhrHandler<T>
}
else
{
// If ev.total is unknown then it is set to 0 automatically.
// If ev.total is unknown then it is set automatically to 0.
if (this.params.onProgress) this.params.onProgress(-1, ev.loaded);
}
};
Expand All @@ -105,9 +105,9 @@ class XhrHandler<T>
//if (this.params.onLoadEnd) this.xhr.removeEventListener('loadend', this.params.onLoadEnd);
if (this.params.onAbort) this.xhr.removeEventListener('abort', this.params.onAbort);

if (this.params.onTimeOut)
if (this.params.onTimeout)
{
this.xhr.removeEventListener('timeout', this.params.onTimeOut);
this.xhr.removeEventListener('timeout', this.params.onTimeout);
}
else
{
Expand All @@ -134,9 +134,8 @@ class XhrHandler<T>
}

// Timeout on client side differs from server timeout. Default timeout is 0 (never).
// If timeout > 0 specified then fetching data will be interrupted after given time
// and the "timeout" event and "loadend" events will be triggered.
this.xhr.timeout = this.params.timeoutMs ?? 60000;
// If timeout > 0 specified then fetching data will be interrupted after given time and the "timeout" event and "loadend" events will be triggered.
this.xhr.timeout = this.params.timeoutMs ?? 0;

// If respType == "json" then XMLHttpRequest will perform JSON.parse().
this.xhr.responseType = this.responseType;
Expand All @@ -155,9 +154,9 @@ class XhrHandler<T>

if (this.params.onAbort) this.xhr.addEventListener('abort', this.params.onAbort);

if (this.params.onTimeOut)
if (this.params.onTimeout)
{
this.xhr.addEventListener('timeout', this.params.onTimeOut);
this.xhr.addEventListener('timeout', this.params.onTimeout);
}
else
{
Expand Down Expand Up @@ -208,6 +207,18 @@ class XhrHandler<T>
);
}

/**
* Returns true if a request is currently being processed.
* You may want to use it to avoid execution of a second await-Promise task on the same Oxhr instance.
*/
get isProcessed(): boolean
{
return (
this.xhr.readyState !== XhrReadyState.DONE &&
this.xhr.readyState !== XhrReadyState.UNSENT
);
}

}

// TODO: implement event handlers for processing uploads
Expand Down

0 comments on commit 8e74e9c

Please sign in to comment.