diff --git a/README.md b/README.md
index 82688f6..926cd8a 100644
--- a/README.md
+++ b/README.md
@@ -1,38 +1,14 @@
![frontexpress](http://fontmeme.com/embed.php?text=frontexpress&name=Atype%201%20Light.ttf&size=90&style_color=6F6F75)
+Code the front-end like on the back-end with [ExpressJS](http://expressjs.com/)
+
+[frontexpress demo](https://github.com/camelaissani/frontexpress-demo) repository.
+
[![Build Status](https://travis-ci.org/camelaissani/frontexpress.svg?branch=master)](https://travis-ci.org/camelaissani/frontexpress)
[![Code Climate](https://codeclimate.com/github/camelaissani/frontexpress/badges/gpa.svg)](https://codeclimate.com/github/camelaissani/frontexpress)
[![Coverage Status](https://coveralls.io/repos/github/camelaissani/frontexpress/badge.svg?branch=master)](https://coveralls.io/github/camelaissani/frontexpress?branch=master)
![dependencies](https://img.shields.io/gemnasium/mathiasbynens/he.svg)
- ![Size Shield](https://img.shields.io/badge/size-2.86kb-brightgreen.svg)
-
-Frontexpress manages routes in browser like [ExpressJS](http://expressjs.com/) does on Node.
-
-Same language same API on all the stack.
-
-Code the front-end logic with the same style than on the back-end with express
-
-```js
-import frontexpress from 'frontexpress';
-
-// Front-end application
-const app = frontexpress();
-
-// front-end logic on navigation path "/page1"
-app.get('/page1', (req, res) => {
- document.querySelector('.content').innerHTML = `
Page 1 content
`;
-});
-
-// front-end logic on navigation path "/page2"
-app.get('/page2', (req, res) => {
- document.querySelector('.content').innerHTML = `Page 2 content
`;
-});
-
-// start front-end application
-app.listen(() => {
- // on DOM ready
-});
-```
+ ![Size Shield](https://img.shields.io/badge/size-3.26kb-brightgreen.svg)
## Installation
@@ -52,43 +28,31 @@ $ bower install frontexpress
On [jsDelivr](https://cdn.jsdelivr.net/npm/frontexpress@1.1.0/frontexpress.min.js)
-## Quick Start
+## Usage
- The quickest way to get started with frontexpress is to clone the [frontexpress-demo](https://github.com/camelaissani/frontexpress-demo) repository.
+```js
+import frontexpress from 'frontexpress';
-## Tests
+// Front-end application
+const app = frontexpress();
- Clone the git repository:
+// front-end logic on navigation path "/page1"
+app.get('/page1', (req, res) => {
+ document.querySelector('.content').innerHTML = res.responseText;
+});
-```bash
-$ git clone git@github.com:camelaissani/frontexpress.git
-$ cd frontexpress
+// front-end logic on navigation path "/page2"
+app.get('/page2', (req, res) => {
+ document.querySelector('.content').innerHTML = res.responseText;
+});
+
+// start front-end application
+app.listen();
```
- Install the dependencies and run the test suite:
+### Routes
-```bash
-$ npm install
-$ npm test
-```
-
-
-## Navigation path and frontexpress routing
-
-### Disclaimer
-
->
-> In this first version of frontexpress, the API is not completely the mirror of the expressjs one.
->
-> There are some missing methods. Currently, the use, get, post... methods having a middleware array as parameter are not available.
-> The string pattern to define route paths is not yet implemented.
->
-> Obviously, the objective is to have the same API as expressjs when the methods make sense browser side.
->
-
-### Basic routing
-
-Listen navigation (GET request) on path /hello:
+Listen GET requests on path /hello:
```js
app.get('/hello', (req, res) => {
@@ -96,7 +60,7 @@ app.get('/hello', (req, res) => {
});
```
-Listen a POST request on path /item:
+Listen POST requests on path /item:
```js
app.post('/item', (req, res) => {
@@ -104,9 +68,7 @@ app.post('/item', (req, res) => {
});
```
-### Routing based on RegExp
-
-Listen navigation on paths which start with /api/:
+Listen GET requests on path starting with /api/:
```js
app.get(/^api\//, (req, res) => {
@@ -114,6 +76,66 @@ app.get(/^api\//, (req, res) => {
});
```
+Get parameters from path
+
+```js
+app.get('/product/:id', (req, res) => {
+ // if we have /product/42 then
+ // req.params.id = 42
+});
+```
+
+```js
+app.get('/user/:firstname?/:lastname', (req, res) => {
+ // if we have /user/camel/aissani then
+ // req.params.firstname = 'camel'
+ // req.params.lastname = 'aissani'
+
+ // if we have /user/aissani then
+ // req.params.firstname = undefined
+ // req.params.lastname = 'aissani'
+});
+```
+
+```js
+app.get('/user/:id', (req, res) => {
+ // if we have /user/1,2,3 then
+ // req.params.id = [1,2,3]
+});
+```
+### Middleware object
+
+The middleware object gives access to more hooks
+
+```js
+ class MyMiddleware = new Middleware {
+ entered(req) {
+ // before request sent
+ }
+
+ updated(req, res) {
+ // after request sent
+ // res has the request response
+ window.alert('Hello World');
+ }
+
+ exited(req) {
+ // before a new request sent
+ }
+
+ failed(req, res) {
+ // on request failed
+ }
+
+ next() {
+ // for chaining
+ return true;
+ }
+
+ }
+ app.get('/hello', new MyMiddleware());
+```
+
### Chain handlers
You can provide multiple handlers functions on a navigation path. Invoking ```next()``` function allows to chain the handlers.
@@ -138,7 +160,7 @@ h2!
h3 is ignored because ```next()``` function was not invoked.
-### app.route()
+#### app.route()
You can create chainable route handlers for a route path by using ```app.route()```.
@@ -149,7 +171,7 @@ app.route('/book')
.put((req, res) => { console.log('Update the book') });
```
-### frontexpress.Router
+#### frontexpress.Router
Use the ```frontexpress.Router``` class to create modular, mountable route handlers.
@@ -187,121 +209,24 @@ import birds from './birds';
app.use('/birds', birds);
```
-## API
+## [API](https://github.com/camelaissani/frontexpress/blob/master/docs/api.md)
-| | Method | Short description |
-| :------------- | :--------------| :----------------- |
-|Frontexpress |||
-||[frontexpress()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpress-1)|Creates an instance of application|
-||[frontexpress.Router()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressrouter)|Creates a Router object|
-||[frontexpress.Middleware](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressmiddleware)|Returns the Middleware class |
-||||
-|Application |||
-||[set(setting, value)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationsetsetting-val)|Assigns a setting|
-||[listen(callback)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationlistencallback)|Starts the application|
-||[route(uri)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationrouteuri)|Gets a Router initialized with a root path|
-||[use(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationuseuri-middleware)|Sets a middleware|
-||||
-||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a GET request|
-||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a POST request|
-||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a PUT request|
-||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a DELETE request|
-||||
-||[httpGet(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a GET ajax request|
-||[httpPost(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a POST ajax request|
-||[httpPut(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a PUT ajax request|
-||[httpDelete(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a DELETE ajax request|
-||||
-|Router |||
-||[use(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerusemiddleware)|Sets a middleware|
-||[all(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerallmiddleware)|Sets a middleware on all HTTP method requests|
-||||
-||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a GET request|
-||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a POST request|
-||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a PUT request|
-||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a DELETE request|
-||||
-|Middleware |||
-||[entered(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareenteredrequest)|Invoked by the app before an ajax request is sent|
-||[exited(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareexitedrequest)|Invoked by the app before a new ajax request is sent|
-||[updated(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareupdatedrequest-response)|Invoked by the app after an ajax request has responded|
-||[failed(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarefailedrequest-response)|Invoked by the app after an ajax request has failed|
-||[next()](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarenext)|Allows to break the middleware chain execution|
+## Tests
+ Clone the git repository:
-### middleware function
-
-After registering a middleware function, the application invokes it with these parameters:
-
-```js
- (request, response, next) => {
- next();
- }
+```bash
+$ git clone git@github.com:camelaissani/frontexpress.git
+$ cd frontexpress
```
-**request**: `Object`, the ajax request information sent by the app
+ Install the dependencies and run the test suite:
-**response**: `Object`, the response of request
-
-**next**: `Function`, the `next()` function to call to not break the middleware execution chain
-
-
-### request object
-
-```js
- {
- method,
- uri,
- headers,
- data,
- history: {
- state,
- title,
- uri
- }
- }
+```bash
+$ npm install
+$ npm test
```
-**method**: `String`, HTTP methods 'GET', 'POST'...
-
-**uri**: `String`, path
-
-**headers**: `Object`, custom HTTP headers
-
-**data**: `Object`, data attached to the request
-
-**history**: `Object`, object with properties state, title and uri
-
-**If the history object is set, it will activate the browser history management.** See [browser pushState() method](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method) for more information about state, title, and uri (url).
-
-> uri and history.uri can be different.
-
-
-### response object
-
-```js
- {
- status,
- statusText,
- responseText,
- errorThrown,
- errors
- }
-```
-
-**status**: `Number`, HTTP status 200, 404, 401, 500...
-
-**statusText**: `String`
-
-**responseText**: `String` response content
-
-**errorThrown**: `Object` exception thrown (if request fails)
-
-**errors**: `String` error description (if request fails)
-
-
## License
[MIT](LICENSE)
-
-
diff --git a/docs/api.md b/docs/api.md
new file mode 100644
index 0000000..94d8453
--- /dev/null
+++ b/docs/api.md
@@ -0,0 +1,109 @@
+# API
+
+| | Method | Short description |
+| :------------- | :--------------| :----------------- |
+|Frontexpress |||
+||[frontexpress()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpress-1)|Creates an instance of application|
+||[frontexpress.Router()](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressrouter)|Creates a Router object|
+||[frontexpress.Middleware](https://github.com/camelaissani/frontexpress/blob/master/docs/frontexpress.md#frontexpressmiddleware)|Returns the Middleware class |
+||||
+|Application |||
+||[set(setting, value)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationsetsetting-val)|Assigns a setting|
+||[listen(callback)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationlistencallback)|Starts the application|
+||[route(uri)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationrouteuri)|Gets a Router initialized with a root path|
+||[use(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationuseuri-middleware)|Sets a middleware|
+||||
+||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a GET request|
+||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a POST request|
+||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a PUT request|
+||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationgeturi-middleware-applicationposturi-middleware)|Applies a middleware on given path for a DELETE request|
+||||
+||[httpGet(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a GET ajax request|
+||[httpPost(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a POST ajax request|
+||[httpPut(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a PUT ajax request|
+||[httpDelete(request, success, failure)](https://github.com/camelaissani/frontexpress/blob/master/docs/application.md#applicationhttpgetrequest-success-failure-applicationhttppostrequest-success-failure)|Invokes a DELETE ajax request|
+||||
+|Router |||
+||[use(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerusemiddleware)|Sets a middleware|
+||[all(middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routerallmiddleware)|Sets a middleware on all HTTP method requests|
+||||
+||[get(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a GET request|
+||[post(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a POST request|
+||[put(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a PUT request|
+||[delete(uri, middleware)](https://github.com/camelaissani/frontexpress/blob/master/docs/router.md#routergeturi-middleware-routerposturi-middleware)|Applies a middleware on given path for a DELETE request|
+||||
+|Middleware |||
+||[entered(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareenteredrequest)|Invoked by the app before an ajax request is sent|
+||[exited(request)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareexitedrequest)|Invoked by the app before a new ajax request is sent|
+||[updated(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewareupdatedrequest-response)|Invoked by the app after an ajax request has responded|
+||[failed(request, response)](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarefailedrequest-response)|Invoked by the app after an ajax request has failed|
+||[next()](https://github.com/camelaissani/frontexpress/blob/master/docs/middleware.md#middlewarenext)|Allows to break the middleware chain execution|
+
+# middleware function
+
+After registering a middleware function, the application invokes it with these parameters:
+
+```js
+ (request, response, next) => {
+ next();
+ }
+```
+
+**request**: `Object`, the ajax request information sent by the app
+
+**response**: `Object`, the response of request
+
+**next**: `Function`, the `next()` function to call to not break the middleware execution chain
+
+# request object
+
+```js
+ {
+ method,
+ uri,
+ headers,
+ data,
+ history: {
+ state,
+ title,
+ uri
+ }
+ }
+```
+
+**method**: `String`, HTTP methods 'GET', 'POST'...
+
+**uri**: `String`, path
+
+**headers**: `Object`, custom HTTP headers
+
+**data**: `Object`, data attached to the request
+
+**history**: `Object`, object with properties state, title and uri
+
+>**If the history object is set, it will activate the browser history management.** See [browser pushState() method](https://developer.mozilla.org/en-US/docs/Web/API/History_API#The_pushState()_method) for more information about state, title, and uri (url).
+> uri and history.uri can be different.
+
+**params**: `Object`, object containing the path parameters
+
+# response object
+
+```js
+ {
+ status,
+ statusText,
+ responseText,
+ errorThrown,
+ errors
+ }
+```
+
+**status**: `Number`, HTTP status 200, 404, 401, 500...
+
+**statusText**: `String`
+
+**responseText**: `String` response content
+
+**errorThrown**: `Object` exception thrown (if request fails)
+
+**errors**: `String` error description (if request fails)
diff --git a/frontexpress.js b/frontexpress.js
index 052c8e3..e2b6ff1 100644
--- a/frontexpress.js
+++ b/frontexpress.js
@@ -136,200 +136,6 @@ var toConsumableArray = function (arr) {
}
};
-/**
- * Module dependencies.
- * @private
- */
-
-var Requester = function () {
- function Requester() {
- classCallCheck(this, Requester);
- }
-
- createClass(Requester, [{
- key: 'fetch',
-
-
- /**
- * Make an ajax request.
- *
- * @param {Object} request
- * @param {Function} success callback
- * @param {Function} failure callback
- * @private
- */
-
- value: function fetch(request, resolve, reject) {
- var method = request.method,
- uri = request.uri,
- headers = request.headers,
- data = request.data;
-
-
- var success = function success(responseText) {
- resolve(request, {
- status: 200,
- statusText: 'OK',
- responseText: responseText
- });
- };
-
- var fail = function fail(_ref) {
- var status = _ref.status,
- statusText = _ref.statusText,
- errorThrown = _ref.errorThrown;
-
- reject(request, {
- status: status,
- statusText: statusText,
- errorThrown: errorThrown,
- errors: 'HTTP ' + status + ' ' + (statusText ? statusText : '')
- });
- };
-
- var xmlhttp = new XMLHttpRequest();
- xmlhttp.onreadystatechange = function () {
- if (xmlhttp.readyState === 4) {
- //XMLHttpRequest.DONE
- if (xmlhttp.status === 200) {
- success(xmlhttp.responseText);
- } else {
- fail({ status: xmlhttp.status, statusText: xmlhttp.statusText });
- }
- }
- };
- try {
- xmlhttp.open(method, uri, true);
- if (headers) {
- Object.keys(headers).forEach(function (header) {
- xmlhttp.setRequestHeader(header, headers[header]);
- });
- }
- if (data) {
- xmlhttp.send(data);
- } else {
- xmlhttp.send();
- }
- } catch (errorThrown) {
- fail({ errorThrown: errorThrown });
- }
- }
- }]);
- return Requester;
-}();
-
-/**
- * Module dependencies.
- * @private
- */
-
-/**
- * Settings object.
- * @private
- */
-
-var Settings = function () {
-
- /**
- * Initialize the settings.
- *
- * - setup default configuration
- *
- * @private
- */
-
- function Settings() {
- classCallCheck(this, Settings);
-
- // default settings
- this.settings = {
- 'http requester': new Requester(),
-
- 'http GET transformer': {
- uri: function uri(_ref) {
- var _uri = _ref.uri,
- headers = _ref.headers,
- data = _ref.data;
-
- if (!data) {
- return _uri;
- }
- var uriWithoutAnchor = _uri,
- anchor = '';
-
- var match = /^(.*)(#.*)$/.exec(_uri);
- if (match) {
- var _$exec = /^(.*)(#.*)$/.exec(_uri);
-
- var _$exec2 = slicedToArray(_$exec, 3);
-
- uriWithoutAnchor = _$exec2[1];
- anchor = _$exec2[2];
- }
- uriWithoutAnchor = Object.keys(data).reduce(function (gUri, d, index) {
- gUri += '' + (index === 0 && gUri.indexOf('?') === -1 ? '?' : '&') + d + '=' + data[d];
- return gUri;
- }, uriWithoutAnchor);
- return uriWithoutAnchor + anchor;
- }
- }
- // 'http POST transformer': {
- // headers({uri, headers, data}) {
- // if (!data) {
- // return headers;
- // }
- // const updatedHeaders = headers || {};
- // if (!updatedHeaders['Content-Type']) {
- // updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
- // }
- // return updatedHeaders;
- // }
- // }
- };
-
- this.rules = {
- 'http requester': function httpRequester(requester) {
- if (typeof requester.fetch !== 'function') {
- throw new TypeError('setting http requester has no fetch method');
- }
- }
- };
- }
-
- /**
- * Assign `setting` to `val`
- *
- * @param {String} setting
- * @param {*} [val]
- * @private
- */
-
- createClass(Settings, [{
- key: 'set',
- value: function set$$1(name, value) {
- var checkRules = this.rules[name];
- if (checkRules) {
- checkRules(value);
- }
- this.settings[name] = value;
- }
-
- /**
- * Return `setting`'s value.
- *
- * @param {String} setting
- * @private
- */
-
- }, {
- key: 'get',
- value: function get$$1(name) {
- return this.settings[name];
- }
- }]);
- return Settings;
-}();
-
/**
* Middleware object.
* @public
@@ -543,26 +349,11 @@ var Router = function () {
}, {
key: 'routes',
- value: function routes(uri, method) {
+ value: function routes(application, request) {
+ request.params = request.params || {};
+ var isRouteMatch = application.get('route matcher');
return this._routes.filter(function (route) {
- if (route.method && route.method !== method) {
- return false;
- }
-
- if (!route.uri || !uri) {
- return true;
- }
-
- //remove query string from uri to test
- //remove anchor from uri to test
- var match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(uri);
- var baseUriToCheck = match[1] || match[2] || match[3];
-
- if (route.uri instanceof RegExp) {
- return baseUriToCheck.match(route.uri);
- }
-
- return route.uri === baseUriToCheck;
+ return isRouteMatch(request, route);
});
}
@@ -724,6 +515,284 @@ HTTP_METHODS.forEach(function (method) {
};
});
+function routeMatcher(request, route) {
+ // check if http method are equals
+ if (route.method && route.method !== request.method) {
+ return false;
+ }
+
+ // route and uri not defined always match
+ if (!route.uri || !request.uri) {
+ return true;
+ }
+
+ //remove query string and anchor from uri to test
+ var match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
+ var baseUriToCheck = match[1] || match[2] || match[3];
+
+ // if route is a regexp path
+ if (route.uri instanceof RegExp) {
+ return baseUriToCheck.match(route.uri) !== null;
+ }
+
+ // if route is parameterized path
+ if (route.uri.indexOf(':') !== -1) {
+
+ var decodeParmeterValue = function decodeParmeterValue(v) {
+ return !isNaN(parseFloat(v)) && isFinite(v) ? Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v) : v;
+ };
+
+ // figure out key names
+ var keys = [];
+ var keysRE = /:([^\/\?]+)\??/g;
+ var keysMatch = keysRE.exec(route.uri);
+ while (keysMatch != null) {
+ keys.push(keysMatch[1]);
+ keysMatch = keysRE.exec(route.uri);
+ }
+
+ // change parameterized path to regexp
+ var regExpUri = route.uri
+ // :parameter?
+ .replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
+ // :parameter
+ .replace(/:[^\/]+/g, '([^\/]+)')
+ // escape all /
+ .replace('/', '\\/');
+
+ // checks if uri match
+ var routeMatch = baseUriToCheck.match(new RegExp('^' + regExpUri + '$'));
+ if (!routeMatch) {
+ return false;
+ }
+
+ // update params in request with keys
+ request.params = Object.assign(request.params, keys.reduce(function (acc, key, index) {
+ var value = routeMatch[index + 1];
+ if (value) {
+ value = value.indexOf(',') !== -1 ? value.split(',').map(function (v) {
+ return decodeParmeterValue(v);
+ }) : value = decodeParmeterValue(value);
+ }
+ acc[key] = value;
+ return acc;
+ }, {}));
+ return true;
+ }
+
+ // if route is a simple path
+ return route.uri === baseUriToCheck;
+}
+
+/**
+ * Module dependencies.
+ * @private
+ */
+
+var Requester = function () {
+ function Requester() {
+ classCallCheck(this, Requester);
+ }
+
+ createClass(Requester, [{
+ key: 'fetch',
+
+
+ /**
+ * Make an ajax request.
+ *
+ * @param {Object} request
+ * @param {Function} success callback
+ * @param {Function} failure callback
+ * @private
+ */
+
+ value: function fetch(request, resolve, reject) {
+ var method = request.method,
+ uri = request.uri,
+ headers = request.headers,
+ data = request.data;
+
+
+ var success = function success(responseText) {
+ resolve(request, {
+ status: 200,
+ statusText: 'OK',
+ responseText: responseText
+ });
+ };
+
+ var fail = function fail(_ref) {
+ var status = _ref.status,
+ statusText = _ref.statusText,
+ errorThrown = _ref.errorThrown;
+
+ reject(request, {
+ status: status,
+ statusText: statusText,
+ errorThrown: errorThrown,
+ errors: 'HTTP ' + status + ' ' + (statusText ? statusText : '')
+ });
+ };
+
+ var xmlhttp = new XMLHttpRequest();
+ xmlhttp.onreadystatechange = function () {
+ if (xmlhttp.readyState === 4) {
+ //XMLHttpRequest.DONE
+ if (xmlhttp.status === 200) {
+ success(xmlhttp.responseText);
+ } else {
+ fail({ status: xmlhttp.status, statusText: xmlhttp.statusText });
+ }
+ }
+ };
+ try {
+ xmlhttp.open(method, uri, true);
+ if (headers) {
+ Object.keys(headers).forEach(function (header) {
+ xmlhttp.setRequestHeader(header, headers[header]);
+ });
+ }
+ if (data) {
+ xmlhttp.send(data);
+ } else {
+ xmlhttp.send();
+ }
+ } catch (errorThrown) {
+ fail({ errorThrown: errorThrown });
+ }
+ }
+ }]);
+ return Requester;
+}();
+
+var httpGetTransformer = {
+ uri: function uri(_ref2) {
+ var _uri = _ref2.uri,
+ headers = _ref2.headers,
+ data = _ref2.data;
+
+ if (!data) {
+ return _uri;
+ }
+ var uriWithoutAnchor = _uri,
+ anchor = '';
+
+ var match = /^(.*)(#.*)$/.exec(_uri);
+ if (match) {
+ var _$exec = /^(.*)(#.*)$/.exec(_uri);
+
+ var _$exec2 = slicedToArray(_$exec, 3);
+
+ uriWithoutAnchor = _$exec2[1];
+ anchor = _$exec2[2];
+ }
+ uriWithoutAnchor = Object.keys(data).reduce(function (gUri, d, index) {
+ gUri += '' + (index === 0 && gUri.indexOf('?') === -1 ? '?' : '&') + d + '=' + data[d];
+ return gUri;
+ }, uriWithoutAnchor);
+ return uriWithoutAnchor + anchor;
+ }
+};
+
+// export const httpPostTransformer = {
+// headers({uri, headers, data}) {
+// if (!data) {
+// return headers;
+// }
+// const updatedHeaders = headers || {};
+// if (!updatedHeaders['Content-Type']) {
+// updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
+// }
+// return updatedHeaders;
+// }
+// };
+
+/**
+ * Module dependencies.
+ * @private
+ */
+function errorIfNotFunction(toTest, message) {
+ if (typeof toTest !== 'function') {
+ throw new TypeError(message);
+ }
+}
+
+/**
+ * Settings object.
+ * @private
+ */
+
+var Settings = function () {
+
+ /**
+ * Initialize the settings.
+ *
+ * - setup default configuration
+ *
+ * @private
+ */
+
+ function Settings() {
+ classCallCheck(this, Settings);
+
+ // default settings
+ this.settings = {
+ 'http requester': new Requester(),
+ 'http GET transformer': httpGetTransformer,
+ // 'http POST transformer': httpPostTransformer,
+ 'route matcher': routeMatcher
+ };
+
+ this.rules = {
+ 'http requester': function httpRequester(requester) {
+ errorIfNotFunction(requester.fetch, 'setting http requester has no fetch function');
+ },
+ 'http GET transformer': function httpGETTransformer(transformer) {
+ if (!transformer || !transformer.uri && !transformer.headers && !transformer.data) {
+ throw new TypeError('setting http transformer one of functions: uri, headers, data is missing');
+ }
+ },
+ 'route matcher': function routeMatcher$$1(_routeMatcher) {
+ errorIfNotFunction(_routeMatcher, 'setting route matcher is not a function');
+ }
+ };
+ }
+
+ /**
+ * Assign `setting` to `val`
+ *
+ * @param {String} setting
+ * @param {*} [val]
+ * @private
+ */
+
+ createClass(Settings, [{
+ key: 'set',
+ value: function set$$1(name, value) {
+ var checkRules = this.rules[name];
+ if (checkRules) {
+ checkRules(value);
+ }
+ this.settings[name] = value;
+ }
+
+ /**
+ * Return `setting`'s value.
+ *
+ * @param {String} setting
+ * @private
+ */
+
+ }, {
+ key: 'get',
+ value: function get$$1(name) {
+ return this.settings[name];
+ }
+ }]);
+ return Settings;
+}();
+
/**
* Module dependencies.
* @private
@@ -747,9 +816,8 @@ var Application = function () {
classCallCheck(this, Application);
this.routers = [];
- // this.isDOMLoaded = false;
- // this.isDOMReady = false;
this.settings = new Settings();
+ this.plugins = [];
}
/**
@@ -768,6 +836,8 @@ var Application = function () {
createClass(Application, [{
key: 'set',
value: function set$$1() {
+ var _settings;
+
for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
args[_key] = arguments[_key];
}
@@ -778,10 +848,7 @@ var Application = function () {
}
// set behaviour
- var name = args[0],
- value = args[1];
-
- this.settings.set(name, value);
+ (_settings = this.settings).set.apply(_settings, args);
return this;
}
@@ -807,6 +874,12 @@ var Application = function () {
value: function listen(callback) {
var _this = this;
+ var request = { method: 'GET', uri: window.location.pathname + window.location.search };
+ var response = { status: 200, statusText: 'OK' };
+ var currentRoutes = this._routes(request);
+
+ this._callMiddlewareMethod('entered', currentRoutes, request);
+
// manage history
window.onpopstate = function (event) {
if (event.state) {
@@ -814,32 +887,27 @@ var Application = function () {
_request = _event$state.request,
_response = _event$state.response;
- var _currentRoutes = _this._routes(_request.uri, _request.method);
-
- _this._callMiddlewareMethod('exited');
- _this._callMiddlewareMethod('entered', _currentRoutes, _request);
- _this._callMiddlewareMethod('updated', _currentRoutes, _request, _response);
+ ['exited', 'entered', 'updated'].forEach(function (middlewareMethod) {
+ return _this._callMiddlewareMethod(middlewareMethod, _this._routes(_request), _request, _response);
+ });
}
};
// manage page loading/refreshing
- var request = { method: 'GET', uri: window.location.pathname + window.location.search };
- var response = { status: 200, statusText: 'OK' };
- var currentRoutes = this._routes();
+ window.onbeforeunload = function () {
+ _this._callMiddlewareMethod('exited');
+ };
var whenPageIsInteractiveFn = function whenPageIsInteractiveFn() {
+ _this.plugins.forEach(function (pluginObject) {
+ return pluginObject.plugin(_this);
+ });
_this._callMiddlewareMethod('updated', currentRoutes, request, response);
if (callback) {
callback(request, response);
}
};
- window.onbeforeunload = function () {
- _this._callMiddlewareMethod('exited');
- };
-
- this._callMiddlewareMethod('entered', currentRoutes, request);
-
document.onreadystatechange = function () {
// DOM ready state
if (document.readyState === 'interactive') {
@@ -876,6 +944,7 @@ var Application = function () {
/**
* Use the given middleware function or object, with optional _uri_.
* Default _uri_ is "/".
+ * Or use the given plugin
*
* // middleware function will be applied on path "/"
* app.use((req, res, next) => {console.log('Hello')});
@@ -883,8 +952,16 @@ var Application = function () {
* // middleware object will be applied on path "/"
* app.use(new Middleware());
*
+ * // use a plugin
+ * app.use({
+ * name: 'My plugin name',
+ * plugin(application) {
+ * // here plugin implementation
+ * }
+ * });
+ *
* @param {String} uri
- * @param {Middleware|Function} middleware object or function
+ * @param {Middleware|Function|plugin} middleware object, middleware function, plugin
* @return {app} for chaining
*
* @public
@@ -900,19 +977,24 @@ var Application = function () {
var _toParameters = toParameters(args),
baseUri = _toParameters.baseUri,
router = _toParameters.router,
- middleware = _toParameters.middleware;
+ middleware = _toParameters.middleware,
+ plugin = _toParameters.plugin;
- if (router) {
- router.baseUri = baseUri;
- } else if (middleware) {
- router = new Router(baseUri);
- HTTP_METHODS.forEach(function (method) {
- router[method.toLowerCase()](middleware);
- });
+ if (plugin) {
+ this.plugins.push(plugin);
} else {
- throw new TypeError('method takes at least a middleware or a router');
+ if (router) {
+ router.baseUri = baseUri;
+ } else if (middleware) {
+ router = new Router(baseUri);
+ HTTP_METHODS.forEach(function (method) {
+ router[method.toLowerCase()](middleware);
+ });
+ } else {
+ throw new TypeError('method takes at least a middleware or a router');
+ }
+ this.routers.push(router);
}
- this.routers.push(router);
return this;
}
@@ -926,16 +1008,13 @@ var Application = function () {
}, {
key: '_routes',
- value: function _routes() {
- var uri = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : window.location.pathname + window.location.search;
- var method = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'GET';
+ value: function _routes(request) {
+ var _this2 = this;
- var currentRoutes = [];
- this.routers.forEach(function (router) {
- currentRoutes.push.apply(currentRoutes, toConsumableArray(router.routes(uri, method)));
- });
-
- return currentRoutes;
+ return this.routers.reduce(function (acc, router) {
+ acc.push.apply(acc, toConsumableArray(router.routes(_this2, request)));
+ return acc;
+ }, []);
}
/**
@@ -995,7 +1074,7 @@ var Application = function () {
}, {
key: '_fetch',
value: function _fetch(req, resolve, reject) {
- var _this2 = this;
+ var _this3 = this;
var method = req.method,
uri = req.uri,
@@ -1019,7 +1098,7 @@ var Application = function () {
this._callMiddlewareMethod('exited');
// gathers all routes impacted by the uri
- var currentRoutes = this._routes(uri, method);
+ var currentRoutes = this._routes(req);
// calls middleware entered method
this._callMiddlewareMethod('entered', currentRoutes, req);
@@ -1029,12 +1108,12 @@ var Application = function () {
if (history) {
window.history.pushState({ request: request, response: response }, history.title, history.uri);
}
- _this2._callMiddlewareMethod('updated', currentRoutes, request, response);
+ _this3._callMiddlewareMethod('updated', currentRoutes, request, response);
if (resolve) {
resolve(request, response);
}
}, function (request, response) {
- _this2._callMiddlewareMethod('failed', currentRoutes, request, response);
+ _this3._callMiddlewareMethod('failed', currentRoutes, request, response);
if (reject) {
reject(request, response);
}
@@ -1136,29 +1215,25 @@ HTTP_METHODS.reduce(function (reqProto, method) {
}, Application.prototype);
function toParameters(args) {
+ var _args, _args2, _args3, _args4;
+
var baseUri = void 0,
middleware = void 0,
router = void 0,
+ plugin = void 0,
which = void 0;
- if (args && args.length > 0) {
- if (args.length === 1) {
- var _args = slicedToArray(args, 1);
- which = _args[0];
- } else {
- var _args2 = slicedToArray(args, 2);
+ args.length === 1 ? (_args = args, _args2 = slicedToArray(_args, 1), which = _args2[0], _args) : (_args3 = args, _args4 = slicedToArray(_args3, 2), baseUri = _args4[0], which = _args4[1], _args3);
- baseUri = _args2[0];
- which = _args2[1];
- }
-
- if (which instanceof Router) {
- router = which;
- } else if (which instanceof Middleware || typeof which === 'function') {
- middleware = which;
- }
+ if (which instanceof Router) {
+ router = which;
+ } else if (which instanceof Middleware || typeof which === 'function') {
+ middleware = which;
+ } else if (which && which.plugin && typeof which.plugin === 'function') {
+ plugin = which;
}
- return { baseUri: baseUri, middleware: middleware, router: router, which: which };
+
+ return { baseUri: baseUri, middleware: middleware, router: router, plugin: plugin, which: which };
}
/**
diff --git a/frontexpress.min.js b/frontexpress.min.js
index 89c6456..17acda0 100644
--- a/frontexpress.min.js
+++ b/frontexpress.min.js
@@ -1,2 +1,2 @@
-var frontexpress=function(){"use strict";function e(e){var t=void 0,r=void 0,i=void 0,n=void 0;if(e&&e.length>0){if(1===e.length)n=a(e,1)[0];else{var o=a(e,2);t=o[0],n=o[1]}n instanceof f?i=n:(n instanceof d||"function"==typeof n)&&(r=n)}return{baseUri:t,middleware:r,router:i,which:n}}var t=["GET","POST","PUT","PATCH","DELETE"],r="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},i=function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")},n=function(){function e(e,t){for(var r=0;r0&&void 0!==arguments[0]?arguments[0]:"";i(this,e),this.name=t}return n(e,[{key:"entered",value:function(e){}},{key:"exited",value:function(e){}},{key:"updated",value:function(e,t){}},{key:"failed",value:function(e,t){}},{key:"next",value:function(){return!0}}]),e}(),h=function(){function e(t,r,n,a){i(this,e),this.router=t,this.uriPart=r,this.method=n,this.middleware=a,this.visited=!1}return n(e,[{key:"uri",get:function(){if(this.uriPart||this.method){if(this.uriPart instanceof RegExp)return this.uriPart;if(this.router.baseUri instanceof RegExp)return this.router.baseUri;if(this.router.baseUri){var e=this.router.baseUri.trim();return this.uriPart?(e+this.uriPart.trim()).replace(/\/{2,}/,"/"):e}return this.uriPart}}}]),e}(),f=function(){function a(e){i(this,a),this._baseUri=e,this._routes=[]}return n(a,[{key:"_add",value:function(e){return this._routes.push(e),this}},{key:"routes",value:function(e,t){return this._routes.filter(function(r){if(r.method&&r.method!==t)return!1;if(!r.uri||!e)return!0;var i=/^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(e),n=i[1]||i[2]||i[3];return r.uri instanceof RegExp?n.match(r.uri):r.uri===n})}},{key:"visited",value:function(){return this._routes.filter(function(e){return e.visited})}},{key:"use",value:function(e){if(!(e instanceof d)&&"function"!=typeof e)throw new TypeError("method takes at least a middleware");return this._add(new h(this,void 0,void 0,e)),this}},{key:"all",value:function(){for(var r=this,i=arguments.length,n=Array(i),a=0;a0&&void 0!==arguments[0]?arguments[0]:window.location.pathname+window.location.search,t=arguments.length>1&&void 0!==arguments[1]?arguments[1]:"GET",r=[];return this.routers.forEach(function(i){r.push.apply(r,o(i.routes(e,t)))}),r}},{key:"_callMiddlewareMethod",value:function(e,t,r,i){if("exited"===e)return void this.routers.forEach(function(e){e.visited().forEach(function(e){e.middleware.exited&&(e.middleware.exited(e.visited),e.visited=null)})});t.some(function(t){if("updated"===e&&(t.visited=r),t.middleware[e]){if(t.middleware[e](r,i),t.middleware.next&&!t.middleware.next())return!0}else if("entered"!==e){var n=!0,a=function(){n=!1};if(t.middleware(r,i,a),n)return!0}return!1})}},{key:"_fetch",value:function(e,t,r){var i=this,n=e.method,a=e.uri,o=e.headers,u=e.data,s=e.history,d=this.get("http "+n+" transformer");if(d){var h=d.uri,f=d.headers,c=d.data;e.uri=h?h({uri:a,headers:o,data:u}):a,e.headers=f?f({uri:a,headers:o,data:u}):o,e.data=c?c({uri:a,headers:o,data:u}):u}this._callMiddlewareMethod("exited");var l=this._routes(a,n);this._callMiddlewareMethod("entered",l,e),this.settings.get("http requester").fetch(e,function(e,r){s&&window.history.pushState({request:e,response:r},s.title,s.uri),i._callMiddlewareMethod("updated",l,e,r),t&&t(e,r)},function(e,t){i._callMiddlewareMethod("failed",l,e,t),r&&r(e,t)})}}]),r}();t.reduce(function(t,r){var i=r.toLowerCase();return t[i]=function(){for(var t=arguments.length,r=Array(t),n=0;n0&&void 0!==arguments[0]?arguments[0]:"";a(this,e),this.name=t}return u(e,[{key:"entered",value:function(e){}},{key:"exited",value:function(e){}},{key:"updated",value:function(e,t){}},{key:"failed",value:function(e,t){}},{key:"next",value:function(){return!0}}]),e}(),h=function(){function e(t,r,n,i){a(this,e),this.router=t,this.uriPart=r,this.method=n,this.middleware=i,this.visited=!1}return u(e,[{key:"uri",get:function(){if(this.uriPart||this.method){if(this.uriPart instanceof RegExp)return this.uriPart;if(this.router.baseUri instanceof RegExp)return this.router.baseUri;if(this.router.baseUri){var e=this.router.baseUri.trim();return this.uriPart?(e+this.uriPart.trim()).replace(/\/{2,}/,"/"):e}return this.uriPart}}}]),e}(),d=function(){function e(t){a(this,e),this._baseUri=t,this._routes=[]}return u(e,[{key:"_add",value:function(e){return this._routes.push(e),this}},{key:"routes",value:function(e,t){t.params=t.params||{};var r=e.get("route matcher");return this._routes.filter(function(e){return r(t,e)})}},{key:"visited",value:function(){return this._routes.filter(function(e){return e.visited})}},{key:"use",value:function(e){if(!(e instanceof f)&&"function"!=typeof e)throw new TypeError("method takes at least a middleware");return this._add(new h(this,void 0,void 0,e)),this}},{key:"all",value:function(){for(var e=this,t=arguments.length,i=Array(t),a=0;a \"bar\"\n *\n * @param {String} setting\n * @param {*} [val]\n * @return {app} for chaining\n * @public\n */\n\n set(...args) {\n // get behaviour\n if (args.length === 1) {\n return this.settings.get([args]);\n }\n\n // set behaviour\n const [name, value] = args;\n this.settings.set(name, value);\n\n return this;\n }\n\n\n /**\n * Listen for DOM initialization and history state changes.\n *\n * The callback function is called once the DOM has\n * the `document.readyState` equals to 'interactive'.\n *\n * app.listen(()=> {\n * console.log('App is listening requests');\n * console.log('DOM is ready!');\n * });\n *\n *\n * @param {Function} callback\n * @public\n */\n\n listen(callback) {\n\n // manage history\n window.onpopstate = (event) => {\n if (event.state) {\n const {request, response} = event.state;\n const currentRoutes = this._routes(request.uri, request.method);\n\n this._callMiddlewareMethod('exited');\n this._callMiddlewareMethod('entered', currentRoutes, request);\n this._callMiddlewareMethod('updated', currentRoutes, request, response);\n }\n };\n\n // manage page loading/refreshing\n const request = {method: 'GET', uri: window.location.pathname + window.location.search};\n const response = {status: 200, statusText: 'OK'};\n const currentRoutes = this._routes();\n\n const whenPageIsInteractiveFn = () => {\n this._callMiddlewareMethod('updated', currentRoutes, request, response);\n if (callback) {\n callback(request, response);\n }\n };\n\n window.onbeforeunload = () => {\n this._callMiddlewareMethod('exited');\n };\n\n this._callMiddlewareMethod('entered', currentRoutes, request);\n\n document.onreadystatechange = () => {\n // DOM ready state\n if (document.readyState === 'interactive') {\n whenPageIsInteractiveFn();\n }\n };\n\n if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {\n whenPageIsInteractiveFn();\n }\n\n }\n\n\n /**\n * Returns a new `Router` instance for the _uri_.\n * See the Router api docs for details.\n *\n * app.route('/');\n * // => new Router instance\n *\n * @param {String} uri\n * @return {Router} for chaining\n *\n * @public\n */\n\n route(uri) {\n const router = new Router(uri);\n this.routers.push(router);\n return router;\n }\n\n\n /**\n * Use the given middleware function or object, with optional _uri_.\n * Default _uri_ is \"/\".\n *\n * // middleware function will be applied on path \"/\"\n * app.use((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\"\n * app.use(new Middleware());\n *\n * @param {String} uri\n * @param {Middleware|Function} middleware object or function\n * @return {app} for chaining\n *\n * @public\n */\n\n use(...args) {\n let {baseUri, router, middleware} = toParameters(args);\n if (router) {\n router.baseUri = baseUri;\n } else if (middleware) {\n router = new Router(baseUri);\n HTTP_METHODS.forEach((method) => {\n router[method.toLowerCase()](middleware);\n });\n } else {\n throw new TypeError('method takes at least a middleware or a router');\n }\n this.routers.push(router);\n\n return this;\n }\n\n\n /**\n * Gather routes from all routers filtered by _uri_ and HTTP _method_.\n * See Router#routes() documentation for details.\n *\n * @private\n */\n\n _routes(uri=window.location.pathname + window.location.search, method='GET') {\n const currentRoutes = [];\n this.routers.forEach((router) => {\n currentRoutes.push(...router.routes(uri, method));\n });\n\n return currentRoutes;\n }\n\n\n /**\n * Call `Middleware` method or middleware function on _currentRoutes_.\n *\n * @private\n */\n\n _callMiddlewareMethod(meth, currentRoutes, request, response) {\n if (meth === 'exited') {\n // currentRoutes, request, response params not needed\n this.routers.forEach((router) => {\n router.visited().forEach((route) => {\n if (route.middleware.exited) {\n route.middleware.exited(route.visited);\n route.visited = null;\n }\n });\n });\n return;\n }\n\n currentRoutes.some((route) => {\n if (meth === 'updated') {\n route.visited = request;\n }\n\n if (route.middleware[meth]) {\n route.middleware[meth](request, response);\n if (route.middleware.next && !route.middleware.next()) {\n return true;\n }\n } else if (meth !== 'entered') {\n // calls middleware method\n let breakMiddlewareLoop = true;\n const next = () => {\n breakMiddlewareLoop = false;\n };\n route.middleware(request, response, next);\n if (breakMiddlewareLoop) {\n return true;\n }\n }\n\n return false;\n });\n }\n\n\n /**\n * Make an ajax request. Manage History#pushState if history object set.\n *\n * @private\n */\n\n _fetch(req, resolve, reject) {\n let {method, uri, headers, data, history} = req;\n\n const httpMethodTransformer = this.get(`http ${method} transformer`);\n if (httpMethodTransformer) {\n const {uri: _uriFn, headers: _headersFn, data: _dataFn } = httpMethodTransformer;\n req.uri = _uriFn ? _uriFn({uri, headers, data}) : uri;\n req.headers = _headersFn ? _headersFn({uri, headers, data}) : headers;\n req.data = _dataFn ? _dataFn({uri, headers, data}) : data;\n }\n\n // calls middleware exited method\n this._callMiddlewareMethod('exited');\n\n // gathers all routes impacted by the uri\n const currentRoutes = this._routes(uri, method);\n\n // calls middleware entered method\n this._callMiddlewareMethod('entered', currentRoutes, req);\n\n // invokes http request\n this.settings.get('http requester').fetch(req,\n (request, response) => {\n if (history) {\n window.history.pushState({request, response}, history.title, history.uri);\n }\n this._callMiddlewareMethod('updated', currentRoutes, request, response);\n if (resolve) {\n resolve(request, response);\n }\n },\n (request, response) => {\n this._callMiddlewareMethod('failed', currentRoutes, request, response);\n if (reject) {\n reject(request, response);\n }\n });\n }\n}\n\nHTTP_METHODS.reduce((reqProto, method) => {\n\n\n /**\n * Use the given middleware function or object, with optional _uri_ on\n * HTTP methods: get, post, put, delete...\n * Default _uri_ is \"/\".\n *\n * // middleware function will be applied on path \"/\"\n * app.get((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\" and\n * app.get(new Middleware());\n *\n * // get a setting value\n * app.set('foo', 'bar');\n * app.get('foo');\n * // => \"bar\"\n *\n * @param {String} uri or setting\n * @param {Middleware|Function} middleware object or function\n * @return {app} for chaining\n * @public\n */\n\n const middlewareMethodName = method.toLowerCase();\n reqProto[middlewareMethodName] = function(...args) {\n let {baseUri, middleware, which} = toParameters(args);\n if (middlewareMethodName === 'get' && typeof which === 'string') {\n return this.settings.get(which);\n }\n if (!middleware) {\n throw new TypeError(`method takes a middleware ${middlewareMethodName === 'get' ? 'or a string' : ''}`);\n }\n const router = new Router();\n router[middlewareMethodName](baseUri, middleware);\n\n this.routers.push(router);\n\n return this;\n };\n\n /**\n * Ajax request (get, post, put, delete...).\n *\n * // HTTP GET method\n * httpGet('/route1');\n *\n * // HTTP GET method\n * httpGet({uri: '/route1', data: {'p1': 'val1'});\n * // uri invoked => /route1?p1=val1\n *\n * // HTTP GET method with browser history management\n * httpGet({uri: '/api/users', history: {state: {foo: \"bar\"}, title: 'users page', uri: '/view/users'});\n *\n * Samples above can be applied on other HTTP methods.\n *\n * @param {String|Object} uri or object containing uri, http headers, data, history\n * @param {Function} success callback\n * @param {Function} failure callback\n * @public\n */\n const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();\n reqProto[httpMethodName] = function(request, resolve, reject) {\n let {uri, headers, data, history} = request;\n if (!uri) {\n uri = request;\n }\n return this._fetch({\n uri,\n method,\n headers,\n data,\n history\n }, resolve, reject);\n };\n\n return reqProto;\n}, Application.prototype);\n\n\nexport function toParameters(args) {\n let baseUri, middleware, router, which;\n if (args && args.length > 0) {\n if (args.length === 1) {\n [which,] = args;\n } else {\n [baseUri, which,] = args;\n }\n\n if (which instanceof Router) {\n router = which;\n } else if ((which instanceof Middleware) || (typeof which === 'function')) {\n middleware = which;\n }\n }\n return {baseUri, middleware, router, which};\n}\n","/**\n * HTTP method list\n * @private\n */\n\n export default ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];\n // not supported yet\n // HEAD', 'CONNECT', 'OPTIONS', 'TRACE';\n","/**\n * Module dependencies.\n * @private\n */\n\nexport default class Requester {\n\n /**\n * Make an ajax request.\n *\n * @param {Object} request\n * @param {Function} success callback\n * @param {Function} failure callback\n * @private\n */\n\n fetch(request, resolve, reject) {\n const {method, uri, headers, data} = request;\n\n const success = (responseText) => {\n resolve(\n request,\n {\n status: 200,\n statusText: 'OK',\n responseText\n }\n );\n };\n\n const fail = ({status, statusText, errorThrown}) => {\n reject(\n request,\n {\n status,\n statusText,\n errorThrown,\n errors: `HTTP ${status} ${statusText?statusText:''}`\n }\n );\n };\n\n const xmlhttp = new XMLHttpRequest();\n xmlhttp.onreadystatechange = () => {\n if (xmlhttp.readyState === 4) { //XMLHttpRequest.DONE\n if (xmlhttp.status === 200) {\n success(xmlhttp.responseText);\n } else {\n fail({status: xmlhttp.status, statusText: xmlhttp.statusText});\n }\n }\n };\n try {\n xmlhttp.open(method, uri, true);\n if (headers) {\n Object.keys(headers).forEach((header) => {\n xmlhttp.setRequestHeader(header, headers[header]);\n });\n }\n if (data) {\n xmlhttp.send(data);\n } else {\n xmlhttp.send();\n }\n } catch (errorThrown) {\n fail({errorThrown});\n }\n }\n}\n","/**\n * Module dependencies.\n * @private\n */\n\nimport Requester from './requester';\n\n\n/**\n * Settings object.\n * @private\n */\n\nexport default class Settings {\n\n\n /**\n * Initialize the settings.\n *\n * - setup default configuration\n *\n * @private\n */\n\n constructor() {\n // default settings\n this.settings = {\n 'http requester': new Requester(),\n\n 'http GET transformer': {\n uri({uri, headers, data}) {\n if (!data) {\n return uri;\n }\n let [uriWithoutAnchor, anchor] = [uri, ''];\n const match = /^(.*)(#.*)$/.exec(uri);\n if (match) {\n [,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);\n }\n uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {\n gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;\n return gUri;\n }, uriWithoutAnchor);\n return uriWithoutAnchor + anchor;\n }\n }\n // 'http POST transformer': {\n // headers({uri, headers, data}) {\n // if (!data) {\n // return headers;\n // }\n // const updatedHeaders = headers || {};\n // if (!updatedHeaders['Content-Type']) {\n // updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';\n // }\n // return updatedHeaders;\n // }\n // }\n };\n\n this.rules = {\n 'http requester': (requester) => {\n if(typeof requester.fetch !== 'function') {\n throw new TypeError('setting http requester has no fetch method');\n }\n }\n };\n }\n\n\n /**\n * Assign `setting` to `val`\n *\n * @param {String} setting\n * @param {*} [val]\n * @private\n */\n\n set(name, value) {\n const checkRules = this.rules[name];\n if (checkRules) {\n checkRules(value);\n }\n this.settings[name] = value;\n }\n\n\n /**\n * Return `setting`'s value.\n *\n * @param {String} setting\n * @private\n */\n\n get(name) {\n return this.settings[name];\n }\n};\n","/**\n * Middleware object.\n * @public\n */\n\nexport default class Middleware {\n\n\n /**\n * Middleware initialization\n *\n * @param {String} middleware name\n */\n\n constructor(name='') {\n this.name = name;\n }\n\n /**\n * Invoked by the app before an ajax request is sent or\n * during the DOM loading (document.readyState === 'loading').\n * See Application#_callMiddlewareEntered documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @public\n */\n\n entered(request) { }\n\n\n /**\n * Invoked by the app before a new ajax request is sent or before the DOM is unloaded.\n * See Application#_callMiddlewareExited documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @public\n */\n\n exited(request) { }\n\n\n /**\n * Invoked by the app after an ajax request has responded or on DOM ready\n * (document.readyState === 'interactive').\n * See Application#_callMiddlewareUpdated documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @param {Object} response\n * @public\n */\n\n updated(request, response) { }\n\n\n /**\n * Invoked by the app when an ajax request has failed.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @param {Object} response\n * @public\n */\n failed(request, response) { }\n\n\n /**\n * Allow the hand over to the next middleware object or function.\n *\n * Override this method and return `false` to break execution of\n * middleware chain.\n *\n * @return {Boolean} `true` by default\n *\n * @public\n */\n\n next() {\n return true;\n }\n}\n","/**\n * Module dependencies.\n * @private\n */\n\nimport HTTP_METHODS from './methods';\nimport {toParameters} from './application';\nimport Middleware from './middleware';\n\n\n/**\n * Route object.\n * @private\n */\n\nexport class Route {\n\n\n /**\n * Initialize the route.\n *\n * @private\n */\n\n constructor(router, uriPart, method, middleware) {\n this.router = router;\n this.uriPart = uriPart;\n this.method = method;\n this.middleware = middleware;\n this.visited = false;\n }\n\n\n /**\n * Return route's uri.\n *\n * @private\n */\n\n get uri() {\n if (!this.uriPart && !this.method) {\n return undefined;\n }\n\n if (this.uriPart instanceof RegExp) {\n return this.uriPart;\n }\n\n if (this.router.baseUri instanceof RegExp) {\n return this.router.baseUri;\n }\n\n if (this.router.baseUri) {\n const baseUri = this.router.baseUri.trim();\n if (this.uriPart) {\n return ( baseUri + this.uriPart.trim()).replace(/\\/{2,}/, '/');\n }\n return baseUri;\n }\n\n return this.uriPart;\n }\n}\n\n\n/**\n * Router object.\n * @public\n */\n\nconst error_middleware_message = 'method takes at least a middleware';\nexport default class Router {\n\n\n /**\n * Initialize the router.\n *\n * @private\n */\n\n constructor(uri) {\n this._baseUri = uri;\n this._routes = [];\n }\n\n\n /**\n * Do some checks and set _baseUri.\n *\n * @private\n */\n\n set baseUri(uri) {\n if (!uri) {\n return;\n }\n\n if (!this._baseUri) {\n this._baseUri = uri;\n return;\n }\n\n if (typeof this._baseUri !== typeof uri) {\n throw new TypeError('router cannot mix regexp and uri');\n }\n }\n\n\n /**\n * Return router's _baseUri.\n *\n * @private\n */\n\n get baseUri() {\n return this._baseUri;\n }\n\n\n /**\n * Add a route to the router.\n *\n * @private\n */\n\n _add(route) {\n this._routes.push(route);\n return this;\n }\n\n\n /**\n * Gather routes from routers filtered by _uri_ and HTTP _method_.\n *\n * @private\n */\n\n routes(uri, method) {\n return this._routes.filter((route) => {\n if (route.method && route.method !== method) {\n return false;\n }\n\n if (!route.uri || !uri) {\n return true;\n }\n\n //remove query string from uri to test\n //remove anchor from uri to test\n const match = /^(.*)\\?.*#.*|(.*)(?=\\?|#)|(.*[^\\?#])$/.exec(uri);\n const baseUriToCheck = match[1] || match[2] || match[3];\n\n if (route.uri instanceof RegExp) {\n return baseUriToCheck.match(route.uri);\n }\n\n return route.uri === baseUriToCheck;\n });\n }\n\n\n /**\n * Gather visited routes from routers.\n *\n * @private\n */\n\n visited() {\n return this._routes.filter(route => route.visited);\n }\n\n\n /**\n * Use the given middleware function or object on this router.\n *\n * // middleware function\n * router.use((req, res, next) => {console.log('Hello')});\n *\n * // middleware object\n * router.use(new Middleware());\n *\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n *\n * @public\n */\n\n use(middleware) {\n if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {\n throw new TypeError(error_middleware_message);\n }\n\n this._add(new Route(this, undefined, undefined, middleware));\n\n return this;\n }\n\n\n /**\n * Use the given middleware function or object on this router for\n * all HTTP methods.\n *\n * // middleware function\n * router.all((req, res, next) => {console.log('Hello')});\n *\n * // middleware object\n * router.all(new Middleware());\n *\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n *\n * @public\n */\n\n all(...args) {\n const {middleware} = toParameters(args);\n if (!middleware) {\n throw new TypeError(error_middleware_message);\n }\n\n HTTP_METHODS.forEach((method) => {\n this[method.toLowerCase()](...args);\n });\n return this;\n }\n}\n\nHTTP_METHODS.forEach((method) => {\n\n\n /**\n * Use the given middleware function or object, with optional _uri_ on\n * HTTP methods: get, post, put, delete...\n * Default _uri_ is \"/\".\n *\n * // middleware function will be applied on path \"/\"\n * router.get((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\" and\n * router.get(new Middleware());\n *\n * // middleware function will be applied on path \"/user\"\n * router.post('/user', (req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/user\" and\n * router.post('/user', new Middleware());\n *\n * @param {String} uri\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n * @public\n */\n\n const methodName = method.toLowerCase();\n Router.prototype[methodName] = function(...args) {\n const {baseUri, middleware} = toParameters(args);\n if (!middleware) {\n throw new TypeError(error_middleware_message);\n }\n\n if (baseUri && this._baseUri && this._baseUri instanceof RegExp) {\n throw new TypeError('router cannot mix uri/regexp');\n }\n\n this._add(new Route(this, baseUri, method, middleware));\n\n return this;\n };\n});\n","/**\n * Module dependencies.\n */\n\nimport Application from './application';\nimport Router from './router';\nimport Middleware from './middleware';\n\n\n/**\n * Create a frontexpress application.\n *\n * @return {Function}\n * @api public\n */\n\nconst frontexpress = () => new Application();\n\n/**\n * Expose Router, Middleware constructors.\n */\nfrontexpress.Router = (baseUri) => new Router(baseUri);\nfrontexpress.Middleware = Middleware;\n\nexport default frontexpress;\n"],"names":["toParameters","args","baseUri","middleware","router","which","length","Router","Middleware","Requester","request","resolve","reject","method","uri","headers","data","success","responseText","fail","status","statusText","errorThrown","xmlhttp","XMLHttpRequest","onreadystatechange","readyState","open","keys","forEach","header","setRequestHeader","send","Settings","settings","uriWithoutAnchor","anchor","exec","Object","reduce","gUri","d","index","indexOf","rules","requester","fetch","TypeError","name","value","checkRules","this","response","Route","uriPart","visited","RegExp","trim","replace","_baseUri","_routes","route","push","filter","match","baseUriToCheck","_add","undefined","toLowerCase","babelHelpers.typeof","methodName","prototype","Application","routers","get","set","callback","onpopstate","event","state","currentRoutes","_this","_callMiddlewareMethod","window","location","pathname","search","whenPageIsInteractiveFn","onbeforeunload","document","routes","meth","exited","some","next","breakMiddlewareLoop","req","history","httpMethodTransformer","_uriFn","_headersFn","_dataFn","pushState","title","reqProto","middlewareMethodName","charAt","toUpperCase","slice","_fetch","frontexpress"],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":null,"sources":["lib/router.js","lib/settings.js","lib/application.js","lib/methods.js","lib/middleware.js","lib/requester.js","lib/frontexpress.js"],"sourcesContent":["/**\n * Module dependencies.\n * @private\n */\n\nimport HTTP_METHODS from './methods';\nimport {toParameters} from './application';\nimport Middleware from './middleware';\n\n\n/**\n * Route object.\n * @private\n */\n\nexport class Route {\n\n\n /**\n * Initialize the route.\n *\n * @private\n */\n\n constructor(router, uriPart, method, middleware) {\n this.router = router;\n this.uriPart = uriPart;\n this.method = method;\n this.middleware = middleware;\n this.visited = false;\n }\n\n\n /**\n * Return route's uri.\n *\n * @private\n */\n\n get uri() {\n if (!this.uriPart && !this.method) {\n return undefined;\n }\n\n if (this.uriPart instanceof RegExp) {\n return this.uriPart;\n }\n\n if (this.router.baseUri instanceof RegExp) {\n return this.router.baseUri;\n }\n\n if (this.router.baseUri) {\n const baseUri = this.router.baseUri.trim();\n if (this.uriPart) {\n return ( baseUri + this.uriPart.trim()).replace(/\\/{2,}/, '/');\n }\n return baseUri;\n }\n\n return this.uriPart;\n }\n}\n\n\n/**\n * Router object.\n * @public\n */\n\nconst error_middleware_message = 'method takes at least a middleware';\nexport default class Router {\n\n\n /**\n * Initialize the router.\n *\n * @private\n */\n\n constructor(uri) {\n this._baseUri = uri;\n this._routes = [];\n }\n\n\n /**\n * Do some checks and set _baseUri.\n *\n * @private\n */\n\n set baseUri(uri) {\n if (!uri) {\n return;\n }\n\n if (!this._baseUri) {\n this._baseUri = uri;\n return;\n }\n\n if (typeof this._baseUri !== typeof uri) {\n throw new TypeError('router cannot mix regexp and uri');\n }\n }\n\n\n /**\n * Return router's _baseUri.\n *\n * @private\n */\n\n get baseUri() {\n return this._baseUri;\n }\n\n\n /**\n * Add a route to the router.\n *\n * @private\n */\n\n _add(route) {\n this._routes.push(route);\n return this;\n }\n\n\n /**\n * Gather routes from routers filtered by _uri_ and HTTP _method_.\n *\n * @private\n */\n\n routes(application, request) {\n request.params = request.params || {};\n const isRouteMatch = application.get('route matcher');\n return this._routes.filter((route) => {\n return isRouteMatch(request, route);\n });\n }\n\n\n /**\n * Gather visited routes from routers.\n *\n * @private\n */\n\n visited() {\n return this._routes.filter(route => route.visited);\n }\n\n\n /**\n * Use the given middleware function or object on this router.\n *\n * // middleware function\n * router.use((req, res, next) => {console.log('Hello')});\n *\n * // middleware object\n * router.use(new Middleware());\n *\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n *\n * @public\n */\n\n use(middleware) {\n if (!(middleware instanceof Middleware) && (typeof middleware !== 'function') ) {\n throw new TypeError(error_middleware_message);\n }\n\n this._add(new Route(this, undefined, undefined, middleware));\n\n return this;\n }\n\n\n /**\n * Use the given middleware function or object on this router for\n * all HTTP methods.\n *\n * // middleware function\n * router.all((req, res, next) => {console.log('Hello')});\n *\n * // middleware object\n * router.all(new Middleware());\n *\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n *\n * @public\n */\n\n all(...args) {\n const {middleware} = toParameters(args);\n if (!middleware) {\n throw new TypeError(error_middleware_message);\n }\n\n HTTP_METHODS.forEach((method) => {\n this[method.toLowerCase()](...args);\n });\n return this;\n }\n}\n\nHTTP_METHODS.forEach((method) => {\n\n\n /**\n * Use the given middleware function or object, with optional _uri_ on\n * HTTP methods: get, post, put, delete...\n * Default _uri_ is \"/\".\n *\n * // middleware function will be applied on path \"/\"\n * router.get((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\" and\n * router.get(new Middleware());\n *\n * // middleware function will be applied on path \"/user\"\n * router.post('/user', (req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/user\" and\n * router.post('/user', new Middleware());\n *\n * @param {String} uri\n * @param {Middleware|Function} middleware object or function\n * @return {Router} for chaining\n * @public\n */\n\n const methodName = method.toLowerCase();\n Router.prototype[methodName] = function(...args) {\n const {baseUri, middleware} = toParameters(args);\n if (!middleware) {\n throw new TypeError(error_middleware_message);\n }\n\n if (baseUri && this._baseUri && this._baseUri instanceof RegExp) {\n throw new TypeError('router cannot mix uri/regexp');\n }\n\n this._add(new Route(this, baseUri, method, middleware));\n\n return this;\n };\n});\n\nexport function routeMatcher(request, route) {\n // check if http method are equals\n if (route.method && route.method !== request.method) {\n return false;\n }\n\n\n // route and uri not defined always match\n if (!route.uri || !request.uri) {\n return true;\n }\n\n //remove query string and anchor from uri to test\n const match = /^(.*)\\?.*#.*|(.*)(?=\\?|#)|(.*[^\\?#])$/.exec(request.uri);\n const baseUriToCheck = match[1] || match[2] || match[3];\n\n // if route is a regexp path\n if (route.uri instanceof RegExp) {\n return baseUriToCheck.match(route.uri) !== null;\n }\n\n // if route is parameterized path\n if (route.uri.indexOf(':') !== -1) {\n\n const decodeParmeterValue = (v) => {\n return !isNaN(parseFloat(v)) && isFinite(v) ? (Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v)) : v;\n };\n\n // figure out key names\n const keys = [];\n const keysRE = /:([^\\/\\?]+)\\??/g;\n let keysMatch = keysRE.exec(route.uri);\n while (keysMatch != null) {\n keys.push(keysMatch[1]);\n keysMatch = keysRE.exec(route.uri);\n }\n\n // change parameterized path to regexp\n const regExpUri = route.uri\n // :parameter?\n .replace(/\\/:[^\\/]+\\?/g, '(?:\\/([^\\/]+))?')\n // :parameter\n .replace(/:[^\\/]+/g, '([^\\/]+)')\n // escape all /\n .replace('/', '\\\\/');\n\n // checks if uri match\n const routeMatch = baseUriToCheck.match(new RegExp(`^${regExpUri}$`));\n if (!routeMatch) {\n return false;\n }\n\n // update params in request with keys\n request.params = Object.assign(request.params, keys.reduce((acc, key, index) => {\n let value = routeMatch[index + 1];\n if (value) {\n value = value.indexOf(',') !== -1 ? value.split(',').map(v => decodeParmeterValue(v)) : value = decodeParmeterValue(value);\n }\n acc[key] = value;\n return acc;\n }, {}));\n return true;\n }\n\n // if route is a simple path\n return route.uri === baseUriToCheck;\n}\n","/**\n * Module dependencies.\n * @private\n */\nimport {routeMatcher} from './router';\nimport Requester, {httpGetTransformer} from './requester';\n\n\nfunction errorIfNotFunction(toTest, message) {\n if(typeof toTest !== 'function') {\n throw new TypeError(message);\n }\n}\n\n/**\n * Settings object.\n * @private\n */\n\nexport default class Settings {\n\n\n /**\n * Initialize the settings.\n *\n * - setup default configuration\n *\n * @private\n */\n\n constructor() {\n // default settings\n this.settings = {\n 'http requester': new Requester(),\n 'http GET transformer': httpGetTransformer,\n // 'http POST transformer': httpPostTransformer,\n 'route matcher': routeMatcher\n };\n\n this.rules = {\n 'http requester': (requester) => {\n errorIfNotFunction(requester.fetch , 'setting http requester has no fetch function');\n },\n 'http GET transformer': (transformer) => {\n if (!transformer || (!transformer.uri && !transformer.headers && !transformer.data)) {\n throw new TypeError('setting http transformer one of functions: uri, headers, data is missing');\n }\n },\n 'route matcher': (routeMatcher) => {\n errorIfNotFunction(routeMatcher, 'setting route matcher is not a function');\n }\n };\n }\n\n\n /**\n * Assign `setting` to `val`\n *\n * @param {String} setting\n * @param {*} [val]\n * @private\n */\n\n set(name, value) {\n const checkRules = this.rules[name];\n if (checkRules) {\n checkRules(value);\n }\n this.settings[name] = value;\n }\n\n\n /**\n * Return `setting`'s value.\n *\n * @param {String} setting\n * @private\n */\n\n get(name) {\n return this.settings[name];\n }\n};\n","/**\n * Module dependencies.\n * @private\n */\n\nimport HTTP_METHODS from './methods';\nimport Settings from './settings';\nimport Router, {Route} from './router';\nimport Middleware from './middleware';\n\n\n/**\n * Application class.\n */\n\nexport default class Application {\n\n\n /**\n * Initialize the application.\n *\n * - setup default configuration\n *\n * @private\n */\n\n constructor() {\n this.routers = [];\n this.settings = new Settings();\n this.plugins = [];\n }\n\n\n /**\n * Assign `setting` to `val`, or return `setting`'s value.\n *\n * app.set('foo', 'bar');\n * app.set('foo');\n * // => \"bar\"\n *\n * @param {String} setting\n * @param {*} [val]\n * @return {app} for chaining\n * @public\n */\n\n set(...args) {\n // get behaviour\n if (args.length === 1) {\n return this.settings.get([args]);\n }\n\n // set behaviour\n this.settings.set(...args);\n\n return this;\n }\n\n\n /**\n * Listen for DOM initialization and history state changes.\n *\n * The callback function is called once the DOM has\n * the `document.readyState` equals to 'interactive'.\n *\n * app.listen(()=> {\n * console.log('App is listening requests');\n * console.log('DOM is ready!');\n * });\n *\n *\n * @param {Function} callback\n * @public\n */\n\n listen(callback) {\n const request = {method: 'GET', uri: window.location.pathname + window.location.search};\n const response = {status: 200, statusText: 'OK'};\n const currentRoutes = this._routes(request);\n\n this._callMiddlewareMethod('entered', currentRoutes, request);\n\n // manage history\n window.onpopstate = (event) => {\n if (event.state) {\n const {request, response} = event.state;\n [\n 'exited',\n 'entered',\n 'updated'\n ].forEach(middlewareMethod => this._callMiddlewareMethod(middlewareMethod, this._routes(request), request, response));\n }\n };\n\n // manage page loading/refreshing\n window.onbeforeunload = () => {\n this._callMiddlewareMethod('exited');\n };\n\n const whenPageIsInteractiveFn = () => {\n this.plugins.forEach(pluginObject => pluginObject.plugin(this));\n this._callMiddlewareMethod('updated', currentRoutes, request, response);\n if (callback) {\n callback(request, response);\n }\n };\n\n document.onreadystatechange = () => {\n // DOM ready state\n if (document.readyState === 'interactive') {\n whenPageIsInteractiveFn();\n }\n };\n\n if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {\n whenPageIsInteractiveFn();\n }\n }\n\n\n /**\n * Returns a new `Router` instance for the _uri_.\n * See the Router api docs for details.\n *\n * app.route('/');\n * // => new Router instance\n *\n * @param {String} uri\n * @return {Router} for chaining\n *\n * @public\n */\n\n route(uri) {\n const router = new Router(uri);\n this.routers.push(router);\n return router;\n }\n\n\n /**\n * Use the given middleware function or object, with optional _uri_.\n * Default _uri_ is \"/\".\n * Or use the given plugin\n *\n * // middleware function will be applied on path \"/\"\n * app.use((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\"\n * app.use(new Middleware());\n *\n * // use a plugin\n * app.use({\n * name: 'My plugin name',\n * plugin(application) {\n * // here plugin implementation\n * }\n * });\n *\n * @param {String} uri\n * @param {Middleware|Function|plugin} middleware object, middleware function, plugin\n * @return {app} for chaining\n *\n * @public\n */\n\n use(...args) {\n let {baseUri, router, middleware, plugin} = toParameters(args);\n if (plugin) {\n this.plugins.push(plugin);\n } else {\n if (router) {\n router.baseUri = baseUri;\n } else if (middleware) {\n router = new Router(baseUri);\n HTTP_METHODS.forEach((method) => {\n router[method.toLowerCase()](middleware);\n });\n } else {\n throw new TypeError('method takes at least a middleware or a router');\n }\n this.routers.push(router);\n }\n\n return this;\n }\n\n\n /**\n * Gather routes from all routers filtered by _uri_ and HTTP _method_.\n * See Router#routes() documentation for details.\n *\n * @private\n */\n\n _routes(request) {\n return this.routers.reduce((acc, router) => {\n acc.push(...router.routes(this, request));\n return acc;\n }, []);\n }\n\n\n /**\n * Call `Middleware` method or middleware function on _currentRoutes_.\n *\n * @private\n */\n\n _callMiddlewareMethod(meth, currentRoutes, request, response) {\n if (meth === 'exited') {\n // currentRoutes, request, response params not needed\n this.routers.forEach((router) => {\n router.visited().forEach((route) => {\n if (route.middleware.exited) {\n route.middleware.exited(route.visited);\n route.visited = null;\n }\n });\n });\n return;\n }\n\n currentRoutes.some((route) => {\n if (meth === 'updated') {\n route.visited = request;\n }\n\n if (route.middleware[meth]) {\n route.middleware[meth](request, response);\n if (route.middleware.next && !route.middleware.next()) {\n return true;\n }\n } else if (meth !== 'entered') {\n // calls middleware method\n let breakMiddlewareLoop = true;\n const next = () => {\n breakMiddlewareLoop = false;\n };\n route.middleware(request, response, next);\n if (breakMiddlewareLoop) {\n return true;\n }\n }\n\n return false;\n });\n }\n\n\n /**\n * Make an ajax request. Manage History#pushState if history object set.\n *\n * @private\n */\n\n _fetch(req, resolve, reject) {\n let {method, uri, headers, data, history} = req;\n\n const httpMethodTransformer = this.get(`http ${method} transformer`);\n if (httpMethodTransformer) {\n const {uri: _uriFn, headers: _headersFn, data: _dataFn } = httpMethodTransformer;\n req.uri = _uriFn ? _uriFn({uri, headers, data}) : uri;\n req.headers = _headersFn ? _headersFn({uri, headers, data}) : headers;\n req.data = _dataFn ? _dataFn({uri, headers, data}) : data;\n }\n\n // calls middleware exited method\n this._callMiddlewareMethod('exited');\n\n // gathers all routes impacted by the uri\n const currentRoutes = this._routes(req);\n\n // calls middleware entered method\n this._callMiddlewareMethod('entered', currentRoutes, req);\n\n // invokes http request\n this.settings.get('http requester').fetch(req,\n (request, response) => {\n if (history) {\n window.history.pushState({request, response}, history.title, history.uri);\n }\n this._callMiddlewareMethod('updated', currentRoutes, request, response);\n if (resolve) {\n resolve(request, response);\n }\n },\n (request, response) => {\n this._callMiddlewareMethod('failed', currentRoutes, request, response);\n if (reject) {\n reject(request, response);\n }\n });\n }\n}\n\nHTTP_METHODS.reduce((reqProto, method) => {\n\n\n /**\n * Use the given middleware function or object, with optional _uri_ on\n * HTTP methods: get, post, put, delete...\n * Default _uri_ is \"/\".\n *\n * // middleware function will be applied on path \"/\"\n * app.get((req, res, next) => {console.log('Hello')});\n *\n * // middleware object will be applied on path \"/\" and\n * app.get(new Middleware());\n *\n * // get a setting value\n * app.set('foo', 'bar');\n * app.get('foo');\n * // => \"bar\"\n *\n * @param {String} uri or setting\n * @param {Middleware|Function} middleware object or function\n * @return {app} for chaining\n * @public\n */\n\n const middlewareMethodName = method.toLowerCase();\n reqProto[middlewareMethodName] = function(...args) {\n let {baseUri, middleware, which} = toParameters(args);\n if (middlewareMethodName === 'get' && typeof which === 'string') {\n return this.settings.get(which);\n }\n if (!middleware) {\n throw new TypeError(`method takes a middleware ${middlewareMethodName === 'get' ? 'or a string' : ''}`);\n }\n const router = new Router();\n router[middlewareMethodName](baseUri, middleware);\n\n this.routers.push(router);\n\n return this;\n };\n\n /**\n * Ajax request (get, post, put, delete...).\n *\n * // HTTP GET method\n * httpGet('/route1');\n *\n * // HTTP GET method\n * httpGet({uri: '/route1', data: {'p1': 'val1'});\n * // uri invoked => /route1?p1=val1\n *\n * // HTTP GET method with browser history management\n * httpGet({uri: '/api/users', history: {state: {foo: \"bar\"}, title: 'users page', uri: '/view/users'});\n *\n * Samples above can be applied on other HTTP methods.\n *\n * @param {String|Object} uri or object containing uri, http headers, data, history\n * @param {Function} success callback\n * @param {Function} failure callback\n * @public\n */\n const httpMethodName = 'http'+method.charAt(0).toUpperCase() + method.slice(1).toLowerCase();\n reqProto[httpMethodName] = function(request, resolve, reject) {\n let {uri, headers, data, history} = request;\n if (!uri) {\n uri = request;\n }\n return this._fetch({\n uri,\n method,\n headers,\n data,\n history\n }, resolve, reject);\n };\n\n return reqProto;\n}, Application.prototype);\n\n\nexport function toParameters(args) {\n let baseUri, middleware, router, plugin, which;\n\n args.length === 1 ? [which,] = args : [baseUri, which,] = args;\n\n if (which instanceof Router) {\n router = which;\n } else if (which instanceof Middleware || typeof which === 'function') {\n middleware = which;\n } else if(which && which.plugin && typeof which.plugin === 'function') {\n plugin = which;\n }\n\n return {baseUri, middleware, router, plugin, which};\n}\n","/**\n * HTTP method list\n * @private\n */\n\n export default ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'];\n // not supported yet\n // HEAD', 'CONNECT', 'OPTIONS', 'TRACE';\n","/**\n * Middleware object.\n * @public\n */\n\nexport default class Middleware {\n\n\n /**\n * Middleware initialization\n *\n * @param {String} middleware name\n */\n\n constructor(name='') {\n this.name = name;\n }\n\n /**\n * Invoked by the app before an ajax request is sent or\n * during the DOM loading (document.readyState === 'loading').\n * See Application#_callMiddlewareEntered documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @public\n */\n\n entered(request) { }\n\n\n /**\n * Invoked by the app before a new ajax request is sent or before the DOM is unloaded.\n * See Application#_callMiddlewareExited documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @public\n */\n\n exited(request) { }\n\n\n /**\n * Invoked by the app after an ajax request has responded or on DOM ready\n * (document.readyState === 'interactive').\n * See Application#_callMiddlewareUpdated documentation for details.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @param {Object} response\n * @public\n */\n\n updated(request, response) { }\n\n\n /**\n * Invoked by the app when an ajax request has failed.\n *\n * Override this method to add your custom behaviour\n *\n * @param {Object} request\n * @param {Object} response\n * @public\n */\n failed(request, response) { }\n\n\n /**\n * Allow the hand over to the next middleware object or function.\n *\n * Override this method and return `false` to break execution of\n * middleware chain.\n *\n * @return {Boolean} `true` by default\n *\n * @public\n */\n\n next() {\n return true;\n }\n}\n","/**\n * Module dependencies.\n * @private\n */\n\nexport default class Requester {\n\n /**\n * Make an ajax request.\n *\n * @param {Object} request\n * @param {Function} success callback\n * @param {Function} failure callback\n * @private\n */\n\n fetch(request, resolve, reject) {\n const {method, uri, headers, data} = request;\n\n const success = (responseText) => {\n resolve(\n request,\n {\n status: 200,\n statusText: 'OK',\n responseText\n }\n );\n };\n\n const fail = ({status, statusText, errorThrown}) => {\n reject(\n request,\n {\n status,\n statusText,\n errorThrown,\n errors: `HTTP ${status} ${statusText?statusText:''}`\n }\n );\n };\n\n const xmlhttp = new XMLHttpRequest();\n xmlhttp.onreadystatechange = () => {\n if (xmlhttp.readyState === 4) { //XMLHttpRequest.DONE\n if (xmlhttp.status === 200) {\n success(xmlhttp.responseText);\n } else {\n fail({status: xmlhttp.status, statusText: xmlhttp.statusText});\n }\n }\n };\n try {\n xmlhttp.open(method, uri, true);\n if (headers) {\n Object.keys(headers).forEach((header) => {\n xmlhttp.setRequestHeader(header, headers[header]);\n });\n }\n if (data) {\n xmlhttp.send(data);\n } else {\n xmlhttp.send();\n }\n } catch (errorThrown) {\n fail({errorThrown});\n }\n }\n}\n\nexport const httpGetTransformer = {\n uri({uri, headers, data}) {\n if (!data) {\n return uri;\n }\n let [uriWithoutAnchor, anchor] = [uri, ''];\n const match = /^(.*)(#.*)$/.exec(uri);\n if (match) {\n [,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);\n }\n uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {\n gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;\n return gUri;\n }, uriWithoutAnchor);\n return uriWithoutAnchor + anchor;\n }\n};\n\n// export const httpPostTransformer = {\n// headers({uri, headers, data}) {\n// if (!data) {\n// return headers;\n// }\n// const updatedHeaders = headers || {};\n// if (!updatedHeaders['Content-Type']) {\n// updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';\n// }\n// return updatedHeaders;\n// }\n// };\n","/**\n * Module dependencies.\n */\n\nimport Application from './application';\nimport Router from './router';\nimport Middleware from './middleware';\n\n\n/**\n * Create a frontexpress application.\n *\n * @return {Function}\n * @api public\n */\n\nconst frontexpress = () => new Application();\n\n/**\n * Expose Router, Middleware constructors.\n */\nfrontexpress.Router = (baseUri) => new Router(baseUri);\nfrontexpress.Middleware = Middleware;\n\nexport default frontexpress;\n"],"names":["routeMatcher","request","route","method","uri","match","exec","baseUriToCheck","RegExp","indexOf","decodeParmeterValue","v","isNaN","parseFloat","isFinite","Number","isInteger","parseInt","keys","keysRE","keysMatch","push","regExpUri","replace","routeMatch","params","Object","assign","reduce","acc","key","index","value","split","map","errorIfNotFunction","toTest","message","TypeError","toParameters","args","baseUri","middleware","router","plugin","which","length","Router","Middleware","name","response","Route","uriPart","visited","this","trim","_baseUri","_routes","application","isRouteMatch","get","filter","_add","undefined","forEach","toLowerCase","babelHelpers.typeof","methodName","prototype","Requester","resolve","reject","headers","data","success","responseText","fail","status","statusText","errorThrown","xmlhttp","XMLHttpRequest","onreadystatechange","readyState","open","header","setRequestHeader","send","httpGetTransformer","uriWithoutAnchor","anchor","gUri","d","Settings","settings","rules","requester","fetch","transformer","checkRules","Application","routers","plugins","set","callback","window","location","pathname","search","currentRoutes","_callMiddlewareMethod","onpopstate","event","state","_this","middlewareMethod","onbeforeunload","whenPageIsInteractiveFn","pluginObject","document","routes","meth","exited","some","next","breakMiddlewareLoop","req","history","httpMethodTransformer","_uriFn","_headersFn","_dataFn","pushState","title","reqProto","middlewareMethodName","charAt","toUpperCase","slice","_fetch","frontexpress"],"mappings":"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"}
\ No newline at end of file
diff --git a/lib/application.js b/lib/application.js
index 42e4476..0c533fd 100755
--- a/lib/application.js
+++ b/lib/application.js
@@ -26,9 +26,8 @@ export default class Application {
constructor() {
this.routers = [];
- // this.isDOMLoaded = false;
- // this.isDOMReady = false;
this.settings = new Settings();
+ this.plugins = [];
}
@@ -52,8 +51,7 @@ export default class Application {
}
// set behaviour
- const [name, value] = args;
- this.settings.set(name, value);
+ this.settings.set(...args);
return this;
}
@@ -76,37 +74,37 @@ export default class Application {
*/
listen(callback) {
+ const request = {method: 'GET', uri: window.location.pathname + window.location.search};
+ const response = {status: 200, statusText: 'OK'};
+ const currentRoutes = this._routes(request);
+
+ this._callMiddlewareMethod('entered', currentRoutes, request);
// manage history
window.onpopstate = (event) => {
if (event.state) {
const {request, response} = event.state;
- const currentRoutes = this._routes(request.uri, request.method);
-
- this._callMiddlewareMethod('exited');
- this._callMiddlewareMethod('entered', currentRoutes, request);
- this._callMiddlewareMethod('updated', currentRoutes, request, response);
+ [
+ 'exited',
+ 'entered',
+ 'updated'
+ ].forEach(middlewareMethod => this._callMiddlewareMethod(middlewareMethod, this._routes(request), request, response));
}
};
// manage page loading/refreshing
- const request = {method: 'GET', uri: window.location.pathname + window.location.search};
- const response = {status: 200, statusText: 'OK'};
- const currentRoutes = this._routes();
+ window.onbeforeunload = () => {
+ this._callMiddlewareMethod('exited');
+ };
const whenPageIsInteractiveFn = () => {
+ this.plugins.forEach(pluginObject => pluginObject.plugin(this));
this._callMiddlewareMethod('updated', currentRoutes, request, response);
if (callback) {
callback(request, response);
}
};
- window.onbeforeunload = () => {
- this._callMiddlewareMethod('exited');
- };
-
- this._callMiddlewareMethod('entered', currentRoutes, request);
-
document.onreadystatechange = () => {
// DOM ready state
if (document.readyState === 'interactive') {
@@ -117,7 +115,6 @@ export default class Application {
if (['interactive', 'complete'].indexOf(document.readyState) !== -1) {
whenPageIsInteractiveFn();
}
-
}
@@ -144,6 +141,7 @@ export default class Application {
/**
* Use the given middleware function or object, with optional _uri_.
* Default _uri_ is "/".
+ * Or use the given plugin
*
* // middleware function will be applied on path "/"
* app.use((req, res, next) => {console.log('Hello')});
@@ -151,26 +149,38 @@ export default class Application {
* // middleware object will be applied on path "/"
* app.use(new Middleware());
*
+ * // use a plugin
+ * app.use({
+ * name: 'My plugin name',
+ * plugin(application) {
+ * // here plugin implementation
+ * }
+ * });
+ *
* @param {String} uri
- * @param {Middleware|Function} middleware object or function
+ * @param {Middleware|Function|plugin} middleware object, middleware function, plugin
* @return {app} for chaining
*
* @public
*/
use(...args) {
- let {baseUri, router, middleware} = toParameters(args);
- if (router) {
- router.baseUri = baseUri;
- } else if (middleware) {
- router = new Router(baseUri);
- HTTP_METHODS.forEach((method) => {
- router[method.toLowerCase()](middleware);
- });
+ let {baseUri, router, middleware, plugin} = toParameters(args);
+ if (plugin) {
+ this.plugins.push(plugin);
} else {
- throw new TypeError('method takes at least a middleware or a router');
+ if (router) {
+ router.baseUri = baseUri;
+ } else if (middleware) {
+ router = new Router(baseUri);
+ HTTP_METHODS.forEach((method) => {
+ router[method.toLowerCase()](middleware);
+ });
+ } else {
+ throw new TypeError('method takes at least a middleware or a router');
+ }
+ this.routers.push(router);
}
- this.routers.push(router);
return this;
}
@@ -183,13 +193,11 @@ export default class Application {
* @private
*/
- _routes(uri=window.location.pathname + window.location.search, method='GET') {
- const currentRoutes = [];
- this.routers.forEach((router) => {
- currentRoutes.push(...router.routes(uri, method));
- });
-
- return currentRoutes;
+ _routes(request) {
+ return this.routers.reduce((acc, router) => {
+ acc.push(...router.routes(this, request));
+ return acc;
+ }, []);
}
@@ -261,7 +269,7 @@ export default class Application {
this._callMiddlewareMethod('exited');
// gathers all routes impacted by the uri
- const currentRoutes = this._routes(uri, method);
+ const currentRoutes = this._routes(req);
// calls middleware entered method
this._callMiddlewareMethod('entered', currentRoutes, req);
@@ -368,19 +376,17 @@ HTTP_METHODS.reduce((reqProto, method) => {
export function toParameters(args) {
- let baseUri, middleware, router, which;
- if (args && args.length > 0) {
- if (args.length === 1) {
- [which,] = args;
- } else {
- [baseUri, which,] = args;
- }
+ let baseUri, middleware, router, plugin, which;
- if (which instanceof Router) {
- router = which;
- } else if ((which instanceof Middleware) || (typeof which === 'function')) {
- middleware = which;
- }
+ args.length === 1 ? [which,] = args : [baseUri, which,] = args;
+
+ if (which instanceof Router) {
+ router = which;
+ } else if (which instanceof Middleware || typeof which === 'function') {
+ middleware = which;
+ } else if(which && which.plugin && typeof which.plugin === 'function') {
+ plugin = which;
}
- return {baseUri, middleware, router, which};
+
+ return {baseUri, middleware, router, plugin, which};
}
diff --git a/lib/requester.js b/lib/requester.js
index 7af7e93..e1d1dd8 100755
--- a/lib/requester.js
+++ b/lib/requester.js
@@ -67,3 +67,34 @@ export default class Requester {
}
}
}
+
+export const httpGetTransformer = {
+ uri({uri, headers, data}) {
+ if (!data) {
+ return uri;
+ }
+ let [uriWithoutAnchor, anchor] = [uri, ''];
+ const match = /^(.*)(#.*)$/.exec(uri);
+ if (match) {
+ [,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
+ }
+ uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
+ gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
+ return gUri;
+ }, uriWithoutAnchor);
+ return uriWithoutAnchor + anchor;
+ }
+};
+
+// export const httpPostTransformer = {
+// headers({uri, headers, data}) {
+// if (!data) {
+// return headers;
+// }
+// const updatedHeaders = headers || {};
+// if (!updatedHeaders['Content-Type']) {
+// updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
+// }
+// return updatedHeaders;
+// }
+// };
diff --git a/lib/router.js b/lib/router.js
index 8116be5..1ff025d 100755
--- a/lib/router.js
+++ b/lib/router.js
@@ -135,26 +135,11 @@ export default class Router {
* @private
*/
- routes(uri, method) {
+ routes(application, request) {
+ request.params = request.params || {};
+ const isRouteMatch = application.get('route matcher');
return this._routes.filter((route) => {
- if (route.method && route.method !== method) {
- return false;
- }
-
- if (!route.uri || !uri) {
- return true;
- }
-
- //remove query string from uri to test
- //remove anchor from uri to test
- const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(uri);
- const baseUriToCheck = match[1] || match[2] || match[3];
-
- if (route.uri instanceof RegExp) {
- return baseUriToCheck.match(route.uri);
- }
-
- return route.uri === baseUriToCheck;
+ return isRouteMatch(request, route);
});
}
@@ -267,3 +252,71 @@ HTTP_METHODS.forEach((method) => {
return this;
};
});
+
+export function routeMatcher(request, route) {
+ // check if http method are equals
+ if (route.method && route.method !== request.method) {
+ return false;
+ }
+
+
+ // route and uri not defined always match
+ if (!route.uri || !request.uri) {
+ return true;
+ }
+
+ //remove query string and anchor from uri to test
+ const match = /^(.*)\?.*#.*|(.*)(?=\?|#)|(.*[^\?#])$/.exec(request.uri);
+ const baseUriToCheck = match[1] || match[2] || match[3];
+
+ // if route is a regexp path
+ if (route.uri instanceof RegExp) {
+ return baseUriToCheck.match(route.uri) !== null;
+ }
+
+ // if route is parameterized path
+ if (route.uri.indexOf(':') !== -1) {
+
+ const decodeParmeterValue = (v) => {
+ return !isNaN(parseFloat(v)) && isFinite(v) ? (Number.isInteger(v) ? Number.parseInt(v, 10) : Number.parseFloat(v)) : v;
+ };
+
+ // figure out key names
+ const keys = [];
+ const keysRE = /:([^\/\?]+)\??/g;
+ let keysMatch = keysRE.exec(route.uri);
+ while (keysMatch != null) {
+ keys.push(keysMatch[1]);
+ keysMatch = keysRE.exec(route.uri);
+ }
+
+ // change parameterized path to regexp
+ const regExpUri = route.uri
+ // :parameter?
+ .replace(/\/:[^\/]+\?/g, '(?:\/([^\/]+))?')
+ // :parameter
+ .replace(/:[^\/]+/g, '([^\/]+)')
+ // escape all /
+ .replace('/', '\\/');
+
+ // checks if uri match
+ const routeMatch = baseUriToCheck.match(new RegExp(`^${regExpUri}$`));
+ if (!routeMatch) {
+ return false;
+ }
+
+ // update params in request with keys
+ request.params = Object.assign(request.params, keys.reduce((acc, key, index) => {
+ let value = routeMatch[index + 1];
+ if (value) {
+ value = value.indexOf(',') !== -1 ? value.split(',').map(v => decodeParmeterValue(v)) : value = decodeParmeterValue(value);
+ }
+ acc[key] = value;
+ return acc;
+ }, {}));
+ return true;
+ }
+
+ // if route is a simple path
+ return route.uri === baseUriToCheck;
+}
diff --git a/lib/settings.js b/lib/settings.js
index 5e1f9d7..537457c 100644
--- a/lib/settings.js
+++ b/lib/settings.js
@@ -2,9 +2,15 @@
* Module dependencies.
* @private
*/
+import {routeMatcher} from './router';
+import Requester, {httpGetTransformer} from './requester';
-import Requester from './requester';
+function errorIfNotFunction(toTest, message) {
+ if(typeof toTest !== 'function') {
+ throw new TypeError(message);
+ }
+}
/**
* Settings object.
@@ -26,43 +32,22 @@ export default class Settings {
// default settings
this.settings = {
'http requester': new Requester(),
-
- 'http GET transformer': {
- uri({uri, headers, data}) {
- if (!data) {
- return uri;
- }
- let [uriWithoutAnchor, anchor] = [uri, ''];
- const match = /^(.*)(#.*)$/.exec(uri);
- if (match) {
- [,uriWithoutAnchor, anchor] = /^(.*)(#.*)$/.exec(uri);
- }
- uriWithoutAnchor = Object.keys(data).reduce((gUri, d, index) => {
- gUri += `${(index === 0 && gUri.indexOf('?') === -1)?'?':'&'}${d}=${data[d]}`;
- return gUri;
- }, uriWithoutAnchor);
- return uriWithoutAnchor + anchor;
- }
- }
- // 'http POST transformer': {
- // headers({uri, headers, data}) {
- // if (!data) {
- // return headers;
- // }
- // const updatedHeaders = headers || {};
- // if (!updatedHeaders['Content-Type']) {
- // updatedHeaders['Content-Type'] = 'application/x-www-form-urlencoded';
- // }
- // return updatedHeaders;
- // }
- // }
+ 'http GET transformer': httpGetTransformer,
+ // 'http POST transformer': httpPostTransformer,
+ 'route matcher': routeMatcher
};
this.rules = {
'http requester': (requester) => {
- if(typeof requester.fetch !== 'function') {
- throw new TypeError('setting http requester has no fetch method');
+ errorIfNotFunction(requester.fetch , 'setting http requester has no fetch function');
+ },
+ 'http GET transformer': (transformer) => {
+ if (!transformer || (!transformer.uri && !transformer.headers && !transformer.data)) {
+ throw new TypeError('setting http transformer one of functions: uri, headers, data is missing');
}
+ },
+ 'route matcher': (routeMatcher) => {
+ errorIfNotFunction(routeMatcher, 'setting route matcher is not a function');
}
};
}
diff --git a/package.json b/package.json
index e726af3..cb61c8b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "frontexpress",
- "version": "1.1.0",
+ "version": "1.2.0",
"description": "Frontexpress manages routes in browser like ExpressJS on Node",
"main": "dist/frontexpress.js",
"jsnext:main": "lib/frontexpress.js",
diff --git a/test/application-test.js b/test/application-test.js
index 70f8e5d..b043014 100755
--- a/test/application-test.js
+++ b/test/application-test.js
@@ -989,4 +989,22 @@ describe('Application', () => {
});
});
});
+
+ describe('plugin management', () => {
+ it('setup a plugin', (done) => {
+ const app = frontexpress();
+ app.use({
+ name: 'my plugin',
+ plugin(application) {
+ done();
+ }
+ });
+
+ app.listen();
+
+ //simulate readystatechange
+ document.readyState = 'interactive';
+ document.onreadystatechange();
+ });
+ });
});
diff --git a/test/middleware-test.js b/test/middleware-test.js
new file mode 100644
index 0000000..92b738f
--- /dev/null
+++ b/test/middleware-test.js
@@ -0,0 +1,20 @@
+/*eslint-env mocha*/
+import {assert} from 'chai';
+import Middleware from '../lib/middleware';
+
+describe('Middleware', () => {
+ it('check exposed methods', () => {
+ const middleware = new Middleware();
+ assert(middleware.entered);
+ assert(middleware.exited);
+ assert(middleware.updated);
+ assert(middleware.failed);
+ assert(middleware.next);
+
+ middleware.entered();
+ middleware.exited();
+ middleware.updated();
+ middleware.failed();
+ assert(middleware.next());
+ });
+});
\ No newline at end of file
diff --git a/test/router-test.js b/test/router-test.js
index 30346c1..2d53963 100755
--- a/test/router-test.js
+++ b/test/router-test.js
@@ -4,6 +4,9 @@ import sinon from 'sinon';
import frontexpress from '../lib/frontexpress';
import HTTP_METHODS from '../lib/methods';
+const application = frontexpress();
+const routeMatcher = application.get('route matcher');
+
describe('Router', () => {
describe('generated methods', () => {
@@ -12,6 +15,7 @@ describe('Router', () => {
assert(typeof router.all === 'function');
assert(typeof router.get === 'function');
assert(typeof router.put === 'function');
+ assert(typeof router.patch === 'function');
assert(typeof router.post === 'function');
assert(typeof router.delete === 'function');
});
@@ -46,7 +50,7 @@ describe('Router', () => {
router.get(middleware);
- const r1 = router.routes('/', 'GET');
+ const r1 = router.routes(application, {uri: '/', method: 'GET'});
assert(r1.length === 1);
assert(r1[0].uri === undefined);
assert(r1[0].method === 'GET');
@@ -64,37 +68,37 @@ describe('Router', () => {
.post('/route2', middleware2)
.all('/route3', middleware3);
- const r1 = router.routes('/route1', 'GET');
+ const r1 = router.routes(application, {uri: '/route1', method: 'GET'});
assert(r1.length === 1);
assert(r1[0].uri === '/route1');
assert(r1[0].method === 'GET');
assert(r1[0].middleware === middleware1);
- const r2 = router.routes('/route2', 'POST');
+ const r2 = router.routes(application, {uri: '/route2', method: 'POST'});
assert(r2.length === 1);
assert(r2[0].uri === '/route2');
assert(r2[0].method === 'POST');
assert(r2[0].middleware === middleware2);
- let r3 = router.routes('/route3', 'GET');
+ let r3 = router.routes(application, {uri: '/route3', method: 'GET'});
assert(r3.length === 1);
assert(r3[0].uri === '/route3');
assert(r3[0].method === 'GET');
assert(r3[0].middleware === middleware3);
- r3 = router.routes('/route3', 'POST');
+ r3 = router.routes(application, {uri: '/route3', method: 'POST'});
assert(r3.length === 1);
assert(r3[0].uri === '/route3');
assert(r3[0].method === 'POST');
assert(r3[0].middleware === middleware3);
- r3 = router.routes('/route3', 'PUT');
+ r3 = router.routes(application, {uri: '/route3', method: 'PUT'});
assert(r3.length === 1);
assert(r3[0].uri === '/route3');
assert(r3[0].method === 'PUT');
assert(r3[0].middleware === middleware3);
- r3 = router.routes('/route3', 'DELETE');
+ r3 = router.routes(application, {uri: '/route3', method: 'DELETE'});
assert(r3.length === 1);
assert(r3[0].uri === '/route3');
assert(r3[0].method === 'DELETE');
@@ -107,7 +111,7 @@ describe('Router', () => {
router.get(/^\/route1/, middleware);
- const r = router.routes('/route1', 'GET');
+ const r = router.routes(application, {uri: '/route1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri instanceof RegExp);
assert(r[0].uri.toString() === new RegExp('^\/route1').toString());
@@ -120,7 +124,7 @@ describe('Router', () => {
router.get('/subroute', new frontexpress.Middleware());
- const r = router.routes('/route1/subroute', 'GET');
+ const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
});
@@ -130,7 +134,7 @@ describe('Router', () => {
router.get(new frontexpress.Middleware());
- const r = router.routes('/route1', 'GET');
+ const r = router.routes(application, {uri: '/route1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1');
});
@@ -140,7 +144,7 @@ describe('Router', () => {
router.get('/subroute', new frontexpress.Middleware());
- const r = router.routes('/route1/subroute', 'GET');
+ const r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
});
@@ -150,7 +154,7 @@ describe('Router', () => {
router.get('/subroute ', new frontexpress.Middleware());
- let r = router.routes('/route1/subroute', 'GET');
+ let r = router.routes(application, {uri: '/route1/subroute', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
@@ -160,7 +164,7 @@ describe('Router', () => {
router.get(new frontexpress.Middleware());
- r = router.routes('/route1', 'GET');
+ r = router.routes(application, {uri: '/route1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1');
});
@@ -170,7 +174,7 @@ describe('Router', () => {
router.get('/subroute', new frontexpress.Middleware());
- let r = router.routes('/route1/subroute?a=b&c=d', 'GET');
+ let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
assert(r[0].data === undefined);
@@ -181,7 +185,7 @@ describe('Router', () => {
router.get('/subroute', new frontexpress.Middleware());
- let r = router.routes('/route1/subroute#a=b&c=d', 'GET');
+ let r = router.routes(application, {uri: '/route1/subroute#a=b&c=d', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
assert(r[0].data === undefined);
@@ -192,7 +196,7 @@ describe('Router', () => {
router.get('/subroute', new frontexpress.Middleware());
- let r = router.routes('/route1/subroute?a=b&c=d#anchor1', 'GET');
+ let r = router.routes(application, {uri: '/route1/subroute?a=b&c=d#anchor1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1/subroute');
assert(r[0].data === undefined);
@@ -264,7 +268,7 @@ describe('Router', () => {
router.get(middleware);
- const r = router.routes('/', 'GET');
+ const r = router.routes(application, {uri: '/', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/');
assert(r[0].method === 'GET');
@@ -277,7 +281,7 @@ describe('Router', () => {
router.get('/route1', middleware);
- const r = router.routes('/route1', 'GET');
+ const r = router.routes(application, {uri: '/route1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri === '/route1');
assert(r[0].method === 'GET');
@@ -295,7 +299,7 @@ describe('Router', () => {
const middleware = new frontexpress.Middleware();
router.get(middleware);
- const r = router.routes('/part1', 'GET');
+ const r = router.routes(application, {uri: '/part1', method: 'GET'});
assert(r.length === 1);
assert(r[0].uri instanceof RegExp);
assert(r[0].uri.toString() === new RegExp('^\/part').toString());
@@ -303,4 +307,113 @@ describe('Router', () => {
assert(r[0].middleware === middleware);
});
});
+
+ describe('check route matcher', () => {
+ it('/', () => {
+ const route = {uri: '/', method: 'GET'};
+
+ const request = {uri: '/', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {});
+ });
+
+ it('/a/b/c', () => {
+ const route = {uri: '/a/b/c', method: 'GET'};
+
+ let request = {uri: '/a/b/c', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {});
+
+ request = {uri: '/a/b/c/', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), false);
+ });
+
+ it('/^\//', () => {
+ const route = {uri: /^\//, method: 'GET'};
+
+ const request = {uri: '/a/b/c', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {});
+ });
+
+ it('/:id', () => {
+ const route = {uri: '/:id', method: 'GET'};
+
+ const request = {uri: '/1000', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.strictEqual(request.params.id, 1000);
+ });
+
+ it('/user/:id', () => {
+ const route = {uri: '/user/:id', method: 'GET'};
+
+ let request = {uri: '/user/1000', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.strictEqual(request.params.id, 1000);
+
+ request = {uri: '/user/100.2122', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.strictEqual(request.params.id, 100.2122);
+
+ request = {uri: '/user', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), false);
+
+ request = {uri: '/user/', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), false);
+ });
+
+ it('/user/:id with id as coma separated values', () => {
+ const route = {uri: '/user/:id', method: 'GET'};
+
+ let request = {uri: '/user/1,2,3', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {id: [1,2,3]});
+
+ request = {uri: '/user/1.5,2.55,4.25', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {id: [1.5,2.55,4.25]});
+
+ request = {uri: '/user/a,b,c', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {id: ['a','b','c']});
+ });
+
+ it('/user/:id?', () => {
+ const route = {uri: '/user/:id?', method: 'GET'};
+
+ let request = {uri: '/user/1000', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.strictEqual(request.params.id, 1000);
+
+ request = {uri: '/user', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), true);
+ assert.deepEqual(request.params, {id: undefined});
+
+ request = {uri: '/user/', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), false);
+ });
+
+ it('/user/:firstname/:lastname', () => {
+ const route = {uri: '/user/:firstname/:lastname', method: 'GET'};
+
+ let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
+
+ request = {uri: '/user/camel', method: 'GET', params: {}};
+ assert.strictEqual(routeMatcher(request, route), false);
+ });
+
+ it('/user/:firstname?/:lastname', () => {
+ const route = {uri: '/user/:firstname?/:lastname', method: 'GET'};
+
+ let request = {uri: '/user/camel/aissani', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {firstname: 'camel', lastname:'aissani'} );
+
+ request = {uri: '/user/aissani', method: 'GET', params: {}};
+ assert(routeMatcher(request, route));
+ assert.deepEqual(request.params, {firstname: undefined, lastname:'aissani'} );
+ });
+ });
});
diff --git a/test/settings-test.js b/test/settings-test.js
index 27eeacf..cee3644 100644
--- a/test/settings-test.js
+++ b/test/settings-test.js
@@ -1,11 +1,43 @@
/*eslint-env mocha*/
-import {assert} from 'chai';
+import chai, {assert} from 'chai';
import Settings from '../lib/settings';
describe('Settings', () => {
const settings = new Settings();
describe('http GET method transformer', () => {
+ it('check setting rule', () => {
+ const defaultHttpGetTransformer = settings.get('http GET transformer');
+
+ chai.expect(() => settings.set('http GET transformer', null)).to.throw(TypeError);
+ chai.expect(() => settings.set('http GET transformer', {})).to.throw(TypeError);
+ chai.expect(() => settings.set('http GET transformer', {foo:()=>{}})).to.throw(TypeError);
+
+ const uri = () => {};
+ settings.set('http GET transformer', {uri});
+ assert.deepEqual(settings.get('http GET transformer'), {uri});
+
+ const headers = () => {};
+ settings.set('http GET transformer', {headers});
+ assert.deepEqual(settings.get('http GET transformer'), {headers});
+
+ const data = () => {};
+ settings.set('http GET transformer', {data});
+ assert.deepEqual(settings.get('http GET transformer'), {data});
+
+ settings.set('http GET transformer', defaultHttpGetTransformer);
+
+
+ const defaultRouteMatcher = settings.get('route matcher');
+ chai.expect(() => settings.set('route matcher', null)).to.throw(TypeError);
+ chai.expect(() => settings.set('route matcher', {})).to.throw(TypeError);
+ chai.expect(() => settings.set('route matcher', 1)).to.throw(TypeError);
+
+ const routeMatcher = () => {};
+ settings.set('route matcher', routeMatcher);
+ assert.strictEqual(settings.get('route matcher'), routeMatcher);
+ });
+
it('simple uri', () => {
const uriFn = settings.get('http GET transformer').uri;
const dataFn = settings.get('http GET transformer').data;