susanoo 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cea3a001f96e2e5c895481f0cd45480e1eacb657
4
- data.tar.gz: ce9a4a01a59b2a3804c13e28991ff1baf6fc4609
3
+ metadata.gz: feb35da8654f6e9da115613dcc34d765ee366e58
4
+ data.tar.gz: f352c4939a2caf43e8727813e116aff11d71d638
5
5
  SHA512:
6
- metadata.gz: 93658ba6ca437bce4a6fc3312f64c4cf97ddf3e56242341520e7f41433fed7f22c342fa81685c6029e6b24bb84de3e87b99130d25e6ef95bc4a8a6fddc4039ab
7
- data.tar.gz: 5d47d0fe406d3d0eeb9c588ba6046963547237081ce4db9dff2e3b502852a11282bb34e4fb0b425f2c4503d0eebddd342b0d8f386281d4f4fdbf81da771d62ac
6
+ metadata.gz: dc95c529977db0c1cd06a5783fe6af6bbebbc170e85d4803aa63b5e6305dc99812984ef5d6724abf25d538a31cc6b26f62c5b9a2dc58cd5fc7a00fe9956905e5
7
+ data.tar.gz: b8c5ba8968ca373ee030b66085b6091a55cecd3d946fcfc64477e2855256be81a15866bc7b27788d583de6d92411f4a594db598ad93d72d10178b1450317c4d6
data/TODO.org CHANGED
@@ -1,3 +1,5 @@
1
1
  * Tasks
2
2
  ** TODO transfer changes from example app to main susanoo headline
3
3
  ** TODO Add .gitignore to new projects
4
+ ** TODO Add config.xml to templates
5
+ ** TODO make title tag of index.html dynamic
@@ -3,6 +3,7 @@ require "colorize"
3
3
 
4
4
  module Susanoo
5
5
  end
6
-
7
- require "susanoo/project"
8
- require "susanoo/cli"
6
+ require 'susanoo/application'
7
+ require 'susanoo/assets'
8
+ require 'susanoo/project'
9
+ require 'susanoo/cli'
@@ -0,0 +1,14 @@
1
+ module Susanoo
2
+ class Application
3
+ @@debug = false
4
+
5
+ def self.debug?
6
+ @@debug
7
+ end
8
+
9
+ def self.debug=(value)
10
+ @@debug = value
11
+ end
12
+
13
+ end
14
+ end
@@ -0,0 +1,7 @@
1
+ module Susanoo
2
+ class Assets
3
+ class << self
4
+ attr_accessor :path
5
+ end
6
+ end
7
+ end
@@ -1,12 +1,12 @@
1
- require "pathname"
2
- require "susanoo/cli/global"
3
- require "susanoo/cli/project"
1
+ require 'pathname'
2
+ require 'susanoo/cli/global'
3
+ require 'susanoo/cli/project'
4
4
 
5
5
 
6
6
  module Susanoo
7
7
  module CLI
8
8
 
9
- EXEC_FILES = ["bin/susanoo"]
9
+ EXEC_FILES = ['bin/susanoo']
10
10
 
11
11
  def self.run
12
12
  unless execute
@@ -20,6 +20,7 @@ module Susanoo
20
20
  loop do
21
21
  # Find an executable in bin/susanoo
22
22
  # In other word are we in an susanoo project or not?
23
+ # Note: Single equalsign is on purpose
23
24
  if exec_file = find_executable
24
25
 
25
26
  # Inject path
@@ -29,13 +30,13 @@ module Susanoo
29
30
  end
30
31
 
31
32
  Dir.chdir(cwd) and return false if Pathname.new(Dir.pwd).root?
32
- Dir.chdir("../")
33
+ Dir.chdir('../')
33
34
  end
34
35
  end
35
36
 
36
37
  def self.inject_dev_path
37
- if File.exist? File.expand_path("../../../.git", __FILE__)
38
- ENV["SUSANOO_HOME"] = File.expand_path("../../", __FILE__)
38
+ if File.exist? File.expand_path('../../../.git', __FILE__)
39
+ ENV['SUSANOO_HOME'] = File.expand_path('../../', __FILE__)
39
40
  end
40
41
  end
41
42
 
@@ -10,7 +10,6 @@ module Susanoo
10
10
  def new(name)
11
11
  Susanoo::Project.folder_name = name
12
12
  Susanoo::Generators::Cordova.start
13
-
14
13
  Susanoo::Generators::Frameworks.start
15
14
  end
16
15
 
@@ -35,7 +35,7 @@ module Susanoo
35
35
  def server(port=6000)
36
36
  require 'sprockets'
37
37
  require 'rack'
38
- requireh 'rack/rewrite'
38
+ require 'rack/rewrite'
39
39
 
40
40
  root = Dir.getwd
41
41
  #Rack::Handler::WEBrick.run :Port => 3000, :DocumentRoot => root
@@ -3,7 +3,8 @@ module Susanoo
3
3
  class Cordova < Thor::Group
4
4
  include Thor::Actions
5
5
 
6
- CORDOVA_VERSION = "v3.3.0"
6
+ CORDOVA_VERSION = `cordova -v`
7
+ CORDOVA_PATH = `which cordova`
7
8
 
8
9
  def initialize_project
9
10
  fname = Susanoo::Project.folder_name.dup
@@ -22,7 +23,7 @@ module Susanoo
22
23
  # Project name
23
24
  project_name = ask "Project Name [#{fname.colorize(:light_red)}".colorize(:light_green) + "]: ".colorize(:light_green)
24
25
 
25
- say "Initializing project with Apache Cordova #{version}"
26
+ say "Initializing project with Apache Cordova #{version} at #{CORDOVA_PATH}"
26
27
  system "cordova create #{Susanoo::Project.folder_name} #{package_name.chomp} #{project_name.chomp}"
27
28
  end
28
29
 
@@ -33,6 +34,8 @@ module Susanoo
33
34
  say "Adding #{platform.strip} platform ...".colorize(:green)
34
35
  system "cordova platform add #{platform.strip}"
35
36
  end
37
+ say "Adding console plugin to project".colorize(:green)
38
+ system "cordova plugin add org.apache.cordova.console"
36
39
  end
37
40
  end
38
41
 
@@ -55,7 +58,7 @@ module Susanoo
55
58
  private
56
59
 
57
60
  def version
58
- CORDOVA_VERSION
61
+ CORDOVA_VERSION.chomp
59
62
  end
60
63
 
61
64
  end
@@ -21,7 +21,7 @@ module Susanoo
21
21
  },
22
22
  }
23
23
 
