Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bugfix/fix base #10

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 9 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ import Router from 'es-router';
const router = new Router({
useHash: false,
home: 'home',
notStrictRouting: false,
strictRouting: true,
base: '/testurl/',
routeOnLoad: true,
routes: [
{
name: 'home',
Expand Down Expand Up @@ -41,17 +42,20 @@ let's go through each option.

useHash defines whether or not you'd like to use pushState, or the equivalent to Angular's `html5Mode`. This is a great option if your base is not consistent or you're on IE9

### home (required unless notStrictRouting is false)
### home (required if strictRouting is true)

this is a default path that you'd like to go to if the url doesn't match to any of the paths you've defined

### notStrictRouting (optional)
### strictRouting (optional)

this gives you the ability to go to any path you'd like, regardless of whether you've declared it in this config or not
this enforces strict routing, meaning attempts to go to a path other than the ones defined in `routes` will be redirected to `home`. this can result in the browser back button getting stuck in an infinite redirect loop, so it is false by default

### base (required unless useHash is false)
### base (required if useHash is true)
this is the base url for your application if you aren't using the hash. if this isn't declared and you are using `useHash`, it will try and retrieve the base from the `base` tag in the html5Mode

### routeOnLoad (optional, defaults to true)
this makes a route change event get fired upon loading the page. if this is set to false, no initial route change will be fired. setting this to false may work better for some app configurations (e.g. server side rendering)

### routes (required)

routes are the locations that you've defined to use in your application. the `:` means that it is a variable route, and can be changed
Expand Down
141 changes: 112 additions & 29 deletions es-router.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
'use strict';

var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol ? "symbol" : typeof obj; };
var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };

var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();

Expand All @@ -20,11 +20,15 @@ var EsRouter = function () {
function EsRouter(_ref) {
var _this = this;

var useHash = _ref.useHash;
var routes = _ref.routes;
var notStrictRouting = _ref.notStrictRouting;
var home = _ref.home;
var base = _ref.base;
var _ref$useHash = _ref.useHash,
useHash = _ref$useHash === undefined ? false : _ref$useHash,
routes = _ref.routes,
_ref$strictRouting = _ref.strictRouting,
strictRouting = _ref$strictRouting === undefined ? false : _ref$strictRouting,
home = _ref.home,
base = _ref.base,
_ref$routeOnLoad = _ref.routeOnLoad,
routeOnLoad = _ref$routeOnLoad === undefined ? true : _ref$routeOnLoad;

_classCallCheck(this, EsRouter);

Expand All @@ -36,11 +40,13 @@ var EsRouter = function () {
this.useHash = useHash;
this.routes = routes;
this.base = base;
this.notStrictRouting = notStrictRouting;
this.strictRouting = strictRouting;
this.queryParams = this.getParamsFromUrl();

if (base && base[base.length - 1] === '/') {
if (base && base[base.length - 1] === '/' && base !== '/') {
this.base = this.base.substring(0, this.base.length - 1);
} else {
this.base = base;
}

//get base if needed
Expand Down Expand Up @@ -95,17 +101,30 @@ var EsRouter = function () {
}

//do an initial routing
this.path(this.getPathFromUrl());
//this shouldn't actually push a history state, it should just fire a routing event
//pushing a new history state adds an extra, unnecessray back button click
if (routeOnLoad) {
this.path(this.getPathFromUrl(), false, true);
}
}

//get path we're currently on
/**
* get path we're currently on
* @return {object} - path object with name and route props
*/


_createClass(EsRouter, [{
key: 'getState',
value: function getState() {
return this.currentPathObject;
}

/**
* Parses the current query string params from the url
* @return {object}
*/

}, {
key: 'getParamsFromUrl',
value: function getParamsFromUrl() {
Expand All @@ -116,19 +135,36 @@ var EsRouter = function () {
return prev;
}, {}) || {};
}

/**
* Parses the current routing path from the url, excluding the base
* This will always start with a slash
*
* @return {String}
*/

}, {
key: 'getPathFromUrl',
value: function getPathFromUrl() {
return !this.useHash ? window.location.pathname.split(this.base)[1] || '/' : window.location.hash.split('?')[0].substring(1);
var pathname = window.location.pathname;
var pos = Math.max(0, pathname.indexOf(this.base) + this.base.length - 1);
var path = pathname.slice(pos) || '/';
return this.useHash ? window.location.hash.split('?')[0].substring(1) : path;
}

/**
* Listens for hashchange or popstate events from the window
* And fires off route change events to subscribers
* This can happen when calling path() or when the browser navigates natively
*/

}, {
key: 'eventChangeListener',
value: function eventChangeListener() {
var _this2 = this;

var currentQueryParam = this.getParamsFromUrl();
var currentPath = this.getPathFromUrl();

var allNewParams = this.createParamString(currentQueryParam).join('');
var oldParams = this.createParamString(this.queryParams).join('');

Expand All @@ -151,7 +187,13 @@ var EsRouter = function () {
}
}

//allow items to subscribe to pre and post route changes
/**
* allow items to subscribe to pre and post route changes
* @param {String} topic - event name, e.g. startRouteChange, finishRouteChange
* @param {function} listener - event listener callback
* @return {object} - object containing a remove property to unsubscribe.
* maybe consider changing this to an index, a la setInterval
*/

}, {
key: 'subscribe',
Expand Down Expand Up @@ -283,39 +325,70 @@ var EsRouter = function () {
}, []);
}

//actual routing function
/**
* Build a new url for path changes
* We assume the route has already been verified as a predefined route here (or not)
*
* @param {string} newPath - input path, e.g. /account/login
* @return {string} - output ready for history push, e.g. /base/account/login?foo=bar
*/

}, {
key: 'buildNewUrl',
value: function buildNewUrl(newPath) {
var paramArray = this.createParamString(this.queryParams);
var paramArrayString = paramArray.length ? '?' + paramArray.join('&') : '';
// make sure the new url starts with a slash
var newUrlBase = this.base.match(/^\//) ? this.base : '/' + this.base;
var newUrl = ('' + newUrlBase + newPath + paramArrayString).replace(/\/{2,}/g, '/'); // dedup consecutive slashes
return newUrl;
}

/**
* Actual routing function
*
* @param {string} route - New path to naviate to. Absolute path, not including base
* @param {Boolean} isQueryParam - ???
* @param {Boolean} initialLoad - if true, skips push state/hash change and just fires event
*/

}, {
key: 'path',
value: function path(route, isQueryParam) {
var initialLoad = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;

if (!route) {
return;
}
var newPath = route;

var newPathObject = this.getPreDefinedRoute(newPath);

//if path didn't match and is in strict mode, go home
if (!newPathObject && !this.notStrictRouting) {
var redirect = false;
//if path didn't match and is in strict mode, go home (redirect)
if (!newPathObject && this.strictRouting) {
newPath = this.home.route;
newPathObject = this.home;
redirect = true;
}
//run all pre-move functions
if (!isQueryParam) {
this.startRouteChange(this.currentPathObject, newPathObject);
}

//build new url
var paramArray = this.createParamString(this.queryParams);
var paramArrayString = paramArray.length ? '?' + paramArray.join('&') : '';
var newUrl = '' + (this.base || '') + newPath + paramArrayString;

//set new url
if (this.useHash) {
this.wasChangedByUser = true;
window.location.hash = newUrl;
} else {
window.history.pushState(null, null, newUrl);
var newUrl = this.buildNewUrl(newPath);

//push new state to the window, but only if this is not the initial load
//otherwise we end up with two copies of the initial state in browser history
//
//ignoring this rule for strict routing seems to be the only option right now
//Need to come up with a working solution for "redirects"
//Using hash or pushState for redirects breaks the browser's back button
if (initialLoad === false || redirect === true) {
if (this.useHash) {
this.wasChangedByUser = true;
window.location.hash = newUrl;
} else {
window.history.pushState(null, null, newUrl);
}
}

//finally, set current path state
Expand All @@ -327,13 +400,23 @@ var EsRouter = function () {
this.finishRouteChange(oldPath, this.currentPathObject);
}
}

/**
* Publish 'startRouteChange' event to all subscribers
*/

}, {
key: 'startRouteChange',
value: function startRouteChange(oldPath, newPath) {
this.events.startRouteChange.forEach(function (item) {
item(oldPath, newPath);
});
}

/**
* Publish 'finishRouteChange' event to all subscribers
*/

}, {
key: 'finishRouteChange',
value: function finishRouteChange(oldPath, newPath) {
Expand Down
Loading