fixed generated data for CRM-15934
[civicrm-core.git] / js / angular-crm-util.js
CommitLineData
60ebf0a5
TO
1/// crmUi: Sundry UI helpers
2(function (angular, $, _) {
3 angular.module('crmUtil', []);
4
81eab931
TO
5 // usage:
6 // crmApi('Entity', 'action', {...}).then(function(apiResult){...})
3cf58cc3 7 //
81eab931 8 // Note: To mock API results in unit-tests, override crmApi.backend, e.g.
3cf58cc3 9 // var apiSpy = jasmine.createSpy('crmApi');
81eab931
TO
10 // crmApi.backend = apiSpy.and.returnValue(crmApi.val({
11 // is_error: 1
12 // }));
a8e65974 13 angular.module('crmUtil').factory('crmApi', function($q) {
81eab931 14 var crmApi = function(entity, action, params, message) {
a8e65974
TO
15 // JSON serialization in CRM.api3 is not aware of Angular metadata like $$hash, so use angular.toJson()
16 var deferred = $q.defer();
17 var p;
81eab931 18 var backend = crmApi.backend || CRM.api3;
a8e65974 19 if (_.isObject(entity)) {
f91e4af5
TO
20 // eval content is locally generated.
21 /*jshint -W061 */
81eab931 22 p = backend(eval('('+angular.toJson(entity)+')'), message);
a8e65974 23 } else {
f91e4af5
TO
24 // eval content is locally generated.
25 /*jshint -W061 */
81eab931 26 p = backend(entity, action, eval('('+angular.toJson(params)+')'), message);
a8e65974
TO
27 }
28 // CRM.api3 returns a promise, but the promise doesn't really represent errors as errors, so we
29 // convert them
30 p.then(
31 function(result) {
32 if (result.is_error) {
33 deferred.reject(result);
34 } else {
35 deferred.resolve(result);
36 }
37 },
38 function(error) {
39 deferred.reject(error);
40 }
41 );
42 return deferred.promise;
43 };
81eab931
TO
44 crmApi.backend = null;
45 crmApi.val = function(value) {
46 var d = $.Deferred();
47 d.resolve(value);
48 return d.promise();
49 };
50 return crmApi;
51 });
52
53 // Get and cache the metadata for an API entity.
54 // usage:
55 // $q.when(crmMetadata.getFields('MyEntity'), function(fields){
56 // console.log('The fields are:', options);
57 // });
58 angular.module('crmUtil').factory('crmMetadata', function($q, crmApi) {
59 var cache = {}; // cache[entityName+'::'+action][fieldName].title
60 var deferreds = {}; // deferreds[cacheKey].push($q.defer())
61 var crmMetadata = {
62 // usage: $q.when(crmMetadata.getField('MyEntity', 'my_field')).then(...);
63 getField: function getField(entity, field) {
64 return $q.when(crmMetadata.getFields(entity)).then(function(fields){
65 return fields[field];
66 });
67 },
68 // usage: $q.when(crmMetadata.getFields('MyEntity')).then(...);
69 // usage: $q.when(crmMetadata.getFields(['MyEntity', 'myaction'])).then(...);
70 getFields: function getFields(entity) {
71 var action = '', cacheKey;
72 if (_.isArray(entity)) {
73 action = entity[1];
74 entity = entity[0];
75 cacheKey = entity + '::' + action;
76 } else {
77 cacheKey = entity;
78 }
79
80 if (_.isObject(cache[cacheKey])) {
81 return cache[cacheKey];
82 }
83
84 var needFetch = _.isEmpty(deferreds[cacheKey]);
85 deferreds[cacheKey] = deferreds[cacheKey] || [];
86 var deferred = $q.defer();
87 deferreds[cacheKey].push(deferred);
88
89 if (needFetch) {
a3b34c78 90 crmApi(entity, 'getfields', {action: action, sequential: 1, options: {get_options: 'all'}})
81eab931
TO
91 .then(
92 // on success:
93 function(fields) {
a3b34c78 94 cache[cacheKey] = _.indexBy(fields.values, 'name');
81eab931 95 angular.forEach(deferreds[cacheKey], function(dfr) {
a3b34c78 96 dfr.resolve(cache[cacheKey]);
81eab931
TO
97 });
98 delete deferreds[cacheKey];
99 },
100 // on error:
101 function() {
102 cache[cacheKey] = {}; // cache nack
103 angular.forEach(deferreds[cacheKey], function(dfr) {
104 dfr.reject();
105 });
106 delete deferreds[cacheKey];
107 }
108 );
109 }
110
111 return deferred.promise;
112 }
113 };
114
115 return crmMetadata;
a8e65974
TO
116 });
117
bdd3f781
TO
118 // usage:
119 // var block = $scope.block = crmBlocker();
120 // $scope.save = function() { return block(crmApi('MyEntity','create',...)); };
121 // <button ng-click="save()" ng-disabled="block.check()">Do something</button>
122 angular.module('crmUtil').factory('crmBlocker', function() {
123 return function() {
124 var blocks = 0;
125 var result = function(promise) {
126 blocks++;
650a6ffc 127 return promise.finally(function() {
bdd3f781
TO
128 blocks--;
129 });
130 };
131 result.check = function() {
132 return blocks > 0;
133 };
134 return result;
135 };
136 });
137
a8e65974
TO
138 angular.module('crmUtil').factory('crmLegacy', function() {
139 return CRM;
140 });
141
226ef186
TO
142 // example: scope.$watch('foo', crmLog.wrap(function(newValue, oldValue){ ... }));
143 angular.module('crmUtil').factory('crmLog', function(){
144 var level = 0;
145 var write = console.log;
146 function indent() {
147 var s = '>';
148 for (var i = 0; i < level; i++) s = s + ' ';
149 return s;
150 }
151 var crmLog = {
152 log: function(msg, vars) {
153 write(indent() + msg, vars);
154 },
155 wrap: function(label, f) {
156 return function(){
157 level++;
158 crmLog.log(label + ": start", arguments);
159 var r;
160 try {
161 r = f.apply(this, arguments);
162 } finally {
163 crmLog.log(label + ": end");
164 level--;
165 }
166 return r;
f2bad133 167 };
226ef186 168 }
f286acec 169 };
226ef186 170 return crmLog;
f286acec
TO
171 });
172
a8e65974
TO
173 angular.module('crmUtil').factory('crmNavigator', ['$window', function($window) {
174 return {
175 redirect: function(path) {
176 $window.location.href = path;
177 }
178 };
179 }]);
180
8717390f
TO
181 angular.module('crmUtil').factory('crmNow', function($q){
182 // FIXME: surely there's already some helper which can do this in one line?
183 // @return string "YYYY-MM-DD hh:mm:ss"
184 return function crmNow() {
185 var currentdate = new Date();
186 var yyyy = currentdate.getFullYear();
187 var mm = currentdate.getMonth() + 1;
188 mm = mm < 10 ? '0' + mm : mm;
189 var dd = currentdate.getDate();
190 dd = dd < 10 ? '0' + dd : dd;
191 var hh = currentdate.getHours();
192 hh = hh < 10 ? '0' + hh : hh;
193 var min = currentdate.getMinutes();
194 min = min < 10 ? '0' + min : min;
195 var sec = currentdate.getSeconds();
196 sec = sec < 10 ? '0' + sec : sec;
197 return yyyy + "-" + mm + "-" + dd + " " + hh + ":" + min + ":" + sec;
198 };
199 });
200
226ef186
TO
201 // Adapter for CRM.status which supports Angular promises (instead of jQuery promises)
202 // example: crmStatus('Saving', crmApi(...)).then(function(result){...})
203 angular.module('crmUtil').factory('crmStatus', function($q){
204 return function(options, aPromise){
a66571ab
TO
205 if (aPromise) {
206 return CRM.toAPromise($q, CRM.status(options, CRM.toJqPromise(aPromise)));
207 } else {
208 return CRM.toAPromise($q, CRM.status(options));
209 }
226ef186
TO
210 };
211 });
212
60ebf0a5
TO
213 // crmWatcher allows one to setup event listeners and temporarily suspend
214 // them en masse.
215 //
216 // example:
217 // angular.controller(... function($scope, crmWatcher){
218 // var watcher = crmWatcher();
219 // function myfunc() {
220 // watcher.suspend('foo', function(){
221 // ...do stuff...
222 // });
223 // }
224 // watcher.setup('foo', function(){
225 // return [
226 // $scope.$watch('foo', myfunc),
227 // $scope.$watch('bar', myfunc),
228 // $scope.$watch('whiz', otherfunc)
229 // ];
230 // });
231 // });
232 angular.module('crmUtil').factory('crmWatcher', function(){
233 return function() {
234 var unwatches = {}, watchFactories = {}, suspends = {};
235
236 // Specify the list of watches
237 this.setup = function(name, newWatchFactory) {
238 watchFactories[name] = newWatchFactory;
239 unwatches[name] = watchFactories[name]();
240 suspends[name] = 0;
241 return this;
242 };
243
244 // Temporarily disable watches and run some logic
245 this.suspend = function(name, f) {
246 suspends[name]++;
247 this.teardown(name);
248 var r;
249 try {
250 r = f.apply(this, []);
251 } finally {
252 if (suspends[name] === 1) {
253 unwatches[name] = watchFactories[name]();
254 if (!angular.isArray(unwatches[name])) {
255 unwatches[name] = [unwatches[name]];
256 }
257 }
258 suspends[name]--;
259 }
260 return r;
261 };
262
263 this.teardown = function(name) {
264 if (!unwatches[name]) return;
265 _.each(unwatches[name], function(unwatch){
266 unwatch();
267 });
268 delete unwatches[name];
269 };
270
271 return this;
f2bad133 272 };
60ebf0a5
TO
273 });
274
60ebf0a5 275})(angular, CRM.$, CRM._);