angular-route-segment-rails 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []