baobab-router
Advanced tools
Comparing version 1.0.0 to 2.0.0
@@ -1,12 +0,164 @@ | ||
'use strict'; | ||
/** | ||
* *********************** | ||
* PRIVATE STATIC METHODS: | ||
* *********************** | ||
*/ | ||
const __defaultSolver = /:([^\/:]*)/g; | ||
/** | ||
* This function compares two arrays (usually two paths for Baobab), and returns | ||
* true if they have the same length and equals elements in the same order. | ||
* | ||
* @param {array} a1 The first array. | ||
* @param {array} a2 The second array. | ||
* @return {boolean} True if the values are equals, false else. | ||
*/ | ||
function __compareArrays(a1, a2) { | ||
const l = a1.length; | ||
if ( | ||
!Array.isArray(a1) || | ||
!Array.isArray(a2) || | ||
l !== a2.length | ||
) { | ||
return false; | ||
} | ||
/************************* | ||
* PRIVATE STATIC METHODS: | ||
* *********************** | ||
for (let i = 0; i < l; i++) { | ||
if (a1[i] !== a2[i]) { | ||
return false; | ||
} | ||
} | ||
return true; | ||
} | ||
/** | ||
* This function takes a well formed URL from any BaobabRouter instance's route, | ||
* with potentially dynamic attributes to resolve, and an object with the | ||
* related values, and returns the URL with the values inserted instead of the | ||
* dynamics. | ||
* | ||
* Examples: | ||
* ********* | ||
* > __resolveURL('a/b/c'); // same as __resolveURL('a/b/c', {}); | ||
* > // 'a/b/c' (nothing to solve) | ||
* | ||
* > __resolveURL('a/:b/c/:d', { ':b': 'B', ':d': 'D' }); | ||
* > // 'a/B/c/D' | ||
* | ||
* > __resolveURL('a/:b/:b', { ':b': 'B' }); | ||
* > // 'a/B/B' | ||
* | ||
* > __resolveURL('a/:b/:c', { ':c': 'C', ':d': 'D' }); | ||
* > // 'a/:b/C' | ||
* | ||
* @param {string} url The URL to resolve. | ||
* @param {?object} obj An optional object with the dynamic values to insert. | ||
* @return {string} The resolved URL. | ||
*/ | ||
var __defaultSolver = /:([^\/:]*)/g; | ||
function __resolveURL(url, obj = {}) { | ||
return ( | ||
url | ||
.split('/') | ||
.map(s => (s in obj) ? obj[s] : s) | ||
.join('/') | ||
); | ||
} | ||
/** | ||
* This function builds a proper path from a route's path and its parents paths. | ||
* | ||
* Examples: | ||
* ********* | ||
* > __concatenatePaths('a'); // '/a' | ||
* > __concatenatePaths('/a'); // '/a' | ||
* > __concatenatePaths('a', ''); // '/a' | ||
* > __concatenatePaths('', 'b'); // '/b' | ||
* | ||
* > __concatenatePaths('a', 'b'); // '/a/b' | ||
* > __concatenatePaths('a', '/b'); // '/a/b' | ||
* > __concatenatePaths('a/', '/b'); // '/a/b' | ||
* > __concatenatePaths('a/', 'b'); // '/a/b' | ||
* > __concatenatePaths('/a', '/b'); // '/a/b' | ||
* | ||
* @param {string+} paths the different paths to concatenate. | ||
* @return {string} The cleaned path. | ||
*/ | ||
function __concatenatePaths(...args) { | ||
return ( | ||
('/' + args.map(str => str || '').join('/')) | ||
.replace(/\/+/g, '/') | ||
.replace(/\/+$/g, '') | ||
); | ||
} | ||
/** | ||
* This function will merge multiple objects into one. Each object will | ||
* overrides the previous ones. Also, this function will return an object with | ||
* two keys: "value" contains the new object, and "conflicts" is a flag | ||
* specifying wether some paths contain different values in different objects. | ||
* | ||
* Examples: | ||
* ********* | ||
* > var a = { key: 'value' }, | ||
* > b = __deepMerge(a); | ||
* > a === b; // false | ||
* > b.key; // 'value' | ||
* | ||
* > __deepMerge( | ||
* > { a: 1 }, | ||
* > { b: 1 }, | ||
* > { c: 1 } | ||
* > ); | ||
* > // { a: 1, b: 1, c: 1 } | ||
* | ||
* > __deepMerge( | ||
* > { a: 1 }, | ||
* > { a: 2 }, | ||
* > { a: 3 } | ||
* > ); | ||
* > // { a: 3 } | ||
* | ||
* @param {object*} objects The objects to merge. | ||
* @return {object} An object containing the merged object under the | ||
* key "value", and a flag specifying if some keys | ||
* where having different values in the different | ||
* arguments, under the key "conflicts". | ||
*/ | ||
function __deepMerge() { | ||
let res; | ||
let merged; | ||
let conflicts = false; | ||
for (let i = 0, l = arguments.length; i < l; i++) { | ||
const obj = arguments[i]; | ||
if (obj && typeof obj === 'object') { | ||
if (!res) { | ||
res = Array.isArray(obj) ? [] : {}; | ||
} | ||
for (const k in obj) { | ||
if (obj.hasOwnProperty(k)) { | ||
merged = __deepMerge(res[k], obj[k]); | ||
conflicts = conflicts || merged.conflicts; | ||
res[k] = merged.value; | ||
} | ||
} | ||
} else { | ||
if (res !== undefined && res !== obj) { | ||
conflicts = true; | ||
} | ||
res = obj; | ||
} | ||
} | ||
return { | ||
conflicts, | ||
value: res, | ||
}; | ||
} | ||
/** | ||
* This function takes a route's hash (that might have some expression to solve, | ||
@@ -37,14 +189,12 @@ * such as /toto/:tutu/tata or so), and an actual hash. It will then compare | ||
function __doesHashMatch(routeHash, hash, solver) { | ||
var i, | ||
l, | ||
match, | ||
routeArray = routeHash.split('/'), | ||
hashArray = hash.split('/'); | ||
const routeArray = routeHash.split('/'); | ||
const hashArray = hash.split('/'); | ||
// Check lengths: | ||
if (routeArray.length > hashArray.length) | ||
if (routeArray.length > hashArray.length) { | ||
return false; | ||
} | ||
for (i = 0, l = routeArray.length; i < l; i++) { | ||
match = routeArray[i].match(solver || __defaultSolver); | ||
for (let i = 0, l = routeArray.length; i < l; i++) { | ||
const match = routeArray[i].match(solver || __defaultSolver); | ||
@@ -54,4 +204,5 @@ if ( | ||
(match && !hashArray[i]) | ||
) | ||
) { | ||
return false; | ||
} | ||
} | ||
@@ -78,36 +229,39 @@ | ||
*/ | ||
function __doesStateMatch(routeState, state, dynamicValues) { | ||
var k, | ||
localResults, | ||
results = {}; | ||
function __doesStateMatch(routeState, state, dynamicValues = []) { | ||
let results = {}; | ||
dynamicValues = dynamicValues || []; | ||
function searchRoutes(val, i) { | ||
const localResults = __doesStateMatch(val, state[i], dynamicValues); | ||
if (localResults) { | ||
results = __deepMerge(results, localResults); | ||
return true; | ||
} | ||
} | ||
// Arrays: | ||
if (Array.isArray(routeState)) { | ||
if (!Array.isArray(state) || routeState.length !== state.length) | ||
if (!Array.isArray(state) || routeState.length !== state.length) { | ||
return null; | ||
else { | ||
if (routeState.every(function(val, i) { | ||
if ((localResults = __doesStateMatch(val, state[i], dynamicValues))) { | ||
results = __deepMerge(results, localResults); | ||
return true; | ||
} | ||
})) | ||
return results; | ||
else | ||
return null; | ||
} | ||
if (routeState.every(searchRoutes)) { | ||
return results; | ||
} | ||
return null; | ||
// Objects: | ||
} else if (routeState && typeof routeState === 'object') { | ||
if (!state || typeof state !== 'object') | ||
if (!state || typeof state !== 'object') { | ||
return null; | ||
else { | ||
for (k in routeState) | ||
if ((localResults = __doesStateMatch( | ||
} | ||
for (const k in routeState) { | ||
if (routeState.hasOwnProperty(k)) { | ||
const localResults = __doesStateMatch( | ||
routeState[k], | ||
state[k], | ||
dynamicValues | ||
))) { | ||
); | ||
if (localResults) { | ||
results = __deepMerge(results, localResults).value; | ||
@@ -117,8 +271,9 @@ } else { | ||
} | ||
return results; | ||
} | ||
} | ||
return results; | ||
// Dynamics: | ||
} else if (~dynamicValues.indexOf(routeState)) { | ||
} else if (~dynamicValues.indexOf(routeState) && state) { | ||
results[routeState] = state; | ||
@@ -135,4 +290,5 @@ return results; | ||
// Other scalars: | ||
} else if (routeState === state) | ||
} else if (routeState === state) { | ||
return results; | ||
} | ||
@@ -149,3 +305,3 @@ return null; | ||
* @param {route} state The state constraints to extract the paths from. | ||
* @param {array} dynamics The array of the dynamic values names (like ":toto" | ||
* @param {?array} dynamics The array of the dynamic values names (like ":toto" | ||
* for instance). | ||
@@ -160,16 +316,10 @@ * @param {?array} results The results array to push the newly found paths | ||
*/ | ||
function __extractPaths(state, dynamics, results, path) { | ||
results = results || []; | ||
dynamics = dynamics || []; | ||
var i, | ||
l, | ||
result; | ||
for (i in state) { | ||
function __extractPaths(state, dynamics = [], results = [], path = []) { | ||
for (const i in state) { | ||
if ( | ||
state.hasOwnProperty(i) && | ||
state[i] && | ||
(typeof state[i] === 'object') && | ||
Object.keys(state[i]).length | ||
) | ||
) { | ||
__extractPaths( | ||
@@ -179,10 +329,11 @@ state[i], | ||
results, | ||
(path || []).concat(i) | ||
path.concat(i) | ||
); | ||
else | ||
} else { | ||
results.push({ | ||
path: (path || []).concat(i), | ||
path: path.concat(i), | ||
value: state[i], | ||
dynamic: !!~dynamics.indexOf(state[i]) | ||
dynamic: !!~dynamics.indexOf(state[i]), | ||
}); | ||
} | ||
} | ||
@@ -213,34 +364,40 @@ | ||
*/ | ||
function __makeRoutes(route, solver, baseState, basePath) { | ||
var mergedState = __deepMerge(baseState || {}, route.state || {}); | ||
function __makeRoutes(route, solver, baseTree, basePath = '') { | ||
const { value, conflicts } = __deepMerge( | ||
baseTree || {}, | ||
route.state ? { state: route.state } : {}); | ||
basePath = basePath || ''; | ||
route.fullPath = __concatenatePaths(basePath, route.path); | ||
route.fullState = mergedState.value; | ||
route.overrides = mergedState.conflicts; | ||
route.fullTree = value; | ||
route.overrides = conflicts; | ||
route.dynamics = route.fullPath.match(solver) || []; | ||
route.updates = __extractPaths(route.fullState, route.dynamics); | ||
route.updates = __extractPaths(route.fullTree, route.dynamics); | ||
if (route.defaultRoute) | ||
if (route.defaultRoute) { | ||
route.fullDefaultPath = | ||
__concatenatePaths(route.fullPath, route.defaultRoute); | ||
} | ||
if (route.routes) | ||
route.routes = route.routes.map(function(child) { | ||
return __makeRoutes(child, solver, route.fullState, route.fullPath); | ||
}); | ||
if (route.routes) { | ||
route.routes = route.routes.map( | ||
child => __makeRoutes( | ||
child, | ||
solver, | ||
route.fullTree, | ||
route.fullPath | ||
) | ||
); | ||
} | ||
route.overrides = | ||
route.overrides || | ||
(route.routes || []).some(function(child) { | ||
return child.overrides; | ||
}); | ||
(route.routes || []).some(child => child.overrides); | ||
// Some root-specific verifications: | ||
if (arguments.length <= 2) { | ||
route.readOnly = route.readOnly || []; | ||
// Check read-only paths: | ||
route.readOnly = (route.readOnly || []).map(path => ['state'].concat(path)); | ||
// The root must have a default route: | ||
if (!route.defaultRoute) | ||
if (!route.defaultRoute) { | ||
throw (new Error( | ||
@@ -250,2 +407,3 @@ 'BaobabRouter.__makeRoutes: ' + | ||
)); | ||
} | ||
} | ||
@@ -256,6 +414,6 @@ | ||
route.defaultRoute && | ||
!(route.routes || []).some(function(child) { | ||
return __doesHashMatch(child.path, route.defaultRoute); | ||
}) | ||
) | ||
!(route.routes || []).some( | ||
child => __doesHashMatch(child.path, route.defaultRoute) | ||
) | ||
) { | ||
throw (new Error( | ||
@@ -266,4 +424,5 @@ 'BaobabRouter.__makeRoutes: ' + | ||
)); | ||
} | ||
if (!('path' in route) && !route.defaultRoute) | ||
if (!('path' in route) && !route.defaultRoute) { | ||
throw (new Error( | ||
@@ -273,5 +432,6 @@ 'BaobabRouter.__makeRoutes: ' + | ||
)); | ||
} | ||
// Each route must have some state restriction (except for the root): | ||
if (arguments.length > 2 && !route.updates.length) | ||
if (arguments.length > 2 && !route.updates.length) { | ||
throw (new Error( | ||
@@ -281,2 +441,3 @@ 'BaobabRouter.__makeRoutes: ' + | ||
)); | ||
} | ||
@@ -287,159 +448,2 @@ return route; | ||
/** | ||
* This function will merge multiple objects into one. Each object will | ||
* overrides the previous ones. Also, this function will return an object with | ||
* two keys: "value" contains the new object, and "conflicts" is a flag | ||
* specifying wether some paths contain different values in different objects. | ||
* | ||
* Examples: | ||
* ********* | ||
* > var a = { key: 'value' }, | ||
* > b = __deepMerge(a); | ||
* > a === b; // false | ||
* > b.key; // 'value' | ||
* | ||
* > __deepMerge( | ||
* > { a: 1 }, | ||
* > { b: 1 }, | ||
* > { c: 1 } | ||
* > ); | ||
* > // { a: 1, b: 1, c: 1 } | ||
* | ||
* > __deepMerge( | ||
* > { a: 1 }, | ||
* > { a: 2 }, | ||
* > { a: 3 } | ||
* > ); | ||
* > // { a: 3 } | ||
* | ||
* @param {object*} objects The objects to merge. | ||
* @return {object} An object containing the merged object under the | ||
* key "value", and a flag specifying if some keys | ||
* where having different values in the different | ||
* arguments, under the key "conflicts". | ||
*/ | ||
function __deepMerge() { | ||
var i, | ||
k, | ||
l, | ||
obj, | ||
res, | ||
merged, | ||
conflicts = false; | ||
for (i = 0, l = arguments.length; i < l; i++) { | ||
obj = arguments[i]; | ||
if (obj && typeof obj === 'object') { | ||
if (!res) | ||
res = Array.isArray(obj) ? [] : {}; | ||
for (k in obj) { | ||
merged = __deepMerge(res[k], obj[k]); | ||
conflicts = conflicts || merged.conflicts; | ||
res[k] = merged.value; | ||
} | ||
} else { | ||
if (res !== undefined && res !== obj) | ||
conflicts = true; | ||
res = obj; | ||
} | ||
} | ||
return { | ||
value: res, | ||
conflicts: conflicts | ||
}; | ||
} | ||
/** | ||
* This function compares two arrays (usually two paths for Baobab), and returns | ||
* true if they have the same length and equals elements in the same order. | ||
* | ||
* @param {array} a1 The first array. | ||
* @param {array} a2 The second array. | ||
* @return {boolean} True if the values are equals, false else. | ||
*/ | ||
function __compareArrays(a1, a2) { | ||
var i, | ||
l = a1.length; | ||
if ( | ||
!Array.isArray(a1) || | ||
!Array.isArray(a2) || | ||
l !== a2.length | ||
) | ||
return false; | ||
for (i = 0; i < l; i++) | ||
if (a1[i] !== a2[i]) | ||
return false; | ||
return true; | ||
} | ||
/** | ||
* This function takes a well formed URL from any BaobabRouter instance's route, | ||
* with potentially dynamic attributes to resolve, and a object with the related | ||
* values, and returns the URL with the values inserted instead of the dynamics. | ||
* | ||
* Examples: | ||
* ********* | ||
* > __resolveURL('a/b/c'); // same as __resolveURL('a/b/c', {}); | ||
* > // 'a/b/c' (nothing to solve) | ||
* | ||
* > __resolveURL('a/:b/c/:d', { ':b': 'B', ':d': 'D' }); | ||
* > // 'a/B/c/D' | ||
* | ||
* > __resolveURL('a/:b/:b', { ':b': 'B' }); | ||
* > // 'a/B/B' | ||
* | ||
* > __resolveURL('a/:b/:c', { ':c': 'C', ':d': 'D' }); | ||
* > // 'a/:b/C' | ||
* | ||
* @param {string} url The URL to resolve. | ||
* @param {?object} obj An optional object with the dynamic values to insert. | ||
* @return {string} The resolved URL. | ||
*/ | ||
function __resolveURL(url, obj) { | ||
obj = obj || {}; | ||
return url.split('/').map(function(s) { | ||
return obj.hasOwnProperty(s) ? obj[s] : s; | ||
}).join('/'); | ||
} | ||
/** | ||
* This function builds a proper path from a route's path and its parents paths. | ||
* | ||
* Examples: | ||
* ********* | ||
* > __concatenatePaths('a'); // '/a' | ||
* > __concatenatePaths('/a'); // '/a' | ||
* > __concatenatePaths('a', ''); // '/a' | ||
* > __concatenatePaths('', 'b'); // '/b' | ||
* | ||
* > __concatenatePaths('a', 'b'); // '/a/b' | ||
* > __concatenatePaths('a', '/b'); // '/a/b' | ||
* > __concatenatePaths('a/', '/b'); // '/a/b' | ||
* > __concatenatePaths('a/', 'b'); // '/a/b' | ||
* > __concatenatePaths('/a', '/b'); // '/a/b' | ||
* | ||
* @param {string+} paths the different paths to concatenate. | ||
* @return {string} The cleaned path. | ||
*/ | ||
function __concatenatePaths() { | ||
return ( | ||
('/' + Array.prototype.map.call(arguments, function(str) { | ||
return str || ''; | ||
}).join('/')) | ||
.replace(/\/+/g, '/') | ||
.replace(/\/+$/g, '') | ||
); | ||
} | ||
/** | ||
* The baobab-router constructor. In its current state, the baobab-router does | ||
@@ -452,3 +456,3 @@ * not expose anything to its public API. | ||
* | ||
* @param {Baobab} tree The Baobab instance to connect the router to. | ||
* @param {Baobab} baobab The Baobab instance to connect the router to. | ||
* @param {Object} routes The routes tree. It must have a defaultRoute string | ||
@@ -459,28 +463,22 @@ * and a routes array. | ||
*/ | ||
var BaobabRouter = function(tree, routes, settings) { | ||
/********************* | ||
const BaobabRouter = function BaobabRouterConstr(baobab, routes, settings) { | ||
/* ******************* | ||
* PRIVATE ATTRIBUTES: | ||
* ******************* | ||
*/ | ||
const _tree = baobab; | ||
const _settings = settings || {}; | ||
const _solver = _settings.solver || __defaultSolver; | ||
const _routesTree = __makeRoutes(__deepMerge(routes).value, _solver); | ||
var _facet, | ||
_hashInterval, | ||
_hashListener, | ||
_stateListener, | ||
_tree = tree, | ||
_settings = settings || {}, | ||
_solver = _settings.solver || __defaultSolver, | ||
_stored = window.location.hash.replace(/^#/, ''), | ||
_routesTree = __makeRoutes(__deepMerge(routes).value, _solver); | ||
let _watcher; | ||
let _hashInterval; | ||
let _hashListener; | ||
let _watcherListener; | ||
let _stored = window.location.hash.replace(/^#/, ''); | ||
/****************** | ||
/* **************** | ||
* PRIVATE METHODS: | ||
* **************** | ||
*/ | ||
/** | ||
@@ -493,12 +491,14 @@ * This function will recursively check the hash to find a route that matches. | ||
*/ | ||
function _checkHash(hash, basePath, baseRoute) { | ||
var match, | ||
doCommit, | ||
path = basePath || '', | ||
route = baseRoute || _routesTree; | ||
function _checkHash(baseHash, basePath, baseRoute) { | ||
const route = baseRoute || _routesTree; | ||
const match = __doesHashMatch(route.fullPath, baseHash); | ||
// Check if route match: | ||
match = __doesHashMatch(route.fullPath, hash); | ||
if (!match) | ||
let doCommit; | ||
let doForceCommit; | ||
let hash = baseHash; | ||
let path = basePath || ''; | ||
if (!match) { | ||
return false; | ||
} | ||
@@ -508,7 +508,6 @@ // Check if a child does match (without using default values): | ||
route.routes && | ||
route.routes.some(function(child) { | ||
return _checkHash(hash, route.fullPath, child); | ||
}) | ||
) | ||
route.routes.some(child => _checkHash(hash, route.fullPath, child)) | ||
) { | ||
return true; | ||
} | ||
@@ -518,8 +517,10 @@ // If there is a default route, check which route it does match: | ||
hash = (hash || '').split('/'); | ||
path = route.fullDefaultPath.split('/').map(function(str, i) { | ||
return str.match(_solver) ? | ||
hash[i] || str : | ||
str; | ||
}).join('/'); | ||
_updateHash(path); | ||
path = route.fullDefaultPath | ||
.split('/') | ||
.map((str, i) => str.match(_solver) ? hash[i] || str : str) | ||
.join('/'); | ||
// The following line is no more linted, because of some circular deps on | ||
// the two functions: | ||
_updateHash(path); //eslint-disable-line | ||
return true; | ||
@@ -531,18 +532,26 @@ } | ||
// Apply updates: | ||
route.updates.map(function(obj) { | ||
var update = { | ||
route.updates.map(obj => { | ||
const update = { | ||
path: obj.path, | ||
value: obj.value | ||
value: obj.value, | ||
}; | ||
if (obj.dynamic) | ||
if (obj.dynamic) { | ||
update.value = hash.split('/')[ | ||
route.fullPath.split('/').indexOf(update.value) | ||
]; | ||
} | ||
if (!_routesTree.readOnly.some(function(path) { | ||
return __compareArrays(update.path, path); | ||
})) { | ||
_tree.set(update.path, update.value); | ||
doCommit = true; | ||
if ( | ||
_routesTree.readOnly.every( | ||
str => !__compareArrays(update.path, str) | ||
) && | ||
update.path.length > 1 | ||
) { | ||
if (_tree.get(update.path.slice(1)) !== update.value) { | ||
_tree.set(update.path.slice(1), update.value); | ||
doCommit = true; | ||
} else { | ||
doForceCommit = true; | ||
} | ||
} | ||
@@ -552,4 +561,7 @@ }); | ||
// Commit only if something has actually been updated: | ||
if (doCommit) | ||
if (doCommit) { | ||
_tree.commit(); | ||
} else if (doForceCommit) { | ||
_checkState(); //eslint-disable-line | ||
} | ||
@@ -561,2 +573,17 @@ return true; | ||
/** | ||
* This function will update the hash, and execute the _checkHash method | ||
* if the new hash is different from the stored one. | ||
* | ||
* @param {string} hash The new hash. | ||
*/ | ||
function _updateHash(hash, force = false) { | ||
if ((_stored !== hash) || force) { | ||
window.location.hash = hash; | ||
_stored = hash; | ||
_checkHash(_stored); | ||
} | ||
} | ||
/** | ||
* This function will recursively check the state to find a route with | ||
@@ -568,15 +595,16 @@ * matching state constraints. If none is found, then the default route will | ||
*/ | ||
function _checkState(basePath, baseRoute, baseState) { | ||
var k, | ||
match, | ||
path = basePath || '', | ||
state = baseState || _tree.get(), | ||
route = baseRoute || _routesTree; | ||
function _checkState(basePath, baseRoute, baseTree) { | ||
const tree = baseTree || { | ||
state: _tree.get(), | ||
}; | ||
const route = baseRoute || _routesTree; | ||
// Check if route match: | ||
match = baseState ? | ||
__doesStateMatch(state, route.fullState, route.dynamics) : | ||
__doesStateMatch(route.fullState, state, route.dynamics); | ||
if (!match && arguments.length > 0 && !route.overrides) | ||
const match = baseTree ? | ||
__doesStateMatch(tree, route.fullTree, route.dynamics) : | ||
__doesStateMatch(route.fullTree, tree, route.dynamics); | ||
if (!match && arguments.length > 0 && !route.overrides) { | ||
return false; | ||
} | ||
@@ -586,7 +614,6 @@ // Check if a child does match: | ||
route.routes && | ||
route.routes.some(function(child) { | ||
return _checkState(route.fullPath, child); | ||
}) | ||
) | ||
route.routes.some(child => _checkState(route.fullPath, child)) | ||
) { | ||
return true; | ||
} | ||
@@ -598,13 +625,13 @@ // If the root route did not find any match, let's compare the tree with | ||
var restrictedState = __extractPaths(state).filter(function(obj) { | ||
return _routesTree.readOnly.some(function(path) { | ||
return __compareArrays(obj.path, path); | ||
}); | ||
}).reduce(function(res, obj) { | ||
obj.path.reduce(function(localState, string, i) { | ||
if (i === obj.path.length - 1) | ||
localState[string] = obj.value; | ||
else | ||
localState[string] = | ||
localState[string] || | ||
const restrictedTree = __extractPaths(tree).filter( | ||
obj => _routesTree.readOnly.some( | ||
path => __compareArrays(obj.path, path) | ||
) | ||
).reduce((res, obj) => { | ||
obj.path.reduce((localTree, string, i) => { | ||
if (i === obj.path.length - 1) { | ||
localTree[string] = obj.value; | ||
} else { | ||
localTree[string] = | ||
localTree[string] || | ||
( | ||
@@ -615,4 +642,5 @@ typeof obj.path[i + 1] === 'number' ? | ||
); | ||
} | ||
return localState[string]; | ||
return localTree[string]; | ||
}, res); | ||
@@ -622,17 +650,22 @@ return res; | ||
if ( | ||
route.routes.some(function(child) { | ||
return _checkState(route.fullPath, child, restrictedState); | ||
}) | ||
) | ||
if (route.routes.some( | ||
child => _checkState(route.fullPath, child, restrictedTree) | ||
)) { | ||
return true; | ||
} | ||
} | ||
if (match) { | ||
_updateHash(__resolveURL( | ||
route.defaultRoute ? | ||
route.fullDefaultPath : | ||
route.fullPath, | ||
match | ||
)); | ||
_updateHash( | ||
__resolveURL( | ||
route.defaultRoute ? | ||
route.fullDefaultPath : | ||
route.fullPath, | ||
match | ||
), | ||
// If updating to a default route, then it might come from an invalid | ||
// state. And if the same route is already set, then forcing the hash | ||
// update is necessary to trigger the _checkHash to go back to a valid | ||
// state: | ||
!!route.defaultRoute | ||
); | ||
@@ -643,25 +676,3 @@ return true; | ||
/** | ||
* This function will update the hash, and execute the _checkHash method | ||
* if the new hash is different from the stored one. | ||
* | ||
* @param {string} hash The new hash. | ||
*/ | ||
function _updateHash(hash) { | ||
if (_stored !== hash) { | ||
window.location.hash = hash; | ||
// Force execute _checkHash: | ||
if (hash !== _stored) { | ||
_stored = hash; | ||
_checkHash(_stored); | ||
} | ||
} | ||
} | ||
/***************** | ||
/* *************** | ||
* PUBLIC METHODS: | ||
@@ -672,24 +683,19 @@ * *************** | ||
// Hash update capture: | ||
if (_hashListener) | ||
if (_hashListener) { | ||
window.removeEventListener('hashchange', _hashListener, false); | ||
else if (_hashInterval) | ||
} else if (_hashInterval) { | ||
window.clearInterval(_hashInterval); | ||
} | ||
// Unbind the tree: | ||
// _facet.off(_stateListener); | ||
_facet.release(); | ||
_watcher.release(); | ||
_tree.router = null; | ||
} | ||
/***************** | ||
/* *************** | ||
* INITIALIZATION: | ||
* *************** | ||
*/ | ||
// Check that there is no router already bound to this tree: | ||
if (_tree.router) | ||
if (_tree.router) { | ||
throw (new Error( | ||
@@ -699,2 +705,3 @@ 'BaobabRouter (constructor): ' + | ||
)); | ||
} | ||
_tree.router = this; | ||
@@ -704,4 +711,4 @@ | ||
if ('onhashchange' in window) { | ||
_hashListener = function() { | ||
var hash = window.location.hash.replace(/^#/, ''); | ||
_hashListener = () => { | ||
const hash = window.location.hash.replace(/^#/, ''); | ||
if (hash !== _stored) { | ||
@@ -716,4 +723,4 @@ _stored = hash; | ||
_stored = window.location.hash; | ||
_hashInterval = window.setInterval(function() { | ||
var hash = window.location.hash.replace(/^#/, ''); | ||
_hashInterval = window.setInterval(() => { | ||
const hash = window.location.hash.replace(/^#/, ''); | ||
if (hash !== _stored) { | ||
@@ -727,4 +734,6 @@ _stored = hash; | ||
// Listen to the state changes: | ||
_facet = _tree.createFacet({ | ||
cursors: __extractPaths( | ||
_watcherListener = () => _checkState(); | ||
_watcher = _tree.watch( | ||
__extractPaths( | ||
_routesTree.routes.reduce( | ||
@@ -734,26 +743,26 @@ function _extract(arr, child) { | ||
_extract, | ||
arr.concat(child.updates.map(function(obj) { | ||
return obj.path; | ||
})) | ||
arr.concat(child.updates.map(obj => obj.path.slice(1))) | ||
); | ||
}, | ||
[] | ||
).reduce(function(context, path) { | ||
path.reduce(function(localContext, key) { | ||
return (key in localContext) ? | ||
).filter( | ||
path => path && path.length | ||
).reduce((context, path) => { | ||
path.reduce( | ||
(localContext, key) => (key in localContext) ? | ||
localContext[key] : | ||
(localContext[key] = {}); | ||
}, context); | ||
(localContext[key] = {}), | ||
context | ||
); | ||
return context; | ||
}, {}), | ||
[] | ||
).reduce(function(result, obj, i) { | ||
).reduce((result, obj, i) => { | ||
result['path_' + i] = obj.path; | ||
return result; | ||
}, {}) | ||
}); | ||
); | ||
_facet.on('update', function() { | ||
_checkState(); | ||
}); | ||
_watcher.on('update', _watcherListener); | ||
@@ -768,3 +777,3 @@ // Export publics: | ||
// Baobab-Router version: | ||
BaobabRouter.version = '1.0.0'; | ||
BaobabRouter.version = '2.0.0'; | ||
@@ -784,7 +793,6 @@ // Expose private methods for unit testing: | ||
/**************** | ||
/* ************** | ||
* EXPORT MODULE: | ||
* ************** | ||
*/ | ||
module.exports = BaobabRouter; | ||
export default BaobabRouter; |
{ | ||
"name": "baobab-router", | ||
"version": "1.0.0", | ||
"version": "2.0.0", | ||
"description": "A router for Baobab", | ||
"main": "baobab-router.js", | ||
"main": "baobab-router.dist.js", | ||
"scripts": { | ||
"test": "gulp" | ||
"lint": "eslint baobab-router.js test/collection*.js", | ||
"build": "babel baobab-router.js -o baobab-router.dist.js --presets es2015 --plugins add-module-exports", | ||
"test:build": "browserify test/collections.js -o test/build/test.build.js -t [ babelify --presets [ es2015 ] ]", | ||
"test:exec": "mocha-phantomjs ./test/test.html", | ||
"test": "npm run test:build; npm run test:exec", | ||
"prepublish": "npm run build" | ||
}, | ||
@@ -24,12 +29,32 @@ "repository": { | ||
"devDependencies": { | ||
"baobab": "^1.0.2", | ||
"browserify": "^8.1.3", | ||
"gulp": "^3.8.11", | ||
"gulp-gjslint": "^0.1.4", | ||
"gulp-jshint": "^1.10.0", | ||
"gulp-mocha-phantomjs": "^0.6.1", | ||
"gulp-rename": "^1.2.2", | ||
"jshint-stylish": "^1.0.1", | ||
"vinyl-transform": "^1.0.0" | ||
"babel-cli": "^6.3.17", | ||
"babel-eslint": "^5.0.0-beta6", | ||
"babel-plugin-add-module-exports": "^0.1.1", | ||
"babel-preset-es2015": "^6.3.13", | ||
"babelify": "^7.2.0", | ||
"baobab": "^2.2.1", | ||
"browserify": "^12.0.1", | ||
"eslint": "^1.10.3", | ||
"eslint-config-airbnb": "^3.0.0", | ||
"eslint-plugin-react": "^3.11.3", | ||
"mocha-phantomjs": "^4.0.1" | ||
}, | ||
"eslintConfig": { | ||
"parser": "babel-eslint", | ||
"extends": "airbnb", | ||
"env": { | ||
"browser": true | ||
}, | ||
"rules": { | ||
"no-param-reassign": 0 | ||
}, | ||
"globals": { | ||
"window": true, | ||
"describe": true, | ||
"assert": true, | ||
"it": true, | ||
"afterEach": true, | ||
"beforeEach": true | ||
} | ||
} | ||
} |
@@ -26,3 +26,3 @@ # Baobab-router | ||
// Instanciate Baobab tree: | ||
// Instanciate Baobab tree: | ||
var tree = new Baobab({ | ||
@@ -29,0 +29,0 @@ view: null, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
58819
1298
11