| 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._); |