]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/web/js/ui/default/staff/services/idl.js
webstaff: Provide a fieldmapper-aware cloning method
[working/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 (old._isfieldmapper) {
27             obj = new service[old.classname]()
28
29             for( var i in old.a ) {
30                 var thing = old.a[i];
31                 if(thing === null) continue;
32
33                 if( thing._isfieldmapper ) {
34                     obj.a[i] = service.Clone(thing);
35                 } else {
36
37                     if(angular.isArray(thing)) {
38                         obj.a[i] = [];
39
40                         for( var j in thing ) {
41
42                             if( thing[j]._isfieldmapper )
43                                 obj.a[i][j] = service.Clone(thing[j]);
44                             else
45                                 obj.a[i][j] = angular.copy(thing[j]);
46                         }
47                     } else {
48                         obj.a[i] = angular.copy(thing);
49                     }
50                 }
51             }
52         } else {
53             if(angular.isArray(old)) {
54                 obj = [];
55                 for( var j in old ) {
56                     if( old[j]._isfieldmapper )
57                         obj[j] = service.Clone(old[j]);
58                     else
59                         obj[j] = angular.copy(old[j]);
60                 }
61             } else if(angular.isObject(old)) {
62                 obj = {};
63                 for( var j in old ) {
64                     if( old[j]._isfieldmapper )
65                         obj[j] = service.Clone(old[j]);
66                     else
67                         obj[j] = angular.copy(old[j]);
68                 }
69             } else {
70                 obj = angular.copy(old);
71             }
72         }
73         return obj;
74     };
75
76     service.parseIDL = function() {
77         //console.debug('egIDL.parseIDL()');
78
79         // retain a copy of the full IDL within the service
80         service.classes = $window._preload_fieldmapper_IDL;
81
82         // keep this reference around (note: not a clone, just a ref)
83         // so that unit tests, which repeatedly instantiate the
84         // service will work.
85         //$window._preload_fieldmapper_IDL = null;
86
87         /**
88          * Creates the class constructor and getter/setter
89          * methods for each IDL class.
90          */
91         function mkclass(cls, fields) {
92
93             service[cls] = function(seed) {
94                 this.a = seed || [];
95                 this.classname = cls;
96                 this._isfieldmapper = true;
97             }
98
99             /** creates the getter/setter methods for each field */
100             angular.forEach(fields, function(field, idx) {
101                 service[cls].prototype[fields[idx].name] = function(n) {
102                     if (arguments.length==1) this.a[idx] = n;
103                     return this.a[idx];
104                 }
105             });
106
107             // global class constructors required for JSON_v1.js
108             $window[cls] = service[cls]; 
109         }
110
111         for (var cls in service.classes) 
112             mkclass(cls, service.classes[cls].fields);
113     };
114
115     /**
116      * Generate a hash version of an IDL object.
117      *
118      * Flatten determines if nested objects should be squashed into
119      * the top-level hash.
120      *
121      * If 'flatten' is false, e.g.:
122      *
123      * {"call_number" : {"label" :  "foo"}}
124      *
125      * If 'flatten' is true, e.g.:
126      *
127      * e.g.  {"call_number.label" : "foo"}
128      */
129     service.toHash = function(obj, flatten) {
130         if (!angular.isObject(obj)) return obj; // arrays are objects
131
132         if (angular.isArray(obj)) { // NOTE: flatten arrays not supported
133             return obj.map(function(item) {return service.toHash(item)});
134         }
135
136         var field_names = obj.classname ? 
137             Object.keys(service.classes[obj.classname].field_map) :
138             Object.keys(obj);
139
140         var hash = {};
141         angular.forEach(
142             field_names,
143             function(field) { 
144
145                 var val = service.toHash(
146                     angular.isFunction(obj[field]) ? 
147                         obj[field]() : obj[field], 
148                     flatten
149                 );
150
151                 if (flatten && angular.isObject(val)) {
152                     angular.forEach(val, function(sub_val, key) {
153                         var fname = field + '.' + key;
154                         hash[fname] = sub_val;
155                     });
156
157                 } else if (val !== undefined) {
158                     hash[field] = val;
159                 }
160             }
161         );
162
163         return hash;
164     }
165
166     // Transforms a flattened hash (see toHash() or egGridFlatDataProvider)
167     // to a nested hash.
168     //
169     // e.g. {"call_number.label" : "foo"} => {"call_number":{"label":"foo"}}
170     service.flatToNestedHash = function(obj) {
171         var hash = {};
172         angular.forEach(obj, function(val, key) {
173             var parts = key.split('.');
174             var sub_hash = hash;
175             var last_key;
176             for (var i = 0; i < parts.length; i++) {
177                 var part = parts[i];
178                 if (i == parts.length - 1) {
179                     sub_hash[part] = val;
180                     break;
181                 } else {
182                     if (!sub_hash[part])
183                         sub_hash[part] = {};
184                     sub_hash = sub_hash[part];
185                 }
186             }
187         });
188
189         return hash;
190     }
191
192     return service;
193 }]);
194