]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/services/idl.js
LP#1452950 pat. reg surveys and stat cats
[Evergreen.git] / Open-ILS / web / js / ui / default / staff / services / idl.js
1 /**
2  * Core Service - egIDL
3  *
4  * IDL parser
5  * usage:
6  *  var aou = new egIDL.aou();
7  *  var fullIDL = egIDL.classes;
8  *
9  *  IDL TODO:
10  *
11  * 1. selector field only appears once per class.  We could save
12  *    a lot of IDL (network) space storing it only once at the 
13  *    class level.
14  * 2. we don't need to store array_position in /IDL2js since it
15  *    can be derived at parse time.  Ditto saving space.
16  */
17 angular.module('egCoreMod')
18
19 .factory('egIDL', ['$window', function($window) {
20
21     var service = {};
22
23     // Clones data structures containing fieldmapper objects
24     service.Clone = function(old) {
25         var obj;
26         if (typeof old == 'undefined') {
27             return old;
28         } else if (old._isfieldmapper) {
29             obj = new service[old.classname]()
30
31             for( var i in old.a ) {
32                 var thing = old.a[i];
33                 if(thing === null) continue;
34
35                 if (typeof thing == 'undefined') {
36                     obj.a[i] = thing;
37                 } else if (thing._isfieldmapper) {
38                     obj.a[i] = service.Clone(thing);
39                 } else {
40
41                     if(angular.isArray(thing)) {
42                         obj.a[i] = [];
43
44                         for( var j in thing ) {
45
46                             if (typeof thing[j] == 'undefined')
47                                 obj.a[i][j] = thing[j];
48                             else if( thing[j]._isfieldmapper )
49                                 obj.a[i][j] = service.Clone(thing[j]);
50                             else
51                                 obj.a[i][j] = angular.copy(thing[j]);
52                         }
53                     } else {
54                         obj.a[i] = angular.copy(thing);
55                     }
56                 }
57             }
58         } else {
59             if(angular.isArray(old)) {
60                 obj = [];
61                 for( var j in old ) {
62                     if (typeof old[j] == 'undefined')
63                         obj[j] = old[j];
64                     else if( old[j]._isfieldmapper )
65                         obj[j] = service.Clone(old[j]);
66                     else
67                         obj[j] = angular.copy(old[j]);
68                 }
69             } else if(angular.isObject(old)) {
70                 obj = {};
71                 for( var j in old ) {
72                     if (typeof old[j] == 'undefined')
73                         obj[j] = old[j];
74                     else if( old[j]._isfieldmapper )
75                         obj[j] = service.Clone(old[j]);
76                     else
77                         obj[j] = angular.copy(old[j]);
78                 }
79             } else {
80                 obj = angular.copy(old);
81             }
82         }
83         return obj;
84     };
85
86     service.parseIDL = function() {
87         //console.debug('egIDL.parseIDL()');
88
89         // retain a copy of the full IDL within the service
90         service.classes = $window._preload_fieldmapper_IDL;
91
92         // keep this reference around (note: not a clone, just a ref)
93         // so that unit tests, which repeatedly instantiate the
94         // service will work.
95         //$window._preload_fieldmapper_IDL = null;
96
97         /**
98          * Creates the class constructor and getter/setter
99          * methods for each IDL class.
100          */
101         function mkclass(cls, fields) {
102
103             service[cls] = function(seed) {
104                 this.a = seed || [];
105                 this.classname = cls;
106                 this._isfieldmapper = true;
107             }
108
109             /** creates the getter/setter methods for each field */
110             angular.forEach(fields, function(field, idx) {
111                 service[cls].prototype[fields[idx].name] = function(n) {
112                     if (arguments.length==1) this.a[idx] = n;
113                     return this.a[idx];
114                 }
115             });
116
117             // global class constructors required for JSON_v1.js
118             $window[cls] = service[cls]; 
119         }
120
121         for (var cls in service.classes) 
122             mkclass(cls, service.classes[cls].fields);
123     };
124
125     /**
126      * Generate a hash version of an IDL object.
127      *
128      * Flatten determines if nested objects should be squashed into
129      * the top-level hash.
130      *
131      * If 'flatten' is false, e.g.:
132      *
133      * {"call_number" : {"label" :  "foo"}}
134      *
135      * If 'flatten' is true, e.g.:
136      *
137      * e.g.  {"call_number.label" : "foo"}
138      */
139     service.toHash = function(obj, flatten) {
140         if (!angular.isObject(obj)) return obj; // arrays are objects
141
142         if (angular.isArray(obj)) { // NOTE: flatten arrays not supported
143             return obj.map(function(item) {return service.toHash(item)});
144         }
145
146         var field_names = obj.classname ? 
147             Object.keys(service.classes[obj.classname].field_map) :
148             Object.keys(obj);
149
150         var hash = {};
151         angular.forEach(
152             field_names,
153             function(field) { 
154
155                 var val = service.toHash(
156                     angular.isFunction(obj[field]) ? 
157                         obj[field]() : obj[field], 
158                     flatten
159                 );
160
161                 if (flatten && angular.isObject(val)) {
162                     angular.forEach(val, function(sub_val, key) {
163                         var fname = field + '.' + key;
164                         hash[fname] = sub_val;
165                     });
166
167                 } else if (val !== undefined) {
168                     hash[field] = val;
169                 }
170             }
171         );
172
173         return hash;
174     }
175
176     // returns a simple string key=value string of an IDL object.
177     service.toString = function(obj) {
178         var s = '';
179         angular.forEach(
180             service.classes[obj.classname].fields.sort(
181                 function(a,b) {return a.name < b.name ? -1 : 1}),
182             function(field) {
183                 s += field.name + '=' + obj[field.name]() + '\n';
184             }
185         );
186         return s;
187     }
188
189     // hash-to-IDL object translater.  Does not support nested values.
190     service.fromHash = function(cls, hash) {
191         if (!service.classes[cls]) {
192             console.error('No such IDL class ' + cls);
193             return null;
194         }
195
196         var new_obj = new service[cls]();
197         angular.forEach(hash, function(val, key) {
198             if (!angular.isFunction(new_obj[key])) return;
199             new_obj[key](hash[key]);
200         });
201
202         return new_obj;
203     }
204
205     // Transforms a flattened hash (see toHash() or egGridFlatDataProvider)
206     // to a nested hash.
207     //
208     // e.g. {"call_number.label" : "foo"} => {"call_number":{"label":"foo"}}
209     service.flatToNestedHash = function(obj) {
210         var hash = {};
211         angular.forEach(obj, function(val, key) {
212             var parts = key.split('.');
213             var sub_hash = hash;
214             var last_key;
215             for (var i = 0; i < parts.length; i++) {
216                 var part = parts[i];
217                 if (i == parts.length - 1) {
218                     sub_hash[part] = val;
219                     break;
220                 } else {
221                     if (!sub_hash[part])
222                         sub_hash[part] = {};
223                     sub_hash = sub_hash[part];
224                 }
225             }
226         });
227
228         return hash;
229     }
230
231     return service;
232 }]);
233