angular-state-router
Version:
An AngularJS state-based router designed for flexibility and ease of use.
1,309 lines (1,055 loc) • 94.6 kB
JavaScript
(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require=="function"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error("Cannot find module '"+o+"'");throw f.code="MODULE_NOT_FOUND",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require=="function"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})({1:[function(require,module,exports){
'use strict';
module.exports = ['$state', function ($state) {
return {
restrict: 'A',
scope: {
},
link: function(scope, element, attrs) {
element.css('cursor', 'pointer');
element.on('click', function(e) {
$state.change(attrs.sref);
e.preventDefault();
});
}
};
}];
},{}],2:[function(require,module,exports){
'use strict';
/* global angular:false */
// CommonJS
if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
module.exports = 'angular-state-router';
}
// Instantiate module
angular.module('angular-state-router', [])
.provider('$state', require('./services/state-router'))
.factory('$urlManager', require('./services/url-manager'))
.factory('$resolution', require('./services/resolution'))
.factory('$enact', require('./services/enact'))
.factory('$queueHandler', require('./services/queue-handler'))
.run(['$rootScope', '$state', '$urlManager', '$resolution', '$enact', function($rootScope, $state, $urlManager, $resolution, $enact) {
// Update location changes
$rootScope.$on('$locationChangeSuccess', function() {
$urlManager.location(arguments);
});
$urlManager.$ready();
$resolution.$ready();
$enact.$ready();
// Initialize
$state.$ready();
}])
.directive('sref', require('./directives/sref'));
},{"./directives/sref":1,"./services/enact":3,"./services/queue-handler":4,"./services/resolution":5,"./services/state-router":6,"./services/url-manager":7}],3:[function(require,module,exports){
'use strict';
module.exports = ['$q', '$injector', '$state', '$rootScope', function($q, $injector, $state, $rootScope) {
// Instance
var _self = {};
/**
* Process actions
*
* @param {Object} actions An array of actions items
* @return {Promise} A promise fulfilled when actions processed
*/
var _act = function(actions) {
var actionPromises = [];
angular.forEach(actions, function(value) {
var action = angular.isString(value) ? $injector.get(value) : $injector.invoke(value);
actionPromises.push($q.when(action));
});
return $q.all(actionPromises);
};
_self.process = _act;
/**
* Register middleware layer
*/
_self.$ready = function() {
$state.$use(function(request, next) {
var current = $state.current();
if(!current) {
return next();
}
$rootScope.$broadcast('$stateActionBegin');
_act(current.actions || []).then(function() {
$rootScope.$broadcast('$stateActionEnd');
next();
}, function(err) {
$rootScope.$broadcast('$stateActionError', err);
next(new Error('Error processing state actions'));
});
}, 100);
};
return _self;
}];
},{}],4:[function(require,module,exports){
'use strict';
module.exports = ['$rootScope', function($rootScope) {
/**
* Execute a series of functions; used in tandem with middleware
*/
var Queue = function() {
var _list = [];
var _data = null;
var _self = {
/**
* Add a handler
*
* @param {Mixed} handler A Function or an Array of Functions to add to the queue
* @return {Queue} Itself; chainable
*/
add: function(handler, priority) {
if(handler && handler.constructor === Array) {
handler.forEach(function(layer) {
layer.priority = typeof layer.priority === 'undefined' ? 1 : layer.priority;
});
_list = _list.concat(handler);
} else {
handler.priority = priority || (typeof handler.priority === 'undefined' ? 1 : handler.priority);
_list.push(handler);
}
return this;
},
/**
* Data object
*
* @param {Object} data A data object made available to each handler
* @return {Queue} Itself; chainable
*/
data: function(data) {
_data = data;
return this;
},
/**
* Begin execution and trigger callback at the end
*
* @param {Function} callback A callback, function(err)
* @return {Queue} Itself; chainable
*/
execute: function(callback) {
var nextHandler;
var executionList = _list.slice(0).sort(function(a, b) {
return Math.max(-1, Math.min(1, b.priority - a.priority));
});
nextHandler = function() {
$rootScope.$evalAsync(function() {
var handler = executionList.shift();
// Complete
if(!handler) {
callback(null);
// Next handler
} else {
handler.call(null, _data, function(err) {
// Error
if(err) {
callback(err);
// Continue
} else {
nextHandler();
}
});
}
});
};
// Start
nextHandler();
}
};
return _self;
};
// Instance
return {
/**
* Factory method
*
* @return {Queue} A queue
*/
create: function() {
return Queue();
}
};
}];
},{}],5:[function(require,module,exports){
'use strict';
module.exports = ['$q', '$injector', '$state', '$rootScope', function($q, $injector, $state, $rootScope) {
// Instance
var _self = {};
/**
* Resolve
*
* @param {Object} resolve A hash Object of items to resolve
* @return {Promise} A promise fulfilled when templates retireved
*/
var _resolve = function(resolve) {
var resolvesPromises = {};
angular.forEach(resolve, function(value, key) {
var resolution = angular.isString(value) ? $injector.get(value) : $injector.invoke(value, null, null, key);
resolvesPromises[key] = $q.when(resolution);
});
return $q.all(resolvesPromises);
};
_self.resolve = _resolve;
/**
* Register middleware layer
*/
_self.$ready = function() {
$state.$use(function(request, next) {
var current = $state.current();
if(!current) {
return next();
}
$rootScope.$broadcast('$stateResolveBegin');
_resolve(current.resolve || {}).then(function(locals) {
angular.extend(request.locals, locals);
$rootScope.$broadcast('$stateResolveEnd');
next();
}, function(err) {
$rootScope.$broadcast('$stateResolveError', err);
next(new Error('Error resolving state'));
});
}, 101);
};
return _self;
}];
},{}],6:[function(require,module,exports){
'use strict';
var UrlDictionary = require('../utils/url-dictionary');
var Parameters = require('../utils/parameters');
module.exports = [function StateRouterProvider() {
// Provider
var _provider = this;
// Configuration, global options
var _configuration = {
historyLength: 5
};
// State definition library
var _stateLibrary = {};
var _stateCache = {};
// URL to state dictionary
var _urlDictionary = new UrlDictionary();
// Middleware layers
var _layerList = [];
/**
* Parse state notation name-params.
*
* Assume all parameter values are strings
*
* @param {String} nameParams A name-params string
* @return {Object} A name string and param Object
*/
var _parseName = function(nameParams) {
if(nameParams && nameParams.match(/^[a-zA-Z0-9_\.]*\(.*\)$/)) {
var npart = nameParams.substring(0, nameParams.indexOf('('));
var ppart = Parameters( nameParams.substring(nameParams.indexOf('(')+1, nameParams.lastIndexOf(')')) );
return {
name: npart,
params: ppart
};
} else {
return {
name: nameParams,
params: null
};
}
};
/**
* Add default values to a state
*
* @param {Object} data An Object
* @return {Object} An Object
*/
var _setStateDefaults = function(data) {
// Default values
data.inherit = (typeof data.inherit === 'undefined') ? true : data.inherit;
return data;
};
/**
* Validate state name
*
* @param {String} name A unique identifier for the state; using dot-notation
* @return {Boolean} True if name is valid, false if not
*/
var _validateStateName = function(name) {
name = name || '';
// TODO optimize with RegExp
var nameChain = name.split('.');
for(var i=0; i<nameChain.length; i++) {
if(!nameChain[i].match(/[a-zA-Z0-9_]+/)) {
return false;
}
}
return true;
};
/**
* Validate state query
*
* @param {String} query A query for the state; using dot-notation
* @return {Boolean} True if name is valid, false if not
*/
var _validateStateQuery = function(query) {
query = query || '';
// TODO optimize with RegExp
var nameChain = query.split('.');
for(var i=0; i<nameChain.length; i++) {
if(!nameChain[i].match(/(\*(\*)?|[a-zA-Z0-9_]+)/)) {
return false;
}
}
return true;
};
/**
* Compare two states, compares values.
*
* @return {Boolean} True if states are the same, false if states are different
*/
var _compareStates = function(a, b) {
a = a || {};
b = b || {};
return a.name === b.name && angular.equals(a.params, b.params);
};
/**
* Get a list of parent states
*
* @param {String} name A unique identifier for the state; using dot-notation
* @return {Array} An Array of parent states
*/
var _getNameChain = function(name) {
var nameList = name.split('.');
return nameList
.map(function(item, i, list) {
return list.slice(0, i+1).join('.');
})
.filter(function(item) {
return item !== null;
});
};
/**
* Internal method to crawl library heirarchy
*
* @param {String} name A unique identifier for the state; using state-notation
* @return {Object} A state data Object
*/
var _getState = function(name) {
name = name || '';
var state = null;
// Only use valid state queries
if(!_validateStateName(name)) {
return null;
// Use cache if exists
} else if(_stateCache[name]) {
return _stateCache[name];
}
var nameChain = _getNameChain(name);
var stateChain = nameChain
.map(function(name, i) {
var item = angular.copy(_stateLibrary[name]);
return item;
})
.filter(function(parent) {
return !!parent;
});
// Walk up checking inheritance
for(var i=stateChain.length-1; i>=0; i--) {
if(stateChain[i]) {
var nextState = stateChain[i];
state = angular.merge(nextState, state || {});
}
if(state && state.inherit === false) break;
}
// Store in cache
_stateCache[name] = state;
return state;
};
/**
* Internal method to store a state definition. Parameters should be included in data Object not state name.
*
* @param {String} name A unique identifier for the state; using state-notation
* @param {Object} data A state definition data Object
* @return {Object} A state data Object
*/
var _defineState = function(name, data) {
if(name === null || typeof name === 'undefined') {
throw new Error('Name cannot be null.');
// Only use valid state names
} else if(!_validateStateName(name)) {
throw new Error('Invalid state name.');
}
// Create state
var state = angular.copy(data);
// Use defaults
_setStateDefaults(state);
// Named state
state.name = name;
// Set definition
_stateLibrary[name] = state;
// Reset cache
_stateCache = {};
// URL mapping
if(state.url) {
_urlDictionary.add(state.url, state);
}
return data;
};
/**
* Set configuration data parameters for StateRouter
*
* Including parameters:
*
* - historyLength {Number} Defaults to 5
* - initialLocation {Object} An Object{name:String, params:Object} for initial state transition
*
* @param {Object} options A data Object
* @return {$stateProvider} Itself; chainable
*/
this.options = function(options) {
angular.extend(_configuration, options || {});
return _provider;
};
/**
* Set/get state
*
* @param {String} name A unique identifier for the state; using state-notation
* @param {Object} data A state definition data Object
* @return {$stateProvider} Itself; chainable
*/
this.state = function(name, state) {
// Get
if(!state) {
return _getState(name);
}
// Set
_defineState(name, state);
return _provider;
};
/**
* Set initialization parameters; deferred to $ready()
*
* @param {String} name A iniital state
* @param {Object} params A data object of params
* @return {$stateProvider} Itself; chainable
*/
this.init = function(name, params) {
_configuration.initialLocation = {
name: name,
params: params
};
return _provider;
};
/**
* Get instance
*/
this.$get = ['$rootScope', '$location', '$q', '$queueHandler', function StateRouterFactory($rootScope, $location, $q, $queueHandler) {
// State
var _current;
var _transitionQueue = [];
var _isReady = true;
var _options;
var _initalLocation;
var _history = [];
var _isInit = false;
/**
* Internal method to add history and correct length
*
* @param {Object} data An Object
*/
var _pushHistory = function(data) {
// Keep the last n states (e.g. - defaults 5)
var historyLength = _options.historyLength || 5;
if(data) {
_history.push(data);
}
// Update length
if(_history.length > historyLength) {
_history.splice(0, _history.length - historyLength);
}
};
/**
* Internal method to fulfill change state request. Parameters in `params` takes precedence over state-notation `name` expression.
*
* @param {String} name A unique identifier for the state; using state-notation including optional parameters
* @param {Object} params A data object of params
* @return {Promise} A promise fulfilled when state change occurs
*/
var _changeState = function(name, params) {
var deferred = $q.defer();
$rootScope.$evalAsync(function() {
params = params || {};
// Parse state-notation expression
var nameExpr = _parseName(name);
name = nameExpr.name;
params = angular.extend(nameExpr.params || {}, params);
// Special name notation
if(name === '.' && _current) {
name = _current.name;
}
var error = null;
var request = {
name: name,
params: params,
locals: {},
promise: deferred.promise
};
// Compile execution phases
var queue = $queueHandler.create().data(request);
var nextState = angular.copy(_getState(name));
var prevState = _current;
if(nextState) {
// Set locals
nextState.locals = request.locals;
// Set parameters
nextState.params = angular.extend(nextState.params || {}, params);
}
// Does not exist
if(nextState === null) {
queue.add(function(data, next) {
error = new Error('Requested state was not defined.');
error.code = 'notfound';
$rootScope.$broadcast('$stateChangeErrorNotFound', error, request);
next(error);
}, 200);
// State not changed
} else if(_compareStates(prevState, nextState)) {
queue.add(function(data, next) {
_current = nextState;
next();
}, 200);
// Valid state exists
} else {
// Process started
queue.add(function(data, next) {
$rootScope.$broadcast('$stateChangeBegin', request);
next();
}, 201);
// Make state change
queue.add(function(data, next) {
if(prevState) _pushHistory(prevState);
_current = nextState;
next();
}, 200);
// Add middleware
queue.add(_layerList);
// Process ended
queue.add(function(data, next) {
$rootScope.$broadcast('$stateChangeEnd', request);
next();
}, -200);
}
// Run
queue.execute(function(err) {
if(err) {
$rootScope.$broadcast('$stateChangeError', err, request);
deferred.reject(err);
} else {
deferred.resolve();
}
});
});
return deferred.promise;
};
/**
* Internal method to change to state and broadcast completion
*
* @param {String} name A unique identifier for the state; using state-notation including optional parameters
* @param {Object} params A data object of params
* @return {Promise} A promise fulfilled when state change occurs
*/
var _changeStateAndBroadcastComplete = function(name, params) {
return _changeState(name, params).then(function() {
$rootScope.$broadcast('$stateChangeComplete', null, _current);
}, function(err) {
$rootScope.$broadcast('$stateChangeComplete', err, _current);
});
};
/**
* Reloads the current state
*
* @return {Promise} A promise fulfilled when state change occurs
*/
var _reloadState = function() {
var deferred = $q.defer();
$rootScope.$evalAsync(function() {
var n = _current.name;
var p = angular.copy(_current.params);
if(!_current.params) {
_current.params = {};
}
_current.params.deprecated = true;
// Notify
$rootScope.$broadcast('$stateReload', null, _current);
_changeStateAndBroadcastComplete(n, p).then(function() {
deferred.resolve();
}, function(err) {
deferred.reject(err);
});
});
return deferred.promise;
};
// Instance
var _inst;
_inst = {
/**
* Get options
*
* @return {Object} A configured options
*/
options: function() {
// Hasn't been initialized
if(!_options) {
_options = angular.copy(_configuration);
}
return _options;
},
/**
* Set/get state. Reloads state if current state is affected by defined
* state (when redefining parent or current state)
*
* @param {String} name A unique identifier for the state; using state-notation
* @param {Object} data A state definition data Object
* @return {$state} Itself; chainable
*/
state: function(name, state) {
// Get
if(!state) {
return _getState(name);
}
// Set
_defineState(name, state);
return _inst;
},
/**
* Internal method to add middleware; called during state transition
*
* @param {Function} handler A callback, function(request, next)
* @param {Number} priority A number denoting priority
* @return {$state} Itself; chainable
*/
$use: function(handler, priority) {
if(typeof handler !== 'function') {
throw new Error('Middleware must be a function.');
}
if(typeof priority !== 'undefined') handler.priority = priority;
_layerList.push(handler);
return _inst;
},
/**
* Internal method to perform initialization
*
* @return {$state} Itself; chainable
*/
$ready: function() {
$rootScope.$evalAsync(function() {
if(!_isInit) {
_isInit = true;
// Configuration
if(!_options) {
_options = angular.copy(_configuration);
}
// Initial location
if(_options.hasOwnProperty('initialLocation')) {
_initalLocation = angular.copy(_options.initialLocation);
}
var readyDeferred = null;
// Initial location
if($location.url() !== '') {
readyDeferred = _inst.$location($location.url());
// Initialize with state
} else if(_initalLocation) {
readyDeferred = _changeStateAndBroadcastComplete(_initalLocation.name, _initalLocation.params);
}
$q.when(readyDeferred).then(function() {
$rootScope.$broadcast('$stateInit');
});
}
});
return _inst;
},
// Parse state notation name-params.
parse: _parseName,
/**
* Retrieve definition of states
*
* @return {Object} A hash of all defined states
*/
library: function() {
return _stateLibrary;
},
// Validation
validate: {
name: _validateStateName,
query: _validateStateQuery
},
/**
* Retrieve history
*
* @return {[type]} [description]
*/
history: function() {
return _history;
},
/**
* Request state transition, asynchronous operation
*
* @param {String} name A unique identifier for the state; using dot-notation
* @param {Object} [params] A parameters data object
* @return {Promise} A promise fulfilled when state change complete
*/
change: function(name, params) {
return _changeStateAndBroadcastComplete(name, params);
},
/**
* Reloads the current state
*
* @return {Promise} A promise fulfilled when state change occurs
*/
reload: _reloadState,
/**
* Internal method to change state based on $location.url(), asynchronous operation using internal methods, quiet fallback.
*
* @param {String} url A url matching defind states
* @param {Function} [callback] A callback, function(err)
* @return {$state} Itself; chainable
*/
$location: function(url) {
var data = _urlDictionary.lookup(url);
if(data) {
var state = data.ref;
if(state) {
// Parse params from url
return _changeStateAndBroadcastComplete(state.name, data.params);
}
} else if(!!url && url !== '') {
var error = new Error('Requested state was not defined.');
error.code = 'notfound';
$rootScope.$broadcast('$stateChangeErrorNotFound', error, {
url: url
});
}
return $q.reject(new Error('Unable to find location in library'));
},
/**
* Retrieve copy of current state
*
* @return {Object} A copy of current state
*/
current: function() {
return (!_current) ? null : angular.copy(_current);
},
/**
* Check query against current state
*
* @param {Mixed} query A string using state notation or a RegExp
* @param {Object} params A parameters data object
* @return {Boolean} A true if state is parent to current state
*/
active: function(query, params) {
query = query || '';
// No state
if(!_current) {
return false;
// Use RegExp matching
} else if(query instanceof RegExp) {
return !!_current.name.match(query);
// String; state dot-notation
} else if(typeof query === 'string') {
// Cast string to RegExp
if(query.match(/^\/.*\/$/)) {
var casted = query.substr(1, query.length-2);
return !!_current.name.match(new RegExp(casted));
// Transform to state notation
} else {
var transformed = query
.split('.')
.map(function(item) {
if(item === '*') {
return '[a-zA-Z0-9_]*';
} else if(item === '**') {
return '[a-zA-Z0-9_\\.]*';
} else {
return item;
}
})
.join('\\.');
return !!_current.name.match(new RegExp(transformed));
}
}
// Non-matching
return false;
}
};
return _inst;
}];
}];
},{"../utils/parameters":8,"../utils/url-dictionary":9}],7:[function(require,module,exports){
'use strict';
var UrlDictionary = require('../utils/url-dictionary');
module.exports = ['$state', '$location', '$rootScope', function($state, $location, $rootScope) {
var _url = $location.url();
// Instance
var _self = {};
/**
* Update URL based on state
*/
var _update = function() {
var current = $state.current();
if(current && current.url) {
var path;
path = current.url;
// Add parameters or use default parameters
var params = current.params || {};
var query = {};
for(var name in params) {
var re = new RegExp(':'+name, 'g');
if(path.match(re)) {
path = path.replace(re, params[name]);
} else {
query[name] = params[name];
}
}
$location.path(path);
$location.search(query);
_url = $location.url();
}
};
/**
* Update url based on state
*/
_self.update = function() {
_update();
};
/**
* Detect URL change and dispatch state change
*/
_self.location = function() {
var lastUrl = _url;
var nextUrl = $location.url();
if(nextUrl !== lastUrl) {
_url = nextUrl;
$state.$location(_url);
$rootScope.$broadcast('$locationStateUpdate');
}
};
/**
* Register middleware layer
*/
_self.$ready = function() {
$state.$use(function(request, next) {
_update();
next();
});
};
return _self;
}];
},{"../utils/url-dictionary":9}],8:[function(require,module,exports){
'use strict';
// Parse Object literal name-value pairs
var reParseObjectLiteral = /([,{]\s*(("|')(.*?)\3|\w*)|(:\s*([+-]?(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?|true|false|null|("|')(.*?)\7|\[[^\]]*\])))/g;
// Match Strings
var reString = /^("|')(.*?)\1$/;
// TODO Add escaped string quotes \' and \" to string matcher
// Match Number (int/float/exponential)
var reNumber = /^[+-]?(?=\.\d|\d)(?:\d+)?(?:\.?\d*)(?:[eE][+-]?\d+)?$/;
/**
* Parse string value into Boolean/Number/Array/String/null.
*
* Strings are surrounded by a pair of matching quotes
*
* @param {String} value A String value to parse
* @return {Mixed} A Boolean/Number/Array/String/null
*/
var _resolveValue = function(value) {
// Boolean: true
if(value === 'true') {
return true;
// Boolean: false
} else if(value === 'false') {
return false;
// Null
} else if(value === 'null') {
return null;
// String
} else if(value.match(reString)) {
return value.substr(1, value.length-2);
// Number
} else if(value.match(reNumber)) {
return +value;
// NaN
} else if(value === 'NaN') {
return NaN;
// TODO add matching with Arrays and parse
}
// Unable to resolve
return value;
};
// Find values in an object literal
var _listify = function(str) {
// Trim
str = str.replace(/^\s*/, '').replace(/\s*$/, '');
if(str.match(/^\s*{.*}\s*$/) === null) {
throw new Error('Parameters expects an Object');
}
var sanitizeName = function(name) {
return name.replace(/^[\{,]?\s*["']?/, '').replace(/["']?\s*$/, '');
};
var sanitizeValue = function(value) {
var str = value.replace(/^(:)?\s*/, '').replace(/\s*$/, '');
return _resolveValue(str);
};
return str.match(reParseObjectLiteral).map(function(item, i, list) {
return i%2 === 0 ? sanitizeName(item) : sanitizeValue(item);
});
};
/**
* Create a params Object from string
*
* @param {String} str A stringified version of Object literal
*/
var Parameters = function(str) {
str = str || '';
// Instance
var _self = {};
_listify(str).forEach(function(item, i, list) {
if(i%2 === 0) {
_self[item] = list[i+1];
}
});
return _self;
};
module.exports = Parameters;
module.exports.resolveValue = _resolveValue;
module.exports.listify = _listify;
},{}],9:[function(require,module,exports){
'use strict';
var Url = require('./url');
/**
* Constructor
*/
function UrlDictionary() {
this._patterns = [];
this._refs = [];
this._params = [];
}
/**
* Associate a URL pattern with a reference
*
* @param {String} pattern A URL pattern
* @param {Object} ref A data Object
*/
UrlDictionary.prototype.add = function(pattern, ref) {
pattern = pattern || '';
var _self = this;
var i = this._patterns.length;
var pathChain;
var params = {};
if(pattern.indexOf('?') === -1) {
pathChain = Url(pattern).path().split('/');
} else {
pathChain = Url(pattern).path().split('/');
}
// Start
var searchExpr = '^';
// Items
(pathChain.forEach(function(chunk, i) {
if(i!==0) {
searchExpr += '\\/';
}
if(chunk[0] === ':') {
searchExpr += '[^\\/?]*';
params[chunk.substring(1)] = new RegExp(searchExpr);
} else {
searchExpr += chunk;
}
}));
// End
searchExpr += '[\\/]?$';
this._patterns[i] = new RegExp(searchExpr);
this._refs[i] = ref;
this._params[i] = params;
};
/**
* Find a reference according to a URL pattern and retrieve params defined in URL
*
* @param {String} url A URL to test for
* @param {Object} defaults A data Object of default parameter values
* @return {Object} A reference to a stored object
*/
UrlDictionary.prototype.lookup = function(url, defaults) {
url = url || '';
var p = Url(url).path();
var q = Url(url).queryparams();
var _self = this;
// Check dictionary
var _findPattern = function(check) {
check = check || '';
for(var i=_self._patterns.length-1; i>=0; i--) {
if(check.match(_self._patterns[i]) !== null) {
return i;
}
}
return -1;
};
var i = _findPattern(p);
// Matching pattern found
if(i !== -1) {
// Retrieve params in pattern match
var params = {};
for(var n in this._params[i]) {
var paramParser = this._params[i][n];
var urlMatch = (url.match(paramParser) || []).pop() || '';
var varMatch = urlMatch.split('/').pop();
params[n] = varMatch;
}
// Retrieve params in querystring match
params = angular.extend(q, params);
return {
url: url,
ref: this._refs[i],
params: params
};
// Not in dictionary
} else {
return null;
}
};
module.exports = UrlDictionary;
},{"./url":10}],10:[function(require,module,exports){
'use strict';
function Url(url) {
url = url || '';
// Instance
var _self = {
/**
* Get the path of a URL
*
* @return {String} A querystring from URL
*/
path: function() {
return url.indexOf('?') === -1 ? url : url.substring(0, url.indexOf('?'));
},
/**
* Get the querystring of a URL
*
* @return {String} A querystring from URL
*/
querystring: function() {
return url.indexOf('?') === -1 ? '' : url.substring(url.indexOf('?')+1);
},
/**
* Get the querystring of a URL parameters as a hash
*
* @return {String} A querystring from URL
*/
queryparams: function() {
var pairs = _self.querystring().split('&');
var params = {};
for(var i=0; i<pairs.length; i++) {
if(pairs[i] === '') continue;
var nameValue = pairs[i].split('=');
params[nameValue[0]] = (typeof nameValue[1] === 'undefined' || nameValue[1] === '') ? true : decodeURIComponent(nameValue[1]);
}
return params;
}
};
return _self;
}
module.exports = Url;
},{}]},{},[2])
//# sourceMappingURL=data:application/json;charset:utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIm5vZGVfbW9kdWxlcy9icm93c2VyLXBhY2svX3ByZWx1ZGUuanMiLCIvVXNlcnMvaGVucnkudHNlbmcvc3JjL2FuZ3VsYXItc3RhdGUtcm91dGVyL3NyYy9kaXJlY3RpdmVzL3NyZWYuanMiLCIvVXNlcnMvaGVucnkudHNlbmcvc3JjL2FuZ3VsYXItc3RhdGUtcm91dGVyL3NyYy9pbmRleC5qcyIsIi9Vc2Vycy9oZW5yeS50c2VuZy9zcmMvYW5ndWxhci1zdGF0ZS1yb3V0ZXIvc3JjL3NlcnZpY2VzL2VuYWN0LmpzIiwiL1VzZXJzL2hlbnJ5LnRzZW5nL3NyYy9hbmd1bGFyLXN0YXRlLXJvdXRlci9zcmMvc2VydmljZXMvcXVldWUtaGFuZGxlci5qcyIsIi9Vc2Vycy9oZW5yeS50c2VuZy9zcmMvYW5ndWxhci1zdGF0ZS1yb3V0ZXIvc3JjL3NlcnZpY2VzL3Jlc29sdXRpb24uanMiLCIvVXNlcnMvaGVucnkudHNlbmcvc3JjL2FuZ3VsYXItc3RhdGUtcm91dGVyL3NyYy9zZXJ2aWNlcy9zdGF0ZS1yb3V0ZXIuanMiLCIvVXNlcnMvaGVucnkudHNlbmcvc3JjL2FuZ3VsYXItc3RhdGUtcm91dGVyL3NyYy9zZXJ2aWNlcy91cmwtbWFuYWdlci5qcyIsIi9Vc2Vycy9oZW5yeS50c2VuZy9zcmMvYW5ndWxhci1zdGF0ZS1yb3V0ZXIvc3JjL3V0aWxzL3BhcmFtZXRlcnMuanMiLCIvVXNlcnMvaGVucnkudHNlbmcvc3JjL2FuZ3VsYXItc3RhdGUtcm91dGVyL3NyYy91dGlscy91cmwtZGljdGlvbmFyeS5qcyIsIi9Vc2Vycy9oZW5yeS50c2VuZy9zcmMvYW5ndWxhci1zdGF0ZS1yb3V0ZXIvc3JjL3V0aWxzL3VybC5qcyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTtBQ0FBOztBQUVBLE9BQU8sVUFBVSxDQUFDLFVBQVUsVUFBVSxRQUFRO0VBQzVDLE9BQU87SUFDTCxVQUFVO0lBQ1YsT0FBTzs7SUFFUCxNQUFNLFNBQVMsT0FBTyxTQUFTLE9BQU87TUFD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O0VBRWpCLElBQUksUUFBUTs7O0VBR1osSUFBSSxlQUFlLFNBQVMsT0FBTztJQUNqQyxRQUFRLFNBQVM7SUFDakIsSUFBSSxJQUFJLEVBQUUsTUFBTSxVQUFVLE9BQU8sR0FBRyxHQUFHLEdBQUcsS0FBSztNQUM3QyxHQUFHLE1BQU0sTUFBTSxNQUFNLFVBQVUsUUFBUSxNQUFNO1FBQzNDLE9BQU87OztJQUdYLE9BQU8sQ0FBQzs7O0VBR1YsSUFBSSxJQUFJLGFBQWE7OztFQUdyQixHQUFHLE1BQU0sQ0FBQyxHQUFHOzs7SUFHWCxJQUFJLFNBQVM7SUFDYixJQUFJLElBQUksS0FBSyxLQUFLLFFBQVEsSUFBSTtNQUM1QixJQUFJLGNBQWMsS0FBSyxRQUFRLEdBQUc7TUFDbEMsSUFBSSxXQUFXLENBQUMsSUFBSSxNQUFNLGdCQUFnQixJQUFJLFNBQVM7TUFDdkQsSUFBSSxXQUFXLFNBQVMsTUFBTSxLQUFLO01BQ25DLE9BQU8sS0FBSzs7OztJQUlkLFNBQVMsUUFBUSxPQUFPLEdBQUc7O0lBRTNCLE9BQU87TUFDTCxLQUFLO01BQ0wsS0FBSyxLQUFLLE1BQU07TUFDaEIsUUFBUTs7OztTQUlMO0lBQ0wsT0FBTzs7OztBQUlYLE9BQU8sVUFBVTtBQUNqQjs7QUNuSEE7O0FBRUEsU0FBUyxJQUFJLEtBQUs7RUFDaEIsTUFBTSxPQUFPOzs7RUFHYixJQUFJLFFBQVE7Ozs7Ozs7SUFPVixNQUFNLFdBQVc7TUFDZixPQUFPLElBQUksUUFBUSxTQUFTLENBQUMsSUFBSSxNQUFNLElBQUksVUFBVSxHQUFHLElBQUksUUFBUTs7Ozs7Ozs7SUFRdEUsYUFBYSxXQUFXO01BQ3RCLE9BQU8sSUFBSSxRQUFRLFNBQVMsQ0FBQyxJQUFJLEtBQUssSUFBSSxVQUFVLElBQUksUUFBUSxLQUFLOzs7Ozs7OztJQVF2RSxhQUFhLFdBQVc7TUFDdEIsSUFBSSxRQUFRLE1BQU0sY0FBYyxNQUFNO01BQ3RDLElBQUksU0FBUzs7TUFFYixJQUFJLElBQUksRUFBRSxHQUFHLEVBQUUsTUFBTSxRQUFRLEtBQUs7UUFDaEMsR0FBRyxNQUFNLE9BQU8sSUFBSTtRQUNwQixJQUFJLFlBQVksTUFBTSxHQUFHLE1BQU07UUFDL0IsT0FBTyxVQUFVLE1BQU0sQ0FBQyxPQUFPLFVBQVUsT0FBTyxlQUFlLFVBQVUsT0FBTyxNQUFNLE9BQU8sbUJBQW1CLFVBQVU7OztNQUc1SCxPQUFPOzs7O0VBSVgsT0FBTzs7O0FBR1QsT0FBTyxVQUFVO0FBQ2pCIiwiZmlsZSI6ImdlbmVyYXRlZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzQ29udGVudCI6WyIoZnVuY3Rpb24gZSh0LG4scil7ZnVuY3Rpb24gcyhvLHUpe2lmKCFuW29dKXtpZigh