engine2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (145) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +2 -0
  3. data/Rakefile +138 -0
  4. data/conf/message.yaml +93 -0
  5. data/conf/message_pl.yaml +93 -0
  6. data/engine2.gemspec +34 -0
  7. data/lib/engine2.rb +34 -0
  8. data/lib/engine2/action.rb +217 -0
  9. data/lib/engine2/core.rb +572 -0
  10. data/lib/engine2/handler.rb +134 -0
  11. data/lib/engine2/meta.rb +969 -0
  12. data/lib/engine2/meta/decode_meta.rb +110 -0
  13. data/lib/engine2/meta/delete_meta.rb +73 -0
  14. data/lib/engine2/meta/form_meta.rb +144 -0
  15. data/lib/engine2/meta/infra_meta.rb +292 -0
  16. data/lib/engine2/meta/link_meta.rb +133 -0
  17. data/lib/engine2/meta/list_meta.rb +284 -0
  18. data/lib/engine2/meta/save_meta.rb +63 -0
  19. data/lib/engine2/meta/view_meta.rb +22 -0
  20. data/lib/engine2/model.rb +390 -0
  21. data/lib/engine2/models/Files.rb +38 -0
  22. data/lib/engine2/models/UserInfo.rb +24 -0
  23. data/lib/engine2/post_bootstrap.rb +83 -0
  24. data/lib/engine2/pre_bootstrap.rb +27 -0
  25. data/lib/engine2/scheme.rb +202 -0
  26. data/lib/engine2/templates.rb +229 -0
  27. data/lib/engine2/type_info.rb +342 -0
  28. data/lib/engine2/version.rb +9 -0
  29. data/public/assets/javascripts.js +13 -0
  30. data/public/assets/styles.css +4 -0
  31. data/public/css/angular-motion.css +1022 -0
  32. data/public/css/angular-ui-tree.min.css +1 -0
  33. data/public/css/app.css +196 -0
  34. data/public/css/bootstrap-additions.css +1560 -0
  35. data/public/css/bootstrap.min.css +11 -0
  36. data/public/css/font-awesome.min.css +4 -0
  37. data/public/favicon.ico +0 -0
  38. data/public/fonts/FontAwesome.otf +0 -0
  39. data/public/fonts/fontawesome-webfont.eot +0 -0
  40. data/public/fonts/fontawesome-webfont.svg +655 -0
  41. data/public/fonts/fontawesome-webfont.ttf +0 -0
  42. data/public/fonts/fontawesome-webfont.woff +0 -0
  43. data/public/fonts/fontawesome-webfont.woff2 +0 -0
  44. data/public/fonts/glyphicons-halflings-regular.eot +0 -0
  45. data/public/fonts/glyphicons-halflings-regular.svg +288 -0
  46. data/public/fonts/glyphicons-halflings-regular.ttf +0 -0
  47. data/public/fonts/glyphicons-halflings-regular.woff +0 -0
  48. data/public/fonts/glyphicons-halflings-regular.woff2 +0 -0
  49. data/public/images/file.png +0 -0
  50. data/public/images/folder-closed.png +0 -0
  51. data/public/images/folder.png +0 -0
  52. data/public/images/node-closed-2.png +0 -0
  53. data/public/images/node-closed-light.png +0 -0
  54. data/public/images/node-closed.png +0 -0
  55. data/public/images/node-opened-2.png +0 -0
  56. data/public/images/node-opened-light.png +0 -0
  57. data/public/images/node-opened.png +0 -0
  58. data/public/img/ajax-loader-dark.gif +0 -0
  59. data/public/img/ajax-loader-light.gif +0 -0
  60. data/public/img/ajax-loader.gif +0 -0
  61. data/public/js/angular-animate.js +4115 -0
  62. data/public/js/angular-cookies.js +322 -0
  63. data/public/js/angular-local-storage.js +455 -0
  64. data/public/js/angular-route.js +1022 -0
  65. data/public/js/angular-sanitize.js +717 -0
  66. data/public/js/angular-strap.js +4339 -0
  67. data/public/js/angular-strap.tpl.js +43 -0
  68. data/public/js/angular-ui-tree.js +1569 -0
  69. data/public/js/angular.js +30714 -0
  70. data/public/js/i18n/angular-locale_pl.js +115 -0
  71. data/public/js/lodash.custom.min.js +97 -0
  72. data/public/js/ng-file-upload-shim.min.js +2 -0
  73. data/public/js/ng-file-upload.min.js +3 -0
  74. data/views/app.coffee +3 -0
  75. data/views/engine2.coffee +557 -0
  76. data/views/engine2actions.coffee +849 -0
  77. data/views/engine2templates.coffee +0 -0
  78. data/views/fields/blob.slim +22 -0
  79. data/views/fields/bs_select.slim +10 -0
  80. data/views/fields/bsselect_picker.slim +18 -0
  81. data/views/fields/bsselect_picker_opt.slim +22 -0
  82. data/views/fields/checkbox.slim +11 -0
  83. data/views/fields/checkbox_buttons.slim +6 -0
  84. data/views/fields/checkbox_buttons_opt.slim +8 -0
  85. data/views/fields/currency.slim +10 -0
  86. data/views/fields/date.slim +21 -0
  87. data/views/fields/date_range.slim +44 -0
  88. data/views/fields/date_time.slim +42 -0
  89. data/views/fields/datetime.slim +42 -0
  90. data/views/fields/decimal.slim +11 -0
  91. data/views/fields/decimal_date.slim +22 -0
  92. data/views/fields/decimal_time.slim +26 -0
  93. data/views/fields/email.slim +13 -0
  94. data/views/fields/file_store.slim +61 -0
  95. data/views/fields/input_text.slim +14 -0
  96. data/views/fields/integer.slim +11 -0
  97. data/views/fields/list_bsselect.slim +18 -0
  98. data/views/fields/list_bsselect_opt.slim +21 -0
  99. data/views/fields/list_buttons.slim +3 -0
  100. data/views/fields/list_buttons_opt.slim +5 -0
  101. data/views/fields/list_select.slim +11 -0
  102. data/views/fields/list_select_opt.slim +15 -0
  103. data/views/fields/password.slim +14 -0
  104. data/views/fields/radio_checkbox.slim +10 -0
  105. data/views/fields/scaffold.slim +2 -0
  106. data/views/fields/scaffold_picker.slim +20 -0
  107. data/views/fields/select_picker.slim +12 -0
  108. data/views/fields/select_picker_opt.slim +16 -0
  109. data/views/fields/text_area.slim +10 -0
  110. data/views/fields/time.slim +22 -0
  111. data/views/fields/typeahead_picker.slim +25 -0
  112. data/views/index.slim +44 -0
  113. data/views/infra/index.slim +5 -0
  114. data/views/infra/inspect.slim +81 -0
  115. data/views/modals/close_m.slim +15 -0
  116. data/views/modals/confirm_m.slim +19 -0
  117. data/views/modals/empty_m.slim +12 -0
  118. data/views/modals/menu_m.slim +13 -0
  119. data/views/modals/yes_no_m.slim +19 -0
  120. data/views/panels/menu_m.slim +9 -0
  121. data/views/scaffold/confirm.slim +3 -0
  122. data/views/scaffold/fields.slim +10 -0
  123. data/views/scaffold/form.slim +11 -0
  124. data/views/scaffold/list.slim +42 -0
  125. data/views/scaffold/message.slim +3 -0
  126. data/views/scaffold/search.slim +20 -0
  127. data/views/scaffold/view.slim +18 -0
  128. data/views/search_fields/bsmselect_picker.slim +25 -0
  129. data/views/search_fields/bsselect_picker.slim +24 -0
  130. data/views/search_fields/checkbox.slim +11 -0
  131. data/views/search_fields/checkbox2.slim +14 -0
  132. data/views/search_fields/checkbox_buttons.slim +10 -0
  133. data/views/search_fields/date_range.slim +46 -0
  134. data/views/search_fields/decimal_date_range.slim +47 -0
  135. data/views/search_fields/input_text.slim +18 -0
  136. data/views/search_fields/integer.slim +18 -0
  137. data/views/search_fields/integer_range.slim +27 -0
  138. data/views/search_fields/list_bsmselect.slim +24 -0
  139. data/views/search_fields/list_bsselect.slim +22 -0
  140. data/views/search_fields/list_buttons.slim +8 -0
  141. data/views/search_fields/list_select.slim +17 -0
  142. data/views/search_fields/scaffold_picker.slim +19 -0
  143. data/views/search_fields/select_picker.slim +17 -0
  144. data/views/search_fields/typeahead_picker.slim +25 -0
  145. metadata +327 -0
