susanoo 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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);