Merge pull request #22164 from eileenmcnaughton/parent
[civicrm-core.git] / ang / crmRouteBinder.js
1 (function(angular, $, _) {
2 angular.module('crmRouteBinder', CRM.angRequires('crmRouteBinder'));
3
4 // While processing a change from the $watch()'d data, we set the "pendingUpdates" flag
5 // so that automated URL changes don't cause a reload.
6 var pendingUpdates = null, activeTimer = null, registered = false, ignorable = {};
7
8 function registerGlobalListener($injector) {
9 if (registered) return;
10 registered = true;
11
12 $injector.get('$rootScope').$on('$routeUpdate', function () {
13 // Only reload if someone else -- like the user or an <a href> -- changed URL.
14 if (null === pendingUpdates) {
15 $injector.get('$route').reload();
16 }
17 });
18 }
19
20 var formats = {
21 json: {
22 watcher: '$watchCollection',
23 decode: angular.fromJson,
24 encode: angular.toJson,
25 default: {}
26 },
27 raw: {
28 watcher: '$watch',
29 decode: function(v) { return v; },
30 encode: function(v) { return v; },
31 default: ''
32 },
33 int: {
34 watcher: '$watch',
35 decode: function(v) { return parseInt(v); },
36 encode: function(v) { return v; },
37 default: 0
38 },
39 bool: {
40 watcher: '$watch',
41 decode: function(v) { return v === '1'; },
42 encode: function(v) { return v ? '1' : '0'; },
43 default: false
44 }
45 };
46
47 angular.module('crmRouteBinder').config(function ($provide) {
48 $provide.decorator('$rootScope', function ($delegate, $injector, $parse) {
49 Object.getPrototypeOf($delegate).$bindToRoute = function (options) {
50 registerGlobalListener($injector);
51
52 options.format = options.format || 'json';
53 var fmt = _.clone(formats[options.format]);
54 if (options.deep) {
55 fmt.watcher = '$watch';
56 }
57 if (options.default === undefined) {
58 options.default = fmt.default;
59 }
60 var value,
61 _scope = this,
62 $route = $injector.get('$route'),
63 $timeout = $injector.get('$timeout');
64
65 if (options.param in $route.current.params) {
66 value = fmt.decode($route.current.params[options.param]);
67 }
68 else {
69 value = _.cloneDeep(options.default);
70 ignorable[options.param] = fmt.encode(options.default);
71 }
72 $parse(options.expr).assign(_scope, value);
73
74 // Keep the URL bar up-to-date.
75 _scope[fmt.watcher](options.expr, function (newValue) {
76 var encValue = fmt.encode(newValue);
77 if (!_.isEqual(newValue, options.default) && $route.current.params[options.param] === encValue) {
78 return;
79 }
80
81 pendingUpdates = pendingUpdates || {};
82 pendingUpdates[options.param] = encValue;
83 var p = angular.extend({}, $route.current.params, pendingUpdates);
84
85 angular.forEach(ignorable, function(v, k) {
86 if (p[k] === v) {
87 delete p[k];
88 }
89 });
90
91 // Remove params from url if they equal their defaults
92 if (_.isEqual(newValue, options.default)) {
93 p[options.param] = null;
94 }
95
96 $route.updateParams(p);
97
98 if (activeTimer) $timeout.cancel(activeTimer);
99 activeTimer = $timeout(function () {
100 pendingUpdates = null;
101 activeTimer = null;
102 ignorable = {};
103 }, 50);
104 }, options.deep);
105 };
106
107 return $delegate;
108 });
109 });
110
111 })(angular, CRM.$, CRM._);