24
- @@js_files = ["jquery/jquery",
24
+ @@js_files = ["jquery/dist/jquery",
25
25
  "lodash/dist/lodash",
26
26
  "angular/angular",
27
27
  "angular-animate/angular-animate",
@@ -43,6 +43,7 @@ module Susanoo
43
43
  template "Gemfile", "#{Susanoo::Project.folder_name}/Gemfile"
44
44
  template "Rakefile", "#{Susanoo::Project.folder_name}/Rakefile"
45
45
  template "config.ru", "#{Susanoo::Project.folder_name}/config.ru"
46
+ template ".gitignore", "#{Susanoo::Project.folder_name}/.gitignore"
46
47
  template "bin/susanoo", "#{Susanoo::Project.folder_name}/bin/susanoo"
47
48
  end
48
49
 
@@ -53,23 +54,25 @@ module Susanoo
53
54
  # installing Zurb Foundation
54
55
  @@bower_data[:dependencies][:foundation] = "*"
55
56
 
56
- copy_file "lib/foundation/scss/foundation.scss", "#{Susanoo::Project.folder_name}/www/assets/stylesheets/lib/foundation.scss"
57
- directory "lib/foundation/scss/foundation", "#{Susanoo::Project.folder_name}/www/assets/stylesheets/lib/foundation"
58
-
57
+ @@css_dirs << "foundation/scss"
59
58
  @@js_files.unshift "modernizr/modernizr"
60
59
  @@js_files.unshift "foundation/js/foundation"
61
-
60
+ @@js_dirs << "foundation/js/foundation"
62
61
  @@is_foundation = true
63
62
  return
64
63
  end
65
64
 
66
- if yes? "What aboud ionic framework? (y/n)"
65
+ if yes? "What about ionic framework? (y/n)"
67
66
  # Install ionic framework
68
67
  @@bower_data[:dependencies][:ionic] = "*"
69
- @@js_files.unshift "ionic/js/ionic"
70
- @@js_dirs << "ionic/js/ext"
68
+ @@js_files.unshift "ionic/dist/js/ionic"
69
+ @@js_files << "angular-ui-router"
70
+ @@js_files << "ionic/dist/js/ionic-angular"
71
71
  @@css_dirs.concat(["ionic/scss"])
72
- @is_ionic = true
72
+ # Unfortunately angular-ui-router bower package did not provide current
73
+ # So we have to install it manually
74
+ template "www/assets/javascripts/lib/angular-ui-router.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/lib/angular-ui-router.js"
75
+ @@is_ionic = true
73
76
  end
74
77
  end
75
78
 
@@ -87,9 +90,7 @@ module Susanoo
87
90
 
88
91
  def install_templates
89
92
  template "www/index.html", "#{Susanoo::Project.folder_name}/www/index.html"
90
- create_file "#{Susanoo::Project.folder_name}/www/views/.keep" do
91
- " "
92
- end
93
+ template "www/views/main.html", "#{Susanoo::Project.folder_name}/www/views/main.html"
93
94
  template "www/assets/javascripts/application.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/application.js"
94
95
  template "www/assets/javascripts/functions.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/functions.js"
95
96
  template "www/assets/javascripts/variables.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/variables.js"
@@ -103,9 +104,10 @@ module Susanoo
103
104
  template "www/assets/stylesheets/main.scss", "#{Susanoo::Project.folder_name}/www/assets/stylesheets/main.scss"
104
105
 
105
106
  @source_paths << File.expand_path("#{Susanoo::Project.folder_name}/www/bower_components/")
106
-
107
107
  @@js_files.each do |file|
108
- copy_file "#{file}.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/lib/#{file}.js"
108
+ unless file == "angular-ui-router"
109
+ copy_file "#{file}.js", "#{Susanoo::Project.folder_name}/www/assets/javascripts/lib/#{file}.js"
110
+ end
109
111
  end
110
112
 
111
113
  @@js_dirs.each do |dir|
@@ -138,6 +140,10 @@ module Susanoo
138
140
  @@is_foundation
139
141
  end
140
142
 
143
+ def js_dirs
144
+ @@js_dirs
145
+ end
146
+
141
147
  def is_ionic?
142
148
  @@is_ionic
143
149
  end
@@ -0,0 +1,25 @@
1
+ require "bundler/setup"
2
+
3
+ Bundler.require
4
+
5
+ desc "Compile assets into statics folder"
6
+ task :assets do
7
+ require "sprockets"
8
+ #require "rake/sprocketstask"
9
+
10
+ assets = Sprockets::Environment.new
11
+ assets.append_path "www/assets/javascripts"
12
+ assets.append_path "www/assets/stylesheets"
13
+
14
+ Susanoo::Assets.path = ""
15
+ LOOSE_APP_ASSETS = lambda do |path, filename|
16
+ filename !~ %r~assets~ && !%w[.js .css].include?(File.extname(path))
17
+ end
18
+ precompile = [LOOSE_APP_ASSETS, /(?:\/|\\|\A)application\.(css|js)$/]
19
+ assets.each_logical_path(*precompile).each {|path|
20
+ assets[path].write_to "www/statics/#{path}"
21
+ }
22
+
23
+ system "cp www/assets/images www/statics/images -rv"
24
+ system "cp www/assets/fonts www/statics/fonts -rv"
25
+ end
@@ -1,19 +1,10 @@
1
- desc "Asdasd"
2
- task :assets do
3
-
4
- require "sprockets"
5
- #require "rake/sprocketstask"
6
-
7
- assets = Sprockets::Environment.new
8
- assets.append_path "assets/javascripts"
9
- assets.append_path "assets/stylesheets"
10
- assets.append_path "assets/images"
11
- LOOSE_APP_ASSETS = lambda do |path, filename|
12
- filename !~ %r~assets~ && !%w[.js .css].include?(File.extname(path))
13
- end
14
- precompile = [LOOSE_APP_ASSETS, /(?:\/|\\|\A)application\.(css|js)$/]
15
- #binding.pry
16
- assets.each_logical_path(*precompile).each {|path|
17
- assets[path].write_to "statics/#{path}"
18
- }
1
+ namespace :compile do
2
+ #require 'susanoo'
3
+ [:android, :ios, "amazon-fireos", "ubuntu", "wp7", "wp8", "tizen"].each do |platform|
4
+ desc "compile application for #{platform} platform"
5
+ task platform do
6
+ Rake::Task["assets"].invoke
7
+ system "cordova compile #{platform}"
8
+ end
9
+ end
19
10
  end
@@ -0,0 +1,6 @@
1
+ namespace :monitor do
2
+ desc "Run an android monitor program"
3
+ task :android do
4
+ system "$ANDROID_HOME/tools/monitor"
5
+ end
6
+ end
@@ -0,0 +1,10 @@
1
+ namespace :run do
2
+ #require 'susanoo'
3
+ [:android, :ios, "amazon-fireos", "ubuntu", "wp7", "wp8", "tizen"].each do |platform|
4
+ desc "run application for #{platform} platform"
5
+ task platform do
6
+ Rake::Task["assets"].invoke
7
+ system "cordova run #{platform}"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ *~
2
+ www/statics/*
3
+ # ANDROID / ECLIPSE
4
+
5
+ # built application files
6
+ *.apk
7
+ *.ap_
8
+
9
+ # files for the dex VM
10
+ *.dex
11
+
12
+ # Java class files
13
+ *.class
14
+
15
+ # generated files - android project subfolder
16
+ Android/bin/
17
+ Android/gen/
18
+ Android/assets/
19
+
20
+ # generated files
21
+ bin/
22
+ gen/
23
+
24
+ # Local configuration file (sdk path, etc)
25
+ local.properties
26
+
27
+ # IOS / Xcode
28
+ build/*
29
+ *.pbxuser
30
+ !default.pbxuser
31
+ *.mode1v3
32
+ !default.mode1v3
33
+ *.mode2v3
34
+ !default.mode2v3
35
+ *.perspectivev3
36
+ !default.perspectivev3
37
+ *.xcworkspace
38
+ !default.xcworkspace
39
+ xcuserdata
40
+ profile
41
+ *.moved-aside
42
+ IOS/www/
43
+
44
+ # OSX
45
+ .DS_Store
46
+
47
+ # Thumbnails
48
+ ._*
49
+
50
+ # Files that might appear on external disk
51
+ .Spotlight-V100
52
+ .Trashes
@@ -1,7 +1,7 @@
1
1
  require "bundler/setup"
2
2
  Bundler.require
3
3
 
4
- require "Susanoo/tasks"
4
+ require "susanoo/tasks"
5
5
  Susanoo::Tasks.load_tasks
6
6
 
7
7
  task :default => :assets
@@ -11,13 +11,14 @@ assets.append_path(File.join(project_root, 'www', 'assets'))
11
11
  assets.append_path(File.join(project_root, 'www', 'assets', 'javascripts'))
12
12
  assets.append_path(File.join(project_root, 'www', 'assets', 'stylesheets'))
13
13
  assets.append_path(File.join(project_root, 'www', 'assets', 'images'))
14
+ assets.append_path(File.join(project_root, 'www', 'assets', 'fonts'))
14
15
 
15
16
  map "/statics" do
16
17
  run assets
17
18
  end
18
19
 
19
20
  map "/" do
20
- use Rack::Static, :urls => [""], :index => "www/index.html"
21
+ use Rack::Static, :root => "www", :urls => [""], :index => "index.html"
21
22
  run lambda { |env|
22
23
  [
23
24
  200,
@@ -1 +1,22 @@
1
- var App = angular.module("Application", ["ngTouch", "angular-gestures"<% if is_ionic? %>, "ionic"<% end %>]);
1
+ // Main Application Module
2
+ // -----------------------
3
+ // This module is the start point of application.
4
+ var App = angular.module("Application", ["ngTouch", "ngAnimate", "ngRoute", "gettext", "angular-gestures"<% if is_ionic? %>, "ionic"<% end %>]);
5
+
6
+ // configuration section ---------------------------
7
+ App.config(["$routeProvider", function($routeProvider){
8
+
9
+ // Configuring application index route.
10
+ // Add any route you need here.
11
+ $routeProvider.
12
+ when("/", {
13
+ templateUrl: template("main"),
14
+ controller: "MainController"
15
+ });
16
+
17
+ }]);
18
+
19
+ App.controller("MainController", ["$scope", "gettext", function($scope, gettext){
20
+ // Main controller of application. This controller is responsible for `/` url
21
+ $scope.msg = gettext("Hello world");
22
+ }]);
@@ -3,6 +3,9 @@
3
3
  <% js_files.each do |file| %>
4
4
  //= require lib/<%= file %>
5
5
  <% end %>
6
+ <% js_dirs.each do |dir| %>
7
+ //= require_tree ./lib/<%= dir %>
8
+ <% end %>
6
9
  //= require_tree ./modules
7
10
  //= require app
8
11
  //= require main
@@ -1,6 +1,4 @@
1
- /**
2
- * Return the path of given template
3
- **/
4
- function template(patH) {
5
- return "/www/views/" + path + ".html";
1
+ // Return fixed path to given path
2
+ function template(path) {
3
+ return "views/" + path + ".html";
6
4
  }
@@ -0,0 +1,2657 @@
1
+ /**
2
+ * State-based routing for AngularJS
3
+ * @version v0.2.8
4
+ * @link http://angular-ui.github.com/
5
+ * @license MIT License, http://www.opensource.org/licenses/MIT
6
+ */
7
+
8
+ /* commonjs package manager support (eg componentjs) */
9
+ if (typeof module !== "undefined" && typeof exports !== "undefined" && module.exports === exports){
10
+ module.exports = 'ui.router';
11
+ }
12
+
13
+ (function (window, angular, undefined) {
14
+ /*jshint globalstrict:true*/
15
+ /*global angular:false*/
16
+ 'use strict';
17
+
18
+ var isDefined = angular.isDefined,
19
+ isFunction = angular.isFunction,
20
+ isString = angular.isString,
21
+ isObject = angular.isObject,
22
+ isArray = angular.isArray,
23
+ forEach = angular.forEach,
24
+ extend = angular.extend,
25
+ copy = angular.copy;
26
+
27
+ function inherit(parent, extra) {
28
+ return extend(new (extend(function() {}, { prototype: parent }))(), extra);
29
+ }
30
+
31
+ function merge(dst) {
32
+ forEach(arguments, function(obj) {
33
+ if (obj !== dst) {
34
+ forEach(obj, function(value, key) {
35
+ if (!dst.hasOwnProperty(key)) dst[key] = value;
36
+ });
37
+ }
38
+ });
39
+ return dst;
40
+ }
41
+
42
+ /**
43
+ * Finds the common ancestor path between two states.
44
+ *
45
+ * @param {Object} first The first state.
46
+ * @param {Object} second The second state.
47
+ * @return {Array} Returns an array of state names in descending order, not including the root.
48
+ */
49
+ function ancestors(first, second) {
50
+ var path = [];
51
+
52
+ for (var n in first.path) {
53
+ if (first.path[n] !== second.path[n]) break;
54
+ path.push(first.path[n]);
55
+ }
56
+ return path;
57
+ }
58
+
59
+ /**
60
+ * IE8-safe wrapper for `Object.keys()`.
61
+ *
62
+ * @param {Object} object A JavaScript object.
63
+ * @return {Array} Returns the keys of the object as an array.
64
+ */
65
+ function keys(object) {
66
+ if (Object.keys) {
67
+ return Object.keys(object);
68
+ }
69
+ var result = [];
70
+
71
+ angular.forEach(object, function(val, key) {
72
+ result.push(key);
73
+ });
74
+ return result;
75
+ }
76
+
77
+ /**
78
+ * IE8-safe wrapper for `Array.prototype.indexOf()`.
79
+ *
80
+ * @param {Array} array A JavaScript array.
81
+ * @param {*} value A value to search the array for.
82
+ * @return {Number} Returns the array index value of `value`, or `-1` if not present.
83
+ */
84
+ function arraySearch(array, value) {
85
+ if (Array.prototype.indexOf) {
86
+ return array.indexOf(value, Number(arguments[2]) || 0);
87
+ }
88
+ var len = array.length >>> 0, from = Number(arguments[2]) || 0;
89
+ from = (from < 0) ? Math.ceil(from) : Math.floor(from);
90
+
91
+ if (from < 0) from += len;
92
+
93
+ for (; from < len; from++) {
94
+ if (from in array && array[from] === value) return from;
95
+ }
96
+ return -1;
97
+ }
98
+
99
+ /**
100
+ * Merges a set of parameters with all parameters inherited between the common parents of the
101
+ * current state and a given destination state.
102
+ *
103
+ * @param {Object} currentParams The value of the current state parameters ($stateParams).
104
+ * @param {Object} newParams The set of parameters which will be composited with inherited params.
105
+ * @param {Object} $current Internal definition of object representing the current state.
106
+ * @param {Object} $to Internal definition of object representing state to transition to.
107
+ */
108
+ function inheritParams(currentParams, newParams, $current, $to) {
109
+ var parents = ancestors($current, $to), parentParams, inherited = {}, inheritList = [];
110
+
111
+ for (var i in parents) {
112
+ if (!parents[i].params || !parents[i].params.length) continue;
113
+ parentParams = parents[i].params;
114
+
115
+ for (var j in parentParams) {
116
+ if (arraySearch(inheritList, parentParams[j]) >= 0) continue;
117
+ inheritList.push(parentParams[j]);
118
+ inherited[parentParams[j]] = currentParams[parentParams[j]];
119
+ }
120
+ }
121
+ return extend({}, inherited, newParams);
122
+ }
123
+
124
+ /**
125
+ * Normalizes a set of values to string or `null`, filtering them by a list of keys.
126
+ *
127
+ * @param {Array} keys The list of keys to normalize/return.
128
+ * @param {Object} values An object hash of values to normalize.
129
+ * @return {Object} Returns an object hash of normalized string values.
130
+ */
131
+ function normalize(keys, values) {
132
+ var normalized = {};
133
+
134
+ forEach(keys, function (name) {
135
+ var value = values[name];
136
+ normalized[name] = (value != null) ? String(value) : null;
137
+ });
138
+ return normalized;
139
+ }
140
+
141
+ /**
142
+ * Performs a non-strict comparison of the subset of two objects, defined by a list of keys.
143
+ *
144
+ * @param {Object} a The first object.
145
+ * @param {Object} b The second object.
146
+ * @param {Array} keys The list of keys within each object to compare. If the list is empty or not specified,
147
+ * it defaults to the list of keys in `a`.
148
+ * @return {Boolean} Returns `true` if the keys match, otherwise `false`.
149
+ */
150
+ function equalForKeys(a, b, keys) {
151
+ if (!keys) {
152
+ keys = [];
153
+ for (var n in a) keys.push(n); // Used instead of Object.keys() for IE8 compatibility
154
+ }
155
+
156
+ for (var i=0; i<keys.length; i++) {
157
+ var k = keys[i];
158
+ if (a[k] != b[k]) return false; // Not '===', values aren't necessarily normalized
159
+ }
160
+ return true;
161
+ }
162
+
163
+ /**
164
+ * Returns the subset of an object, based on a list of keys.
165
+ *
166
+ * @param {Array} keys
167
+ * @param {Object} values
168
+ * @return {Boolean} Returns a subset of `values`.
169
+ */
170
+ function filterByKeys(keys, values) {
171
+ var filtered = {};
172
+
173
+ forEach(keys, function (name) {
174
+ filtered[name] = values[name];
175
+ });
176
+ return filtered;
177
+ }
178
+
179
+ /**
180
+ * @ngdoc overview
181
+ * @name ui.router.util
182
+ *
183
+ * @description
184
+ *
185
+ */
186
+ angular.module('ui.router.util', ['ng']);
187
+
188
+ /**
189
+ * @ngdoc overview
190
+ * @name ui.router.router
191
+ *
192
+ * @requires ui.router.util
193
+ *
194
+ * @description
195
+ *
196
+ */
197
+ angular.module('ui.router.router', ['ui.router.util']);
198
+
199
+ /**
200
+ * @ngdoc overview
201
+ * @name ui.router.router
202
+ *
203
+ * @requires ui.router.router
204
+ * @requires ui.router.util
205
+ *
206
+ * @description
207
+ *
208
+ */
209
+ angular.module('ui.router.state', ['ui.router.router', 'ui.router.util']);
210
+
211
+ /**
212
+ * @ngdoc overview
213
+ * @name ui.router
214
+ *
215
+ * @requires ui.router.state
216
+ *
217
+ * @description
218
+ *
219
+ */
220
+ angular.module('ui.router', ['ui.router.state']);
221
+ /**
222
+ * @ngdoc overview
223
+ * @name ui.router.compat
224
+ *
225
+ * @requires ui.router
226
+ *
227
+ * @description
228
+ *
229
+ */
230
+ angular.module('ui.router.compat', ['ui.router']);
231
+
232
+ /**
233
+ * @ngdoc object
234
+ * @name ui.router.util.$resolve
235
+ *
236
+ * @requires $q
237
+ * @requires $injector
238
+ *
239
+ * @description
240
+ * Manages resolution of (acyclic) graphs of promises.
241
+ */
242
+ $Resolve.$inject = ['$q', '$injector'];
243
+ function $Resolve( $q, $injector) {
244
+
245
+ var VISIT_IN_PROGRESS = 1,
246
+ VISIT_DONE = 2,
247
+ NOTHING = {},
248
+ NO_DEPENDENCIES = [],
249
+ NO_LOCALS = NOTHING,
250
+ NO_PARENT = extend($q.when(NOTHING), { $$promises: NOTHING, $$values: NOTHING });
251
+
252
+
253
+ /**
254
+ * @ngdoc function
255
+ * @name ui.router.util.$resolve#study
256
+ * @methodOf ui.router.util.$resolve
257
+ *
258
+ * @description
259
+ * Studies a set of invocables that are likely to be used multiple times.
260
+ * <pre>
261
+ * $resolve.study(invocables)(locals, parent, self)
262
+ * </pre>
263
+ * is equivalent to
264
+ * <pre>
265
+ * $resolve.resolve(invocables, locals, parent, self)
266
+ * </pre>
267
+ * but the former is more efficient (in fact `resolve` just calls `study`
268
+ * internally).
269
+ *
270
+ * @param {object} invocables Invocable objects
271
+ * @return {function} a function to pass in locals, parent and self
272
+ */
273
+ this.study = function (invocables) {
274
+ if (!isObject(invocables)) throw new Error("'invocables' must be an object");
275
+
276
+ // Perform a topological sort of invocables to build an ordered plan
277
+ var plan = [], cycle = [], visited = {};
278
+ function visit(value, key) {
279
+ if (visited[key] === VISIT_DONE) return;
280
+
281
+ cycle.push(key);
282
+ if (visited[key] === VISIT_IN_PROGRESS) {
283
+ cycle.splice(0, cycle.indexOf(key));
284
+ throw new Error("Cyclic dependency: " + cycle.join(" -> "));
285
+ }
286
+ visited[key] = VISIT_IN_PROGRESS;
287
+
288
+ if (isString(value)) {
289
+ plan.push(key, [ function() { return $injector.get(value); }], NO_DEPENDENCIES);
290
+ } else {
291
+ var params = $injector.annotate(value);
292
+ forEach(params, function (param) {
293
+ if (param !== key && invocables.hasOwnProperty(param)) visit(invocables[param], param);
294
+ });
295
+ plan.push(key, value, params);
296
+ }
297
+
298
+ cycle.pop();
299
+ visited[key] = VISIT_DONE;
300
+ }
301
+ forEach(invocables, visit);
302
+ invocables = cycle = visited = null; // plan is all that's required
303
+
304
+ function isResolve(value) {
305
+ return isObject(value) && value.then && value.$$promises;
306
+ }
307
+
308
+ return function (locals, parent, self) {
309
+ if (isResolve(locals) && self === undefined) {
310
+ self = parent; parent = locals; locals = null;
311
+ }
312
+ if (!locals) locals = NO_LOCALS;
313
+ else if (!isObject(locals)) {
314
+ throw new Error("'locals' must be an object");
315
+ }
316
+ if (!parent) parent = NO_PARENT;
317
+ else if (!isResolve(parent)) {
318
+ throw new Error("'parent' must be a promise returned by $resolve.resolve()");
319
+ }
320
+
321
+ // To complete the overall resolution, we have to wait for the parent
322
+ // promise and for the promise for each invokable in our plan.
323
+ var resolution = $q.defer(),
324
+ result = resolution.promise,
325
+ promises = result.$$promises = {},
326
+ values = extend({}, locals),
327
+ wait = 1 + plan.length/3,
328
+ merged = false;
329
+
330
+ function done() {
331
+ // Merge parent values we haven't got yet and publish our own $$values
332
+ if (!--wait) {
333
+ if (!merged) merge(values, parent.$$values);
334
+ result.$$values = values;
335
+ result.$$promises = true; // keep for isResolve()
336
+ resolution.resolve(values);
337
+ }
338
+ }
339
+
340
+ function fail(reason) {
341
+ result.$$failure = reason;
342
+ resolution.reject(reason);
343
+ }
344
+
345
+ // Short-circuit if parent has already failed
346
+ if (isDefined(parent.$$failure)) {
347
+ fail(parent.$$failure);
348
+ return result;
349
+ }
350
+
351
+ // Merge parent values if the parent has already resolved, or merge
352
+ // parent promises and wait if the parent resolve is still in progress.
353
+ if (parent.$$values) {
354
+ merged = merge(values, parent.$$values);
355
+ done();
356
+ } else {
357
+ extend(promises, parent.$$promises);
358
+ parent.then(done, fail);
359
+ }
360
+
361
+ // Process each invocable in the plan, but ignore any where a local of the same name exists.
362
+ for (var i=0, ii=plan.length; i<ii; i+=3) {
363
+ if (locals.hasOwnProperty(plan[i])) done();
364
+ else invoke(plan[i], plan[i+1], plan[i+2]);
365
+ }
366
+
367
+ function invoke(key, invocable, params) {
368
+ // Create a deferred for this invocation. Failures will propagate to the resolution as well.
369
+ var invocation = $q.defer(), waitParams = 0;
370
+ function onfailure(reason) {
371
+ invocation.reject(reason);
372
+ fail(reason);
373
+ }
374
+ // Wait for any parameter that we have a promise for (either from parent or from this
375
+ // resolve; in that case study() will have made sure it's ordered before us in the plan).
376
+ forEach(params, function (dep) {
377
+ if (promises.hasOwnProperty(dep) && !locals.hasOwnProperty(dep)) {
378
+ waitParams++;
379
+ promises[dep].then(function (result) {
380
+ values[dep] = result;
381
+ if (!(--waitParams)) proceed();
382
+ }, onfailure);
383
+ }
384
+ });
385
+ if (!waitParams) proceed();
386
+ function proceed() {
387
+ if (isDefined(result.$$failure)) return;
388
+ try {
389
+ invocation.resolve($injector.invoke(invocable, self, values));
390
+ invocation.promise.then(function (result) {
391
+ values[key] = result;
392
+ done();
393
+ }, onfailure);
394
+ } catch (e) {
395
+ onfailure(e);
396
+ }
397
+ }
398
+ // Publish promise synchronously; invocations further down in the plan may depend on it.
399
+ promises[key] = invocation.promise;
400
+ }
401
+
402
+ return result;
403
+ };
404
+ };
405
+
406
+ /**
407
+ * @ngdoc function
408
+ * @name ui.router.util.$resolve#resolve
409
+ * @methodOf ui.router.util.$resolve
410
+ *
411
+ * @description
412
+ * Resolves a set of invocables. An invocable is a function to be invoked via
413
+ * `$injector.invoke()`, and can have an arbitrary number of dependencies.
414
+ * An invocable can either return a value directly,
415
+ * or a `$q` promise. If a promise is returned it will be resolved and the
416
+ * resulting value will be used instead. Dependencies of invocables are resolved
417
+ * (in this order of precedence)
418
+ *
419
+ * - from the specified `locals`
420
+ * - from another invocable that is part of this `$resolve` call
421
+ * - from an invocable that is inherited from a `parent` call to `$resolve`
422
+ * (or recursively
423
+ * - from any ancestor `$resolve` of that parent).
424
+ *
425
+ * The return value of `$resolve` is a promise for an object that contains
426
+ * (in this order of precedence)
427
+ *
428
+ * - any `locals` (if specified)
429
+ * - the resolved return values of all injectables
430
+ * - any values inherited from a `parent` call to `$resolve` (if specified)
431
+ *
432
+ * The promise will resolve after the `parent` promise (if any) and all promises
433
+ * returned by injectables have been resolved. If any invocable
434
+ * (or `$injector.invoke`) throws an exception, or if a promise returned by an
435
+ * invocable is rejected, the `$resolve` promise is immediately rejected with the
436
+ * same error. A rejection of a `parent` promise (if specified) will likewise be
437
+ * propagated immediately. Once the `$resolve` promise has been rejected, no
438
+ * further invocables will be called.
439
+ *
440
+ * Cyclic dependencies between invocables are not permitted and will caues `$resolve`
441
+ * to throw an error. As a special case, an injectable can depend on a parameter
442
+ * with the same name as the injectable, which will be fulfilled from the `parent`
443
+ * injectable of the same name. This allows inherited values to be decorated.
444
+ * Note that in this case any other injectable in the same `$resolve` with the same
445
+ * dependency would see the decorated value, not the inherited value.
446
+ *
447
+ * Note that missing dependencies -- unlike cyclic dependencies -- will cause an
448
+ * (asynchronous) rejection of the `$resolve` promise rather than a (synchronous)
449
+ * exception.
450
+ *
451
+ * Invocables are invoked eagerly as soon as all dependencies are available.
452
+ * This is true even for dependencies inherited from a `parent` call to `$resolve`.
453
+ *
454
+ * As a special case, an invocable can be a string, in which case it is taken to
455
+ * be a service name to be passed to `$injector.get()`. This is supported primarily
456
+ * for backwards-compatibility with the `resolve` property of `$routeProvider`
457
+ * routes.
458
+ *
459
+ * @param {object} invocables functions to invoke or
460
+ * `$injector` services to fetch.
461
+ * @param {object} locals values to make available to the injectables
462
+ * @param {object} parent a promise returned by another call to `$resolve`.
463
+ * @param {object} self the `this` for the invoked methods
464
+ * @return {object} Promise for an object that contains the resolved return value
465
+ * of all invocables, as well as any inherited and local values.
466
+ */
467
+ this.resolve = function (invocables, locals, parent, self) {
468
+ return this.study(invocables)(locals, parent, self);
469
+ };
470
+ }
471
+
472
+ angular.module('ui.router.util').service('$resolve', $Resolve);
473
+
474
+
475
+ /**
476
+ * @ngdoc object
477
+ * @name ui.router.util.$templateFactory
478
+ *
479
+ * @requires $http
480
+ * @requires $templateCache
481
+ * @requires $injector
482
+ *
483
+ * @description
484
+ * Service. Manages loading of templates.
485
+ */
486
+ $TemplateFactory.$inject = ['$http', '$templateCache', '$injector'];
487
+ function $TemplateFactory( $http, $templateCache, $injector) {
488
+
489
+ /**
490
+ * @ngdoc function
491
+ * @name ui.router.util.$templateFactory#fromConfig
492
+ * @methodOf ui.router.util.$templateFactory
493
+ *
494
+ * @description
495
+ * Creates a template from a configuration object.
496
+ *
497
+ * @param {object} config Configuration object for which to load a template.
498
+ * The following properties are search in the specified order, and the first one
499
+ * that is defined is used to create the template:
500
+ *
501
+ * @param {string|object} config.template html string template or function to
502
+ * load via {@link ui.router.util.$templateFactory#fromString fromString}.
503
+ * @param {string|object} config.templateUrl url to load or a function returning
504
+ * the url to load via {@link ui.router.util.$templateFactory#fromUrl fromUrl}.
505
+ * @param {Function} config.templateProvider function to invoke via
506
+ * {@link ui.router.util.$templateFactory#fromProvider fromProvider}.
507
+ * @param {object} params Parameters to pass to the template function.
508
+ * @param {object} locals Locals to pass to `invoke` if the template is loaded
509
+ * via a `templateProvider`. Defaults to `{ params: params }`.
510
+ *
511
+ * @return {string|object} The template html as a string, or a promise for
512
+ * that string,or `null` if no template is configured.
513
+ */
514
+ this.fromConfig = function (config, params, locals) {
515
+ return (
516
+ isDefined(config.template) ? this.fromString(config.template, params) :
517
+ isDefined(config.templateUrl) ? this.fromUrl(config.templateUrl, params) :
518
+ isDefined(config.templateProvider) ? this.fromProvider(config.templateProvider, params, locals) :
519
+ null
520
+ );
521
+ };
522
+
523
+ /**
524
+ * @ngdoc function
525
+ * @name ui.router.util.$templateFactory#fromString
526
+ * @methodOf ui.router.util.$templateFactory
527
+ *
528
+ * @description
529
+ * Creates a template from a string or a function returning a string.
530
+ *
531
+ * @param {string|object} template html template as a string or function that
532
+ * returns an html template as a string.
533
+ * @param {object} params Parameters to pass to the template function.
534
+ *
535
+ * @return {string|object} The template html as a string, or a promise for that
536
+ * string.
537
+ */
538
+ this.fromString = function (template, params) {
539
+ return isFunction(template) ? template(params) : template;
540
+ };
541
+
542
+ /**
543
+ * @ngdoc function
544
+ * @name ui.router.util.$templateFactory#fromUrl
545
+ * @methodOf ui.router.util.$templateFactory
546
+ *
547
+ * @description
548
+ * Loads a template from the a URL via `$http` and `$templateCache`.
549
+ *
550
+ * @param {string|Function} url url of the template to load, or a function
551
+ * that returns a url.
552
+ * @param {Object} params Parameters to pass to the url function.
553
+ * @return {string|Promise.<string>} The template html as a string, or a promise
554
+ * for that string.
555
+ */
556
+ this.fromUrl = function (url, params) {
557
+ if (isFunction(url)) url = url(params);
558
+ if (url == null) return null;
559
+ else return $http
560
+ .get(url, { cache: $templateCache })
561
+ .then(function(response) { return response.data; });
562
+ };
563
+
564
+ /**
565
+ * @ngdoc function
566
+ * @name ui.router.util.$templateFactory#fromUrl
567
+ * @methodOf ui.router.util.$templateFactory
568
+ *
569
+ * @description
570
+ * Creates a template by invoking an injectable provider function.
571
+ *
572
+ * @param {Function} provider Function to invoke via `$injector.invoke`
573
+ * @param {Object} params Parameters for the template.
574
+ * @param {Object} locals Locals to pass to `invoke`. Defaults to
575
+ * `{ params: params }`.
576
+ * @return {string|Promise.<string>} The template html as a string, or a promise
577
+ * for that string.
578
+ */
579
+ this.fromProvider = function (provider, params, locals) {
580
+ return $injector.invoke(provider, null, locals || { params: params });
581
+ };
582
+ }
583
+
584
+ angular.module('ui.router.util').service('$templateFactory', $TemplateFactory);
585
+
586
+ /**
587
+ * Matches URLs against patterns and extracts named parameters from the path or the search
588
+ * part of the URL. A URL pattern consists of a path pattern, optionally followed by '?' and a list
589
+ * of search parameters. Multiple search parameter names are separated by '&'. Search parameters
590
+ * do not influence whether or not a URL is matched, but their values are passed through into
591
+ * the matched parameters returned by {@link UrlMatcher#exec exec}.
592
+ *
593
+ * Path parameter placeholders can be specified using simple colon/catch-all syntax or curly brace
594
+ * syntax, which optionally allows a regular expression for the parameter to be specified:
595
+ *
596
+ * * ':' name - colon placeholder
597
+ * * '*' name - catch-all placeholder
598
+ * * '{' name '}' - curly placeholder
599
+ * * '{' name ':' regexp '}' - curly placeholder with regexp. Should the regexp itself contain
600
+ * curly braces, they must be in matched pairs or escaped with a backslash.
601
+ *
602
+ * Parameter names may contain only word characters (latin letters, digits, and underscore) and
603
+ * must be unique within the pattern (across both path and search parameters). For colon
604
+ * placeholders or curly placeholders without an explicit regexp, a path parameter matches any
605
+ * number of characters other than '/'. For catch-all placeholders the path parameter matches
606
+ * any number of characters.
607
+ *
608
+ * ### Examples
609
+ *
610
+ * * '/hello/' - Matches only if the path is exactly '/hello/'. There is no special treatment for
611
+ * trailing slashes, and patterns have to match the entire path, not just a prefix.
612
+ * * '/user/:id' - Matches '/user/bob' or '/user/1234!!!' or even '/user/' but not '/user' or
613
+ * '/user/bob/details'. The second path segment will be captured as the parameter 'id'.
614
+ * * '/user/{id}' - Same as the previous example, but using curly brace syntax.
615
+ * * '/user/{id:[^/]*}' - Same as the previous example.
616
+ * * '/user/{id:[0-9a-fA-F]{1,8}}' - Similar to the previous example, but only matches if the id
617
+ * parameter consists of 1 to 8 hex digits.
618
+ * * '/files/{path:.*}' - Matches any URL starting with '/files/' and captures the rest of the
619
+ * path into the parameter 'path'.
620
+ * * '/files/*path' - ditto.
621
+ *
622
+ * @constructor
623
+ * @param {string} pattern the pattern to compile into a matcher.
624
+ *
625
+ * @property {string} prefix A static prefix of this pattern. The matcher guarantees that any
626
+ * URL matching this matcher (i.e. any string for which {@link UrlMatcher#exec exec()} returns
627
+ * non-null) will start with this prefix.
628
+ */
629
+ function UrlMatcher(pattern) {
630
+
631
+ // Find all placeholders and create a compiled pattern, using either classic or curly syntax:
632
+ // '*' name
633
+ // ':' name
634
+ // '{' name '}'
635
+ // '{' name ':' regexp '}'
636
+ // The regular expression is somewhat complicated due to the need to allow curly braces
637
+ // inside the regular expression. The placeholder regexp breaks down as follows:
638
+ // ([:*])(\w+) classic placeholder ($1 / $2)
639
+ // \{(\w+)(?:\:( ... ))?\} curly brace placeholder ($3) with optional regexp ... ($4)
640
+ // (?: ... | ... | ... )+ the regexp consists of any number of atoms, an atom being either
641
+ // [^{}\\]+ - anything other than curly braces or backslash
642
+ // \\. - a backslash escape
643
+ // \{(?:[^{}\\]+|\\.)*\} - a matched set of curly braces containing other atoms
644
+ var placeholder = /([:*])(\w+)|\{(\w+)(?:\:((?:[^{}\\]+|\\.|\{(?:[^{}\\]+|\\.)*\})+))?\}/g,
645
+ names = {}, compiled = '^', last = 0, m,
646
+ segments = this.segments = [],
647
+ params = this.params = [];
648
+
649
+ function addParameter(id) {
650
+ if (!/^\w+(-+\w+)*$/.test(id)) throw new Error("Invalid parameter name '" + id + "' in pattern '" + pattern + "'");
651
+ if (names[id]) throw new Error("Duplicate parameter name '" + id + "' in pattern '" + pattern + "'");
652
+ names[id] = true;
653
+ params.push(id);
654
+ }
655
+
656
+ function quoteRegExp(string) {
657
+ return string.replace(/[\\\[\]\^$*+?.()|{}]/g, "\\$&");
658
+ }
659
+
660
+ this.source = pattern;
661
+
662
+ // Split into static segments separated by path parameter placeholders.
663
+ // The number of segments is always 1 more than the number of parameters.
664
+ var id, regexp, segment;
665
+ while ((m = placeholder.exec(pattern))) {
666
+ id = m[2] || m[3]; // IE[78] returns '' for unmatched groups instead of null
667
+ regexp = m[4] || (m[1] == '*' ? '.*' : '[^/]*');
668
+ segment = pattern.substring(last, m.index);
669
+ if (segment.indexOf('?') >= 0) break; // we're into the search part
670
+ compiled += quoteRegExp(segment) + '(' + regexp + ')';
671
+ addParameter(id);
672
+ segments.push(segment);
673
+ last = placeholder.lastIndex;
674
+ }
675
+ segment = pattern.substring(last);
676
+
677
+ // Find any search parameter names and remove them from the last segment
678
+ var i = segment.indexOf('?');
679
+ if (i >= 0) {
680
+ var search = this.sourceSearch = segment.substring(i);
681
+ segment = segment.substring(0, i);
682
+ this.sourcePath = pattern.substring(0, last+i);
683
+
684
+ // Allow parameters to be separated by '?' as well as '&' to make concat() easier
685
+ forEach(search.substring(1).split(/[&?]/), addParameter);
686
+ } else {
687
+ this.sourcePath = pattern;
688
+ this.sourceSearch = '';
689
+ }
690
+
691
+ compiled += quoteRegExp(segment) + '$';
692
+ segments.push(segment);
693
+ this.regexp = new RegExp(compiled);
694
+ this.prefix = segments[0];
695
+ }
696
+
697
+ /**
698
+ * Returns a new matcher for a pattern constructed by appending the path part and adding the
699
+ * search parameters of the specified pattern to this pattern. The current pattern is not
700
+ * modified. This can be understood as creating a pattern for URLs that are relative to (or
701
+ * suffixes of) the current pattern.
702
+ *
703
+ * ### Example
704
+ * The following two matchers are equivalent:
705
+ * ```
706
+ * new UrlMatcher('/user/{id}?q').concat('/details?date');
707
+ * new UrlMatcher('/user/{id}/details?q&date');
708
+ * ```
709
+ *
710
+ * @param {string} pattern The pattern to append.
711
+ * @return {UrlMatcher} A matcher for the concatenated pattern.
712
+ */
713
+ UrlMatcher.prototype.concat = function (pattern) {
714
+ // Because order of search parameters is irrelevant, we can add our own search
715
+ // parameters to the end of the new pattern. Parse the new pattern by itself
716
+ // and then join the bits together, but it's much easier to do this on a string level.
717
+ return new UrlMatcher(this.sourcePath + pattern + this.sourceSearch);
718
+ };
719
+
720
+ UrlMatcher.prototype.toString = function () {
721
+ return this.source;
722
+ };
723
+
724
+ /**
725
+ * Tests the specified path against this matcher, and returns an object containing the captured
726
+ * parameter values, or null if the path does not match. The returned object contains the values
727
+ * of any search parameters that are mentioned in the pattern, but their value may be null if
728
+ * they are not present in `searchParams`. This means that search parameters are always treated
729
+ * as optional.
730
+ *
731
+ * ### Example
732
+ * ```
733
+ * new UrlMatcher('/user/{id}?q&r').exec('/user/bob', { x:'1', q:'hello' });
734
+ * // returns { id:'bob', q:'hello', r:null }
735
+ * ```
736
+ *
737
+ * @param {string} path The URL path to match, e.g. `$location.path()`.
738
+ * @param {Object} searchParams URL search parameters, e.g. `$location.search()`.
739
+ * @return {Object} The captured parameter values.
740
+ */
741
+ UrlMatcher.prototype.exec = function (path, searchParams) {
742
+ var m = this.regexp.exec(path);
743
+ if (!m) return null;
744
+
745
+ var params = this.params, nTotal = params.length,
746
+ nPath = this.segments.length-1,
747
+ values = {}, i;
748
+
749
+ if (nPath !== m.length - 1) throw new Error("Unbalanced capture group in route '" + this.source + "'");
750
+
751
+ for (i=0; i<nPath; i++) values[params[i]] = m[i+1];
752
+ for (/**/; i<nTotal; i++) values[params[i]] = searchParams[params[i]];
753
+
754
+ return values;
755
+ };
756
+
757
+ /**
758
+ * Returns the names of all path and search parameters of this pattern in an unspecified order.
759
+ * @return {Array.<string>} An array of parameter names. Must be treated as read-only. If the
760
+ * pattern has no parameters, an empty array is returned.
761
+ */
762
+ UrlMatcher.prototype.parameters = function () {
763
+ return this.params;
764
+ };
765
+
766
+ /**
767
+ * Creates a URL that matches this pattern by substituting the specified values
768
+ * for the path and search parameters. Null values for path parameters are
769
+ * treated as empty strings.
770
+ *
771
+ * ### Example
772
+ * ```
773
+ * new UrlMatcher('/user/{id}?q').format({ id:'bob', q:'yes' });
774
+ * // returns '/user/bob?q=yes'
775
+ * ```
776
+ *
777
+ * @param {Object} values the values to substitute for the parameters in this pattern.
778
+ * @return {string} the formatted URL (path and optionally search part).
779
+ */
780
+ UrlMatcher.prototype.format = function (values) {
781
+ var segments = this.segments, params = this.params;
782
+ if (!values) return segments.join('');
783
+
784
+ var nPath = segments.length-1, nTotal = params.length,
785
+ result = segments[0], i, search, value;
786
+
787
+ for (i=0; i<nPath; i++) {
788
+ value = values[params[i]];
789
+ // TODO: Maybe we should throw on null here? It's not really good style to use '' and null interchangeabley
790
+ if (value != null) result += encodeURIComponent(value);
791
+ result += segments[i+1];
792
+ }
793
+ for (/**/; i<nTotal; i++) {
794
+ value = values[params[i]];
795
+ if (value != null) {
796
+ result += (search ? '&' : '?') + params[i] + '=' + encodeURIComponent(value);
797
+ search = true;
798
+ }
799
+ }
800
+
801
+ return result;
802
+ };
803
+
804
+ /**
805
+ * Service. Factory for {@link UrlMatcher} instances. The factory is also available to providers
806
+ * under the name `$urlMatcherFactoryProvider`.
807
+ * @constructor
808
+ * @name $urlMatcherFactory
809
+ */
810
+ function $UrlMatcherFactory() {
811
+ /**
812
+ * Creates a {@link UrlMatcher} for the specified pattern.
813
+ * @function
814
+ * @name $urlMatcherFactory#compile
815
+ * @methodOf $urlMatcherFactory
816
+ * @param {string} pattern The URL pattern.
817
+ * @return {UrlMatcher} The UrlMatcher.
818
+ */
819
+ this.compile = function (pattern) {
820
+ return new UrlMatcher(pattern);
821
+ };
822
+
823
+ /**
824
+ * Returns true if the specified object is a UrlMatcher, or false otherwise.
825
+ * @function
826
+ * @name $urlMatcherFactory#isMatcher
827
+ * @methodOf $urlMatcherFactory
828
+ * @param {Object} o
829
+ * @return {boolean}
830
+ */
831
+ this.isMatcher = function (o) {
832
+ return isObject(o) && isFunction(o.exec) && isFunction(o.format) && isFunction(o.concat);
833
+ };
834
+
835
+ this.$get = function () {
836
+ return this;
837
+ };
838
+ }
839
+
840
+ // Register as a provider so it's available to other providers
841
+ angular.module('ui.router.util').provider('$urlMatcherFactory', $UrlMatcherFactory);
842
+
843
+ /**
844
+ * @ngdoc object
845
+ * @name ui.router.router.$urlRouterProvider
846
+ *
847
+ * @requires ui.router.util.$urlMatcherFactoryProvider
848
+ *
849
+ * @description
850
+ * `$urlRouterProvider` has the responsibility of watching `$location`.
851
+ * When `$location` changes it runs through a list of rules one by one until a
852
+ * match is found. `$urlRouterProvider` is used behind the scenes anytime you specify
853
+ * a url in a state configuration. All urls are compiled into a UrlMatcher object.
854
+ *
855
+ * There are several methods on `$urlRouterProvider` that make it useful to use directly
856
+ * in your module config.
857
+ */
858
+ $UrlRouterProvider.$inject = ['$urlMatcherFactoryProvider'];
859
+ function $UrlRouterProvider( $urlMatcherFactory) {
860
+ var rules = [],
861
+ otherwise = null;
862
+
863
+ // Returns a string that is a prefix of all strings matching the RegExp
864
+ function regExpPrefix(re) {
865
+ var prefix = /^\^((?:\\[^a-zA-Z0-9]|[^\\\[\]\^$*+?.()|{}]+)*)/.exec(re.source);
866
+ return (prefix != null) ? prefix[1].replace(/\\(.)/g, "$1") : '';
867
+ }
868
+
869
+ // Interpolates matched values into a String.replace()-style pattern
870
+ function interpolate(pattern, match) {
871
+ return pattern.replace(/\$(\$|\d{1,2})/, function (m, what) {
872
+ return match[what === '$' ? 0 : Number(what)];
873
+ });
874
+ }
875
+
876
+ /**
877
+ * @ngdoc function
878
+ * @name ui.router.router.$urlRouterProvider#rule
879
+ * @methodOf ui.router.router.$urlRouterProvider
880
+ *
881
+ * @description
882
+ * Defines rules that are used by `$urlRouterProvider to find matches for
883
+ * specific URLs.
884
+ *
885
+ * @example
886
+ * <pre>
887
+ * var app = angular.module('app', ['ui.router.router']);
888
+ *
889
+ * app.config(function ($urlRouterProvider) {
890
+ * // Here's an example of how you might allow case insensitive urls
891
+ * $urlRouterProvider.rule(function ($injector, $location) {
892
+ * var path = $location.path(),
893
+ * normalized = path.toLowerCase();
894
+ *
895
+ * if (path !== normalized) {
896
+ * return normalized;
897
+ * }
898
+ * });
899
+ * });
900
+ * </pre>
901
+ *
902
+ * @param {object} rule Handler function that takes `$injector` and `$location`
903
+ * services as arguments. You can use them to return a valid path as a string.
904
+ *
905
+ * @return {object} $urlRouterProvider - $urlRouterProvider instance
906
+ */
907
+ this.rule =
908
+ function (rule) {
909
+ if (!isFunction(rule)) throw new Error("'rule' must be a function");
910
+ rules.push(rule);
911
+ return this;
912
+ };
913
+
914
+ /**
915
+ * @ngdoc object
916
+ * @name ui.router.router.$urlRouterProvider#otherwise
917
+ * @methodOf ui.router.router.$urlRouterProvider
918
+ *
919
+ * @description
920
+ * Defines a path that is used when an invalied route is requested.
921
+ *
922
+ * @example
923
+ * <pre>
924
+ * var app = angular.module('app', ['ui.router.router']);
925
+ *
926
+ * app.config(function ($urlRouterProvider) {
927
+ * // if the path doesn't match any of the urls you configured
928
+ * // otherwise will take care of routing the user to the
929
+ * // specified url
930
+ * $urlRouterProvider.otherwise('/index');
931
+ *
932
+ * // Example of using function rule as param
933
+ * $urlRouterProvider.otherwise(function ($injector, $location) {
934
+ * ...
935
+ * });
936
+ * });
937
+ * </pre>
938
+ *
939
+ * @param {string|object} rule The url path you want to redirect to or a function
940
+ * rule that returns the url path. The function version is passed two params:
941
+ * `$injector` and `$location` services.
942
+ *
943
+ * @return {object} $urlRouterProvider - $urlRouterProvider instance
944
+ */
945
+ this.otherwise =
946
+ function (rule) {
947
+ if (isString(rule)) {
948
+ var redirect = rule;
949
+ rule = function () { return redirect; };
950
+ }
951
+ else if (!isFunction(rule)) throw new Error("'rule' must be a function");
952
+ otherwise = rule;
953
+ return this;
954
+ };
955
+
956
+
957
+ function handleIfMatch($injector, handler, match) {
958
+ if (!match) return false;
959
+ var result = $injector.invoke(handler, handler, { $match: match });
960
+ return isDefined(result) ? result : true;
961
+ }
962
+
963
+ /**
964
+ * @ngdoc function
965
+ * @name ui.router.router.$urlRouterProvider#when
966
+ * @methodOf ui.router.router.$urlRouterProvider
967
+ *
968
+ * @description
969
+ * Registers a handler for a given url matching. if handle is a string, it is
970
+ * treated as a redirect, and is interpolated according to the syyntax of match
971
+ * (i.e. like String.replace() for RegExp, or like a UrlMatcher pattern otherwise).
972
+ *
973
+ * If the handler is a function, it is injectable. It gets invoked if `$location`
974
+ * matches. You have the option of inject the match object as `$match`.
975
+ *
976
+ * The handler can return
977
+ *
978
+ * - **falsy** to indicate that the rule didn't match after all, then `$urlRouter`
979
+ * will continue trying to find another one that matches.
980
+ * - **string** which is treated as a redirect and passed to `$location.url()`
981
+ * - **void** or any **truthy** value tells `$urlRouter` that the url was handled.
982
+ *
983
+ * @example
984
+ * <pre>
985
+ * var app = angular.module('app', ['ui.router.router']);
986
+ *
987
+ * app.config(function ($urlRouterProvider) {
988
+ * $urlRouterProvider.when($state.url, function ($match, $stateParams) {
989
+ * if ($state.$current.navigable !== state ||
990
+ * !equalForKeys($match, $stateParams) {
991
+ * $state.transitionTo(state, $match, false);
992
+ * }
993
+ * });
994
+ * });
995
+ * </pre>
996
+ *
997
+ * @param {string|object} what The incoming path that you want to redirect.
998
+ * @param {string|object} handler The path you want to redirect your user to.
999
+ */
1000
+ this.when =
1001
+ function (what, handler) {
1002
+ var redirect, handlerIsString = isString(handler);
1003
+ if (isString(what)) what = $urlMatcherFactory.compile(what);
1004
+
1005
+ if (!handlerIsString && !isFunction(handler) && !isArray(handler))
1006
+ throw new Error("invalid 'handler' in when()");
1007
+
1008
+ var strategies = {
1009
+ matcher: function (what, handler) {
1010
+ if (handlerIsString) {
1011
+ redirect = $urlMatcherFactory.compile(handler);
1012
+ handler = ['$match', function ($match) { return redirect.format($match); }];
1013
+ }
1014
+ return extend(function ($injector, $location) {
1015
+ return handleIfMatch($injector, handler, what.exec($location.path(), $location.search()));
1016
+ }, {
1017
+ prefix: isString(what.prefix) ? what.prefix : ''
1018
+ });
1019
+ },
1020
+ regex: function (what, handler) {
1021
+ if (what.global || what.sticky) throw new Error("when() RegExp must not be global or sticky");
1022
+
1023
+ if (handlerIsString) {
1024
+ redirect = handler;
1025
+ handler = ['$match', function ($match) { return interpolate(redirect, $match); }];
1026
+ }
1027
+ return extend(function ($injector, $location) {
1028
+ return handleIfMatch($injector, handler, what.exec($location.path()));
1029
+ }, {
1030
+ prefix: regExpPrefix(what)
1031
+ });
1032
+ }
1033
+ };
1034
+
1035
+ var check = { matcher: $urlMatcherFactory.isMatcher(what), regex: what instanceof RegExp };
1036
+
1037
+ for (var n in check) {
1038
+ if (check[n]) {
1039
+ return this.rule(strategies[n](what, handler));
1040
+ }
1041
+ }
1042
+
1043
+ throw new Error("invalid 'what' in when()");
1044
+ };
1045
+
1046
+ /**
1047
+ * @ngdoc object
1048
+ * @name ui.router.router.$urlRouter
1049
+ *
1050
+ * @requires $location
1051
+ * @requires $rootScope
1052
+ * @requires $injector
1053
+ *
1054
+ * @description
1055
+ *
1056
+ */
1057
+ this.$get =
1058
+ [ '$location', '$rootScope', '$injector',
1059
+ function ($location, $rootScope, $injector) {
1060
+ // TODO: Optimize groups of rules with non-empty prefix into some sort of decision tree
1061
+ function update(evt) {
1062
+ if (evt && evt.defaultPrevented) return;
1063
+ function check(rule) {
1064
+ var handled = rule($injector, $location);
1065
+ if (handled) {
1066
+ if (isString(handled)) $location.replace().url(handled);
1067
+ return true;
1068
+ }
1069
+ return false;
1070
+ }
1071
+ var n=rules.length, i;
1072
+ for (i=0; i<n; i++) {
1073
+ if (check(rules[i])) return;
1074
+ }
1075
+ // always check otherwise last to allow dynamic updates to the set of rules
1076
+ if (otherwise) check(otherwise);
1077
+ }
1078
+
1079
+ $rootScope.$on('$locationChangeSuccess', update);
1080
+
1081
+ return {
1082
+ /**
1083
+ * @ngdoc function
1084
+ * @name ui.router.router.$urlRouter#sync
1085
+ * @methodOf ui.router.router.$urlRouter
1086
+ *
1087
+ * @description
1088
+ * Triggers an update; the same update that happens when the address bar url changes, aka `$locationChangeSuccess`.
1089
+ * This method is useful when you need to use `preventDefault()` on the `$locationChangeSuccess` event,
1090
+ * perform some custom logic (route protection, auth, config, redirection, etc) and then finally proceed
1091
+ * with the transition by calling `$urlRouter.sync()`.
1092
+ *
1093
+ * @example
1094
+ * <pre>
1095
+ * angular.module('app', ['ui.router']);
1096
+ * .run(function($rootScope, $urlRouter) {
1097
+ * $rootScope.$on('$locationChangeSuccess', function(evt) {
1098
+ * // Halt state change from even starting
1099
+ * evt.preventDefault();
1100
+ * // Perform custom logic
1101
+ * var meetsRequirement = ...
1102
+ * // Continue with the update and state transition if logic allows
1103
+ * if (meetsRequirement) $urlRouter.sync();
1104
+ * });
1105
+ * });
1106
+ * </pre>
1107
+ */
1108
+ sync: function () {
1109
+ update();
1110
+ }
1111
+ };
1112
+ }];
1113
+ }
1114
+
1115
+ angular.module('ui.router.router').provider('$urlRouter', $UrlRouterProvider);
1116
+
1117
+ /**
1118
+ * @ngdoc object
1119
+ * @name ui.router.state.$stateProvider
1120
+ *
1121
+ * @requires ui.router.router.$urlRouterProvider
1122
+ * @requires ui.router.util.$urlMatcherFactoryProvider
1123
+ * @requires $locationProvider
1124
+ *
1125
+ * @description
1126
+ * The new `$stateProvider` works similar to Angular's v1 router, but it focuses purely
1127
+ * on state.
1128
+ *
1129
+ * A state corresponds to a "place" in the application in terms of the overall UI and
1130
+ * navigation. A state describes (via the controller / template / view properties) what
1131
+ * the UI looks like and does at that place.
1132
+ *
1133
+ * States often have things in common, and the primary way of factoring out these
1134
+ * commonalities in this model is via the state hierarchy, i.e. parent/child states aka
1135
+ * nested states.
1136
+ *
1137
+ * The `$stateProvider` provides interfaces to declare these states for your app.
1138
+ */
1139
+ $StateProvider.$inject = ['$urlRouterProvider', '$urlMatcherFactoryProvider', '$locationProvider'];
1140
+ function $StateProvider( $urlRouterProvider, $urlMatcherFactory, $locationProvider) {
1141
+
1142
+ var root, states = {}, $state, queue = {}, abstractKey = 'abstract';
1143
+
1144
+ // Builds state properties from definition passed to registerState()
1145
+ var stateBuilder = {
1146
+
1147
+ // Derive parent state from a hierarchical name only if 'parent' is not explicitly defined.
1148
+ // state.children = [];
1149
+ // if (parent) parent.children.push(state);
1150
+ parent: function(state) {
1151
+ if (isDefined(state.parent) && state.parent) return findState(state.parent);
1152
+ // regex matches any valid composite state name
1153
+ // would match "contact.list" but not "contacts"
1154
+ var compositeName = /^(.+)\.[^.]+$/.exec(state.name);
1155
+ return compositeName ? findState(compositeName[1]) : root;
1156
+ },
1157
+
1158
+ // inherit 'data' from parent and override by own values (if any)
1159
+ data: function(state) {
1160
+ if (state.parent && state.parent.data) {
1161
+ state.data = state.self.data = extend({}, state.parent.data, state.data);
1162
+ }
1163
+ return state.data;
1164
+ },
1165
+
1166
+ // Build a URLMatcher if necessary, either via a relative or absolute URL
1167
+ url: function(state) {
1168
+ var url = state.url;
1169
+
1170
+ if (isString(url)) {
1171
+ if (url.charAt(0) == '^') {
1172
+ return $urlMatcherFactory.compile(url.substring(1));
1173
+ }
1174
+ return (state.parent.navigable || root).url.concat(url);
1175
+ }
1176
+
1177
+ if ($urlMatcherFactory.isMatcher(url) || url == null) {
1178
+ return url;
1179
+ }
1180
+ throw new Error("Invalid url '" + url + "' in state '" + state + "'");
1181
+ },
1182
+
1183
+ // Keep track of the closest ancestor state that has a URL (i.e. is navigable)
1184
+ navigable: function(state) {
1185
+ return state.url ? state : (state.parent ? state.parent.navigable : null);
1186
+ },
1187
+
1188
+ // Derive parameters for this state and ensure they're a super-set of parent's parameters
1189
+ params: function(state) {
1190
+ if (!state.params) {
1191
+ return state.url ? state.url.parameters() : state.parent.params;
1192
+ }
1193
+ if (!isArray(state.params)) throw new Error("Invalid params in state '" + state + "'");
1194
+ if (state.url) throw new Error("Both params and url specicified in state '" + state + "'");
1195
+ return state.params;
1196
+ },
1197
+
1198
+ // If there is no explicit multi-view configuration, make one up so we don't have
1199
+ // to handle both cases in the view directive later. Note that having an explicit
1200
+ // 'views' property will mean the default unnamed view properties are ignored. This
1201
+ // is also a good time to resolve view names to absolute names, so everything is a
1202
+ // straight lookup at link time.
1203
+ views: function(state) {
1204
+ var views = {};
1205
+
1206
+ forEach(isDefined(state.views) ? state.views : { '': state }, function (view, name) {
1207
+ if (name.indexOf('@') < 0) name += '@' + state.parent.name;
1208
+ views[name] = view;
1209
+ });
1210
+ return views;
1211
+ },
1212
+
1213
+ ownParams: function(state) {
1214
+ if (!state.parent) {
1215
+ return state.params;
1216
+ }
1217
+ var paramNames = {}; forEach(state.params, function (p) { paramNames[p] = true; });
1218
+
1219
+ forEach(state.parent.params, function (p) {
1220
+ if (!paramNames[p]) {
1221
+ throw new Error("Missing required parameter '" + p + "' in state '" + state.name + "'");
1222
+ }
1223
+ paramNames[p] = false;
1224
+ });
1225
+ var ownParams = [];
1226
+
1227
+ forEach(paramNames, function (own, p) {
1228
+ if (own) ownParams.push(p);
1229
+ });
1230
+ return ownParams;
1231
+ },
1232
+
1233
+ // Keep a full path from the root down to this state as this is needed for state activation.
1234
+ path: function(state) {
1235
+ return state.parent ? state.parent.path.concat(state) : []; // exclude root from path
1236
+ },
1237
+
1238
+ // Speed up $state.contains() as it's used a lot
1239
+ includes: function(state) {
1240
+ var includes = state.parent ? extend({}, state.parent.includes) : {};
1241
+ includes[state.name] = true;
1242
+ return includes;
1243
+ },
1244
+
1245
+ $delegates: {}
1246
+ };
1247
+
1248
+ function isRelative(stateName) {
1249
+ return stateName.indexOf(".") === 0 || stateName.indexOf("^") === 0;
1250
+ }
1251
+
1252
+ function findState(stateOrName, base) {
1253
+ var isStr = isString(stateOrName),
1254
+ name = isStr ? stateOrName : stateOrName.name,
1255
+ path = isRelative(name);
1256
+
1257
+ if (path) {
1258
+ if (!base) throw new Error("No reference point given for path '" + name + "'");
1259
+ var rel = name.split("."), i = 0, pathLength = rel.length, current = base;
1260
+
1261
+ for (; i < pathLength; i++) {
1262
+ if (rel[i] === "" && i === 0) {
1263
+ current = base;
1264
+ continue;
1265
+ }
1266
+ if (rel[i] === "^") {
1267
+ if (!current.parent) throw new Error("Path '" + name + "' not valid for state '" + base.name + "'");
1268
+ current = current.parent;
1269
+ continue;
1270
+ }
1271
+ break;
1272
+ }
1273
+ rel = rel.slice(i).join(".");
1274
+ name = current.name + (current.name && rel ? "." : "") + rel;
1275
+ }
1276
+ var state = states[name];
1277
+
1278
+ if (state && (isStr || (!isStr && (state === stateOrName || state.self === stateOrName)))) {
1279
+ return state;
1280
+ }
1281
+ return undefined;
1282
+ }
1283
+
1284
+ function queueState(parentName, state) {
1285
+ if (!queue[parentName]) {
1286
+ queue[parentName] = [];
1287
+ }
1288
+ queue[parentName].push(state);
1289
+ }
1290
+
1291
+ function registerState(state) {
1292
+ // Wrap a new object around the state so we can store our private details easily.
1293
+ state = inherit(state, {
1294
+ self: state,
1295
+ resolve: state.resolve || {},
1296
+ toString: function() { return this.name; }
1297
+ });
1298
+
1299
+ var name = state.name;
1300
+ if (!isString(name) || name.indexOf('@') >= 0) throw new Error("State must have a valid name");
1301
+ if (states.hasOwnProperty(name)) throw new Error("State '" + name + "'' is already defined");
1302
+
1303
+ // Get parent name
1304
+ var parentName = (name.indexOf('.') !== -1) ? name.substring(0, name.lastIndexOf('.'))
1305
+ : (isString(state.parent)) ? state.parent
1306
+ : '';
1307
+
1308
+ // If parent is not registered yet, add state to queue and register later
1309
+ if (parentName && !states[parentName]) {
1310
+ return queueState(parentName, state.self);
1311
+ }
1312
+
1313
+ for (var key in stateBuilder) {
1314
+ if (isFunction(stateBuilder[key])) state[key] = stateBuilder[key](state, stateBuilder.$delegates[key]);
1315
+ }
1316
+ states[name] = state;
1317
+
1318
+ // Register the state in the global state list and with $urlRouter if necessary.
1319
+ if (!state[abstractKey] && state.url) {
1320
+ $urlRouterProvider.when(state.url, ['$match', '$stateParams', function ($match, $stateParams) {
1321
+ if ($state.$current.navigable != state || !equalForKeys($match, $stateParams)) {
1322
+ $state.transitionTo(state, $match, { location: false });
1323
+ }
1324
+ }]);
1325
+ }
1326
+
1327
+ // Register any queued children
1328
+ if (queue[name]) {
1329
+ for (var i = 0; i < queue[name].length; i++) {
1330
+ registerState(queue[name][i]);
1331
+ }
1332
+ }
1333
+
1334
+ return state;
1335
+ }
1336
+
1337
+
1338
+ // Implicit root state that is always active
1339
+ root = registerState({
1340
+ name: '',
1341
+ url: '^',
1342
+ views: null,
1343
+ 'abstract': true
1344
+ });
1345
+ root.navigable = null;
1346
+
1347
+
1348
+ /**
1349
+ * @ngdoc function
1350
+ * @name ui.router.state.$stateProvider#decorator
1351
+ * @methodOf ui.router.state.$stateProvider
1352
+ *
1353
+ * @description
1354
+ * Allows you to extend (carefully) or override (at your own peril) the
1355
+ * `stateBuilder` object used internally by `$stateProvider`. This can be used
1356
+ * to add custom functionality to ui-router, for example inferring templateUrl
1357
+ * based on the state name.
1358
+ *
1359
+ * When passing only a name, it returns the current (original or decorated) builder
1360
+ * function that matches `name`.
1361
+ *
1362
+ * The builder functions that can be decorated are listed below. Though not all
1363
+ * necessarily have a good use case for decoration, that is up to you to decide.
1364
+ *
1365
+ * In addition, users can attach custom decorators, which will generate new
1366
+ * properties within the state's internal definition. There is currently no clear
1367
+ * use-case for this beyond accessing internal states (i.e. $state.$current),
1368
+ * however, expect this to become increasingly relevant as we introduce additional
1369
+ * meta-programming features.
1370
+ *
1371
+ * **Warning**: Decorators should not be interdependent because the order of
1372
+ * execution of the builder functions in nondeterministic. Builder functions
1373
+ * should only be dependent on the state definition object and super function.
1374
+ *
1375
+ *
1376
+ * Existing builder functions and current return values:
1377
+ *
1378
+ * - parent - `{object}` - returns the parent state object.
1379
+ * - data - `{object}` - returns state data, including any inherited data that is not
1380
+ * overridden by own values (if any).
1381
+ * - url - `{object}` - returns a UrlMatcher or null.
1382
+ * - navigable - returns closest ancestor state that has a URL (aka is
1383
+ * navigable).
1384
+ * - params - `{object}` - returns an array of state params that are ensured to
1385
+ * be a super-set of parent's params.
1386
+ * - views - `{object}` - returns a views object where each key is an absolute view
1387
+ * name (i.e. "viewName@stateName") and each value is the config object
1388
+ * (template, controller) for the view. Even when you don't use the views object
1389
+ * explicitly on a state config, one is still created for you internally.
1390
+ * So by decorating this builder function you have access to decorating template
1391
+ * and controller properties.
1392
+ * - ownParams - `{object}` - returns an array of params that belong to the state,
1393
+ * not including any params defined by ancestor states.
1394
+ * - path - `{string}` - returns the full path from the root down to this state.
1395
+ * Needed for state activation.
1396
+ * - includes - `{object}` - returns an object that includes every state that
1397
+ * would pass a '$state.includes()' test.
1398
+ *
1399
+ * @example
1400
+ * <pre>
1401
+ * // Override the internal 'views' builder with a function that takes the state
1402
+ * // definition, and a reference to the internal function being overridden:
1403
+ * $stateProvider.decorator('views', function ($state, parent) {
1404
+ * var result = {},
1405
+ * views = parent(state);
1406
+ *
1407
+ * angular.forEach(view, function (config, name) {
1408
+ * var autoName = (state.name + '.' + name).replace('.', '/');
1409
+ * config.templateUrl = config.templateUrl || '/partials/' + autoName + '.html';
1410
+ * result[name] = config;
1411
+ * });
1412
+ * return result;
1413
+ * });
1414
+ *
1415
+ * $stateProvider.state('home', {
1416
+ * views: {
1417
+ * 'contact.list': { controller: 'ListController' },
1418
+ * 'contact.item': { controller: 'ItemController' }
1419
+ * }
1420
+ * });
1421
+ *
1422
+ * // ...
1423
+ *
1424
+ * $state.go('home');
1425
+ * // Auto-populates list and item views with /partials/home/contact/list.html,
1426
+ * // and /partials/home/contact/item.html, respectively.
1427
+ * </pre>
1428
+ *
1429
+ * @param {string} name The name of the builder function to decorate.
1430
+ * @param {object} func A function that is responsible for decorating the original
1431
+ * builder function. The function receives two parameters:
1432
+ *
1433
+ * - `{object}` - state - The state config object.
1434
+ * - `{object}` - super - The original builder function.
1435
+ *
1436
+ * @return {object} $stateProvider - $stateProvider instance
1437
+ */
1438
+ this.decorator = decorator;
1439
+ function decorator(name, func) {
1440
+ /*jshint validthis: true */
1441
+ if (isString(name) && !isDefined(func)) {
1442
+ return stateBuilder[name];
1443
+ }
1444
+ if (!isFunction(func) || !isString(name)) {
1445
+ return this;
1446
+ }
1447
+ if (stateBuilder[name] && !stateBuilder.$delegates[name]) {
1448
+ stateBuilder.$delegates[name] = stateBuilder[name];
1449
+ }
1450
+ stateBuilder[name] = func;
1451
+ return this;
1452
+ }
1453
+
1454
+ /**
1455
+ * @ngdoc function
1456
+ * @name ui.router.state.$stateProvider#state
1457
+ * @methodOf ui.router.state.$stateProvider
1458
+ *
1459
+ * @description
1460
+ * Registers a state configuration under a given state name. The stateConfig object
1461
+ * has the following acceptable properties.
1462
+ *
1463
+ * - [`template`, `templateUrl`, `templateProvider`] - There are three ways to setup
1464
+ * your templates.
1465
+ *
1466
+ * - `{string|object}` - template - String HTML content, or function that returns an HTML
1467
+ * string.
1468
+ * - `{string}` - templateUrl - String URL path to template file OR function,
1469
+ * that returns URL path string.
1470
+ * - `{object}` - templateProvider - Provider function that returns HTML content
1471
+ * string.
1472
+ *
1473
+ * - [`controller`, `controllerProvider`] - A controller paired to the state. You can
1474
+ * either use a controller, or a controller provider.
1475
+ *
1476
+ * - `{string|object}` - controller - Controller function or controller name.
1477
+ * - `{object}` - controllerProvider - Injectable provider function that returns
1478
+ * the actual controller or string.
1479
+ *
1480
+ * - `{object}` - resolve - A map of dependencies which should be injected into the
1481
+ * controller.
1482
+ *
1483
+ * - `{string}` - url - A url with optional parameters. When a state is navigated or
1484
+ * transitioned to, the `$stateParams` service will be populated with any
1485
+ * parameters that were passed.
1486
+ *
1487
+ * - `{object}` - params - An array of parameter names or regular expressions. Only
1488
+ * use this within a state if you are not using url. Otherwise you can specify your
1489
+ * parameters within the url. When a state is navigated or transitioned to, the
1490
+ * $stateParams service will be populated with any parameters that were passed.
1491
+ *
1492
+ * - `{object}` - views - Use the views property to set up multiple views.
1493
+ * If you don't need multiple views within a single state this property is not
1494
+ * needed. Tip: remember that often nested views are more useful and powerful
1495
+ * than multiple sibling views.
1496
+ *
1497
+ * - `{boolean}` - abstract - An abstract state will never be directly activated,
1498
+ * but can provide inherited properties to its common children states.
1499
+ *
1500
+ * - `{object}` - onEnter - Callback function for when a state is entered. Good way
1501
+ * to trigger an action or dispatch an event, such as opening a dialog.
1502
+ *
1503
+ * - `{object}` - onExit - Callback function for when a state is exited. Good way to
1504
+ * trigger an action or dispatch an event, such as opening a dialog.
1505
+ *
1506
+ * - `{object}` - data - Arbitrary data object, useful for custom configuration.
1507
+ *
1508
+ * @example
1509
+ * <pre>
1510
+ * // The state() method takes a unique stateName (String) and a stateConfig (Object)
1511
+ * $stateProvider.state(stateName, stateConfig);
1512
+ *
1513
+ * // stateName can be a single top-level name (must be unique).
1514
+ * $stateProvider.state("home", {});
1515
+ *
1516
+ * // Or it can be a nested state name. This state is a child of the above "home" state.
1517
+ * $stateProvider.state("home.newest", {});
1518
+ *
1519
+ * // Nest states as deeply as needed.
1520
+ * $stateProvider.state("home.newest.abc.xyz.inception", {});
1521
+ *
1522
+ * // state() returns $stateProvider, so you can chain state declarations.
1523
+ * $stateProvider
1524
+ * .state("home", {})
1525
+ * .state("about", {})
1526
+ * .state("contacts", {});
1527
+ * </pre>
1528
+ *
1529
+ * @param {string} name A unique state name, e.g. "home", "about", "contacts".
1530
+ * To create a parent/child state use a dot, e.g. "about.sales", "home.newest".
1531
+ * @param {object} definition State configuratino object.
1532
+ */
1533
+ this.state = state;
1534
+ function state(name, definition) {
1535
+ /*jshint validthis: true */
1536
+ if (isObject(name)) definition = name;
1537
+ else definition.name = name;
1538
+ registerState(definition);
1539
+ return this;
1540
+ }
1541
+
1542
+ /**
1543
+ * @ngdoc object
1544
+ * @name ui.router.state.$state
1545
+ *
1546
+ * @requires $rootScope
1547
+ * @requires $q
1548
+ * @requires ui.router.state.$view
1549
+ * @requires $injector
1550
+ * @requires ui.router.util.$resolve
1551
+ * @requires ui.router.state.$stateParams
1552
+ *
1553
+ * @property {object} params A param object, e.g. {sectionId: section.id)}, that
1554
+ * you'd like to test against the current active state.
1555
+ * @property {object} current A reference to the state's config object. However
1556
+ * you passed it in. Useful for accessing custom data.
1557
+ * @property {object} transition Currently pending transition. A promise that'll
1558
+ * resolve or reject.
1559
+ *
1560
+ * @description
1561
+ * `$state` service is responsible for representing states as well as transitioning
1562
+ * between them. It also provides interfaces to ask for current state or even states
1563
+ * you're coming from.
1564
+ */
1565
+ // $urlRouter is injected just to ensure it gets instantiated
1566
+ this.$get = $get;
1567
+ $get.$inject = ['$rootScope', '$q', '$view', '$injector', '$resolve', '$stateParams', '$location', '$urlRouter'];
1568
+ function $get( $rootScope, $q, $view, $injector, $resolve, $stateParams, $location, $urlRouter) {
1569
+
1570
+ var TransitionSuperseded = $q.reject(new Error('transition superseded'));
1571
+ var TransitionPrevented = $q.reject(new Error('transition prevented'));
1572
+ var TransitionAborted = $q.reject(new Error('transition aborted'));
1573
+ var TransitionFailed = $q.reject(new Error('transition failed'));
1574
+ var currentLocation = $location.url();
1575
+
1576
+ function syncUrl() {
1577
+ if ($location.url() !== currentLocation) {
1578
+ $location.url(currentLocation);
1579
+ $location.replace();
1580
+ }
1581
+ }
1582
+
1583
+ root.locals = { resolve: null, globals: { $stateParams: {} } };
1584
+ $state = {
1585
+ params: {},
1586
+ current: root.self,
1587
+ $current: root,
1588
+ transition: null
1589
+ };
1590
+
1591
+ /**
1592
+ * @ngdoc function
1593
+ * @name ui.router.state.$state#reload
1594
+ * @methodOf ui.router.state.$state
1595
+ *
1596
+ * @description
1597
+ * Reloads the current state by re-transitioning to it.
1598
+ *
1599
+ * @example
1600
+ * <pre>
1601
+ * var app angular.module('app', ['ui.router.state']);
1602
+ *
1603
+ * app.controller('ctrl', function ($state) {
1604
+ * $state.reload();
1605
+ * });
1606
+ * </pre>
1607
+ */
1608
+ $state.reload = function reload() {
1609
+ $state.transitionTo($state.current, $stateParams, { reload: true, inherit: false, notify: false });
1610
+ };
1611
+
1612
+ /**
1613
+ * @ngdoc function
1614
+ * @name ui.router.state.$state#go
1615
+ * @methodOf ui.router.state.$state
1616
+ *
1617
+ * @description
1618
+ * Convenience method for transitioning to a new state. `$state.go` calls
1619
+ * `$state.transitionTo` internally but automatically sets options to
1620
+ * `{ location: true, inherit: true, relative: $state.$current, notify: true }`.
1621
+ * This allows you to easily use an absolute or relative to path and specify
1622
+ * only the parameters you'd like to update (while letting unspecified parameters
1623
+ * inherit from the current state.
1624
+ *
1625
+ * Some examples:
1626
+ *
1627
+ * - `$state.go('contact.detail')` - will go to the `contact.detail` state
1628
+ * - `$state.go('^')` - will go to a parent state
1629
+ * - `$state.go('^.sibling')` - will go to a sibling state
1630
+ * - `$state.go('.child.grandchild')` - will go to grandchild state
1631
+ *
1632
+ * @example
1633
+ * <pre>
1634
+ * var app = angular.module('app', ['ui.router.state']);
1635
+ *
1636
+ * app.controller('ctrl', function ($scope, $state) {
1637
+ * $scope.changeState = function () {
1638
+ * $state.go('contact.detail');
1639
+ * };
1640
+ * });
1641
+ * </pre>
1642
+ *
1643
+ * @param {string} to Absolute State Name or Relative State Path.
1644
+ * @param {object} params A map of the parameters that will be sent to the state,
1645
+ * will populate $stateParams.
1646
+ * @param {object} options If Object is passed, object is an options hash.
1647
+ */
1648
+ $state.go = function go(to, params, options) {
1649
+ return this.transitionTo(to, params, extend({ inherit: true, relative: $state.$current }, options));
1650
+ };
1651
+
1652
+ /**
1653
+ * @ngdoc function
1654
+ * @name ui.router.state.$state#transitionTo
1655
+ * @methodOf ui.router.state.$state
1656
+ *
1657
+ * @description
1658
+ * Low-level method for transitioning to a new state. {@link ui.router.state.$state#methods_go $state.go}
1659
+ * uses `transitionTo` internally. `$state.go` is recommended in most situations.
1660
+ *
1661
+ * @example
1662
+ * <pre>
1663
+ * var app = angular.module('app', ['ui.router.state']);
1664
+ *
1665
+ * app.controller('ctrl', function ($scope, $state) {
1666
+ * $scope.changeState = function () {
1667
+ * $state.transitionTo('contact.detail');
1668
+ * };
1669
+ * });
1670
+ * </pre>
1671
+ *
1672
+ * @param {string} to Absolute State Name or Relative State Path.
1673
+ * @param {object} params A map of the parameters that will be sent to the state,
1674
+ * will populate $stateParams.
1675
+ * @param {object} options If Object is passed, object is an options hash.
1676
+ */
1677
+ $state.transitionTo = function transitionTo(to, toParams, options) {
1678
+ toParams = toParams || {};
1679
+ options = extend({
1680
+ location: true, inherit: false, relative: null, notify: true, reload: false, $retry: false
1681
+ }, options || {});
1682
+
1683
+ var from = $state.$current, fromParams = $state.params, fromPath = from.path;
1684
+ var evt, toState = findState(to, options.relative);
1685
+
1686
+ if (!isDefined(toState)) {
1687
+ // Broadcast not found event and abort the transition if prevented
1688
+ var redirect = { to: to, toParams: toParams, options: options };
1689
+ evt = $rootScope.$broadcast('$stateNotFound', redirect, from.self, fromParams);
1690
+ if (evt.defaultPrevented) {
1691
+ syncUrl();
1692
+ return TransitionAborted;
1693
+ }
1694
+
1695
+ // Allow the handler to return a promise to defer state lookup retry
1696
+ if (evt.retry) {
1697
+ if (options.$retry) {
1698
+ syncUrl();
1699
+ return TransitionFailed;
1700
+ }
1701
+ var retryTransition = $state.transition = $q.when(evt.retry);
1702
+ retryTransition.then(function() {
1703
+ if (retryTransition !== $state.transition) return TransitionSuperseded;
1704
+ redirect.options.$retry = true;
1705
+ return $state.transitionTo(redirect.to, redirect.toParams, redirect.options);
1706
+ }, function() {
1707
+ return TransitionAborted;
1708
+ });
1709
+ syncUrl();
1710
+ return retryTransition;
1711
+ }
1712
+
1713
+ // Always retry once if the $stateNotFound was not prevented
1714
+ // (handles either redirect changed or state lazy-definition)
1715
+ to = redirect.to;
1716
+ toParams = redirect.toParams;
1717
+ options = redirect.options;
1718
+ toState = findState(to, options.relative);
1719
+ if (!isDefined(toState)) {
1720
+ if (options.relative) throw new Error("Could not resolve '" + to + "' from state '" + options.relative + "'");
1721
+ throw new Error("No such state '" + to + "'");
1722
+ }
1723
+ }
1724
+ if (toState[abstractKey]) throw new Error("Cannot transition to abstract state '" + to + "'");
1725
+ if (options.inherit) toParams = inheritParams($stateParams, toParams || {}, $state.$current, toState);
1726
+ to = toState;
1727
+
1728
+ var toPath = to.path;
1729
+
1730
+ // Starting from the root of the path, keep all levels that haven't changed
1731
+ var keep, state, locals = root.locals, toLocals = [];
1732
+ for (keep = 0, state = toPath[keep];
1733
+ state && state === fromPath[keep] && equalForKeys(toParams, fromParams, state.ownParams) && !options.reload;
1734
+ keep++, state = toPath[keep]) {
1735
+ locals = toLocals[keep] = state.locals;
1736
+ }
1737
+
1738
+ // If we're going to the same state and all locals are kept, we've got nothing to do.
1739
+ // But clear 'transition', as we still want to cancel any other pending transitions.
1740
+ // TODO: We may not want to bump 'transition' if we're called from a location change that we've initiated ourselves,
1741
+ // because we might accidentally abort a legitimate transition initiated from code?
1742
+ if (shouldTriggerReload(to, from, locals, options) ) {
1743
+ if ( to.self.reloadOnSearch !== false )
1744
+ syncUrl();
1745
+ $state.transition = null;
1746
+ return $q.when($state.current);
1747
+ }
1748
+
1749
+ // Normalize/filter parameters before we pass them to event handlers etc.
1750
+ toParams = normalize(to.params, toParams || {});
1751
+
1752
+ // Broadcast start event and cancel the transition if requested
1753
+ if (options.notify) {
1754
+ evt = $rootScope.$broadcast('$stateChangeStart', to.self, toParams, from.self, fromParams);
1755
+ if (evt.defaultPrevented) {
1756
+ syncUrl();
1757
+ return TransitionPrevented;
1758
+ }
1759
+ }
1760
+
1761
+ // Resolve locals for the remaining states, but don't update any global state just
1762
+ // yet -- if anything fails to resolve the current state needs to remain untouched.
1763
+ // We also set up an inheritance chain for the locals here. This allows the view directive
1764
+ // to quickly look up the correct definition for each view in the current state. Even
1765
+ // though we create the locals object itself outside resolveState(), it is initially
1766
+ // empty and gets filled asynchronously. We need to keep track of the promise for the
1767
+ // (fully resolved) current locals, and pass this down the chain.
1768
+ var resolved = $q.when(locals);
1769
+ for (var l=keep; l<toPath.length; l++, state=toPath[l]) {
1770
+ locals = toLocals[l] = inherit(locals);
1771
+ resolved = resolveState(state, toParams, state===to, resolved, locals);
1772
+ }
1773
+
1774
+ // Once everything is resolved, we are ready to perform the actual transition
1775
+ // and return a promise for the new state. We also keep track of what the
1776
+ // current promise is, so that we can detect overlapping transitions and
1777
+ // keep only the outcome of the last transition.
1778
+ var transition = $state.transition = resolved.then(function () {
1779
+ var l, entering, exiting;
1780
+
1781
+ if ($state.transition !== transition) return TransitionSuperseded;
1782
+
1783
+ // Exit 'from' states not kept
1784
+ for (l=fromPath.length-1; l>=keep; l--) {
1785
+ exiting = fromPath[l];
1786
+ if (exiting.self.onExit) {
1787
+ $injector.invoke(exiting.self.onExit, exiting.self, exiting.locals.globals);
1788
+ }
1789
+ exiting.locals = null;
1790
+ }
1791
+
1792
+ // Enter 'to' states not kept
1793
+ for (l=keep; l<toPath.length; l++) {
1794
+ entering = toPath[l];
1795
+ entering.locals = toLocals[l];
1796
+ if (entering.self.onEnter) {
1797
+ $injector.invoke(entering.self.onEnter, entering.self, entering.locals.globals);
1798
+ }
1799
+ }
1800
+
1801
+ // Run it again, to catch any transitions in callbacks
1802
+ if ($state.transition !== transition) return TransitionSuperseded;
1803
+
1804
+ // Update globals in $state
1805
+ $state.$current = to;
1806
+ $state.current = to.self;
1807
+ $state.params = toParams;
1808
+ copy($state.params, $stateParams);
1809
+ $state.transition = null;
1810
+
1811
+ // Update $location
1812
+ var toNav = to.navigable;
1813
+ if (options.location && toNav) {
1814
+ $location.url(toNav.url.format(toNav.locals.globals.$stateParams));
1815
+
1816
+ if (options.location === 'replace') {
1817
+ $location.replace();
1818
+ }
1819
+ }
1820
+
1821
+ if (options.notify) {
1822
+ $rootScope.$broadcast('$stateChangeSuccess', to.self, toParams, from.self, fromParams);
1823
+ }
1824
+ currentLocation = $location.url();
1825
+
1826
+ return $state.current;
1827
+ }, function (error) {
1828
+ if ($state.transition !== transition) return TransitionSuperseded;
1829
+
1830
+ $state.transition = null;
1831
+ $rootScope.$broadcast('$stateChangeError', to.self, toParams, from.self, fromParams, error);
1832
+ syncUrl();
1833
+
1834
+ return $q.reject(error);
1835
+ });
1836
+
1837
+ return transition;
1838
+ };
1839
+
1840
+ /**
1841
+ * @ngdoc function
1842
+ * @name ui.router.state.$state#is
1843
+ * @methodOf ui.router.state.$state
1844
+ *
1845
+ * @description
1846
+ * Similar to {@link ui.router.state.$state#methods_includes $state.includes},
1847
+ * but only checks for the full state name. If params is supplied then it will be
1848
+ * tested for strict equality against the current active params object, so all params
1849
+ * must match with none missing and no extras.
1850
+ *
1851
+ * @example
1852
+ * <pre>
1853
+ * $state.is('contact.details.item'); // returns true
1854
+ * $state.is(contactDetailItemStateObject); // returns true
1855
+ *
1856
+ * // everything else would return false
1857
+ * </pre>
1858
+ *
1859
+ * @param {string|object} stateName The state name or state object you'd like to check.
1860
+ * @param {object} params A param object, e.g. `{sectionId: section.id}`, that you'd like
1861
+ * to test against the current active state.
1862
+ * @returns {boolean} Returns true or false whether its the state or not.
1863
+ */
1864
+ $state.is = function is(stateOrName, params) {
1865
+ var state = findState(stateOrName);
1866
+
1867
+ if (!isDefined(state)) {
1868
+ return undefined;
1869
+ }
1870
+
1871
+ if ($state.$current !== state) {
1872
+ return false;
1873
+ }
1874
+
1875
+ return isDefined(params) && params !== null ? angular.equals($stateParams, params) : true;
1876
+ };
1877
+
1878
+ /**
1879
+ * @ngdoc function
1880
+ * @name ui.router.state.$state#includes
1881
+ * @methodOf ui.router.state.$state
1882
+ *
1883
+ * @description
1884
+ * A method to determine if the current active state is equal to or is the child of the
1885
+ * state stateName. If any params are passed then they will be tested for a match as well.
1886
+ * Not all the parameters need to be passed, just the ones you'd like to test for equality.
1887
+ *
1888
+ * @example
1889
+ * <pre>
1890
+ * $state.includes("contacts"); // returns true
1891
+ * $state.includes("contacts.details"); // returns true
1892
+ * $state.includes("contacts.details.item"); // returns true
1893
+ * $state.includes("contacts.list"); // returns false
1894
+ * $state.includes("about"); // returns false
1895
+ * </pre>
1896
+ *
1897
+ * @param {string} stateOrName A partial name to be searched for within the current state name.
1898
+ * @param {object} params A param object, e.g. `{sectionId: section.id}`,
1899
+ * that you'd like to test against the current active state.
1900
+ * @returns {boolean} True or false
1901
+ */
1902
+ $state.includes = function includes(stateOrName, params) {
1903
+ var state = findState(stateOrName);
1904
+ if (!isDefined(state)) {
1905
+ return undefined;
1906
+ }
1907
+
1908
+ if (!isDefined($state.$current.includes[state.name])) {
1909
+ return false;
1910
+ }
1911
+
1912
+ var validParams = true;
1913
+ angular.forEach(params, function(value, key) {
1914
+ if (!isDefined($stateParams[key]) || $stateParams[key] !== value) {
1915
+ validParams = false;
1916
+ }
1917
+ });
1918
+ return validParams;
1919
+ };
1920
+
1921
+ /**
1922
+ * @ngdoc function
1923
+ * @name ui.router.state.$state#href
1924
+ * @methodOf ui.router.state.$state
1925
+ *
1926
+ * @description
1927
+ * A url generation method that returns the compiled url for the given state populated with the given params.
1928
+ *
1929
+ * @example
1930
+ * <pre>
1931
+ * expect($state.href("about.person", { person: "bob" })).toEqual("/about/bob");
1932
+ * </pre>
1933
+ *
1934
+ * @param {string|object} stateOrName The state name or state object you'd like to generate a url from.
1935
+ * @param {object} params An object of parameter values to fill the state's required parameters.
1936
+ * @returns {string} url
1937
+ */
1938
+ $state.href = function href(stateOrName, params, options) {
1939
+ options = extend({ lossy: true, inherit: false, absolute: false, relative: $state.$current }, options || {});
1940
+ var state = findState(stateOrName, options.relative);
1941
+ if (!isDefined(state)) return null;
1942
+
1943
+ params = inheritParams($stateParams, params || {}, $state.$current, state);
1944
+ var nav = (state && options.lossy) ? state.navigable : state;
1945
+ var url = (nav && nav.url) ? nav.url.format(normalize(state.params, params || {})) : null;
1946
+ if (!$locationProvider.html5Mode() && url) {
1947
+ url = "#" + $locationProvider.hashPrefix() + url;
1948
+ }
1949
+ if (options.absolute && url) {
1950
+ url = $location.protocol() + '://' +
1951
+ $location.host() +
1952
+ ($location.port() == 80 || $location.port() == 443 ? '' : ':' + $location.port()) +
1953
+ (!$locationProvider.html5Mode() && url ? '/' : '') +
1954
+ url;
1955
+ }
1956
+ return url;
1957
+ };
1958
+
1959
+ /**
1960
+ * @ngdoc function
1961
+ * @name ui.router.state.$state#get
1962
+ * @methodOf ui.router.state.$state
1963
+ *
1964
+ * @description
1965
+ * Returns the state configuration object for any state by passing the name
1966
+ * as a string. Without any arguments it'll return a array of all configured
1967
+ * state objects.
1968
+ *
1969
+ * @param {string|object} stateOrName The name of the state for which you'd like
1970
+ * to get the original state configuration object for.
1971
+ * @returns {object} State configuration object or array of all objects.
1972
+ */
1973
+ $state.get = function (stateOrName, context) {
1974
+ if (!isDefined(stateOrName)) {
1975
+ var list = [];
1976
+ forEach(states, function(state) { list.push(state.self); });
1977
+ return list;
1978
+ }
1979
+ var state = findState(stateOrName, context);
1980
+ return (state && state.self) ? state.self : null;
1981
+ };
1982
+
1983
+ function resolveState(state, params, paramsAreFiltered, inherited, dst) {
1984
+ // Make a restricted $stateParams with only the parameters that apply to this state if
1985
+ // necessary. In addition to being available to the controller and onEnter/onExit callbacks,
1986
+ // we also need $stateParams to be available for any $injector calls we make during the
1987
+ // dependency resolution process.
1988
+ var $stateParams = (paramsAreFiltered) ? params : filterByKeys(state.params, params);
1989
+ var locals = { $stateParams: $stateParams };
1990
+
1991
+ // Resolve 'global' dependencies for the state, i.e. those not specific to a view.
1992
+ // We're also including $stateParams in this; that way the parameters are restricted
1993
+ // to the set that should be visible to the state, and are independent of when we update
1994
+ // the global $state and $stateParams values.
1995
+ dst.resolve = $resolve.resolve(state.resolve, locals, dst.resolve, state);
1996
+ var promises = [ dst.resolve.then(function (globals) {
1997
+ dst.globals = globals;
1998
+ }) ];
1999
+ if (inherited) promises.push(inherited);
2000
+
2001
+ // Resolve template and dependencies for all views.
2002
+ forEach(state.views, function (view, name) {
2003
+ var injectables = (view.resolve && view.resolve !== state.resolve ? view.resolve : {});
2004
+ injectables.$template = [ function () {
2005
+ return $view.load(name, { view: view, locals: locals, params: $stateParams, notify: false }) || '';
2006
+ }];
2007
+
2008
+ promises.push($resolve.resolve(injectables, locals, dst.resolve, state).then(function (result) {
2009
+ // References to the controller (only instantiated at link time)
2010
+ if (isFunction(view.controllerProvider) || isArray(view.controllerProvider)) {
2011
+ var injectLocals = angular.extend({}, injectables, locals);
2012
+ result.$$controller = $injector.invoke(view.controllerProvider, null, injectLocals);
2013
+ } else {
2014
+ result.$$controller = view.controller;
2015
+ }
2016
+ // Provide access to the state itself for internal use
2017
+ result.$$state = state;
2018
+ dst[name] = result;
2019
+ }));
2020
+ });
2021
+
2022
+ // Wait for all the promises and then return the activation object
2023
+ return $q.all(promises).then(function (values) {
2024
+ return dst;
2025
+ });
2026
+ }
2027
+
2028
+ return $state;
2029
+ }
2030
+
2031
+ function shouldTriggerReload(to, from, locals, options) {
2032
+ if ( to === from && ((locals === from.locals && !options.reload) || (to.self.reloadOnSearch === false)) ) {
2033
+ return true;
2034
+ }
2035
+ }
2036
+ }
2037
+
2038
+ angular.module('ui.router.state')
2039
+ .value('$stateParams', {})
2040
+ .provider('$state', $StateProvider);
2041
+
2042
+
2043
+ $ViewProvider.$inject = [];
2044
+ function $ViewProvider() {
2045
+
2046
+ this.$get = $get;
2047
+ /**
2048
+ * @ngdoc object
2049
+ * @name ui.router.state.$view
2050
+ *
2051
+ * @requires ui.router.util.$templateFactory
2052
+ * @requires $rootScope
2053
+ *
2054
+ * @description
2055
+ *
2056
+ */
2057
+ $get.$inject = ['$rootScope', '$templateFactory'];
2058
+ function $get( $rootScope, $templateFactory) {
2059
+ return {
2060
+ // $view.load('full.viewName', { template: ..., controller: ..., resolve: ..., async: false, params: ... })
2061
+ /**
2062
+ * @ngdoc function
2063
+ * @name ui.router.state.$view#load
2064
+ * @methodOf ui.router.state.$view
2065
+ *
2066
+ * @description
2067
+ *
2068
+ * @param {string} name name
2069
+ * @param {object} options option object.
2070
+ */
2071
+ load: function load(name, options) {
2072
+ var result, defaults = {
2073
+ template: null, controller: null, view: null, locals: null, notify: true, async: true, params: {}
2074
+ };
2075
+ options = extend(defaults, options);
2076
+
2077
+ if (options.view) {
2078
+ result = $templateFactory.fromConfig(options.view, options.params, options.locals);
2079
+ }
2080
+ if (result && options.notify) {
2081
+ $rootScope.$broadcast('$viewContentLoading', options);
2082
+ }
2083
+ return result;
2084
+ }
2085
+ };
2086
+ }
2087
+ }
2088
+
2089
+ angular.module('ui.router.state').provider('$view', $ViewProvider);
2090
+
2091
+ /**
2092
+ * @ngdoc object
2093
+ * @name ui.router.state.$uiViewScroll
2094
+ *
2095
+ * @requires $anchorScroll
2096
+ * @requires $timeout
2097
+ *
2098
+ * @description
2099
+ * When called with a jqLite element, it scrolls the element into view (after a
2100
+ * `$timeout` so the DOM has time to refresh).
2101
+ *
2102
+ * If you prefer to rely on `$anchorScroll` to scroll the view to the anchor,
2103
+ * this can be enabled by calling `$uiViewScrollProvider.useAnchorScroll()`.
2104
+ */
2105
+ function $ViewScrollProvider() {
2106
+
2107
+ var useAnchorScroll = false;
2108
+
2109
+ this.useAnchorScroll = function () {
2110
+ useAnchorScroll = true;
2111
+ };
2112
+
2113
+ this.$get = ['$anchorScroll', '$timeout', function ($anchorScroll, $timeout) {
2114
+ if (useAnchorScroll) {
2115
+ return $anchorScroll;
2116
+ }
2117
+
2118
+ return function ($element) {
2119
+ $timeout(function () {
2120
+ $element[0].scrollIntoView();
2121
+ }, 0, false);
2122
+ };
2123
+ }];
2124
+ }
2125
+
2126
+ angular.module('ui.router.state').provider('$uiViewScroll', $ViewScrollProvider);
2127
+
2128
+ /**
2129
+ * @ngdoc directive
2130
+ * @name ui.router.state.diretive.ui-view
2131
+ *
2132
+ * @requires ui.router.state.$state
2133
+ * @requires $compile
2134
+ * @requires $controller
2135
+ * @requires $injector
2136
+ *
2137
+ * @restrict ECA
2138
+ *
2139
+ * @description
2140
+ * The ui-view directive tells $state where to place your templates.
2141
+ * A view can be unnamed or named.
2142
+ *
2143
+ * @param {string} ui-view A view name.
2144
+ */
2145
+ $ViewDirective.$inject = ['$state', '$compile', '$controller', '$injector', '$uiViewScroll', '$document'];
2146
+ function $ViewDirective( $state, $compile, $controller, $injector, $uiViewScroll, $document) {
2147
+
2148
+ function getService() {
2149
+ return ($injector.has) ? function(service) {
2150
+ return $injector.has(service) ? $injector.get(service) : null;
2151
+ } : function(service) {
2152
+ try {
2153
+ return $injector.get(service);
2154
+ } catch (e) {
2155
+ return null;
2156
+ }
2157
+ };
2158
+ }
2159
+
2160
+ var viewIsUpdating = false,
2161
+ service = getService(),
2162
+ $animator = service('$animator'),
2163
+ $animate = service('$animate');
2164
+
2165
+ // Returns a set of DOM manipulation functions based on whether animation
2166
+ // should be performed
2167
+ function getRenderer(element, attrs, scope) {
2168
+ var statics = function() {
2169
+ return {
2170
+ leave: function (element) { element.remove(); },
2171
+ enter: function (element, parent, anchor) { anchor.after(element); }
2172
+ };
2173
+ };
2174
+
2175
+ if ($animate) {
2176
+ return function(shouldAnimate) {
2177
+ return !shouldAnimate ? statics() : {
2178
+ enter: function(element, parent, anchor) { $animate.enter(element, null, anchor); },
2179
+ leave: function(element) { $animate.leave(element, function() { element.remove(); }); }
2180
+ };
2181
+ };
2182
+ }
2183
+
2184
+ if ($animator) {
2185
+ var animate = $animator && $animator(scope, attrs);
2186
+
2187
+ return function(shouldAnimate) {
2188
+ return !shouldAnimate ? statics() : {
2189
+ enter: function(element, parent, anchor) { animate.enter(element, parent); },
2190
+ leave: function(element) { animate.leave(element.contents(), element); }
2191
+ };
2192
+ };
2193
+ }
2194
+
2195
+ return statics;
2196
+ }
2197
+
2198
+ var directive = {
2199
+ restrict: 'ECA',
2200
+ compile: function (element, attrs) {
2201
+ var initial = element.html(),
2202
+ isDefault = true,
2203
+ anchor = angular.element($document[0].createComment(' ui-view-anchor ')),
2204
+ parentEl = element.parent();
2205
+
2206
+ element.prepend(anchor);
2207
+
2208
+ return function ($scope) {
2209
+ var inherited = parentEl.inheritedData('$uiView');
2210
+
2211
+ var currentScope, currentEl, viewLocals,
2212
+ name = attrs[directive.name] || attrs.name || '',
2213
+ onloadExp = attrs.onload || '',
2214
+ autoscrollExp = attrs.autoscroll,
2215
+ renderer = getRenderer(element, attrs, $scope);
2216
+
2217
+ if (name.indexOf('@') < 0) name = name + '@' + (inherited ? inherited.state.name : '');
2218
+ var view = { name: name, state: null };
2219
+
2220
+ var eventHook = function () {
2221
+ if (viewIsUpdating) return;
2222
+ viewIsUpdating = true;
2223
+
2224
+ try { updateView(true); } catch (e) {
2225
+ viewIsUpdating = false;
2226
+ throw e;
2227
+ }
2228
+ viewIsUpdating = false;
2229
+ };
2230
+
2231
+ $scope.$on('$stateChangeSuccess', eventHook);
2232
+ $scope.$on('$viewContentLoading', eventHook);
2233
+
2234
+ updateView(false);
2235
+
2236
+ function cleanupLastView() {
2237
+ if (currentEl) {
2238
+ renderer(true).leave(currentEl);
2239
+ currentEl = null;
2240
+ }
2241
+
2242
+ if (currentScope) {
2243
+ currentScope.$destroy();
2244
+ currentScope = null;
2245
+ }
2246
+ }
2247
+
2248
+ function updateView(shouldAnimate) {
2249
+ var locals = $state.$current && $state.$current.locals[name];
2250
+
2251
+ if (isDefault) {
2252
+ isDefault = false;
2253
+ element.replaceWith(anchor);
2254
+ }
2255
+
2256
+ if (!locals) {
2257
+ cleanupLastView();
2258
+ currentEl = element.clone();
2259
+ currentEl.html(initial);
2260
+ renderer(shouldAnimate).enter(currentEl, parentEl, anchor);
2261
+
2262
+ currentScope = $scope.$new();
2263
+ $compile(currentEl.contents())(currentScope);
2264
+ return;
2265
+ }
2266
+
2267
+ if (locals === viewLocals) return; // nothing to do
2268
+
2269
+ cleanupLastView();
2270
+
2271
+ currentEl = element.clone();
2272
+ currentEl.html(locals.$template ? locals.$template : initial);
2273
+ renderer(true).enter(currentEl, parentEl, anchor);
2274
+
2275
+ currentEl.data('$uiView', view);
2276
+
2277
+ viewLocals = locals;
2278
+ view.state = locals.$$state;
2279
+
2280
+ var link = $compile(currentEl.contents());
2281
+
2282
+ currentScope = $scope.$new();
2283
+
2284
+ if (locals.$$controller) {
2285
+ locals.$scope = currentScope;
2286
+ var controller = $controller(locals.$$controller, locals);
2287
+ currentEl.children().data('$ngControllerController', controller);
2288
+ }
2289
+
2290
+ link(currentScope);
2291
+
2292
+ currentScope.$emit('$viewContentLoaded');
2293
+ if (onloadExp) currentScope.$eval(onloadExp);
2294
+
2295
+ if (!angular.isDefined(autoscrollExp) || !autoscrollExp || $scope.$eval(autoscrollExp)) {
2296
+ $uiViewScroll(currentEl);
2297
+ }
2298
+ }
2299
+ };
2300
+ }
2301
+ };
2302
+
2303
+ return directive;
2304
+ }
2305
+
2306
+ angular.module('ui.router.state').directive('uiView', $ViewDirective);
2307
+
2308
+ function parseStateRef(ref) {
2309
+ var parsed = ref.replace(/\n/g, " ").match(/^([^(]+?)\s*(\((.*)\))?$/);
2310
+ if (!parsed || parsed.length !== 4) throw new Error("Invalid state ref '" + ref + "'");
2311
+ return { state: parsed[1], paramExpr: parsed[3] || null };
2312
+ }
2313
+
2314
+ function stateContext(el) {
2315
+ var stateData = el.parent().inheritedData('$uiView');
2316
+
2317
+ if (stateData && stateData.state && stateData.state.name) {
2318
+ return stateData.state;
2319
+ }
2320
+ }
2321
+
2322
+ /**
2323
+ * @ngdoc directive
2324
+ * @name ui.router.state.directive:ui-sref
2325
+ *
2326
+ * @requires ui.router.state.$state
2327
+ * @requires $timeout
2328
+ *
2329
+ * @restrict A
2330
+ *
2331
+ * @description
2332
+ * A directive that binds a link (`<a>` tag) to a state. If the state has an associated
2333
+ * URL, the directive will automatically generate & update the `href` attribute via
2334
+ * the {@link ui.router.state.$state#methods_href $state.href()} method. Clicking
2335
+ * the link will trigger a state transition with optional parameters.
2336
+ *
2337
+ * Also middle-clicking, right-clicking, and ctrl-clicking on the link will be
2338
+ * handled natively by the browser.
2339
+ *
2340
+ * You can also use relative state paths within ui-sref, just like the relative
2341
+ * paths passed to `$state.go()`. You just need to be aware that the path is relative
2342
+ * to the state that the link lives in, in other words the state that loaded the
2343
+ * template containing the link.
2344
+ *
2345
+ * @example
2346
+ * <pre>
2347
+ * <a ui-sref="home">Home</a> | <a ui-sref="about">About</a>
2348
+ *
2349
+ * <ul>
2350
+ * <li ng-repeat="contact in contacts">
2351
+ * <a ui-sref="contacts.detail({ id: contact.id })">{{ contact.name }}</a>
2352
+ * </li>
2353
+ * </ul>
2354
+ * </pre>
2355
+ *
2356
+ * @param {string} ui-sref 'stateName' can be any valid absolute or relative state
2357
+ */
2358
+ $StateRefDirective.$inject = ['$state', '$timeout'];
2359
+ function $StateRefDirective($state, $timeout) {
2360
+ return {
2361
+ restrict: 'A',
2362
+ require: '?^uiSrefActive',
2363
+ link: function(scope, element, attrs, uiSrefActive) {
2364
+ var ref = parseStateRef(attrs.uiSref);
2365
+ var params = null, url = null, base = stateContext(element) || $state.$current;
2366
+ var isForm = element[0].nodeName === "FORM";
2367
+ var attr = isForm ? "action" : "href", nav = true;
2368
+
2369
+ var update = function(newVal) {
2370
+ if (newVal) params = newVal;
2371
+ if (!nav) return;
2372
+
2373
+ var newHref = $state.href(ref.state, params, { relative: base });
2374
+
2375
+ if (uiSrefActive) {
2376
+ uiSrefActive.$$setStateInfo(ref.state, params);
2377
+ }
2378
+ if (!newHref) {
2379
+ nav = false;
2380
+ return false;
2381
+ }
2382
+ element[0][attr] = newHref;
2383
+ };
2384
+
2385
+ if (ref.paramExpr) {
2386
+ scope.$watch(ref.paramExpr, function(newVal, oldVal) {
2387
+ if (newVal !== params) update(newVal);
2388
+ }, true);
2389
+ params = scope.$eval(ref.paramExpr);
2390
+ }
2391
+ update();
2392
+
2393
+ if (isForm) return;
2394
+
2395
+ element.bind("click", function(e) {
2396
+ var button = e.which || e.button;
2397
+ if ((button === 0 || button == 1) && !e.ctrlKey && !e.metaKey && !e.shiftKey && !element.attr('target')) {
2398
+ // HACK: This is to allow ng-clicks to be processed before the transition is initiated:
2399
+ $timeout(function() {
2400
+ $state.go(ref.state, params, { relative: base });
2401
+ });
2402
+ e.preventDefault();
2403
+ }
2404
+ });
2405
+ }
2406
+ };
2407
+ }
2408
+
2409
+ /**
2410
+ * @ngdoc directive
2411
+ * @name ui.router.state.directive:ui-sref-active
2412
+ *
2413
+ * @requires ui.router.state.$state
2414
+ * @requires ui.router.state.$stateParams
2415
+ * @requires $interpolate
2416
+ *
2417
+ * @restrict A
2418
+ *
2419
+ * @description
2420
+ * A directive working alongside ui-sref to add classes to an element when the
2421
+ * related ui-sref directive's state is active, and removing them when it is inactive.
2422
+ * The primary use-case is to simplify the special appearance of navigation menus
2423
+ * relying on `ui-sref`, by having the "active" state's menu button appear different,
2424
+ * distinguishing it from the inactive menu items.
2425
+ *
2426
+ * @example
2427
+ * <pre>
2428
+ * <ul>
2429
+ * <li ui-sref-active="active" class="item active">
2430
+ * <a ui-sref="app.user({user: 'bilbobaggins'})" href="/users/bilbobaggins">@bilbobaggins</a>
2431
+ * </li>
2432
+ * <!-- ... -->
2433
+ * </ul>
2434
+ * </pre>
2435
+ */
2436
+ $StateActiveDirective.$inject = ['$state', '$stateParams', '$interpolate'];
2437
+ function $StateActiveDirective($state, $stateParams, $interpolate) {
2438
+ return {
2439
+ restrict: "A",
2440
+ controller: ['$scope', '$element', '$attrs', function($scope, $element, $attrs) {
2441
+ var state, params, activeClass;
2442
+
2443
+ // There probably isn't much point in $observing this
2444
+ activeClass = $interpolate($attrs.uiSrefActive || '', false)($scope);
2445
+
2446
+ // Allow uiSref to communicate with uiSrefActive
2447
+ this.$$setStateInfo = function(newState, newParams) {
2448
+ state = $state.get(newState, stateContext($element));
2449
+ params = newParams;
2450
+ update();
2451
+ };
2452
+
2453
+ $scope.$on('$stateChangeSuccess', update);
2454
+
2455
+ // Update route state
2456
+ function update() {
2457
+ if ($state.$current.self === state && matchesParams()) {
2458
+ $element.addClass(activeClass);
2459
+ } else {
2460
+ $element.removeClass(activeClass);
2461
+ }
2462
+ }
2463
+
2464
+ function matchesParams() {
2465
+ return !params || equalForKeys(params, $stateParams);
2466
+ }
2467
+ }]
2468
+ };
2469
+ }
2470
+
2471
+ angular.module('ui.router.state')
2472
+ .directive('uiSref', $StateRefDirective)
2473
+ .directive('uiSrefActive', $StateActiveDirective);
2474
+
2475
+ /**
2476
+ * @ngdoc filter
2477
+ * @name ui.router.state.filter:isState
2478
+ *
2479
+ * @requires ui.router.state.$state
2480
+ *
2481
+ * @description
2482
+ * Translates to {@link ui.router.state.$state#is $state.is("stateName")}.
2483
+ */
2484
+ $IsStateFilter.$inject = ['$state'];
2485
+ function $IsStateFilter($state) {
2486
+ return function(state) {
2487
+ return $state.is(state);
2488
+ };
2489
+ }
2490
+
2491
+ /**
2492
+ * @ngdoc filter
2493
+ * @name ui.router.state.filter:includeByState
2494
+ *
2495
+ * @requires ui.router.state.$state
2496
+ *
2497
+ * @description
2498
+ * Translates to {@link ui.router.state.$state#includes $state.includes()}.
2499
+ */
2500
+ $IncludedByStateFilter.$inject = ['$state'];
2501
+ function $IncludedByStateFilter($state) {
2502
+ return function(state) {
2503
+ return $state.includes(state);
2504
+ };
2505
+ }
2506
+
2507
+ angular.module('ui.router.state')
2508
+ .filter('isState', $IsStateFilter)
2509
+ .filter('includedByState', $IncludedByStateFilter);
2510
+
2511
+ /**
2512
+ * @ngdoc object
2513
+ * @name ui.router.compat.$routeProvider
2514
+ *
2515
+ * @requires ui.router.state.$stateProvider
2516
+ * @requires ui.router.router.$urlRouterProvider
2517
+ *
2518
+ * @description
2519
+ * `$routeProvider` of the `ui.router.compat` module overwrites the existing
2520
+ * `routeProvider` from the core. This is done to provide compatibility between
2521
+ * the UI Router and the core router.
2522
+ *
2523
+ * It also provides a `when()` method to register routes that map to certain urls.
2524
+ * Behind the scenes it actually delegates either to
2525
+ * {@link ui.router.router.$urlRouterProvider $urlRouterProvider} or to the
2526
+ * {@link ui.router.state.$stateProvider $stateProvider} to postprocess the given
2527
+ * router definition object.
2528
+ */
2529
+ $RouteProvider.$inject = ['$stateProvider', '$urlRouterProvider'];
2530
+ function $RouteProvider( $stateProvider, $urlRouterProvider) {
2531
+
2532
+ var routes = [];
2533
+
2534
+ onEnterRoute.$inject = ['$$state'];
2535
+ function onEnterRoute( $$state) {
2536
+ /*jshint validthis: true */
2537
+ this.locals = $$state.locals.globals;
2538
+ this.params = this.locals.$stateParams;
2539
+ }
2540
+
2541
+ function onExitRoute() {
2542
+ /*jshint validthis: true */
2543
+ this.locals = null;
2544
+ this.params = null;
2545
+ }
2546
+
2547
+ this.when = when;
2548
+ /**
2549
+ * @ngdoc function
2550
+ * @name ui.router.compat.$routeProvider#when
2551
+ * @methodOf ui.router.compat.$routeProvider
2552
+ *
2553
+ * @description
2554
+ * Registers a route with a given route definition object. The route definition
2555
+ * object has the same interface the angular core route definition object has.
2556
+ *
2557
+ * @example
2558
+ * <pre>
2559
+ * var app = angular.module('app', ['ui.router.compat']);
2560
+ *
2561
+ * app.config(function ($routeProvider) {
2562
+ * $routeProvider.when('home', {
2563
+ * controller: function () { ... },
2564
+ * templateUrl: 'path/to/template'
2565
+ * });
2566
+ * });
2567
+ * </pre>
2568
+ *
2569
+ * @param {string} url URL as string
2570
+ * @param {object} route Route definition object
2571
+ *
2572
+ * @return {object} $routeProvider - $routeProvider instance
2573
+ */
2574
+ function when(url, route) {
2575
+ /*jshint validthis: true */
2576
+ if (route.redirectTo != null) {
2577
+ // Redirect, configure directly on $urlRouterProvider
2578
+ var redirect = route.redirectTo, handler;
2579
+ if (isString(redirect)) {
2580
+ handler = redirect; // leave $urlRouterProvider to handle
2581
+ } else if (isFunction(redirect)) {
2582
+ // Adapt to $urlRouterProvider API
2583
+ handler = function (params, $location) {
2584
+ return redirect(params, $location.path(), $location.search());
2585
+ };
2586
+ } else {
2587
+ throw new Error("Invalid 'redirectTo' in when()");
2588
+ }
2589
+ $urlRouterProvider.when(url, handler);
2590
+ } else {
2591
+ // Regular route, configure as state
2592
+ $stateProvider.state(inherit(route, {
2593
+ parent: null,
2594
+ name: 'route:' + encodeURIComponent(url),
2595
+ url: url,
2596
+ onEnter: onEnterRoute,
2597
+ onExit: onExitRoute
2598
+ }));
2599
+ }
2600
+ routes.push(route);
2601
+ return this;
2602
+ }
2603
+
2604
+ /**
2605
+ * @ngdoc object
2606
+ * @name ui.router.compat.$route
2607
+ *
2608
+ * @requires ui.router.state.$state
2609
+ * @requires $rootScope
2610
+ * @requires $routeParams
2611
+ *
2612
+ * @property {object} routes - Array of registered routes.
2613
+ * @property {object} params - Current route params as object.
2614
+ * @property {string} current - Name of the current route.
2615
+ *
2616
+ * @description
2617
+ * The `$route` service provides interfaces to access defined routes. It also let's
2618
+ * you access route params through `$routeParams` service, so you have fully
2619
+ * control over all the stuff you would actually get from angular's core `$route`
2620
+ * service.
2621
+ */
2622
+ this.$get = $get;
2623
+ $get.$inject = ['$state', '$rootScope', '$routeParams'];
2624
+ function $get( $state, $rootScope, $routeParams) {
2625
+
2626
+ var $route = {
2627
+ routes: routes,
2628
+ params: $routeParams,
2629
+ current: undefined
2630
+ };
2631
+
2632
+ function stateAsRoute(state) {
2633
+ return (state.name !== '') ? state : undefined;
2634
+ }
2635
+
2636
+ $rootScope.$on('$stateChangeStart', function (ev, to, toParams, from, fromParams) {
2637
+ $rootScope.$broadcast('$routeChangeStart', stateAsRoute(to), stateAsRoute(from));
2638
+ });
2639
+
2640
+ $rootScope.$on('$stateChangeSuccess', function (ev, to, toParams, from, fromParams) {
2641
+ $route.current = stateAsRoute(to);
2642
+ $rootScope.$broadcast('$routeChangeSuccess', stateAsRoute(to), stateAsRoute(from));
2643
+ copy(toParams, $route.params);
2644
+ });
2645
+
2646
+ $rootScope.$on('$stateChangeError', function (ev, to, toParams, from, fromParams, error) {
2647
+ $rootScope.$broadcast('$routeChangeError', stateAsRoute(to), stateAsRoute(from), error);
2648
+ });
2649
+
2650
+ return $route;
2651
+ }
2652
+ }
2653
+
2654
+ angular.module('ui.router.compat')
2655
+ .provider('$route', $RouteProvider)
2656
+ .directive('ngView', $ViewDirective);
2657
+ })(window, window.angular);