@@ -0,0 +1,1022 @@
1
+ /**
2
+ * @license AngularJS v1.5.3
3
+ * (c) 2010-2016 Google, Inc. http://angularjs.org
4
+ * License: MIT
5
+ */
6
+ (function(window, angular, undefined) {'use strict';
7
+
8
+ /**
9
+ * @ngdoc module
10
+ * @name ngRoute
11
+ * @description
12
+ *
13
+ * # ngRoute
14
+ *
15
+ * The `ngRoute` module provides routing and deeplinking services and directives for angular apps.
16
+ *
17
+ * ## Example
18
+ * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
19
+ *
20
+ *
21
+ * <div doc-module-components="ngRoute"></div>
22
+ */
23
+ /* global -ngRouteModule */
24
+ var ngRouteModule = angular.module('ngRoute', ['ng']).
25
+ provider('$route', $RouteProvider).
26
+ // Ensure `$route` will be instantiated in time to capture the initial
27
+ // `$locationChangeSuccess` event. This is necessary in case `ngView` is
28
+ // included in an asynchronously loaded template.
29
+ run(['$route', angular.noop]),
30
+ $routeMinErr = angular.$$minErr('ngRoute');
31
+
32
+ /**
33
+ * @ngdoc provider
34
+ * @name $routeProvider
35
+ *
36
+ * @description
37
+ *
38
+ * Used for configuring routes.
39
+ *
40
+ * ## Example
41
+ * See {@link ngRoute.$route#example $route} for an example of configuring and using `ngRoute`.
42
+ *
43
+ * ## Dependencies
44
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
45
+ */
46
+ function $RouteProvider() {
47
+ function inherit(parent, extra) {
48
+ return angular.extend(Object.create(parent), extra);
49
+ }
50
+
51
+ var routes = {};
52
+
53
+ /**
54
+ * @ngdoc method
55
+ * @name $routeProvider#when
56
+ *
57
+ * @param {string} path Route path (matched against `$location.path`). If `$location.path`
58
+ * contains redundant trailing slash or is missing one, the route will still match and the
59
+ * `$location.path` will be updated to add or drop the trailing slash to exactly match the
60
+ * route definition.
61
+ *
62
+ * * `path` can contain named groups starting with a colon: e.g. `:name`. All characters up
63
+ * to the next slash are matched and stored in `$routeParams` under the given `name`
64
+ * when the route matches.
65
+ * * `path` can contain named groups starting with a colon and ending with a star:
66
+ * e.g.`:name*`. All characters are eagerly stored in `$routeParams` under the given `name`
67
+ * when the route matches.
68
+ * * `path` can contain optional named groups with a question mark: e.g.`:name?`.
69
+ *
70
+ * For example, routes like `/color/:color/largecode/:largecode*\/edit` will match
71
+ * `/color/brown/largecode/code/with/slashes/edit` and extract:
72
+ *
73
+ * * `color: brown`
74
+ * * `largecode: code/with/slashes`.
75
+ *
76
+ *
77
+ * @param {Object} route Mapping information to be assigned to `$route.current` on route
78
+ * match.
79
+ *
80
+ * Object properties:
81
+ *
82
+ * - `controller` – `{(string|function()=}` – Controller fn that should be associated with
83
+ * newly created scope or the name of a {@link angular.Module#controller registered
84
+ * controller} if passed as a string.
85
+ * - `controllerAs` – `{string=}` – An identifier name for a reference to the controller.
86
+ * If present, the controller will be published to scope under the `controllerAs` name.
87
+ * - `template` – `{string=|function()=}` – html template as a string or a function that
88
+ * returns an html template as a string which should be used by {@link
89
+ * ngRoute.directive:ngView ngView} or {@link ng.directive:ngInclude ngInclude} directives.
90
+ * This property takes precedence over `templateUrl`.
91
+ *
92
+ * If `template` is a function, it will be called with the following parameters:
93
+ *
94
+ * - `{Array.<Object>}` - route parameters extracted from the current
95
+ * `$location.path()` by applying the current route
96
+ *
97
+ * - `templateUrl` – `{string=|function()=}` – path or function that returns a path to an html
98
+ * template that should be used by {@link ngRoute.directive:ngView ngView}.
99
+ *
100
+ * If `templateUrl` is a function, it will be called with the following parameters:
101
+ *
102
+ * - `{Array.<Object>}` - route parameters extracted from the current
103
+ * `$location.path()` by applying the current route
104
+ *
105
+ * - `resolve` - `{Object.<string, function>=}` - An optional map of dependencies which should
106
+ * be injected into the controller. If any of these dependencies are promises, the router
107
+ * will wait for them all to be resolved or one to be rejected before the controller is
108
+ * instantiated.
109
+ * If all the promises are resolved successfully, the values of the resolved promises are
110
+ * injected and {@link ngRoute.$route#$routeChangeSuccess $routeChangeSuccess} event is
111
+ * fired. If any of the promises are rejected the
112
+ * {@link ngRoute.$route#$routeChangeError $routeChangeError} event is fired.
113
+ * For easier access to the resolved dependencies from the template, the `resolve` map will
114
+ * be available on the scope of the route, under `$resolve` (by default) or a custom name
115
+ * specified by the `resolveAs` property (see below). This can be particularly useful, when
116
+ * working with {@link angular.Module#component components} as route templates.<br />
117
+ * <div class="alert alert-warning">
118
+ * **Note:** If your scope already contains a property with this name, it will be hidden
119
+ * or overwritten. Make sure, you specify an appropriate name for this property, that
120
+ * does not collide with other properties on the scope.
121
+ * </div>
122
+ * The map object is:
123
+ *
124
+ * - `key` – `{string}`: a name of a dependency to be injected into the controller.
125
+ * - `factory` - `{string|function}`: If `string` then it is an alias for a service.
126
+ * Otherwise if function, then it is {@link auto.$injector#invoke injected}
127
+ * and the return value is treated as the dependency. If the result is a promise, it is
128
+ * resolved before its value is injected into the controller. Be aware that
129
+ * `ngRoute.$routeParams` will still refer to the previous route within these resolve
130
+ * functions. Use `$route.current.params` to access the new route parameters, instead.
131
+ *
132
+ * - `resolveAs` - `{string=}` - The name under which the `resolve` map will be available on
133
+ * the scope of the route. If omitted, defaults to `$resolve`.
134
+ *
135
+ * - `redirectTo` – `{(string|function())=}` – value to update
136
+ * {@link ng.$location $location} path with and trigger route redirection.
137
+ *
138
+ * If `redirectTo` is a function, it will be called with the following parameters:
139
+ *
140
+ * - `{Object.<string>}` - route parameters extracted from the current
141
+ * `$location.path()` by applying the current route templateUrl.
142
+ * - `{string}` - current `$location.path()`
143
+ * - `{Object}` - current `$location.search()`
144
+ *
145
+ * The custom `redirectTo` function is expected to return a string which will be used
146
+ * to update `$location.path()` and `$location.search()`.
147
+ *
148
+ * - `[reloadOnSearch=true]` - `{boolean=}` - reload route when only `$location.search()`
149
+ * or `$location.hash()` changes.
150
+ *
151
+ * If the option is set to `false` and url in the browser changes, then
152
+ * `$routeUpdate` event is broadcasted on the root scope.
153
+ *
154
+ * - `[caseInsensitiveMatch=false]` - `{boolean=}` - match routes without being case sensitive
155
+ *
156
+ * If the option is set to `true`, then the particular route can be matched without being
157
+ * case sensitive
158
+ *
159
+ * @returns {Object} self
160
+ *
161
+ * @description
162
+ * Adds a new route definition to the `$route` service.
163
+ */
164
+ this.when = function(path, route) {
165
+ //copy original route object to preserve params inherited from proto chain
166
+ var routeCopy = angular.copy(route);
167
+ if (angular.isUndefined(routeCopy.reloadOnSearch)) {
168
+ routeCopy.reloadOnSearch = true;
169
+ }
170
+ if (angular.isUndefined(routeCopy.caseInsensitiveMatch)) {
171
+ routeCopy.caseInsensitiveMatch = this.caseInsensitiveMatch;
172
+ }
173
+ routes[path] = angular.extend(
174
+ routeCopy,
175
+ path && pathRegExp(path, routeCopy)
176
+ );
177
+
178
+ // create redirection for trailing slashes
179
+ if (path) {
180
+ var redirectPath = (path[path.length - 1] == '/')
181
+ ? path.substr(0, path.length - 1)
182
+ : path + '/';
183
+
184
+ routes[redirectPath] = angular.extend(
185
+ {redirectTo: path},
186
+ pathRegExp(redirectPath, routeCopy)
187
+ );
188
+ }
189
+
190
+ return this;
191
+ };
192
+
193
+ /**
194
+ * @ngdoc property
195
+ * @name $routeProvider#caseInsensitiveMatch
196
+ * @description
197
+ *
198
+ * A boolean property indicating if routes defined
199
+ * using this provider should be matched using a case insensitive
200
+ * algorithm. Defaults to `false`.
201
+ */
202
+ this.caseInsensitiveMatch = false;
203
+
204
+ /**
205
+ * @param path {string} path
206
+ * @param opts {Object} options
207
+ * @return {?Object}
208
+ *
209
+ * @description
210
+ * Normalizes the given path, returning a regular expression
211
+ * and the original path.
212
+ *
213
+ * Inspired by pathRexp in visionmedia/express/lib/utils.js.
214
+ */
215
+ function pathRegExp(path, opts) {
216
+ var insensitive = opts.caseInsensitiveMatch,
217
+ ret = {
218
+ originalPath: path,
219
+ regexp: path
220
+ },
221
+ keys = ret.keys = [];
222
+
223
+ path = path
224
+ .replace(/([().])/g, '\\$1')
225
+ .replace(/(\/)?:(\w+)(\*\?|[\?\*])?/g, function(_, slash, key, option) {
226
+ var optional = (option === '?' || option === '*?') ? '?' : null;
227
+ var star = (option === '*' || option === '*?') ? '*' : null;
228
+ keys.push({ name: key, optional: !!optional });
229
+ slash = slash || '';
230
+ return ''
231
+ + (optional ? '' : slash)
232
+ + '(?:'
233
+ + (optional ? slash : '')
234
+ + (star && '(.+?)' || '([^/]+)')
235
+ + (optional || '')
236
+ + ')'
237
+ + (optional || '');
238
+ })
239
+ .replace(/([\/$\*])/g, '\\$1');
240
+
241
+ ret.regexp = new RegExp('^' + path + '$', insensitive ? 'i' : '');
242
+ return ret;
243
+ }
244
+
245
+ /**
246
+ * @ngdoc method
247
+ * @name $routeProvider#otherwise
248
+ *
249
+ * @description
250
+ * Sets route definition that will be used on route change when no other route definition
251
+ * is matched.
252
+ *
253
+ * @param {Object|string} params Mapping information to be assigned to `$route.current`.
254
+ * If called with a string, the value maps to `redirectTo`.
255
+ * @returns {Object} self
256
+ */
257
+ this.otherwise = function(params) {
258
+ if (typeof params === 'string') {
259
+ params = {redirectTo: params};
260
+ }
261
+ this.when(null, params);
262
+ return this;
263
+ };
264
+
265
+
266
+ this.$get = ['$rootScope',
267
+ '$location',
268
+ '$routeParams',
269
+ '$q',
270
+ '$injector',
271
+ '$templateRequest',
272
+ '$sce',
273
+ function($rootScope, $location, $routeParams, $q, $injector, $templateRequest, $sce) {
274
+
275
+ /**
276
+ * @ngdoc service
277
+ * @name $route
278
+ * @requires $location
279
+ * @requires $routeParams
280
+ *
281
+ * @property {Object} current Reference to the current route definition.
282
+ * The route definition contains:
283
+ *
284
+ * - `controller`: The controller constructor as defined in the route definition.
285
+ * - `locals`: A map of locals which is used by {@link ng.$controller $controller} service for
286
+ * controller instantiation. The `locals` contain
287
+ * the resolved values of the `resolve` map. Additionally the `locals` also contain:
288
+ *
289
+ * - `$scope` - The current route scope.
290
+ * - `$template` - The current route template HTML.
291
+ *
292
+ * The `locals` will be assigned to the route scope's `$resolve` property. You can override
293
+ * the property name, using `resolveAs` in the route definition. See
294
+ * {@link ngRoute.$routeProvider $routeProvider} for more info.
295
+ *
296
+ * @property {Object} routes Object with all route configuration Objects as its properties.
297
+ *
298
+ * @description
299
+ * `$route` is used for deep-linking URLs to controllers and views (HTML partials).
300
+ * It watches `$location.url()` and tries to map the path to an existing route definition.
301
+ *
302
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
303
+ *
304
+ * You can define routes through {@link ngRoute.$routeProvider $routeProvider}'s API.
305
+ *
306
+ * The `$route` service is typically used in conjunction with the
307
+ * {@link ngRoute.directive:ngView `ngView`} directive and the
308
+ * {@link ngRoute.$routeParams `$routeParams`} service.
309
+ *
310
+ * @example
311
+ * This example shows how changing the URL hash causes the `$route` to match a route against the
312
+ * URL, and the `ngView` pulls in the partial.
313
+ *
314
+ * <example name="$route-service" module="ngRouteExample"
315
+ * deps="angular-route.js" fixBase="true">
316
+ * <file name="index.html">
317
+ * <div ng-controller="MainController">
318
+ * Choose:
319
+ * <a href="Book/Moby">Moby</a> |
320
+ * <a href="Book/Moby/ch/1">Moby: Ch1</a> |
321
+ * <a href="Book/Gatsby">Gatsby</a> |
322
+ * <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
323
+ * <a href="Book/Scarlet">Scarlet Letter</a><br/>
324
+ *
325
+ * <div ng-view></div>
326
+ *
327
+ * <hr />
328
+ *
329
+ * <pre>$location.path() = {{$location.path()}}</pre>
330
+ * <pre>$route.current.templateUrl = {{$route.current.templateUrl}}</pre>
331
+ * <pre>$route.current.params = {{$route.current.params}}</pre>
332
+ * <pre>$route.current.scope.name = {{$route.current.scope.name}}</pre>
333
+ * <pre>$routeParams = {{$routeParams}}</pre>
334
+ * </div>
335
+ * </file>
336
+ *
337
+ * <file name="book.html">
338
+ * controller: {{name}}<br />
339
+ * Book Id: {{params.bookId}}<br />
340
+ * </file>
341
+ *
342
+ * <file name="chapter.html">
343
+ * controller: {{name}}<br />
344
+ * Book Id: {{params.bookId}}<br />
345
+ * Chapter Id: {{params.chapterId}}
346
+ * </file>
347
+ *
348
+ * <file name="script.js">
349
+ * angular.module('ngRouteExample', ['ngRoute'])
350
+ *
351
+ * .controller('MainController', function($scope, $route, $routeParams, $location) {
352
+ * $scope.$route = $route;
353
+ * $scope.$location = $location;
354
+ * $scope.$routeParams = $routeParams;
355
+ * })
356
+ *
357
+ * .controller('BookController', function($scope, $routeParams) {
358
+ * $scope.name = "BookController";
359
+ * $scope.params = $routeParams;
360
+ * })
361
+ *
362
+ * .controller('ChapterController', function($scope, $routeParams) {
363
+ * $scope.name = "ChapterController";
364
+ * $scope.params = $routeParams;
365
+ * })
366
+ *
367
+ * .config(function($routeProvider, $locationProvider) {
368
+ * $routeProvider
369
+ * .when('/Book/:bookId', {
370
+ * templateUrl: 'book.html',
371
+ * controller: 'BookController',
372
+ * resolve: {
373
+ * // I will cause a 1 second delay
374
+ * delay: function($q, $timeout) {
375
+ * var delay = $q.defer();
376
+ * $timeout(delay.resolve, 1000);
377
+ * return delay.promise;
378
+ * }
379
+ * }
380
+ * })
381
+ * .when('/Book/:bookId/ch/:chapterId', {
382
+ * templateUrl: 'chapter.html',
383
+ * controller: 'ChapterController'
384
+ * });
385
+ *
386
+ * // configure html5 to get links working on jsfiddle
387
+ * $locationProvider.html5Mode(true);
388
+ * });
389
+ *
390
+ * </file>
391
+ *
392
+ * <file name="protractor.js" type="protractor">
393
+ * it('should load and compile correct template', function() {
394
+ * element(by.linkText('Moby: Ch1')).click();
395
+ * var content = element(by.css('[ng-view]')).getText();
396
+ * expect(content).toMatch(/controller\: ChapterController/);
397
+ * expect(content).toMatch(/Book Id\: Moby/);
398
+ * expect(content).toMatch(/Chapter Id\: 1/);
399
+ *
400
+ * element(by.partialLinkText('Scarlet')).click();
401
+ *
402
+ * content = element(by.css('[ng-view]')).getText();
403
+ * expect(content).toMatch(/controller\: BookController/);
404
+ * expect(content).toMatch(/Book Id\: Scarlet/);
405
+ * });
406
+ * </file>
407
+ * </example>
408
+ */
409
+
410
+ /**
411
+ * @ngdoc event
412
+ * @name $route#$routeChangeStart
413
+ * @eventType broadcast on root scope
414
+ * @description
415
+ * Broadcasted before a route change. At this point the route services starts
416
+ * resolving all of the dependencies needed for the route change to occur.
417
+ * Typically this involves fetching the view template as well as any dependencies
418
+ * defined in `resolve` route property. Once all of the dependencies are resolved
419
+ * `$routeChangeSuccess` is fired.
420
+ *
421
+ * The route change (and the `$location` change that triggered it) can be prevented
422
+ * by calling `preventDefault` method of the event. See {@link ng.$rootScope.Scope#$on}
423
+ * for more details about event object.
424
+ *
425
+ * @param {Object} angularEvent Synthetic event object.
426
+ * @param {Route} next Future route information.
427
+ * @param {Route} current Current route information.
428
+ */
429
+
430
+ /**
431
+ * @ngdoc event
432
+ * @name $route#$routeChangeSuccess
433
+ * @eventType broadcast on root scope
434
+ * @description
435
+ * Broadcasted after a route change has happened successfully.
436
+ * The `resolve` dependencies are now available in the `current.locals` property.
437
+ *
438
+ * {@link ngRoute.directive:ngView ngView} listens for the directive
439
+ * to instantiate the controller and render the view.
440
+ *
441
+ * @param {Object} angularEvent Synthetic event object.
442
+ * @param {Route} current Current route information.
443
+ * @param {Route|Undefined} previous Previous route information, or undefined if current is
444
+ * first route entered.
445
+ */
446
+
447
+ /**
448
+ * @ngdoc event
449
+ * @name $route#$routeChangeError
450
+ * @eventType broadcast on root scope
451
+ * @description
452
+ * Broadcasted if any of the resolve promises are rejected.
453
+ *
454
+ * @param {Object} angularEvent Synthetic event object
455
+ * @param {Route} current Current route information.
456
+ * @param {Route} previous Previous route information.
457
+ * @param {Route} rejection Rejection of the promise. Usually the error of the failed promise.
458
+ */
459
+
460
+ /**
461
+ * @ngdoc event
462
+ * @name $route#$routeUpdate
463
+ * @eventType broadcast on root scope
464
+ * @description
465
+ * The `reloadOnSearch` property has been set to false, and we are reusing the same
466
+ * instance of the Controller.
467
+ *
468
+ * @param {Object} angularEvent Synthetic event object
469
+ * @param {Route} current Current/previous route information.
470
+ */
471
+
472
+ var forceReload = false,
473
+ preparedRoute,
474
+ preparedRouteIsUpdateOnly,
475
+ $route = {
476
+ routes: routes,
477
+
478
+ /**
479
+ * @ngdoc method
480
+ * @name $route#reload
481
+ *
482
+ * @description
483
+ * Causes `$route` service to reload the current route even if
484
+ * {@link ng.$location $location} hasn't changed.
485
+ *
486
+ * As a result of that, {@link ngRoute.directive:ngView ngView}
487
+ * creates new scope and reinstantiates the controller.
488
+ */
489
+ reload: function() {
490
+ forceReload = true;
491
+
492
+ var fakeLocationEvent = {
493
+ defaultPrevented: false,
494
+ preventDefault: function fakePreventDefault() {
495
+ this.defaultPrevented = true;
496
+ forceReload = false;
497
+ }
498
+ };
499
+
500
+ $rootScope.$evalAsync(function() {
501
+ prepareRoute(fakeLocationEvent);
502
+ if (!fakeLocationEvent.defaultPrevented) commitRoute();
503
+ });
504
+ },
505
+
506
+ /**
507
+ * @ngdoc method
508
+ * @name $route#updateParams
509
+ *
510
+ * @description
511
+ * Causes `$route` service to update the current URL, replacing
512
+ * current route parameters with those specified in `newParams`.
513
+ * Provided property names that match the route's path segment
514
+ * definitions will be interpolated into the location's path, while
515
+ * remaining properties will be treated as query params.
516
+ *
517
+ * @param {!Object<string, string>} newParams mapping of URL parameter names to values
518
+ */
519
+ updateParams: function(newParams) {
520
+ if (this.current && this.current.$$route) {
521
+ newParams = angular.extend({}, this.current.params, newParams);
522
+ $location.path(interpolate(this.current.$$route.originalPath, newParams));
523
+ // interpolate modifies newParams, only query params are left
524
+ $location.search(newParams);
525
+ } else {
526
+ throw $routeMinErr('norout', 'Tried updating route when with no current route');
527
+ }
528
+ }
529
+ };
530
+
531
+ $rootScope.$on('$locationChangeStart', prepareRoute);
532
+ $rootScope.$on('$locationChangeSuccess', commitRoute);
533
+
534
+ return $route;
535
+
536
+ /////////////////////////////////////////////////////
537
+
538
+ /**
539
+ * @param on {string} current url
540
+ * @param route {Object} route regexp to match the url against
541
+ * @return {?Object}
542
+ *
543
+ * @description
544
+ * Check if the route matches the current url.
545
+ *
546
+ * Inspired by match in
547
+ * visionmedia/express/lib/router/router.js.
548
+ */
549
+ function switchRouteMatcher(on, route) {
550
+ var keys = route.keys,
551
+ params = {};
552
+
553
+ if (!route.regexp) return null;
554
+
555
+ var m = route.regexp.exec(on);
556
+ if (!m) return null;
557
+
558
+ for (var i = 1, len = m.length; i < len; ++i) {
559
+ var key = keys[i - 1];
560
+
561
+ var val = m[i];
562
+
563
+ if (key && val) {
564
+ params[key.name] = val;
565
+ }
566
+ }
567
+ return params;
568
+ }
569
+
570
+ function prepareRoute($locationEvent) {
571
+ var lastRoute = $route.current;
572
+
573
+ preparedRoute = parseRoute();
574
+ preparedRouteIsUpdateOnly = preparedRoute && lastRoute && preparedRoute.$$route === lastRoute.$$route
575
+ && angular.equals(preparedRoute.pathParams, lastRoute.pathParams)
576
+ && !preparedRoute.reloadOnSearch && !forceReload;
577
+
578
+ if (!preparedRouteIsUpdateOnly && (lastRoute || preparedRoute)) {
579
+ if ($rootScope.$broadcast('$routeChangeStart', preparedRoute, lastRoute).defaultPrevented) {
580
+ if ($locationEvent) {
581
+ $locationEvent.preventDefault();
582
+ }
583
+ }
584
+ }
585
+ }
586
+
587
+ function commitRoute() {
588
+ var lastRoute = $route.current;
589
+ var nextRoute = preparedRoute;
590
+
591
+ if (preparedRouteIsUpdateOnly) {
592
+ lastRoute.params = nextRoute.params;
593
+ angular.copy(lastRoute.params, $routeParams);
594
+ $rootScope.$broadcast('$routeUpdate', lastRoute);
595
+ } else if (nextRoute || lastRoute) {
596
+ forceReload = false;
597
+ $route.current = nextRoute;
598
+ if (nextRoute) {
599
+ if (nextRoute.redirectTo) {
600
+ if (angular.isString(nextRoute.redirectTo)) {
601
+ $location.path(interpolate(nextRoute.redirectTo, nextRoute.params)).search(nextRoute.params)
602
+ .replace();
603
+ } else {
604
+ $location.url(nextRoute.redirectTo(nextRoute.pathParams, $location.path(), $location.search()))
605
+ .replace();
606
+ }
607
+ }
608
+ }
609
+
610
+ $q.when(nextRoute).
611
+ then(function() {
612
+ if (nextRoute) {
613
+ var locals = angular.extend({}, nextRoute.resolve),
614
+ template, templateUrl;
615
+
616
+ angular.forEach(locals, function(value, key) {
617
+ locals[key] = angular.isString(value) ?
618
+ $injector.get(value) : $injector.invoke(value, null, null, key);
619
+ });
620
+
621
+ if (angular.isDefined(template = nextRoute.template)) {
622
+ if (angular.isFunction(template)) {
623
+ template = template(nextRoute.params);
624
+ }
625
+ } else if (angular.isDefined(templateUrl = nextRoute.templateUrl)) {
626
+ if (angular.isFunction(templateUrl)) {
627
+ templateUrl = templateUrl(nextRoute.params);
628
+ }
629
+ if (angular.isDefined(templateUrl)) {
630
+ nextRoute.loadedTemplateUrl = $sce.valueOf(templateUrl);
631
+ template = $templateRequest(templateUrl);
632
+ }
633
+ }
634
+ if (angular.isDefined(template)) {
635
+ locals['$template'] = template;
636
+ }
637
+ return $q.all(locals);
638
+ }
639
+ }).
640
+ then(function(locals) {
641
+ // after route change
642
+ if (nextRoute == $route.current) {
643
+ if (nextRoute) {
644
+ nextRoute.locals = locals;
645
+ angular.copy(nextRoute.params, $routeParams);
646
+ }
647
+ $rootScope.$broadcast('$routeChangeSuccess', nextRoute, lastRoute);
648
+ }
649
+ }, function(error) {
650
+ if (nextRoute == $route.current) {
651
+ $rootScope.$broadcast('$routeChangeError', nextRoute, lastRoute, error);
652
+ }
653
+ });
654
+ }
655
+ }
656
+
657
+
658
+ /**
659
+ * @returns {Object} the current active route, by matching it against the URL
660
+ */
661
+ function parseRoute() {
662
+ // Match a route
663
+ var params, match;
664
+ angular.forEach(routes, function(route, path) {
665
+ if (!match && (params = switchRouteMatcher($location.path(), route))) {
666
+ match = inherit(route, {
667
+ params: angular.extend({}, $location.search(), params),
668
+ pathParams: params});
669
+ match.$$route = route;
670
+ }
671
+ });
672
+ // No route matched; fallback to "otherwise" route
673
+ return match || routes[null] && inherit(routes[null], {params: {}, pathParams:{}});
674
+ }
675
+
676
+ /**
677
+ * @returns {string} interpolation of the redirect path with the parameters
678
+ */
679
+ function interpolate(string, params) {
680
+ var result = [];
681
+ angular.forEach((string || '').split(':'), function(segment, i) {
682
+ if (i === 0) {
683
+ result.push(segment);
684
+ } else {
685
+ var segmentMatch = segment.match(/(\w+)(?:[?*])?(.*)/);
686
+ var key = segmentMatch[1];
687
+ result.push(params[key]);
688
+ result.push(segmentMatch[2] || '');
689
+ delete params[key];
690
+ }
691
+ });
692
+ return result.join('');
693
+ }
694
+ }];
695
+ }
696
+
697
+ ngRouteModule.provider('$routeParams', $RouteParamsProvider);
698
+
699
+
700
+ /**
701
+ * @ngdoc service
702
+ * @name $routeParams
703
+ * @requires $route
704
+ *
705
+ * @description
706
+ * The `$routeParams` service allows you to retrieve the current set of route parameters.
707
+ *
708
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
709
+ *
710
+ * The route parameters are a combination of {@link ng.$location `$location`}'s
711
+ * {@link ng.$location#search `search()`} and {@link ng.$location#path `path()`}.
712
+ * The `path` parameters are extracted when the {@link ngRoute.$route `$route`} path is matched.
713
+ *
714
+ * In case of parameter name collision, `path` params take precedence over `search` params.
715
+ *
716
+ * The service guarantees that the identity of the `$routeParams` object will remain unchanged
717
+ * (but its properties will likely change) even when a route change occurs.
718
+ *
719
+ * Note that the `$routeParams` are only updated *after* a route change completes successfully.
720
+ * This means that you cannot rely on `$routeParams` being correct in route resolve functions.
721
+ * Instead you can use `$route.current.params` to access the new route's parameters.
722
+ *
723
+ * @example
724
+ * ```js
725
+ * // Given:
726
+ * // URL: http://server.com/index.html#/Chapter/1/Section/2?search=moby
727
+ * // Route: /Chapter/:chapterId/Section/:sectionId
728
+ * //
729
+ * // Then
730
+ * $routeParams ==> {chapterId:'1', sectionId:'2', search:'moby'}
731
+ * ```
732
+ */
733
+ function $RouteParamsProvider() {
734
+ this.$get = function() { return {}; };
735
+ }
736
+
737
+ ngRouteModule.directive('ngView', ngViewFactory);
738
+ ngRouteModule.directive('ngView', ngViewFillContentFactory);
739
+
740
+
741
+ /**
742
+ * @ngdoc directive
743
+ * @name ngView
744
+ * @restrict ECA
745
+ *
746
+ * @description
747
+ * # Overview
748
+ * `ngView` is a directive that complements the {@link ngRoute.$route $route} service by
749
+ * including the rendered template of the current route into the main layout (`index.html`) file.
750
+ * Every time the current route changes, the included view changes with it according to the
751
+ * configuration of the `$route` service.
752
+ *
753
+ * Requires the {@link ngRoute `ngRoute`} module to be installed.
754
+ *
755
+ * @animations
756
+ * | Animation | Occurs |
757
+ * |----------------------------------|-------------------------------------|
758
+ * | {@link ng.$animate#enter enter} | when the new element is inserted to the DOM |
759
+ * | {@link ng.$animate#leave leave} | when the old element is removed from to the DOM |
760
+ *
761
+ * The enter and leave animation occur concurrently.
762
+ *
763
+ * @scope
764
+ * @priority 400
765
+ * @param {string=} onload Expression to evaluate whenever the view updates.
766
+ *
767
+ * @param {string=} autoscroll Whether `ngView` should call {@link ng.$anchorScroll
768
+ * $anchorScroll} to scroll the viewport after the view is updated.
769
+ *
770
+ * - If the attribute is not set, disable scrolling.
771
+ * - If the attribute is set without value, enable scrolling.
772
+ * - Otherwise enable scrolling only if the `autoscroll` attribute value evaluated
773
+ * as an expression yields a truthy value.
774
+ * @example
775
+ <example name="ngView-directive" module="ngViewExample"
776
+ deps="angular-route.js;angular-animate.js"
777
+ animations="true" fixBase="true">
778
+ <file name="index.html">
779
+ <div ng-controller="MainCtrl as main">
780
+ Choose:
781
+ <a href="Book/Moby">Moby</a> |
782
+ <a href="Book/Moby/ch/1">Moby: Ch1</a> |
783
+ <a href="Book/Gatsby">Gatsby</a> |
784
+ <a href="Book/Gatsby/ch/4?key=value">Gatsby: Ch4</a> |
785
+ <a href="Book/Scarlet">Scarlet Letter</a><br/>
786
+
787
+ <div class="view-animate-container">
788
+ <div ng-view class="view-animate"></div>
789
+ </div>
790
+ <hr />
791
+
792
+ <pre>$location.path() = {{main.$location.path()}}</pre>
793
+ <pre>$route.current.templateUrl = {{main.$route.current.templateUrl}}</pre>
794
+ <pre>$route.current.params = {{main.$route.current.params}}</pre>
795
+ <pre>$routeParams = {{main.$routeParams}}</pre>
796
+ </div>
797
+ </file>
798
+
799
+ <file name="book.html">
800
+ <div>
801
+ controller: {{book.name}}<br />
802
+ Book Id: {{book.params.bookId}}<br />
803
+ </div>
804
+ </file>
805
+
806
+ <file name="chapter.html">
807
+ <div>
808
+ controller: {{chapter.name}}<br />
809
+ Book Id: {{chapter.params.bookId}}<br />
810
+ Chapter Id: {{chapter.params.chapterId}}
811
+ </div>
812
+ </file>
813
+
814
+ <file name="animations.css">
815
+ .view-animate-container {
816
+ position:relative;
817
+ height:100px!important;
818
+ background:white;
819
+ border:1px solid black;
820
+ height:40px;
821
+ overflow:hidden;
822
+ }
823
+
824
+ .view-animate {
825
+ padding:10px;
826
+ }
827
+
828
+ .view-animate.ng-enter, .view-animate.ng-leave {
829
+ transition:all cubic-bezier(0.250, 0.460, 0.450, 0.940) 1.5s;
830
+
831
+ display:block;
832
+ width:100%;
833
+ border-left:1px solid black;
834
+
835
+ position:absolute;
836
+ top:0;
837
+ left:0;
838
+ right:0;
839
+ bottom:0;
840
+ padding:10px;
841
+ }
842
+
843
+ .view-animate.ng-enter {
844
+ left:100%;
845
+ }
846
+ .view-animate.ng-enter.ng-enter-active {
847
+ left:0;
848
+ }
849
+ .view-animate.ng-leave.ng-leave-active {
850
+ left:-100%;
851
+ }
852
+ </file>
853
+
854
+ <file name="script.js">
855
+ angular.module('ngViewExample', ['ngRoute', 'ngAnimate'])
856
+ .config(['$routeProvider', '$locationProvider',
857
+ function($routeProvider, $locationProvider) {
858
+ $routeProvider
859
+ .when('/Book/:bookId', {
860
+ templateUrl: 'book.html',
861
+ controller: 'BookCtrl',
862
+ controllerAs: 'book'
863
+ })
864
+ .when('/Book/:bookId/ch/:chapterId', {
865
+ templateUrl: 'chapter.html',
866
+ controller: 'ChapterCtrl',
867
+ controllerAs: 'chapter'
868
+ });
869
+
870
+ $locationProvider.html5Mode(true);
871
+ }])
872
+ .controller('MainCtrl', ['$route', '$routeParams', '$location',
873
+ function($route, $routeParams, $location) {
874
+ this.$route = $route;
875
+ this.$location = $location;
876
+ this.$routeParams = $routeParams;
877
+ }])
878
+ .controller('BookCtrl', ['$routeParams', function($routeParams) {
879
+ this.name = "BookCtrl";
880
+ this.params = $routeParams;
881
+ }])
882
+ .controller('ChapterCtrl', ['$routeParams', function($routeParams) {
883
+ this.name = "ChapterCtrl";
884
+ this.params = $routeParams;
885
+ }]);
886
+
887
+ </file>
888
+
889
+ <file name="protractor.js" type="protractor">
890
+ it('should load and compile correct template', function() {
891
+ element(by.linkText('Moby: Ch1')).click();
892
+ var content = element(by.css('[ng-view]')).getText();
893
+ expect(content).toMatch(/controller\: ChapterCtrl/);
894
+ expect(content).toMatch(/Book Id\: Moby/);
895
+ expect(content).toMatch(/Chapter Id\: 1/);
896
+
897
+ element(by.partialLinkText('Scarlet')).click();
898
+
899
+ content = element(by.css('[ng-view]')).getText();
900
+ expect(content).toMatch(/controller\: BookCtrl/);
901
+ expect(content).toMatch(/Book Id\: Scarlet/);
902
+ });
903
+ </file>
904
+ </example>
905
+ */
906
+
907
+
908
+ /**
909
+ * @ngdoc event
910
+ * @name ngView#$viewContentLoaded
911
+ * @eventType emit on the current ngView scope
912
+ * @description
913
+ * Emitted every time the ngView content is reloaded.
914
+ */
915
+ ngViewFactory.$inject = ['$route', '$anchorScroll', '$animate'];
916
+ function ngViewFactory($route, $anchorScroll, $animate) {
917
+ return {
918
+ restrict: 'ECA',
919
+ terminal: true,
920
+ priority: 400,
921
+ transclude: 'element',
922
+ link: function(scope, $element, attr, ctrl, $transclude) {
923
+ var currentScope,
924
+ currentElement,
925
+ previousLeaveAnimation,
926
+ autoScrollExp = attr.autoscroll,
927
+ onloadExp = attr.onload || '';
928
+
929
+ scope.$on('$routeChangeSuccess', update);
930
+ update();
931
+
932
+ function cleanupLastView() {
933
+ if (previousLeaveAnimation) {
934
+ $animate.cancel(previousLeaveAnimation);
935
+ previousLeaveAnimation = null;
936
+ }
937
+
938
+ if (currentScope) {
939
+ currentScope.$destroy();
940
+ currentScope = null;
941
+ }
942
+ if (currentElement) {
943
+ previousLeaveAnimation = $animate.leave(currentElement);
944
+ previousLeaveAnimation.then(function() {
945
+ previousLeaveAnimation = null;
946
+ });
947
+ currentElement = null;
948
+ }
949
+ }
950
+
951
+ function update() {
952
+ var locals = $route.current && $route.current.locals,
953
+ template = locals && locals.$template;
954
+
955
+ if (angular.isDefined(template)) {
956
+ var newScope = scope.$new();
957
+ var current = $route.current;
958
+
959
+ // Note: This will also link all children of ng-view that were contained in the original
960
+ // html. If that content contains controllers, ... they could pollute/change the scope.
961
+ // However, using ng-view on an element with additional content does not make sense...
962
+ // Note: We can't remove them in the cloneAttchFn of $transclude as that
963
+ // function is called before linking the content, which would apply child
964
+ // directives to non existing elements.
965
+ var clone = $transclude(newScope, function(clone) {
966
+ $animate.enter(clone, null, currentElement || $element).then(function onNgViewEnter() {
967
+ if (angular.isDefined(autoScrollExp)
968
+ && (!autoScrollExp || scope.$eval(autoScrollExp))) {
969
+ $anchorScroll();
970
+ }
971
+ });
972
+ cleanupLastView();
973
+ });
974
+
975
+ currentElement = clone;
976
+ currentScope = current.scope = newScope;
977
+ currentScope.$emit('$viewContentLoaded');
978
+ currentScope.$eval(onloadExp);
979
+ } else {
980
+ cleanupLastView();
981
+ }
982
+ }
983
+ }
984
+ };
985
+ }
986
+
987
+ // This directive is called during the $transclude call of the first `ngView` directive.
988
+ // It will replace and compile the content of the element with the loaded template.
989
+ // We need this directive so that the element content is already filled when
990
+ // the link function of another directive on the same element as ngView
991
+ // is called.
992
+ ngViewFillContentFactory.$inject = ['$compile', '$controller', '$route'];
993
+ function ngViewFillContentFactory($compile, $controller, $route) {
994
+ return {
995
+ restrict: 'ECA',
996
+ priority: -400,
997
+ link: function(scope, $element) {
998
+ var current = $route.current,
999
+ locals = current.locals;
1000
+
1001
+ $element.html(locals.$template);
1002
+
1003
+ var link = $compile($element.contents());
1004
+
1005
+ if (current.controller) {
1006
+ locals.$scope = scope;
1007
+ var controller = $controller(current.controller, locals);
1008
+ if (current.controllerAs) {
1009
+ scope[current.controllerAs] = controller;
1010
+ }
1011
+ $element.data('$ngControllerController', controller);
1012
+ $element.children().data('$ngControllerController', controller);
1013
+ }
1014
+ scope[current.resolveAs || '$resolve'] = locals;
1015
+
1016
+ link(scope);
1017
+ }
1018
+ };
1019
+ }
1020
+
1021
+
1022
+ })(window, window.angular);