mirror of
https://github.com/Athou/commafeed.git
synced 2026-03-21 21:37:29 +00:00
use ui-router instead of routeProvider
This commit is contained in:
@@ -5,29 +5,8 @@
|
||||
</head>
|
||||
<body>
|
||||
<div ng-app="commafeed" id="main" class="main">
|
||||
<div class="container-fluid">
|
||||
<div class="row-fluid">
|
||||
<div class="span2 sidebar-nav-fixed">
|
||||
<subscribe></subscribe>
|
||||
<div class="css-treeview" ng-controller="CategoryTreeCtrl">
|
||||
<ul>
|
||||
<category node="SubscriptionService.subscriptions" feed-click="feedClicked(id)"
|
||||
category-click="categoryClicked(id)" selected-type="selectedType"
|
||||
selected-id="selectedId"
|
||||
format-category-name="formatCategoryName(category)"
|
||||
format-feed-name="formatFeedName(feed)"> </category>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="span10">
|
||||
<div class="toolbar">
|
||||
<toolbar></toolbar>
|
||||
</div>
|
||||
<div class="entryList">
|
||||
<ng:view></ng:view>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div ui-view>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.commafeed.frontend.references.angular.AngularResourceReference;
|
||||
import com.commafeed.frontend.references.angular.AngularSanitizeReference;
|
||||
import com.commafeed.frontend.references.angularui.AngularUIReference;
|
||||
import com.commafeed.frontend.references.angularuibootstrap.AngularUIBootstrapReference;
|
||||
import com.commafeed.frontend.references.angularuistate.AngularUIStateReference;
|
||||
import com.commafeed.frontend.references.csstreeview.CssTreeViewReference;
|
||||
import com.commafeed.frontend.references.mousetrap.MouseTrapReference;
|
||||
import com.commafeed.frontend.references.nginfinitescroll.NGInfiniteScrollReference;
|
||||
@@ -30,6 +31,7 @@ public class HomePage extends BasePage {
|
||||
AngularSanitizeReference.renderHead(response);
|
||||
AngularUIReference.renderHead(response);
|
||||
AngularUIBootstrapReference.renderHead(response);
|
||||
AngularUIStateReference.renderHead(response);
|
||||
NGUploadReference.renderHead(response);
|
||||
NGInfiniteScrollReference.renderHead(response);
|
||||
Select2Reference.renderHead(response);
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.commafeed.frontend.references.angularuistate;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.apache.wicket.markup.head.HeaderItem;
|
||||
import org.apache.wicket.markup.head.IHeaderResponse;
|
||||
import org.apache.wicket.markup.head.JavaScriptHeaderItem;
|
||||
import org.apache.wicket.request.resource.JavaScriptResourceReference;
|
||||
|
||||
import com.commafeed.frontend.references.angular.AngularReference;
|
||||
|
||||
public class AngularUIStateReference extends JavaScriptResourceReference {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
public static final AngularUIStateReference INSTANCE = new AngularUIStateReference();
|
||||
|
||||
private AngularUIStateReference() {
|
||||
super(AngularUIStateReference.class, "angular-ui-states.js");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends HeaderItem> getDependencies() {
|
||||
return Arrays.asList(JavaScriptHeaderItem
|
||||
.forReference(AngularReference.INSTANCE));
|
||||
}
|
||||
|
||||
public static void renderHead(final IHeaderResponse response) {
|
||||
response.render(JavaScriptHeaderItem.forReference(INSTANCE));
|
||||
}
|
||||
}
|
||||
995
src/main/java/com/commafeed/frontend/references/angularuistate/angular-ui-states.js
vendored
Normal file
995
src/main/java/com/commafeed/frontend/references/angularuistate/angular-ui-states.js
vendored
Normal file
@@ -0,0 +1,995 @@
|
||||
/*jshint globalstrict:true*/
|
||||
/*global angular:false*/
|
||||
'use strict';
|
||||
|
||||
var isDefined = angular.isDefined,
|
||||
isFunction = angular.isFunction,
|
||||
isString = angular.isString,
|
||||
isObject = angular.isObject,
|
||||
isArray = angular.isArray,
|
||||
forEach = angular.forEach,
|
||||
extend = angular.extend,
|
||||
copy = angular.copy;
|
||||
|
||||
function inherit(parent, extra) {
|
||||
return extend(new (extend(function() {}, { prototype: parent }))(), extra);
|
||||
}
|
||||
|
||||
/**
|
||||
* Extends the destination object `dst` by copying all of the properties from the `src` object(s)
|
||||
* to `dst` if the `dst` object has no own property of the same name. You can specify multiple
|
||||
* `src` objects.
|
||||
*
|
||||
* @param {Object} dst Destination object.
|
||||
* @param {...Object} src Source object(s).
|
||||
* @see angular.extend
|
||||
*/
|
||||
function merge(dst) {
|
||||
forEach(arguments, function(obj) {
|
||||
if (obj !== dst) {
|
||||
forEach(obj, function(value, key) {
|
||||
if (!dst.hasOwnProperty(key)) dst[key] = value;
|
||||
});
|
||||
}
|
||||
});
|
||||
return dst;
|
||||
}
|
||||
|
||||
angular.module('ui.util', ['ng']);
|
||||
angular.module('ui.router', ['ui.util']);
|
||||
angular.module('ui.state', ['ui.router', 'ui.util']);
|
||||
angular.module('ui.compat', ['ui.state']);
|
||||
|
||||
/**
|
||||
* Service. Manages loading of templates.
|
||||
* @constructor
|
||||
* @name $templateFactory
|
||||
* @requires $http
|
||||
* @requires $templateCache
|
||||
* @requires $injector
|
||||
*/
|
||||
$TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
|
||||
function $TemplateFactory( $http, $templateCache, $injector) {
|
||||
|
||||
/**
|
||||
* Creates a template from a configuration object.
|
||||
* @function
|
||||
* @name $templateFactory#fromConfig
|
||||
* @methodOf $templateFactory
|
||||
* @param {Object} config Configuration object for which to load a template. The following
|
||||
* properties are search in the specified order, and the first one that is defined is
|
||||
* used to create the template:
|
||||
* @param {string|Function} config.template html string template or function to load via
|
||||
* {@link $templateFactory#fromString fromString}.
|
||||
* @param {string|Function} config.templateUrl url to load or a function returning the url
|
||||
* to load via {@link $templateFactory#fromUrl fromUrl}.
|
||||
* @param {Function} config.templateProvider function to invoke via
|
||||
* {@link $templateFactory#fromProvider fromProvider}.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @param {Object} [locals] Locals to pass to `invoke` if the template is loaded via a
|
||||
* `templateProvider`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string,
|
||||
* or `null` if no template is configured.
|
||||
*/
|
||||
this.fromConfig = function (config, params, locals) {
|
||||
return (
|
||||
isDefined(config.template) ? this.fromString(config.template, params) :
|
||||
isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
|
||||
isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template from a string or a function returning a string.
|
||||
* @function
|
||||
* @name $templateFactory#fromString
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} template html template as a string or function that returns an html
|
||||
* template as a string.
|
||||
* @param {Object} params Parameters to pass to the template function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromString = function (template, params) {
|
||||
return isFunction(template) ? template(params) : template;
|
||||
};
|
||||
|
||||
/**
|
||||
* Loads a template from the a URL via `$http` and `$templateCache`.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {string|Function} url url of the template to load, or a function that returns a url.
|
||||
* @param {Object} params Parameters to pass to the url function.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromUrl = function (url, params) {
|
||||
if (isFunction(url)) url = url(params);
|
||||
if (url == null) return null;
|
||||
else return $http
|
||||
.get(url, { cache: $templateCache })
|
||||
.then(function(response) { return response.data; });
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a template by invoking an injectable provider function.
|
||||
* @function
|
||||
* @name $templateFactory#fromUrl
|
||||
* @methodOf $templateFactory
|
||||
* @param {Function} provider Function to invoke via `$injector.invoke`
|
||||
* @param {Object} params Parameters for the template.
|
||||
* @param {Object} [locals] Locals to pass to `invoke`. Defaults to `{ params: params }`.
|
||||
* @return {string|Promise.<string>} The template html as a string, or a promise for that string.
|
||||
*/
|
||||
this.fromProvider = function (provider, params, locals) {
|
||||
return $injector.invoke(provider, null, locals || { params: params });
|
||||
};
|
||||
}
|
||||
|
||||
angular.module('ui.util').service('$templateFactory', $TemplateFactory);
|
||||
|
||||
/**
|
||||
* Matches URLs against patterns and extracts named parameters from the path or the search
|
||||
* part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
|
||||
* of search parameters. Multiple search parameter names are separated by '&'. Search parameters
|
||||
* do not influence whether or not a URL is matched, but their values are passed through into
|
||||
* the matched parameters returned by {@link UrlMatcher#exec exec}.
|
||||
*
|
||||
* Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
|
||||
* syntax, which optionally allows a regular expression for the parameter to be specified:
|
||||
*
|
||||
* * ':' name - colon placeholder
|
||||
* * '*' name - catch-all placeholder
|
||||
* * '{' name '}' - curly placeholder
|
||||
* * '{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain
|
||||
* curly braces, they must be in matched pairs or escaped with a backslash.
|
||||
*
|
||||
* Parameter names may contain only word characters (latin letters, digits, and underscore) and
|
||||
* must be unique within the pattern (across both path and search parameters). For colon
|
||||
* placeholders or curly placeholders without an explicit regexp, a path parameter matches any
|
||||
* number of characters other than '/'. For catch-all placeholders the path parameter matches
|
||||
* any number of characters.
|
||||
*
|
||||
* ### Examples
|
||||
*
|
||||
* * '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for
|
||||
* trailing slashes, and patterns have to match the entire path, not just a prefix.
|
||||
* * '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
|
||||
* '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
|
||||
* * '/user/{id}' - Same as the previous example, but using curly brace syntax.
|
||||
* * '/user/{id:[^/]*}' - Same as the previous example.
|
||||
* * '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id
|
||||
* parameter consists of 1 to 8 hex digits.
|
||||
* * '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the
|
||||
* path into the parameter 'path'.
|
||||
* * '/files/*path' - ditto.
|
||||
*
|
||||
* @constructor
|
||||
* @param {string} pattern the pattern to compile into a matcher.
|
||||
*
|
||||
* @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
|
||||
* URL matching this matcher (i.e. any string for which {@link UrlMatcher#exec exec()} returns
|
||||
* non-null) will start with this prefix.
|
||||
*/
|
||||
function UrlMatcher(pattern) {
|
||||
|
||||
// Find all placeholders and create a compiled pattern, using either classic or curly syntax:
|
||||
// '*' name
|
||||
// ':' name
|
||||
// '{' name '}'
|
||||
// '{' name ':' regexp '}'
|
||||
// The regular expression is somewhat complicated due to the need to allow curly braces
|
||||
// inside the regular expression. The placeholder regexp breaks down as follows:
|
||||
// ([:*])(\w+) classic placeholder ($1 / $2)
|
||||
// \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
|
||||
// (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
|
||||
// [^{}\\]+ - anything other than curly braces or backslash
|
||||
// \\. - a backslash escape
|
||||
// \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
|
||||
var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
|
||||
names = {}, compiled = '^', last = 0, m,
|
||||
segments = this.segments = [],
|
||||
params = this.params = [];
|
||||
|
||||
function addParameter(id) {
|
||||
if (!/^\w+$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
|
||||
names[id] = true;
|
||||
params.push(id);
|
||||
}
|
||||
|
||||
function quoteRegExp(string) {
|
||||
return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
|
||||
}
|
||||
|
||||
this.source = pattern;
|
||||
|
||||
// Split into static segments separated by path parameter placeholders.
|
||||
// The number of segments is always 1 more than the number of parameters.
|
||||
var id, regexp, segment;
|
||||
while ((m = placeholder.exec(pattern))) {
|
||||
id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
|
||||
regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
|
||||
segment = pattern.substring(last, m.index);
|
||||
if (segment.indexOf('?') >= 0) break; // we're into the search part
|
||||
compiled += quoteRegExp(segment) + '(' + regexp + ')';
|
||||
addParameter(id);
|
||||
segments.push(segment);
|
||||
last = placeholder.lastIndex;
|
||||
}
|
||||
segment = pattern.substring(last);
|
||||
|
||||
// Find any search parameter names and remove them from the last segment
|
||||
var i = segment.indexOf('?');
|
||||
if (i >= 0) {
|
||||
var search = this.sourceSearch = segment.substring(i);
|
||||
segment = segment.substring(0, i);
|
||||
this.sourcePath = pattern.substring(0, last+i);
|
||||
|
||||
// Allow parameters to be separated by '?' as well as '&' to make concat() easier
|
||||
forEach(search.substring(1).split(/[&?]/), addParameter);
|
||||
} else {
|
||||
this.sourcePath = pattern;
|
||||
this.sourceSearch = '';
|
||||
}
|
||||
|
||||
compiled += quoteRegExp(segment) + '$';
|
||||
segments.push(segment);
|
||||
this.regexp = new RegExp(compiled);
|
||||
this.prefix = segments[0];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new matcher for a pattern constructed by appending the path part and adding the
|
||||
* search parameters of the specified pattern to this pattern. The current pattern is not
|
||||
* modified. This can be understood as creating a pattern for URLs that are relative to (or
|
||||
* suffixes of) the current pattern.
|
||||
*
|
||||
* ### Example
|
||||
* The following two matchers are equivalent:
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').concat('/details?date');
|
||||
* new UrlMatcher('/user/{id}/details?q&date');
|
||||
* ```
|
||||
*
|
||||
* @param {string} pattern The pattern to append.
|
||||
* @return {UrlMatcher} A matcher for the concatenated pattern.
|
||||
*/
|
||||
UrlMatcher.prototype.concat = function (pattern) {
|
||||
// Because order of search parameters is irrelevant, we can add our own search
|
||||
// parameters to the end of the new pattern. Parse the new pattern by itself
|
||||
// and then join the bits together, but it's much easier to do this on a string level.
|
||||
return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch);
|
||||
};
|
||||
|
||||
UrlMatcher.prototype.toString = function () {
|
||||
return this.source;
|
||||
};
|
||||
|
||||
/**
|
||||
* Tests the specified path against this matcher, and returns an object containing the captured
|
||||
* parameter values, or null if the path does not match. The returned object contains the values
|
||||
* of any search parameters that are mentioned in the pattern, but their value may be null if
|
||||
* they are not present in `searchParams`. This means that search parameters are always treated
|
||||
* as optional.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
|
||||
* // returns { id:'bob', q:'hello', r:null }
|
||||
* ```
|
||||
*
|
||||
* @param {string} path The URL path to match, e.g. `$location.path()`.
|
||||
* @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
|
||||
* @return {Object} The captured parameter values.
|
||||
*/
|
||||
UrlMatcher.prototype.exec = function (path, searchParams) {
|
||||
var m = this.regexp.exec(path);
|
||||
if (!m) return null;
|
||||
|
||||
var params = this.params, nTotal = params.length,
|
||||
nPath = this.segments.length-1,
|
||||
values = {}, i;
|
||||
|
||||
for (i=0; i<nPath; i++) values[params[i]] = decodeURIComponent(m[i+1]);
|
||||
for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
|
||||
|
||||
return values;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the names of all path and search parameters of this pattern in an unspecified order.
|
||||
* @return {Array.<string>} An array of parameter names. Must be treated as read-only. If the
|
||||
* pattern has no parameters, an empty array is returned.
|
||||
*/
|
||||
UrlMatcher.prototype.parameters = function () {
|
||||
return this.params;
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates a URL that matches this pattern by substituting the specified values
|
||||
* for the path and search parameters. Null values for path parameters are
|
||||
* treated as empty strings.
|
||||
*
|
||||
* ### Example
|
||||
* ```
|
||||
* new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
|
||||
* // returns '/user/bob?q=yes'
|
||||
* ```
|
||||
*
|
||||
* @param {Object} values the values to substitute for the parameters in this pattern.
|
||||
* @return {string} the formatted URL (path and optionally search part).
|
||||
*/
|
||||
UrlMatcher.prototype.format = function (values) {
|
||||
var segments = this.segments, params = this.params;
|
||||
if (!values) return segments.join('');
|
||||
|
||||
var nPath = segments.length-1, nTotal = params.length,
|
||||
result = segments[0], i, search, value;
|
||||
|
||||
for (i=0; i<nPath; i++) {
|
||||
value = values[params[i]];
|
||||
// TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
|
||||
if (value != null) result += value;
|
||||
result += segments[i+1];
|
||||
}
|
||||
for (/**/; i<nTotal; i++) {
|
||||
value = values[params[i]];
|
||||
if (value != null) {
|
||||
result += (search ? '&' : '?') + params[i] + '=' + encodeURIComponent(value);
|
||||
search = true;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
/**
|
||||
* Service. Factory for {@link UrlMatcher} instances. The factory is also available to providers
|
||||
* under the name `$urlMatcherFactoryProvider`.
|
||||
* @constructor
|
||||
* @name $urlMatcherFactory
|
||||
*/
|
||||
function $UrlMatcherFactory() {
|
||||
/**
|
||||
* Creates a {@link UrlMatcher} for the specified pattern.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#compile
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {string} pattern The URL pattern.
|
||||
* @return {UrlMatcher} The UrlMatcher.
|
||||
*/
|
||||
this.compile = function (pattern) {
|
||||
return new UrlMatcher(pattern);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns true if the specified object is a UrlMatcher, or false otherwise.
|
||||
* @function
|
||||
* @name $urlMatcherFactory#isMatcher
|
||||
* @methodOf $urlMatcherFactory
|
||||
* @param {Object} o
|
||||
* @return {boolean}
|
||||
*/
|
||||
this.isMatcher = function (o) {
|
||||
return o instanceof UrlMatcher;
|
||||
};
|
||||
|
||||
this.$get = function () {
|
||||
return this;
|
||||
};
|
||||
}
|
||||
|
||||
// Register as a provider so it's available to other providers
|
||||
angular.module('ui.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
|
||||
|
||||
|
||||
$UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider'];
|
||||
function $UrlRouterProvider( $urlMatcherFactory) {
|
||||
var rules = [], otherwise = null;
|
||||
|
||||
// Returns a string that is a prefix of all strings matching the RegExp
|
||||
function regExpPrefix(re) {
|
||||
var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
|
||||
return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
|
||||
}
|
||||
|
||||
// Interpolates matched values into a String.replace()-style pattern
|
||||
function interpolate(pattern, match) {
|
||||
return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
|
||||
return match[what === '$' ? 0 : Number(what)];
|
||||
});
|
||||
}
|
||||
|
||||
this.rule =
|
||||
function (rule) {
|
||||
if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
rules.push(rule);
|
||||
return this;
|
||||
};
|
||||
|
||||
this.otherwise =
|
||||
function (rule) {
|
||||
if (isString(rule)) {
|
||||
var redirect = rule;
|
||||
rule = function () { return redirect; };
|
||||
}
|
||||
else if (!isFunction(rule)) throw new Error("'rule' must be a function");
|
||||
otherwise = rule;
|
||||
return this;
|
||||
};
|
||||
|
||||
|
||||
function handleIfMatch($location, handler, match) {
|
||||
if (!match) return false;
|
||||
var result = handler(match, $location);
|
||||
return isDefined(result) ? result : true;
|
||||
}
|
||||
|
||||
this.when =
|
||||
function (what, handler) {
|
||||
var rule, redirect;
|
||||
if (isString(what)) what = $urlMatcherFactory.compile(what);
|
||||
if ($urlMatcherFactory.isMatcher(what)) {
|
||||
if (isString(handler)) {
|
||||
redirect = $urlMatcherFactory.compile(handler);
|
||||
handler = function (match) { return redirect.format(match); };
|
||||
}
|
||||
else if (!isFunction(handler)) throw new Error("invalid 'handler' in when()");
|
||||
rule = function ($location) {
|
||||
return handleIfMatch($location, handler, what.exec($location.path(), $location.search()));
|
||||
};
|
||||
rule.prefix = isString(what.prefix) ? what.prefix : '';
|
||||
}
|
||||
else if (what instanceof RegExp) {
|
||||
if (isString(handler)) {
|
||||
redirect = handler;
|
||||
handler = function (match) { return interpolate(redirect, match); };
|
||||
}
|
||||
else if (!isFunction(handler)) throw new Error("invalid 'handler' in when()");
|
||||
if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
|
||||
rule = function ($location) {
|
||||
return handleIfMatch($location, handler, what.exec($location.path()));
|
||||
};
|
||||
rule.prefix = regExpPrefix(what);
|
||||
}
|
||||
else throw new Error("invalid 'what' in when()");
|
||||
return this.rule(rule);
|
||||
};
|
||||
|
||||
this.$get =
|
||||
[ '$location', '$rootScope',
|
||||
function ($location, $rootScope) {
|
||||
if (otherwise) rules.push(otherwise);
|
||||
|
||||
// TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
|
||||
function update() {
|
||||
var n=rules.length, i, handled;
|
||||
for (i=0; i<n; i++) {
|
||||
handled = rules[i]($location);
|
||||
if (handled) {
|
||||
if (isString(handled)) $location.replace().url(handled);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$rootScope.$on('$locationChangeSuccess', update);
|
||||
return {};
|
||||
}];
|
||||
}
|
||||
|
||||
angular.module('ui.router').provider('$urlRouter', $UrlRouterProvider);
|
||||
|
||||
$StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider'];
|
||||
function $StateProvider( $urlRouterProvider, $urlMatcherFactory) {
|
||||
|
||||
var root, states = {}, $state;
|
||||
|
||||
function findState(stateOrName) {
|
||||
var state;
|
||||
if (isString(stateOrName)) {
|
||||
state = states[stateOrName];
|
||||
if (!state) throw new Error("No such state '" + stateOrName + "'");
|
||||
} else {
|
||||
state = states[stateOrName.name];
|
||||
if (!state || state !== stateOrName && state.self !== stateOrName)
|
||||
throw new Error("Invalid or unregistered state");
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
function registerState(state) {
|
||||
// Wrap a new object around the state so we can store our private details easily.
|
||||
state = inherit(state, {
|
||||
self: state,
|
||||
toString: function () { return this.name; }
|
||||
});
|
||||
|
||||
var name = state.name;
|
||||
if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
|
||||
if (states[name]) throw new Error("State '" + name + "'' is already defined");
|
||||
|
||||
// Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
|
||||
var parent = root;
|
||||
if (!isDefined(state.parent)) {
|
||||
// regex matches any valid composite state name
|
||||
// would match "contact.list" but not "contacts"
|
||||
var compositeName = /^(.+)\.[^.]+$/.exec(name);
|
||||
if (compositeName != null) {
|
||||
parent = findState(compositeName[1]);
|
||||
}
|
||||
} else if (state.parent != null) {
|
||||
parent = findState(state.parent);
|
||||
}
|
||||
state.parent = parent;
|
||||
// state.children = [];
|
||||
// if (parent) parent.children.push(state);
|
||||
|
||||
// Build a URLMatcher if necessary, either via a relative or absolute URL
|
||||
var url = state.url;
|
||||
if (isString(url)) {
|
||||
if (url.charAt(0) == '^') {
|
||||
url = state.url = $urlMatcherFactory.compile(url.substring(1));
|
||||
} else {
|
||||
url = state.url = (parent.navigable || root).url.concat(url);
|
||||
}
|
||||
} else if (isObject(url) &&
|
||||
isFunction(url.exec) && isFunction(url.format) && isFunction(url.concat)) {
|
||||
/* use UrlMatcher (or compatible object) as is */
|
||||
} else if (url != null) {
|
||||
throw new Error("Invalid url '" + url + "' in state '" + state + "'");
|
||||
}
|
||||
|
||||
// Keep track of the closest ancestor state that has a URL (i.e. is navigable)
|
||||
state.navigable = url ? state : parent ? parent.navigable : null;
|
||||
|
||||
// Derive parameters for this state and ensure they're a super-set of parent's parameters
|
||||
var params = state.params;
|
||||
if (params) {
|
||||
if (!isArray(params)) throw new Error("Invalid params in state '" + state + "'");
|
||||
if (url) throw new Error("Both params and url specicified in state '" + state + "'");
|
||||
} else {
|
||||
params = state.params = url ? url.parameters() : state.parent.params;
|
||||
}
|
||||
|
||||
var paramNames = {}; forEach(params, function (p) { paramNames[p] = true; });
|
||||
if (parent) {
|
||||
forEach(parent.params, function (p) {
|
||||
if (!paramNames[p]) {
|
||||
throw new Error("Missing required parameter '" + p + "' in state '" + name + "'");
|
||||
}
|
||||
paramNames[p] = false;
|
||||
});
|
||||
|
||||
var ownParams = state.ownParams = [];
|
||||
forEach(paramNames, function (own, p) {
|
||||
if (own) ownParams.push(p);
|
||||
});
|
||||
} else {
|
||||
state.ownParams = params;
|
||||
}
|
||||
|
||||
// If there is no explicit multi-view configuration, make one up so we don't have
|
||||
// to handle both cases in the view directive later. Note that having an explicit
|
||||
// 'views' property will mean the default unnamed view properties are ignored. This
|
||||
// is also a good time to resolve view names to absolute names, so everything is a
|
||||
// straight lookup at link time.
|
||||
var views = {};
|
||||
forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
|
||||
if (name.indexOf('@') < 0) name = name + '@' + state.parent.name;
|
||||
views[name] = view;
|
||||
});
|
||||
state.views = views;
|
||||
|
||||
// Keep a full path from the root down to this state as this is needed for state activation.
|
||||
state.path = parent ? parent.path.concat(state) : []; // exclude root from path
|
||||
|
||||
// Speed up $state.contains() as it's used a lot
|
||||
var includes = state.includes = parent ? extend({}, parent.includes) : {};
|
||||
includes[name] = true;
|
||||
|
||||
if (!state.resolve) state.resolve = {}; // prevent null checks later
|
||||
|
||||
// Register the state in the global state list and with $urlRouter if necessary.
|
||||
if (!state.abstract && url) {
|
||||
$urlRouterProvider.when(url, function (params) {
|
||||
$state.transitionTo(state, params, false);
|
||||
});
|
||||
}
|
||||
states[name] = state;
|
||||
return state;
|
||||
}
|
||||
|
||||
// Implicit root state that is always active
|
||||
root = registerState({
|
||||
name: '',
|
||||
url: '^',
|
||||
views: null,
|
||||
abstract: true
|
||||
});
|
||||
root.locals = { globals: { $stateParams: {} } };
|
||||
root.navigable = null;
|
||||
|
||||
|
||||
// .state(state)
|
||||
// .state(name, state)
|
||||
this.state = state;
|
||||
function state(name, definition) {
|
||||
/*jshint validthis: true */
|
||||
if (isObject(name)) definition = name;
|
||||
else definition.name = name;
|
||||
registerState(definition);
|
||||
return this;
|
||||
}
|
||||
|
||||
// $urlRouter is injected just to ensure it gets instantiated
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$rootScope', '$q', '$templateFactory', '$injector', '$stateParams', '$location', '$urlRouter'];
|
||||
function $get( $rootScope, $q, $templateFactory, $injector, $stateParams, $location, $urlRouter) {
|
||||
|
||||
var TransitionSuperseded = $q.reject(new Error('transition superseded'));
|
||||
var TransitionPrevented = $q.reject(new Error('transition prevented'));
|
||||
|
||||
$state = {
|
||||
params: {},
|
||||
current: root.self,
|
||||
$current: root,
|
||||
transition: null
|
||||
};
|
||||
|
||||
// $state.go = function go(to, params) {
|
||||
// };
|
||||
|
||||
$state.transitionTo = function transitionTo(to, toParams, updateLocation) {
|
||||
if (!isDefined(updateLocation)) updateLocation = true;
|
||||
|
||||
to = findState(to);
|
||||
if (to.abstract) throw new Error("Cannot transition to abstract state '" + to + "'");
|
||||
var toPath = to.path,
|
||||
from = $state.$current, fromParams = $state.params, fromPath = from.path;
|
||||
|
||||
// Starting from the root of the path, keep all levels that haven't changed
|
||||
var keep, state, locals = root.locals, toLocals = [];
|
||||
for (keep = 0, state = toPath[keep];
|
||||
state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams);
|
||||
keep++, state = toPath[keep]) {
|
||||
locals = toLocals[keep] = state.locals;
|
||||
}
|
||||
|
||||
// If we're going to the same state and all locals are kept, we've got nothing to do.
|
||||
// But clear 'transition', as we still want to cancel any other pending transitions.
|
||||
// TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
|
||||
// because we might accidentally abort a legitimate transition initiated from code?
|
||||
if (to === from && locals === from.locals) {
|
||||
$state.transition = null;
|
||||
return $q.when($state.current);
|
||||
}
|
||||
|
||||
// Normalize/filter parameters before we pass them to event handlers etc.
|
||||
var normalizedToParams = {};
|
||||
forEach(to.params, function (name) {
|
||||
var value = toParams[name];
|
||||
normalizedToParams[name] = (value != null) ? String(value) : null;
|
||||
});
|
||||
toParams = normalizedToParams;
|
||||
|
||||
// Broadcast start event and cancel the transition if requested
|
||||
if ($rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams)
|
||||
.defaultPrevented) return TransitionPrevented;
|
||||
|
||||
// Resolve locals for the remaining states, but don't update any global state just
|
||||
// yet -- if anything fails to resolve the current state needs to remain untouched.
|
||||
// We also set up an inheritance chain for the locals here. This allows the view directive
|
||||
// to quickly look up the correct definition for each view in the current state. Even
|
||||
// though we create the locals object itself outside resolveState(), it is initially
|
||||
// empty and gets filled asynchronously. We need to keep track of the promise for the
|
||||
// (fully resolved) current locals, and pass this down the chain.
|
||||
var resolved = $q.when(locals);
|
||||
for (var l=keep; l<toPath.length; l++, state=toPath[l]) {
|
||||
locals = toLocals[l] = inherit(locals);
|
||||
resolved = resolveState(state, toParams, state===to, resolved, locals);
|
||||
}
|
||||
|
||||
// Once everything is resolved, we are ready to perform the actual transition
|
||||
// and return a promise for the new state. We also keep track of what the
|
||||
// current promise is, so that we can detect overlapping transitions and
|
||||
// keep only the outcome of the last transition.
|
||||
var transition = $state.transition = resolved.then(function () {
|
||||
var l, entering, exiting;
|
||||
|
||||
if ($state.transition !== transition) return TransitionSuperseded;
|
||||
|
||||
// Exit 'from' states not kept
|
||||
for (l=fromPath.length-1; l>=keep; l--) {
|
||||
exiting = fromPath[l];
|
||||
if (exiting.self.onExit) {
|
||||
$injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
|
||||
}
|
||||
exiting.locals = null;
|
||||
}
|
||||
|
||||
// Enter 'to' states not kept
|
||||
for (l=keep; l<toPath.length; l++) {
|
||||
entering = toPath[l];
|
||||
entering.locals = toLocals[l];
|
||||
if (entering.self.onEnter) {
|
||||
$injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
|
||||
}
|
||||
}
|
||||
|
||||
// Update globals in $state
|
||||
$state.$current = to;
|
||||
$state.current = to.self;
|
||||
$state.params = toParams;
|
||||
copy($state.params, $stateParams);
|
||||
$state.transition = null;
|
||||
|
||||
// Update $location
|
||||
var toNav = to.navigable;
|
||||
if (updateLocation && toNav) {
|
||||
$location.url(toNav.url.format(toNav.locals.globals.$stateParams));
|
||||
}
|
||||
|
||||
$rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
|
||||
|
||||
return $state.current;
|
||||
}, function (error) {
|
||||
if ($state.transition !== transition) return TransitionSuperseded;
|
||||
|
||||
$state.transition = null;
|
||||
$rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
|
||||
|
||||
return $q.reject(error);
|
||||
});
|
||||
|
||||
return transition;
|
||||
};
|
||||
|
||||
$state.is = function (stateOrName) {
|
||||
return $state.$current === findState(stateOrName);
|
||||
};
|
||||
|
||||
$state.includes = function (stateOrName) {
|
||||
return $state.$current.includes[findState(stateOrName).name];
|
||||
};
|
||||
|
||||
|
||||
function resolveState(state, params, paramsAreFiltered, inherited, dst) {
|
||||
// We need to track all the promises generated during the resolution process.
|
||||
// The first of these is for the fully resolved parent locals.
|
||||
var promises = [inherited];
|
||||
|
||||
// Make a restricted $stateParams with only the parameters that apply to this state if
|
||||
// necessary. In addition to being available to the controller and onEnter/onExit callbacks,
|
||||
// we also need $stateParams to be available for any $injector calls we make during the
|
||||
// dependency resolution process.
|
||||
var $stateParams;
|
||||
if (paramsAreFiltered) $stateParams = params;
|
||||
else {
|
||||
$stateParams = {};
|
||||
forEach(state.params, function (name) {
|
||||
$stateParams[name] = params[name];
|
||||
});
|
||||
}
|
||||
var locals = { $stateParams: $stateParams };
|
||||
|
||||
// Resolves the values from an individual 'resolve' dependency spec
|
||||
function resolve(deps, dst) {
|
||||
forEach(deps, function (value, key) {
|
||||
promises.push($q
|
||||
.when(isString(value) ?
|
||||
$injector.get(value) :
|
||||
$injector.invoke(value, state.self, locals))
|
||||
.then(function (result) {
|
||||
dst[key] = result;
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
||||
// Resolve 'global' dependencies for the state, i.e. those not specific to a view.
|
||||
// We're also including $stateParams in this; that we're the parameters are restricted
|
||||
// to the set that should be visible to the state, and are independent of when we update
|
||||
// the global $state and $stateParams values.
|
||||
var globals = dst.globals = { $stateParams: $stateParams };
|
||||
resolve(state.resolve, globals);
|
||||
globals.$$state = state; // Provide access to the state itself for internal use
|
||||
|
||||
// Resolve template and dependencies for all views.
|
||||
forEach(state.views, function (view, name) {
|
||||
// References to the controller (only instantiated at link time)
|
||||
var $view = dst[name] = {
|
||||
$$controller: view.controller
|
||||
};
|
||||
|
||||
// Template
|
||||
promises.push($q
|
||||
.when($templateFactory.fromConfig(view, $stateParams, locals) || '')
|
||||
.then(function (result) {
|
||||
$view.$template = result;
|
||||
}));
|
||||
|
||||
// View-local dependencies. If we've reused the state definition as the default
|
||||
// view definition in .state(), we can end up with state.resolve === view.resolve.
|
||||
// Avoid resolving everything twice in that case.
|
||||
if (view.resolve !== state.resolve) resolve(view.resolve, $view);
|
||||
});
|
||||
|
||||
// Once we've resolved all the dependencies for this state, merge
|
||||
// in any inherited dependencies, and merge common state dependencies
|
||||
// into the dependency set for each view. Finally return a promise
|
||||
// for the fully popuplated state dependencies.
|
||||
return $q.all(promises).then(function (values) {
|
||||
merge(dst.globals, values[0].globals); // promises[0] === inherited
|
||||
forEach(state.views, function (view, name) {
|
||||
merge(dst[name], dst.globals);
|
||||
});
|
||||
return dst;
|
||||
});
|
||||
}
|
||||
|
||||
function equalForKeys(a, b, keys) {
|
||||
for (var i=0; i<keys.length; i++) {
|
||||
var k = keys[i];
|
||||
if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return $state;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.state')
|
||||
.value('$stateParams', {})
|
||||
.provider('$state', $StateProvider);
|
||||
|
||||
|
||||
$ViewDirective.$inject = ['$state', '$compile', '$controller', '$anchorScroll'];
|
||||
function $ViewDirective( $state, $compile, $controller, $anchorScroll) {
|
||||
var directive = {
|
||||
restrict: 'ECA',
|
||||
terminal: true,
|
||||
link: function(scope, element, attr) {
|
||||
var viewScope, viewLocals,
|
||||
name = attr[directive.name] || attr.name || '',
|
||||
onloadExp = attr.onload || '';
|
||||
|
||||
// Find the details of the parent view directive (if any) and use it
|
||||
// to derive our own qualified view name, then hang our own details
|
||||
// off the DOM so child directives can find it.
|
||||
var parent = element.parent().inheritedData('$uiView');
|
||||
if (name.indexOf('@') < 0) name = name + '@' + (parent ? parent.state.name : '');
|
||||
var view = { name: name, state: null };
|
||||
element.data('$uiView', view);
|
||||
|
||||
scope.$on('$stateChangeSuccess', updateView);
|
||||
updateView();
|
||||
|
||||
function updateView() {
|
||||
var locals = $state.$current && $state.$current.locals[name];
|
||||
if (locals === viewLocals) return; // nothing to do
|
||||
|
||||
// Destroy previous view scope (if any)
|
||||
if (viewScope) {
|
||||
viewScope.$destroy();
|
||||
viewScope = null;
|
||||
}
|
||||
|
||||
if (locals) {
|
||||
viewLocals = locals;
|
||||
view.state = locals.$$state;
|
||||
|
||||
element.html(locals.$template);
|
||||
// element.html('<div style="height:0;position:relative;z-index:999"><span style="background:red;color:white;font-size:12px;padding:1px">' + name + '</span></div>' + locals.$template);
|
||||
var link = $compile(element.contents());
|
||||
viewScope = scope.$new();
|
||||
if (locals.$$controller) {
|
||||
locals.$scope = viewScope;
|
||||
var controller = $controller(locals.$$controller, locals);
|
||||
element.contents().data('$ngControllerController', controller);
|
||||
}
|
||||
link(viewScope);
|
||||
viewScope.$emit('$viewContentLoaded');
|
||||
viewScope.$eval(onloadExp);
|
||||
|
||||
// TODO: This seems strange, shouldn't $anchorScroll listen for $viewContentLoaded if necessary?
|
||||
// $anchorScroll might listen on event...
|
||||
$anchorScroll();
|
||||
} else {
|
||||
viewLocals = null;
|
||||
view.state = null;
|
||||
element.html('');
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
return directive;
|
||||
}
|
||||
|
||||
angular.module('ui.state').directive('uiView', $ViewDirective);
|
||||
|
||||
$RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider'];
|
||||
function $RouteProvider( $stateProvider, $urlRouterProvider) {
|
||||
|
||||
var routes = [];
|
||||
|
||||
onEnterRoute.$inject = ['$$state'];
|
||||
function onEnterRoute( $$state) {
|
||||
/*jshint validthis: true */
|
||||
this.locals = $$state.locals.globals;
|
||||
this.params = this.locals.$stateParams;
|
||||
}
|
||||
|
||||
function onExitRoute() {
|
||||
/*jshint validthis: true */
|
||||
this.locals = null;
|
||||
this.params = null;
|
||||
}
|
||||
|
||||
this.when = when;
|
||||
function when(url, route) {
|
||||
/*jshint validthis: true */
|
||||
if (route.redirectTo != null) {
|
||||
// Redirect, configure directly on $urlRouterProvider
|
||||
var redirect = route.redirectTo, handler;
|
||||
if (isString(redirect)) {
|
||||
handler = redirect; // leave $urlRouterProvider to handle
|
||||
} else if (isFunction(redirect)) {
|
||||
// Adapt to $urlRouterProvider API
|
||||
handler = function (params, $location) {
|
||||
return redirect(params, $location.path(), $location.search());
|
||||
};
|
||||
} else {
|
||||
throw new Error("Invalid 'redirectTo' in when()");
|
||||
}
|
||||
$urlRouterProvider.when(url, handler);
|
||||
} else {
|
||||
// Regular route, configure as state
|
||||
$stateProvider.state(inherit(route, {
|
||||
parent: null,
|
||||
name: 'route:' + encodeURIComponent(url),
|
||||
url: url,
|
||||
onEnter: onEnterRoute,
|
||||
onExit: onExitRoute
|
||||
}));
|
||||
}
|
||||
routes.push(route);
|
||||
return this;
|
||||
}
|
||||
|
||||
this.$get = $get;
|
||||
$get.$inject = ['$state', '$rootScope', '$routeParams'];
|
||||
function $get( $state, $rootScope, $routeParams) {
|
||||
|
||||
var $route = {
|
||||
routes: routes,
|
||||
params: $routeParams,
|
||||
current: undefined
|
||||
};
|
||||
|
||||
function stateAsRoute(state) {
|
||||
return (state.name !== '') ? state : undefined;
|
||||
}
|
||||
|
||||
$rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
|
||||
$rootScope.$broadcast('$routeChangeStart', stateAsRoute(to), stateAsRoute(from));
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
|
||||
$route.current = stateAsRoute(to);
|
||||
$rootScope.$broadcast('$routeChangeSuccess', stateAsRoute(to), stateAsRoute(from));
|
||||
copy(toParams, $route.params);
|
||||
});
|
||||
|
||||
$rootScope.$on('$stateChangeError', function (ev, to, toParams, from, fromParams, error) {
|
||||
$rootScope.$broadcast('$routeChangeError', stateAsRoute(to), stateAsRoute(from), error);
|
||||
});
|
||||
|
||||
return $route;
|
||||
}
|
||||
}
|
||||
|
||||
angular.module('ui.compat')
|
||||
.provider('$route', $RouteProvider)
|
||||
.directive('ngView', $ViewDirective);
|
||||
1
src/main/java/com/commafeed/frontend/references/angularuistate/angular-ui-states.min.js
vendored
Normal file
1
src/main/java/com/commafeed/frontend/references/angularuistate/angular-ui-states.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user