angular-route-segment-rails 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8e9c8e1d1e08d7c04468156e2927fc95bdab69d6
4
+ data.tar.gz: 6672145b993f8caa0313a16043ce89ba3f8fcd0c
5
+ SHA512:
6
+ metadata.gz: 80d8bd252daa20b6b79ddca0ce95055f3b4c961b36db197146f7ad66e61e272f813a8d79a51fbf5250d1c51ede9077f0a1b24c63a16704127802e13443134784
7
+ data.tar.gz: fe61a90e85f8b4983639d63812bc1a2cbe0ec1dd95fad1bfaf0df499f1bed5e708690a724da34fc6a6e5953ad410962eafe38276792bd054bf9e527e9203d78a
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2016 Andy Alekseenko
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,17 @@
1
+ # angular-route-segment-rails
2
+
3
+ angular-route-segment-rails wraps the [angular-route-segment](https://github.com/alekseenko/angular-route-segment) library for use in Rails 3.1 and above. Assets will minify automatically during production.
4
+ It depends on [angularjs-rails gem](https://github.com/hiravgandhi/angularjs-rails).
5
+
6
+ ## Usage
7
+
8
+ Add the following to your Gemfile:
9
+
10
+ gem 'angularjs-rails'
11
+ gem 'angular-route-segment-rails'
12
+
13
+ Add the following directive to your JavaScript manifest file (application.js) after requiring angular:
14
+
15
+ //= require angular
16
+ //= require angular-route
17
+ //= require angular-route-segment
@@ -0,0 +1,11 @@
1
+ require "angular-route-segment-rails/version"
2
+
3
+ module AngularRouteSegment
4
+ module Rails
5
+ if defined? ::Rails::Engine
6
+ require 'angular-route-segment-rails/engine'
7
+ elsif defined? Sprockets
8
+ require 'angular-route-segment-rails/sprockets'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,6 @@
1
+ module AngularRouteSegment
2
+ module Rails
3
+ class Engine < ::Rails::Engine
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ require 'sprockets'
2
+
3
+ Sprockets.append_path File.expand_path("../../../vendor/assets/javascripts", __FILE__)
@@ -0,0 +1,5 @@
1
+ module AngularRouteSegment
2
+ module Rails
3
+ VERSION = "0.2.0"
4
+ end
5
+ end
@@ -0,0 +1,1029 @@
1
+ /**
2
+ * angular-route-segment 1.5.1
3
+ * https://angular-route-segment.com
4
+ * @author Artem Chivchalov
5
+ * @license MIT License http://opensource.org/licenses/MIT
6
+ */
7
+ 'use strict';
8
+ (function(angular) {
9
+
10
+ /**
11
+ * @ngdoc module
12
+ * @module route-segment
13
+ * @name route-segment
14
+ * @packageName angular-route-segment
15
+ * @requires ngRoute
16
+ * @description
17
+ * This library is intended to provide the lacking functionality of nested routing to [AngularJS](http://angularjs.org) applications.
18
+ * It is widely known, there are no ways to keep the parent state unchanged when children are updated via routing mechanics - the
19
+ * [$route](https://docs.angularjs.org/api/ngRoute/service/$route) service re-creates the whole scope after a route is changed, losing its state completely.
20
+ * *route-segment* gives you a way to handle this.
21
+ *
22
+ * The library provides two pieces of code: {@link $routeSegment $routeSegment} service and {@link appViewSegment appViewSegment} directive.
23
+ * Both are placed in their own modules which you must include as dependencies in your app module:
24
+ *
25
+ * ```js
26
+ * var app = angular.module('app', ['ngRoute', 'route-segment', 'view-segment']);
27
+ * ```
28
+ * $routeSegment is a layer on top of built-in Angular [$route](https://docs.angularjs.org/api/ngRoute/service/$route) service and is meant to be used instead of it.
29
+ * Its provider exposes configuration methods which can be used to traverse the tree of route segments and setup it properly.
30
+ */
31
+ var mod = angular.module( 'route-segment', [] );
32
+ /**
33
+ * @ngdoc provider
34
+ * @module route-segment
35
+ * @name $routeSegmentProvider
36
+ * @requires https://docs.angularjs.org/api/ngRoute/provider/$routeProvider $routeProvider
37
+ * @description Replaces [$routeProvider](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) for the app routing configuration
38
+ *
39
+ * Example:
40
+ * ```js
41
+ * app.config(function ($routeSegmentProvider) {
42
+ *
43
+ * $routeSegmentProvider.
44
+ *
45
+ * when('/section1', 's1').
46
+ * when('/section1/prefs', 's1.prefs').
47
+ * when('/section1/:id', 's1.itemInfo').
48
+ * when('/section1/:id/edit', 's1.itemInfo.edit').
49
+ * when('/section2', 's2').
50
+ *
51
+ * segment('s1', {
52
+ * templateUrl: 'templates/section1.html',
53
+ * controller: MainCtrl}).
54
+ *
55
+ * within().
56
+ *
57
+ * segment('home', {
58
+ * default: true,
59
+ * templateUrl: 'templates/section1/home.html'}).
60
+ *
61
+ * segment('itemInfo', {
62
+ * templateUrl: 'templates/section1/item.html',
63
+ * controller: Section1ItemCtrl,
64
+ * dependencies: ['id']}).
65
+ *
66
+ * within().
67
+ *
68
+ * segment('overview', {
69
+ * default: true
70
+ * templateUrl: 'templates/section1/item/overview.html'}).
71
+ *
72
+ * segment('edit', {
73
+ * templateUrl: 'templates/section1/item/edit.html'}).
74
+ *
75
+ * up().
76
+ *
77
+ * segment('prefs', {
78
+ * templateUrl: 'templates/section1/prefs.html'}).
79
+ *
80
+ * up().
81
+ *
82
+ * segment('s2', {
83
+ * templateUrl: 'templates/section2.html',
84
+ * controller: MainCtrl});
85
+ * });
86
+ * ```
87
+ *
88
+ * Alternatively, you can use this syntax instead of traversing (useful if you want modules to have their own separately defined routes):
89
+ * ```js
90
+ * $routeSegmentProvider.segment('s1', {
91
+ * templateUrl: 'templates/section1.html',
92
+ * controller: MainCtrl});
93
+ *
94
+ * $routeSegmentProvider.within('s1').segment('home', {
95
+ * templateUrl: 'templates/section1/home.html'});
96
+ *
97
+ * $routeSegmentProvider.within('s1').segment('itemInfo', {
98
+ * templateUrl: 'templates/section1/item.html',
99
+ * controller: Section1ItemCtrl,
100
+ * dependencies: ['id']});
101
+ *
102
+ * $routeSegmentProvider.within('s1').within('itemInfo').segment('overview', {
103
+ * templateUrl: 'templates/section1/item/overview.html'});
104
+ * ```
105
+ *
106
+ * See {@link appViewSegment appViewSegment} for details on views configuration
107
+ */
108
+ mod.provider( '$routeSegment',
109
+ ['$routeProvider', function $routeSegmentProvider ($routeProvider) {
110
+
111
+ var $routeSegmentProvider = this;
112
+
113
+ /**
114
+ * @ngdoc type
115
+ * @module route-segment
116
+ * @name $routeSegmentProvider.options
117
+ * @description Contains configuration options for {@link $routeSegmentProvider $routeSegmentProvider}
118
+ */
119
+ /**
120
+ * @ngdoc property
121
+ * @name $routeSegmentProvider#options
122
+ * @type {$routeSegmentProvider.options}
123
+ * @description Provider configuration object
124
+ */
125
+ var options = $routeSegmentProvider.options = {
126
+
127
+ /**
128
+ * @ngdoc property
129
+ * @name $routeSegmentProvider.options#autoLoadTemplates
130
+ * @type {Boolean}
131
+ * @description
132
+ * When true, it will resolve `templateUrl` automatically via ($http)[https://docs.angularjs.org/api/ng/service/$http]
133
+ * service and put its contents into `template`.
134
+ */
135
+ autoLoadTemplates: true,
136
+
137
+ /**
138
+ * @ngdoc property
139
+ * @name $routeSegmentProvider.options#strictMode
140
+ * @type {Boolean}
141
+ * @description
142
+ * When true, all attempts to call `within` method on non-existing segments will throw an error (you would
143
+ * usually want this behavior in production). When false, it will transparently create new empty segment
144
+ * (can be useful in isolated tests).
145
+ */
146
+ strictMode: false
147
+ };
148
+
149
+ /**
150
+ * @ngdoc property
151
+ * @name $routeSegmentProvider#property
152
+ * @type {Object}
153
+ * @private
154
+ * @description Registered routing segments
155
+ */
156
+ var segments = this.segments = {},
157
+ rootPointer = pointer(segments, null),
158
+ segmentRoutes = {};
159
+
160
+ function camelCase(name) {
161
+ return name.replace(/([\:\-\_]+(.))/g, function(_, separator, letter, offset) {
162
+ return offset ? letter.toUpperCase() : letter;
163
+ });
164
+ }
165
+
166
+ /**
167
+ * @ngdoc type
168
+ * @module route-segment
169
+ * @name $routeSegmentProvider.Pointer
170
+ * @description Segment traversal element.
171
+ *
172
+ * Each {@link $routeSegmentProvider#segment $routeSegmentProvider#segment} call creates new navigation pointer
173
+ */
174
+ function pointer(segment, parent) {
175
+
176
+ if(!segment)
177
+ throw new Error('Invalid pointer segment');
178
+
179
+ var lastAddedName;
180
+
181
+ return {
182
+
183
+ /**
184
+ * @ngdoc method
185
+ * @name $routeSegmentProvider#segment
186
+ * @see $routeSegmentProvider.Pointer#segment
187
+ */
188
+ /**
189
+ * @ngdoc method
190
+ * @name $routeSegmentProvider.Pointer#segment
191
+ * @param {string} name Name of a segment.
192
+ * @param {Object} params Segment's parameters hash. The following params are supported:
193
+ * @param {String|Function} [params.template] provides HTML for the given segment view;
194
+ * @param {String|Function} [params.templateUrl] template to fetch from network via this URL;
195
+ * @param {String|Function} [params.controller] cotroller attached to the given segment view when compiled and linked,
196
+ * this can be any controller definition AngularJS supports;
197
+ * @param {String} [params.controllerAs] controller alias name, if present the controller will be
198
+ * published to scope under the controllerAs name
199
+ * @param {Array<String>} [params.dependencies] array of route param names which are forcing the view to recreate when changed;
200
+ * @param {Function} [params.watcher] $watch-function for recreating the view when its returning value is changed;
201
+ * @param {Object<String, Function>} resolve hash of functions or injectable names which should be resolved
202
+ * prior to instantiating the template and the controller;
203
+ * @param {Object} [params.untilResolved] alternate set of params (e.g. `template` and `controller`)
204
+ * which should be used before resolving is completed;
205
+ * @param {Object} [params.resolveFailed] alternate set of params which should be used if resolving failed;
206
+ * @param {Boolean} [params.default] when set to true this child segment should be loaded by
207
+ * default when no child is specified in the route.
208
+ * @returns {$routeSegmentProvider.Pointer} The same level pointer.
209
+ * @description Adds new segment at current pointer level.
210
+ *
211
+ */
212
+ segment: function(name, params) {
213
+ segment[camelCase(name)] = {name: name, params: params};
214
+ lastAddedName = name;
215
+ return this;
216
+ },
217
+
218
+ /**
219
+ * @ngdoc method
220
+ * @name $routeSegmentProvider#within
221
+ * @see $routeSegmentProvider.Pointer#within
222
+ */
223
+ /**
224
+ * @ngdoc method
225
+ * @name $routeSegmentProvider.Pointer#within
226
+ * @param {String=} childName An existing segment's name. If undefined, then the last added segment is selected.
227
+ * @returns {$routeSegmentProvider.Pointer} The pointer to the child segment.
228
+ * @throws {Error} when {@link $routeSegmentProvider.options#strictMode $routeSegmentProvider.options#strictMode} is true and segment with given name is not found
229
+ * @description Traverses into an existing segment, so that subsequent `segment` calls
230
+ * will add new segments as its descendants.
231
+ */
232
+ within: function(childName) {
233
+ var child;
234
+ childName = childName || lastAddedName;
235
+
236
+ if(child = segment[camelCase(childName)]) {
237
+ if(child.children == undefined)
238
+ child.children = {};
239
+ }
240
+ else {
241
+ if(options.strictMode)
242
+ throw new Error('Cannot get into unknown `'+childName+'` segment');
243
+ else {
244
+ child = segment[camelCase(childName)] = {params: {}, children: {}};
245
+ }
246
+ }
247
+ return pointer(child.children, this);
248
+ },
249
+
250
+ /**
251
+ * @ngdoc method
252
+ * @name $routeSegmentProvider.Pointer#up
253
+ * @returns {$routeSegmentProvider.Pointer} The pointer which are parent to the current one;
254
+ * @description Traverses up in the tree.
255
+ */
256
+ up: function() {
257
+ return parent;
258
+ },
259
+
260
+ /**
261
+ * @ngdoc method
262
+ * @name $routeSegmentProvider.Pointer#up
263
+ * @returns {$routeSegmentProvider.Pointer} The root pointer.
264
+ * @description Traverses to the root.
265
+ */
266
+ root: function() {
267
+ return rootPointer;
268
+ }
269
+ }
270
+ }
271
+
272
+ /**
273
+ * @ngdoc method
274
+ * @name $routeSegmentProvider#when
275
+ * @param {String} path Route URL, e.g. '/foo/bar'
276
+ * @param {String} name Fully qualified route name, e.g. 'foo.bar'
277
+ * @param {Object} route Mapping information to be assigned to $route.current on route match.
278
+ * @returns {$routeSegmentProvider} instance
279
+ * @description The shorthand for [$routeProvider.when()](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider#when) method with specified route name.
280
+ */
281
+ $routeSegmentProvider.when = function(path, name, route) {
282
+ if (route == undefined)
283
+ route = {};
284
+ route.segment = name;
285
+
286
+ $routeProvider.when(path, route);
287
+ segmentRoutes[name] = path;
288
+ return this;
289
+ };
290
+
291
+ /**
292
+ * The shorthand for $routeProvider.otherwise() method with specified route.
293
+ * @param {string|function} route Route URL, e.g. '/'; or function that return a URL
294
+ */
295
+ $routeSegmentProvider.otherwise = function(route) {
296
+ $routeProvider.otherwise({redirectTo: route});
297
+ return this;
298
+ };
299
+
300
+ // Extending the provider with the methods of rootPointer
301
+ // to start configuration.
302
+ angular.extend($routeSegmentProvider, rootPointer);
303
+
304
+ /**
305
+ * @ngdoc service
306
+ * @module route-segment
307
+ * @name $routeSegment
308
+ * @requires https://docs.angularjs.org/api/ng/service/$rootScope $rootScope
309
+ * @requires https://docs.angularjs.org/api/ng/service/$q $q
310
+ * @requires https://docs.angularjs.org/api/ng/service/$http $http
311
+ * @requires https://docs.angularjs.org/api/ng/service/$templateCache $templateCache
312
+ * @requires https://docs.angularjs.org/api/ngRoute/service/$route $route
313
+ * @requires https://docs.angularjs.org/api/ngRoute/service/$routeParams $routeParams
314
+ * @requires https://docs.angularjs.org/api/auto/service/$injector $injector
315
+ * @requires https://docs.angularjs.org/api/ng/service/$location $location
316
+ * @description Provides state and operations for the current segment
317
+ */
318
+ this.$get = ['$rootScope', '$q', '$http', '$templateCache', '$route', '$routeParams', '$injector',
319
+ function($rootScope, $q, $http, $templateCache, $route, $routeParams, $injector) {
320
+
321
+ var $routeSegment = {
322
+
323
+ /**
324
+ * @ngdoc property
325
+ * @name $routeSegment#name
326
+ * @type {String}
327
+ * @description Fully qualified name of current active route
328
+ */
329
+ name: '',
330
+
331
+ /**
332
+ * @ngdoc property
333
+ * @name $routeSegment#$routeParams
334
+ * @type {Object}
335
+ * @description A copy of `$routeParams` in its state of the latest successful segment update.
336
+ *
337
+ * It may be not equal to `$routeParams` while some resolving is not completed yet. Should be used instead of original
338
+ * `$routeParams` in most cases.
339
+ */
340
+ $routeParams: angular.copy($routeParams),
341
+
342
+ /**
343
+ * @ngdoc property
344
+ * @name $routeSegment#chain
345
+ * @type {Array.<$routeSegment.Segment>}
346
+ * @description Array of segments splitted by each level separately. Each item contains the following properties:
347
+ *
348
+ * - `name` is the name of a segment;
349
+ * - `params` is the config params hash of a segment;
350
+ * - `locals` is a hash which contains resolve results if any;
351
+ * - `reload` is a function to reload a segment (restart resolving, reinstantiate a controller, etc)
352
+ */
353
+ chain: [],
354
+
355
+ /**
356
+ * @ngdoc method
357
+ * @name $routeSegment#startsWith
358
+ * @param {String} val segment name to test
359
+ * @returns {Boolean}
360
+ * @description Helper method for checking whether current route starts with the given string
361
+ */
362
+ startsWith: function (val) {
363
+ var regexp = new RegExp('^'+val);
364
+ return regexp.test($routeSegment.name);
365
+ },
366
+
367
+ /**
368
+ * @ngdoc method
369
+ * @name $routeSegment#contains
370
+ * @param {String} val segment name to test
371
+ * @returns {Boolean} true if given segment present in the current route
372
+ * @description Helper method for checking whether current route contains the given string
373
+ */
374
+ contains: function (val) {
375
+ for(var i=0; i<this.chain.length; i++)
376
+ if(this.chain[i] && this.chain[i].name == val)
377
+ return true;
378
+ return false;
379
+ },
380
+
381
+ /**
382
+ * @ngdoc method
383
+ * @name $routeSegment#getSegmentUrl
384
+ * @param {String} segmentName The name of a segment as defined in `when()`
385
+ * @param {Object} routeParams Route params hash to be put into route URL template
386
+ * @returns {String} segment url
387
+ * @throws {Error} if url for the given name is not found
388
+ * @description A method for reverse routing which can return the route URL for the specified segment name
389
+ */
390
+ getSegmentUrl: function(segmentName, routeParams) {
391
+ var url, i, m;
392
+ if(!segmentRoutes[segmentName])
393
+ throw new Error('Can not get URL for segment with name `'+segmentName+'`');
394
+
395
+ routeParams = angular.extend({}, $routeParams, routeParams || {});
396
+
397
+ url = segmentRoutes[segmentName];
398
+ for(i in routeParams) {
399
+ var regexp = new RegExp('\:'+i+'[\*\?]?','g');
400
+ url = url.replace(regexp, routeParams[i]);
401
+ }
402
+ url = url.replace(/\/\:.*?\?/g, '');
403
+
404
+ if(m = url.match(/\/\:([^\/]*)/))
405
+ throw new Error('Route param `'+m[1]+'` is not specified for route `'+segmentRoutes[segmentName]+'`');
406
+
407
+ return url;
408
+ }
409
+ };
410
+
411
+ var resolvingSemaphoreChain = {};
412
+
413
+ // When a route changes, all interested parties should be notified about new segment chain
414
+ $rootScope.$on('$routeChangeSuccess', function(event, args) {
415
+
416
+ var route = args.$route || args.$$route;
417
+ if(route && route.segment) {
418
+
419
+ var segmentName = route.segment;
420
+ var segmentNameChain = segmentName.split(".");
421
+ var updates = [], lastUpdateIndex = -1;
422
+
423
+ for(var i=0; i < segmentNameChain.length; i++) {
424
+
425
+ var newSegment = getSegmentInChain( i, segmentNameChain );
426
+
427
+ if(resolvingSemaphoreChain[i] != newSegment.name || updates.length > 0 || isDependenciesChanged(newSegment)) {
428
+
429
+ if($routeSegment.chain[i] && $routeSegment.chain[i].name == newSegment.name &&
430
+ updates.length == 0 && !isDependenciesChanged(newSegment))
431
+ // if we went back to the same state as we were before resolving new segment
432
+ resolvingSemaphoreChain[i] = newSegment.name;
433
+ else {
434
+ updates.push({index: i, newSegment: newSegment});
435
+ lastUpdateIndex = i;
436
+ }
437
+ }
438
+ }
439
+
440
+ var curSegmentPromise = $q.when();
441
+
442
+ if(updates.length > 0) {
443
+
444
+ for(var i=0; i<updates.length; i++) {
445
+ (function(i) {
446
+ curSegmentPromise = curSegmentPromise.then(function() {
447
+
448
+ return updateSegment(updates[i].index, updates[i].newSegment);
449
+
450
+ }).then(function(result) {
451
+
452
+ if(result.success != undefined) {
453
+
454
+ broadcast(result.success);
455
+
456
+ for(var j = updates[i].index + 1; j < $routeSegment.chain.length; j++) {
457
+
458
+ if($routeSegment.chain[j]) {
459
+ if ($routeSegment.chain[j].clearWatcher) {
460
+ $routeSegment.chain[j].clearWatcher();
461
+ }
462
+
463
+ $routeSegment.chain[j] = null;
464
+ updateSegment(j, null);
465
+ }
466
+ }
467
+ }
468
+ })
469
+ })(i);
470
+ }
471
+ }
472
+
473
+ curSegmentPromise.then(function() {
474
+
475
+ // Removing redundant segment in case if new segment chain is shorter than old one
476
+ if($routeSegment.chain.length > segmentNameChain.length) {
477
+ var oldLength = $routeSegment.chain.length;
478
+ var shortenBy = $routeSegment.chain.length - segmentNameChain.length;
479
+ $routeSegment.chain.splice(-shortenBy, shortenBy);
480
+ for(var i=segmentNameChain.length; i < oldLength; i++) {
481
+ updateSegment(i, null);
482
+ lastUpdateIndex = $routeSegment.chain.length-1;
483
+ }
484
+ }
485
+ }).then(function() {
486
+
487
+ var defaultChildUpdatePromise = $q.when();
488
+
489
+ if(lastUpdateIndex == $routeSegment.chain.length-1) {
490
+
491
+ var curSegment = getSegmentInChain(lastUpdateIndex, $routeSegment.name.split("."));
492
+
493
+ while(curSegment) {
494
+ var children = curSegment.children, index = lastUpdateIndex+1;
495
+ curSegment = null;
496
+ for (var i in children) {
497
+ (function(i, children, index) {
498
+ if (children[i].params['default']) {
499
+ defaultChildUpdatePromise = defaultChildUpdatePromise.then(function () {
500
+ return updateSegment(index, {name: children[i].name, params: children[i].params})
501
+ .then(function (result) {
502
+ if (result.success) broadcast(result.success);
503
+ });
504
+ });
505
+ curSegment = children[i];
506
+ lastUpdateIndex = index;
507
+ }
508
+ })(i, children, index);
509
+
510
+
511
+ }
512
+ }
513
+ }
514
+
515
+ return defaultChildUpdatePromise;
516
+ });
517
+ }
518
+ });
519
+
520
+ function isDependenciesChanged(segment) {
521
+
522
+ var result = false;
523
+ if(segment.params.dependencies)
524
+ angular.forEach(segment.params.dependencies, function(name) {
525
+ if(!angular.equals($routeSegment.$routeParams[name], $routeParams[name]))
526
+ result = true;
527
+ });
528
+ return result;
529
+ }
530
+
531
+ function updateSegment(index, segment) {
532
+
533
+ if($routeSegment.chain[index] && $routeSegment.chain[index].clearWatcher) {
534
+ $routeSegment.chain[index].clearWatcher();
535
+ }
536
+
537
+ if(!segment) {
538
+ resolvingSemaphoreChain[index] = null;
539
+ broadcast(index);
540
+ return;
541
+ }
542
+
543
+ resolvingSemaphoreChain[index] = segment.name;
544
+
545
+ if(segment.params.untilResolved) {
546
+ return resolve(index, segment.name, segment.params.untilResolved)
547
+ .then(function(result) {
548
+ if(result.success != undefined)
549
+ broadcast(index);
550
+ return resolve(index, segment.name, segment.params);
551
+ })
552
+ }
553
+ else
554
+ return resolve(index, segment.name, segment.params);
555
+ }
556
+
557
+ function resolve(index, name, params) {
558
+
559
+ var locals = angular.extend({}, params.resolve);
560
+
561
+ angular.forEach(locals, function(value, key) {
562
+ locals[key] = angular.isString(value) ? $injector.get(value) : $injector.invoke(value);
563
+ });
564
+
565
+ if(params.template) {
566
+
567
+ locals.$template = params.template;
568
+ if(angular.isFunction(locals.$template))
569
+ locals.$template = $injector.invoke(locals.$template);
570
+ }
571
+
572
+ if(options.autoLoadTemplates && params.templateUrl) {
573
+
574
+ locals.$template = params.templateUrl;
575
+ if(angular.isFunction(locals.$template))
576
+ locals.$template = $injector.invoke(locals.$template);
577
+
578
+ locals.$template =
579
+ $http.get(locals.$template, {cache: $templateCache})
580
+ .then(function (response) {
581
+ return response.data;
582
+ });
583
+ }
584
+
585
+ return $q.all(locals).then(
586
+
587
+ function(resolvedLocals) {
588
+
589
+ if(resolvingSemaphoreChain[index] != name)
590
+ return $q.reject();
591
+
592
+ /**
593
+ * @ngdoc type
594
+ * @module route-segment
595
+ * @name $routeSegment.Segment
596
+ * @description Segment record
597
+ */
598
+ $routeSegment.chain[index] = {
599
+ /**
600
+ * @ngdoc property
601
+ * @name $routeSegment.Segment#name
602
+ * @type {String}
603
+ * @description segment name as registered with {@link $routeSegmentProvider#segment $routeSegmentProvider#segment}
604
+ */
605
+ name: name,
606
+ /**
607
+ * @ngdoc property
608
+ * @name $routeSegment.Segment#params
609
+ * @type {Object=}
610
+ * @description [$routeParams](https://docs.angularjs.org/api/ngRoute/provider/$routeProvider) parameters for segment
611
+ */
612
+ params: params,
613
+ /**
614
+ * @ngdoc property
615
+ * @name $routeSegment.Segment#locals
616
+ * @type {Object=}
617
+ * @description resolved segment data
618
+ */
619
+ locals: resolvedLocals,
620
+ /**
621
+ * @ngdoc method
622
+ * @name $routeSegment.Segment#reload
623
+ * @description reloads current segment from scratch
624
+ */
625
+ reload: function() {
626
+ var originalSegment = getSegmentInChain(index, $routeSegment.name.split("."));
627
+ updateSegment(index, originalSegment).then(function(result) {
628
+ if(result.success != undefined)
629
+ broadcast(index);
630
+ })
631
+ }
632
+ };
633
+
634
+ if(params.watcher) {
635
+
636
+ var getWatcherValue = function() {
637
+ if(!angular.isFunction(params.watcher) && !angular.isArray(params.watcher))
638
+ throw new Error('Watcher is not a function in segment `'+name+'`');
639
+
640
+ return $injector.invoke(
641
+ params.watcher,
642
+ {},
643
+ {segment: $routeSegment.chain[index]});
644
+ }
645
+
646
+ var lastWatcherValue = getWatcherValue();
647
+
648
+ $routeSegment.chain[index].clearWatcher = $rootScope.$watch(
649
+ getWatcherValue,
650
+ function(value) {
651
+ if(value == lastWatcherValue) // should not being run when $digest-ing at first time
652
+ return;
653
+ lastWatcherValue = value;
654
+ $routeSegment.chain[index].reload();
655
+ })
656
+ }
657
+
658
+ return {success: index};
659
+ },
660
+
661
+ function(error) {
662
+
663
+ if(params.resolveFailed) {
664
+ var newResolve = {error: function() { return $q.when(error); }};
665
+ return resolve(index, name, angular.extend({resolve: newResolve}, params.resolveFailed));
666
+ }
667
+ else
668
+ throw new Error('Resolving failed with a reason `'+error+'`, but no `resolveFailed` ' +
669
+ 'provided for segment `'+name+'`');
670
+ })
671
+ }
672
+
673
+ function broadcast(index) {
674
+
675
+ $routeSegment.$routeParams = angular.copy($routeParams);
676
+
677
+ $routeSegment.name = '';
678
+ for(var i=0; i<$routeSegment.chain.length; i++)
679
+ if($routeSegment.chain[i])
680
+ $routeSegment.name += $routeSegment.chain[i].name+".";
681
+ $routeSegment.name = $routeSegment.name.substr(0, $routeSegment.name.length-1);
682
+
683
+ /**
684
+ * @ngdoc event
685
+ * @name $routeSegment#routeSegmentChange
686
+ * @eventType broadcast on $rootScope
687
+ * @param {Object} object object containing
688
+ * - `index` {number} index id in {@link $routeSegment#chain $routeSegment#chain}
689
+ * - segment {$routeSegment.Segment|Null} current segment
690
+ * @description event is thrown when segment is loaded
691
+ */
692
+ $rootScope.$broadcast( 'routeSegmentChange', {
693
+ index: index,
694
+ segment: $routeSegment.chain[index] || null } );
695
+ }
696
+
697
+ function getSegmentInChain(segmentIdx, segmentNameChain) {
698
+
699
+ if(!segmentNameChain)
700
+ return null;
701
+
702
+ if(segmentIdx >= segmentNameChain.length)
703
+ return null;
704
+
705
+ var curSegment = segments, nextName;
706
+ for(var i=0;i<=segmentIdx;i++) {
707
+
708
+ nextName = segmentNameChain[i];
709
+
710
+ if(curSegment[ camelCase(nextName) ] != undefined)
711
+ curSegment = curSegment[ camelCase(nextName) ];
712
+
713
+ if(i < segmentIdx)
714
+ curSegment = curSegment.children;
715
+ }
716
+
717
+ return {
718
+ name: nextName,
719
+ params: curSegment.params,
720
+ children: curSegment.children
721
+ };
722
+ }
723
+
724
+ return $routeSegment;
725
+ }];
726
+ }]);
727
+
728
+ /**
729
+ * @ngdoc filter
730
+ * @module route-segment
731
+ * @name routeSegmentUrl
732
+ * @param {String} name fully qualified segment name
733
+ * @param {Object} params params to resolve segment
734
+ * @returns {string} given url
735
+ * @description Returns url for a given segment
736
+ *
737
+ * Usage:
738
+ * ```html
739
+ * <a ng-href="{{ 'index.list' | routeSegmentUrl }}">
740
+ * <a ng-href="{{ 'index.list.itemInfo' | routeSegmentUrl: {id: 123} }}">
741
+ * ```
742
+ */
743
+ mod.filter('routeSegmentUrl', ['$routeSegment', function($routeSegment) {
744
+ var filter = function(segmentName, params) {
745
+ return $routeSegment.getSegmentUrl(segmentName, params);
746
+ };
747
+ filter.$stateful = true;
748
+ return filter;
749
+ }]);
750
+
751
+ /**
752
+ * @ngdoc filter
753
+ * @module route-segment
754
+ * @name routeSegmentEqualsTo
755
+ * @param {String} name fully qualified segment name
756
+ * @returns {boolean} true if given segment name is the active one
757
+ * @description Check whether active segment equals to the given segment name
758
+ *
759
+ * Usage:
760
+ * ```html
761
+ * <li ng-class="{active: ('index.list' | routeSegmentEqualsTo)}">
762
+ * ```
763
+ */
764
+ mod.filter('routeSegmentEqualsTo', ['$routeSegment', function($routeSegment) {
765
+ var filter = function(value) {
766
+ return $routeSegment.name == value;
767
+ };
768
+ filter.$stateful = true;
769
+ return filter;
770
+ }]);
771
+
772
+ /**
773
+ * @ngdoc filter
774
+ * @module route-segment
775
+ * @name routeSegmentStartsWith
776
+ * @param {String} name segment name
777
+ * @returns {boolean} true if active segment name begins with given name
778
+ * @description Check whether active segment starts with the given segment name
779
+ *
780
+ * Usage:
781
+ * ```html
782
+ * <li ng-class="{active: ('section1' | routeSegmentStartsWith)}">
783
+ * ```
784
+ */
785
+ mod.filter('routeSegmentStartsWith', ['$routeSegment', function($routeSegment) {
786
+ var filter = function(value) {
787
+ return $routeSegment.startsWith(value);
788
+ };
789
+ filter.$stateful = true;
790
+ return filter;
791
+ }]);
792
+
793
+ /**
794
+ * @ngdoc filter
795
+ * @module route-segment
796
+ * @name routeSegmentContains
797
+ * @param {String} name segment name
798
+ * @returns {boolean} true if active segment contains given name
799
+ * @description Check whether active segment contains the given segment name
800
+ *
801
+ * Usage:
802
+ * ```html
803
+ * <li ng-class="{active: ('itemInfo' | routeSegmentContains)}">
804
+ * ```
805
+ */
806
+ mod.filter('routeSegmentContains', ['$routeSegment', function($routeSegment) {
807
+ var filter = function(value) {
808
+ return $routeSegment.contains(value);
809
+ };
810
+ filter.$stateful = true;
811
+ return filter;
812
+ }]);
813
+
814
+ /**
815
+ * @ngdoc filter
816
+ * @module route-segment
817
+ * @name routeSegmentParam
818
+ * @param {String} name param name
819
+ * @returns {string|undefined} param value or undefined
820
+ * @description Returns segment parameter by name
821
+ *
822
+ * Usage:
823
+ * ```html
824
+ * <li ng-class="{active: ('index.list.itemInfo' | routeSegmentEqualsTo) && ('id' | routeSegmentParam) == 123}">
825
+ * ```
826
+ */
827
+ mod.filter('routeSegmentParam', ['$routeSegment', function($routeSegment) {
828
+ var filter = function(value) {
829
+ return $routeSegment.$routeParams[value];
830
+ };
831
+ filter.$stateful = true;
832
+ return filter;
833
+ }]);
834
+
835
+
836
+ })(angular);;'use strict';
837
+
838
+ /**
839
+ * @ngdoc module
840
+ * @module view-segment
841
+ * @name view-segment
842
+ * @packageName angular-route-segment
843
+ * @requires route-segment
844
+ * @description
845
+ * view-segment is a replacement for [ngView](https://docs.angularjs.org/api/ngRoute/directive/ngView) AngularJS directive.
846
+ *
847
+ * {@link appViewSegment appViewSegment} tags in the DOM will be populated with the corresponding route segment item.
848
+ * You must provide a segment index as an argument to this directive to make it aware about which segment level in the tree
849
+ * it should be linked to.
850
+ *
851
+ * *index.html*:
852
+ * ```html
853
+ * <ul>
854
+ * <li ng-class="{active: $routeSegment.startsWith('s1')}">
855
+ * <a href="/section1">Section 1</a>
856
+ * </li>
857
+ * <li ng-class="{active: $routeSegment.startsWith('s2')}">
858
+ * <a href="/section2">Section 2</a>
859
+ * </li>
860
+ * </ul>
861
+ * <div id="contents" app-view-segment="0"></div>
862
+ * ```
863
+ *
864
+ * *section1.html*: (it will be loaded to div#contents in index.html)
865
+ * ```html
866
+ * <h4>Section 1</h4>
867
+ * Section 1 contents.
868
+ * <div app-view-segment="1"></div>
869
+ * ```
870
+ *
871
+ * ...etc. You can reach any nesting level here. Every view will be handled independently, keeping the state of top-level views.
872
+ *
873
+ * You can also use filters to define link hrefs. It will resolve segment URLs automatically:
874
+ *
875
+ * ```html
876
+ * <ul>
877
+ * <li ng-class="{active: ('s1' | routeSegmentStartsWith)}">
878
+ * <a href="{{ 's1' | routeSegmentUrl }}">Section 1</a>
879
+ * </li>
880
+ * <li ng-class="{active: ('s2' | routeSegmentStartsWith)}">
881
+ * <a href="{{ 's2' | routeSegmentUrl }}">Section 2</a>
882
+ * </li>
883
+ * </ul>
884
+ * ```
885
+ */
886
+ /**
887
+ * @ngdoc directive
888
+ * @module view-segment
889
+ * @name appViewSegment
890
+ * @requires https://docs.angularjs.org/api/ngRoute/service/$route $route
891
+ * @requires https://docs.angularjs.org/api/ng/service/$compile $compile
892
+ * @requires https://docs.angularjs.org/api/ng/service/$controller $controller
893
+ * @requires https://docs.angularjs.org/api/ngRoute/service/$routeParams $routeParams
894
+ * @requires $routeSegment
895
+ * @requires https://docs.angularjs.org/api/ng/service/$q $q
896
+ * @requires https://docs.angularjs.org/api/auto/service/$injector $injector
897
+ * @requires https://docs.angularjs.org/api/ng/service/$timeout $timeout
898
+ * @requires https://docs.angularjs.org/api/ng/service/$animate $animate
899
+ * @restrict ECA
900
+ * @priority 400
901
+ * @param {String} appViewSegment render depth level
902
+ * @description Renders active segment as specified by parameter
903
+ *
904
+ * It is based on [ngView directive code](https://github.com/angular/angular.js/blob/master/src/ngRoute/directive/ngView.js)
905
+ */
906
+
907
+ (function(angular) {
908
+
909
+ angular.module( 'view-segment', [ 'route-segment' ] ).directive( 'appViewSegment',
910
+ ['$route', '$compile', '$controller', '$routeParams', '$routeSegment', '$q', '$injector', '$timeout', '$animate',
911
+ function($route, $compile, $controller, $routeParams, $routeSegment, $q, $injector, $timeout, $animate) {
912
+
913
+ return {
914
+ restrict : 'ECA',
915
+ priority: 400,
916
+ transclude: 'element',
917
+
918
+ compile : function(tElement, tAttrs) {
919
+
920
+ return function($scope, element, attrs, ctrl, $transclude) {
921
+
922
+ var currentScope, currentElement, currentSegment = {}, onloadExp = tAttrs.onload || '',
923
+ viewSegmentIndex = parseInt(tAttrs.appViewSegment), updatePromise, previousLeaveAnimation;
924
+
925
+ if($routeSegment.chain[viewSegmentIndex]) {
926
+ updatePromise = $timeout(function () {
927
+ update($routeSegment.chain[viewSegmentIndex]);
928
+ }, 0);
929
+ }
930
+ else {
931
+ update();
932
+ }
933
+
934
+ // Watching for the specified route segment and updating contents
935
+ $scope.$on('routeSegmentChange', function(event, args) {
936
+
937
+ if(updatePromise)
938
+ $timeout.cancel(updatePromise);
939
+
940
+ if(args.index == viewSegmentIndex && currentSegment != args.segment) {
941
+ update(args.segment);
942
+ }
943
+ });
944
+
945
+ function clearContent() {
946
+ if (previousLeaveAnimation) {
947
+ $animate.cancel(previousLeaveAnimation);
948
+ previousLeaveAnimation = null;
949
+ }
950
+
951
+ if (currentScope) {
952
+ currentScope.$destroy();
953
+ currentScope = null;
954
+ }
955
+ if (currentElement) {
956
+ previousLeaveAnimation = $animate.leave(currentElement);
957
+ if(previousLeaveAnimation) {
958
+ previousLeaveAnimation.then(function () {
959
+ previousLeaveAnimation = null;
960
+ });
961
+ }
962
+ currentElement = null;
963
+ }
964
+ }
965
+
966
+ function update(segment) {
967
+
968
+ currentSegment = segment;
969
+
970
+ var newScope = $scope.$new();
971
+
972
+ var clone = $transclude(newScope, function(clone) {
973
+ if(segment) {
974
+ clone.data('viewSegment', segment);
975
+ }
976
+ $animate.enter(clone, null, currentElement || element);
977
+ clearContent();
978
+ });
979
+
980
+ currentElement = clone;
981
+ currentScope = newScope;
982
+ /*
983
+ * @ngdoc event
984
+ * @name appViewSegment#$viewContentLoaded
985
+ * @description Indicates that segment content has been loaded and transcluded
986
+ */
987
+ currentScope.$emit('$viewContentLoaded');
988
+ currentScope.$eval(onloadExp);
989
+ }
990
+ }
991
+ }
992
+ }
993
+ }]);
994
+
995
+ angular.module( 'view-segment').directive( 'appViewSegment',
996
+ ['$route', '$compile', '$controller', function($route, $compile, $controller) {
997
+
998
+ return {
999
+ restrict: 'ECA',
1000
+ priority: -400,
1001
+ link: function ($scope, element) {
1002
+
1003
+ var segment = element.data('viewSegment') || {};
1004
+
1005
+ var locals = angular.extend({}, segment.locals),
1006
+ template = locals && locals.$template;
1007
+
1008
+ if(template) {
1009
+ element.html(template);
1010
+ }
1011
+
1012
+ var link = $compile(element.contents());
1013
+
1014
+ if (segment.params && segment.params.controller) {
1015
+ locals.$scope = $scope;
1016
+ var controller = $controller(segment.params.controller, locals);
1017
+ if(segment.params.controllerAs)
1018
+ $scope[segment.params.controllerAs] = controller;
1019
+ element.data('$ngControllerController', controller);
1020
+ element.children().data('$ngControllerController', controller);
1021
+ }
1022
+
1023
+ link($scope);
1024
+ }
1025
+ }
1026
+
1027
+ }]);
1028
+
1029
+ })(angular);
metadata ADDED
@@ -0,0 +1,64 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: angular-route-segment-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - alekseenko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: angularjs-rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ description: Injects angular-route-segment module into your asset pipeline.
28
+ email: mailto.alekseenko@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - MIT-LICENSE
34
+ - README.md
35
+ - lib/angular-route-segment-rails.rb
36
+ - lib/angular-route-segment-rails/engine.rb
37
+ - lib/angular-route-segment-rails/sprockets.rb
38
+ - lib/angular-route-segment-rails/version.rb
39
+ - vendor/assets/javascripts/angular-route-segment.js
40
+ homepage: https://github.com/alekseenko/angular-route-segment-rails/
41
+ licenses:
42
+ - MIT
43
+ metadata: {}
44
+ post_install_message:
45
+ rdoc_options: []
46
+ require_paths:
47
+ - lib
48
+ required_ruby_version: !ruby/object:Gem::Requirement
49
+ requirements:
50
+ - - ">="
51
+ - !ruby/object:Gem::Version
52
+ version: '0'
53
+ required_rubygems_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ requirements: []
59
+ rubyforge_project:
60
+ rubygems_version: 2.5.1
61
+ signing_key:
62
+ specification_version: 4
63
+ summary: angular-route-segment for Rails
64
+ test_files: []