1 /// crmUi: Sundry UI helpers
2 (function (angular
, $, _
) {
3 angular
.module('crmUtil', []);
6 // crmApi('Entity', 'action', {...}).then(function(apiResult){...})
8 // Note: To mock API results in unit-tests, override crmApi.backend, e.g.
9 // var apiSpy = jasmine.createSpy('crmApi');
10 // crmApi.backend = apiSpy.and.returnValue(crmApi.val({
13 angular
.module('crmUtil').factory('crmApi', function($q
) {
14 var crmApi = function(entity
, action
, params
, message
) {
15 // JSON serialization in CRM.api3 is not aware of Angular metadata like $$hash, so use angular.toJson()
16 var deferred
= $q
.defer();
18 var backend
= crmApi
.backend
|| CRM
.api3
;
19 if (_
.isObject(entity
)) {
20 // eval content is locally generated.
22 p
= backend(eval('('+angular
.toJson(entity
)+')'), message
);
24 // eval content is locally generated.
26 p
= backend(entity
, action
, eval('('+angular
.toJson(params
)+')'), message
);
28 // CRM.api3 returns a promise, but the promise doesn't really represent errors as errors, so we
32 if (result
.is_error
) {
33 deferred
.reject(result
);
35 deferred
.resolve(result
);
39 deferred
.reject(error
);
42 return deferred
.promise
;
44 crmApi
.backend
= null;
45 crmApi
.val = function(value
) {
53 // Get and cache the metadata for an API entity.
55 // $q.when(crmMetadata.getFields('MyEntity'), function(fields){
56 // console.log('The fields are:', options);
58 angular
.module('crmUtil').factory('crmMetadata', function($q
, crmApi
) {
60 // Convert {key:$,value:$} sequence to unordered {$key: $value} map.
61 function convertOptionsToMap(options
) {
63 angular
.forEach(options
, function(o
) {
64 result
[o
.key
] = o
.value
;
69 var cache
= {}; // cache[entityName+'::'+action][fieldName].title
70 var deferreds
= {}; // deferreds[cacheKey].push($q.defer())
72 // usage: $q.when(crmMetadata.getField('MyEntity', 'my_field')).then(...);
73 getField
: function getField(entity
, field
) {
74 return $q
.when(crmMetadata
.getFields(entity
)).then(function(fields
){
78 // usage: $q.when(crmMetadata.getFields('MyEntity')).then(...);
79 // usage: $q.when(crmMetadata.getFields(['MyEntity', 'myaction'])).then(...);
80 getFields
: function getFields(entity
) {
81 var action
= '', cacheKey
;
82 if (_
.isArray(entity
)) {
85 cacheKey
= entity
+ '::' + action
;
90 if (_
.isObject(cache
[cacheKey
])) {
91 return cache
[cacheKey
];
94 var needFetch
= _
.isEmpty(deferreds
[cacheKey
]);
95 deferreds
[cacheKey
] = deferreds
[cacheKey
] || [];
96 var deferred
= $q
.defer();
97 deferreds
[cacheKey
].push(deferred
);
100 crmApi(entity
, 'getfields', {action
: action
, sequential
: 1, options
: {get_options
: 'all'}})
104 cache
[cacheKey
] = _
.indexBy(fields
.values
, 'name');
105 angular
.forEach(cache
[cacheKey
],function (field
){
107 field
.optionsMap
= convertOptionsToMap(field
.options
);
110 angular
.forEach(deferreds
[cacheKey
], function(dfr
) {
111 dfr
.resolve(cache
[cacheKey
]);
113 delete deferreds
[cacheKey
];
117 cache
[cacheKey
] = {}; // cache nack
118 angular
.forEach(deferreds
[cacheKey
], function(dfr
) {
121 delete deferreds
[cacheKey
];
126 return deferred
.promise
;
134 // var block = $scope.block = crmBlocker();
135 // $scope.save = function() { return block(crmApi('MyEntity','create',...)); };
136 // <button ng-click="save()" ng-disabled="block.check()">Do something</button>
137 angular
.module('crmUtil').factory('crmBlocker', function() {
140 var result = function(promise
) {
142 return promise
.finally(function() {
146 result
.check = function() {
153 angular
.module('crmUtil').factory('crmLegacy', function() {
157 // example: scope.$watch('foo', crmLog.wrap(function(newValue, oldValue){ ... }));
158 angular
.module('crmUtil').factory('crmLog', function(){
160 var write
= console
.log
;
163 for (var i
= 0; i
< level
; i
++) s
= s
+ ' ';
167 log: function(msg
, vars
) {
168 write(indent() + msg
, vars
);
170 wrap: function(label
, f
) {
173 crmLog
.log(label
+ ": start", arguments
);
176 r
= f
.apply(this, arguments
);
178 crmLog
.log(label
+ ": end");
188 angular
.module('crmUtil').factory('crmNavigator', ['$window', function($window
) {
190 redirect: function(path
) {
191 $window
.location
.href
= path
;
196 angular
.module('crmUtil').factory('crmNow', function($q
){
197 // FIXME: surely there's already some helper which can do this in one line?
198 // @return string "YYYY-MM-DD hh:mm:ss"
199 return function crmNow() {
200 var currentdate
= new Date();
201 var yyyy
= currentdate
.getFullYear();
202 var mm
= currentdate
.getMonth() + 1;
203 mm
= mm
< 10 ? '0' + mm
: mm
;
204 var dd
= currentdate
.getDate();
205 dd
= dd
< 10 ? '0' + dd
: dd
;
206 var hh
= currentdate
.getHours();
207 hh
= hh
< 10 ? '0' + hh
: hh
;
208 var min
= currentdate
.getMinutes();
209 min
= min
< 10 ? '0' + min
: min
;
210 var sec
= currentdate
.getSeconds();
211 sec
= sec
< 10 ? '0' + sec
: sec
;
212 return yyyy
+ "-" + mm
+ "-" + dd
+ " " + hh
+ ":" + min
+ ":" + sec
;
216 // Adapter for CRM.status which supports Angular promises (instead of jQuery promises)
217 // example: crmStatus('Saving', crmApi(...)).then(function(result){...})
218 angular
.module('crmUtil').factory('crmStatus', function($q
){
219 return function(options
, aPromise
){
221 return CRM
.toAPromise($q
, CRM
.status(options
, CRM
.toJqPromise(aPromise
)));
223 return CRM
.toAPromise($q
, CRM
.status(options
));
228 // crmWatcher allows one to setup event listeners and temporarily suspend
232 // angular.controller(... function($scope, crmWatcher){
233 // var watcher = crmWatcher();
234 // function myfunc() {
235 // watcher.suspend('foo', function(){
239 // watcher.setup('foo', function(){
241 // $scope.$watch('foo', myfunc),
242 // $scope.$watch('bar', myfunc),
243 // $scope.$watch('whiz', otherfunc)
247 angular
.module('crmUtil').factory('crmWatcher', function(){
249 var unwatches
= {}, watchFactories
= {}, suspends
= {};
251 // Specify the list of watches
252 this.setup = function(name
, newWatchFactory
) {
253 watchFactories
[name
] = newWatchFactory
;
254 unwatches
[name
] = watchFactories
[name
]();
259 // Temporarily disable watches and run some logic
260 this.suspend = function(name
, f
) {
265 r
= f
.apply(this, []);
267 if (suspends
[name
] === 1) {
268 unwatches
[name
] = watchFactories
[name
]();
269 if (!angular
.isArray(unwatches
[name
])) {
270 unwatches
[name
] = [unwatches
[name
]];
278 this.teardown = function(name
) {
279 if (!unwatches
[name
]) return;
280 _
.each(unwatches
[name
], function(unwatch
){
283 delete unwatches
[name
];
290 })(angular
, CRM
.$, CRM
._
);