Merge pull request #13314 from colemanw/dev/core#337
[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 = 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 _scope = this;
61
62 var $route = $injector.get('$route'), $timeout = $injector.get('$timeout');
63
64 var value;
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 ($route.current.params[options.param] === encValue) return;
78
79 pendingUpdates = pendingUpdates || {};
80 pendingUpdates[options.param] = encValue;
81 var p = angular.extend({}, $route.current.params, pendingUpdates);
82 angular.forEach(ignorable, function(v,k){ if (p[k] === v) delete p[k]; });
83 $route.updateParams(p);
84
85 if (activeTimer) $timeout.cancel(activeTimer);
86 activeTimer = $timeout(function () {
87 pendingUpdates = null;
88 activeTimer = null;
89 ignorable = {};
90 }, 50);
91 }, options.deep);
92 };
93
94 return $delegate;
95 });
96 });
97
98 })(angular, CRM.$, CRM._);