Rip modal_xulG_stack out, replace with openDialog
[working/Evergreen.git] / Open-ILS / xul / staff_client / server / serial / editor_base.js
1 dump('entering serial/editor_base.js\n');
2 // vim:et:sw=4:ts=4:
3
4 if (typeof serial == 'undefined') serial = {};
5
6 serial.editor_base = {
7
8     'editor_base_init' : function (params) {
9         var obj = this;
10         try {
11             /******************************************************************************************************/
12             /* setup JSAN and some initial libraries */
13
14             if (typeof JSAN == 'undefined') {
15                 throw( $('commonStrings').getString('common.jsan.missing') );
16             }
17             JSAN.errorLevel = "die"; // none, warn, or die
18             JSAN.addRepository('/xul/server/');
19             JSAN.use('util.error'); obj.error = new util.error();
20             obj.error.sdump('D_TRACE','my_init() for serial/editor_base.js');
21
22             JSAN.use('util.functional');
23             JSAN.use('OpenILS.data'); obj.data = new OpenILS.data(); obj.data.init({'via':'stash'});
24             JSAN.use('util.network'); obj.network = new util.network();
25
26
27             /******************************************************************************************************/
28             /* base vars */
29
30             obj.docid = xul_param('docid');
31             
32             if (typeof params.handle_update == 'undefined') {
33                 obj.handle_update = xul_param('handle_update');
34             } else {
35                 obj.handle_update = params.handle_update;
36             }
37
38             obj.trigger_refresh = params.trigger_refresh;
39             obj.refresh_command = params.refresh_command;
40             var fm_type = obj.fm_type;
41             var fm_type_plural = obj.fm_type_plural;
42             var retrieve_function = params.retrieve_function;
43             var retrieve_params = params.retrieve_params;
44             if (!retrieve_params) {
45                 retrieve_params = [];
46             }
47             if (params.xul_id_prefix) {
48                 obj.xul_id_prefix = params.xul_id_prefix;
49             } else {
50                 obj.xul_id_prefix = fm_type;
51             }
52
53             /******************************************************************************************************/
54             /* Get the fm_type ids from various sources and flesh them */
55
56             var fm_type_ids = params[fm_type + '_ids'];
57             if (!fm_type_ids) fm_type_ids = xul_param(fm_type + '_ids',{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xulG':true,'JSON2js_if_xpcom':true,'stash_name':'temp_' + fm_type + '_ids','clear_xpcom':true});
58             if (!fm_type_ids) fm_type_ids = [];
59
60             obj[fm_type_plural] = [];
61             retrieve_params.push(fm_type_ids);
62             if (fm_type_ids.length > 0) obj[fm_type_plural] = obj.network.simple_request(
63                 retrieve_function,
64                 retrieve_params
65             );
66
67
68             /******************************************************************************************************/
69             /* And other fleshed copies if any */
70
71             if (!obj[fm_type_plural]) obj[fm_type_plural] = [];
72             var fms = params[fm_type_plural];
73             if (!fms) fms = xul_param(fm_type_plural,{'concat':true,'JSON2js_if_cgi':true,'JSON2js_if_xpcom':true,'stash_name':'temp_' + fm_type_plural,'clear_xpcom':true})
74             if (fms) obj[fm_type_plural] = obj[fm_type_plural].concat(fms);
75
76
77             // If we have just one, wrap in array
78             if (!obj[fm_type_plural].length) {
79                 obj[fm_type_plural] = [obj[fm_type_plural]];
80             }
81
82
83             /******************************************************************************************************/
84
85             //obj.init_panes0();
86             obj.init_panes();
87
88             /******************************************************************************************************/
89             /* Is the interface an editor or a viewer, single or multi copy, existing copies or new copies? */
90
91             var do_edit;
92             if (typeof params.do_edit == 'undefined') {
93                 do_edit = xul_param('do_edit');
94             } else {
95                 do_edit = params.do_edit;
96             }
97
98             if (do_edit) { 
99
100                 // Editor desired, but let's check permissions
101                 obj.do_edit = false;
102
103                 try {
104                     /* FIXME: add permission check
105                     var check = obj.network.simple_request(
106                         'PERM_MULTI_ORG_CHECK',
107                         [ 
108                             ses(), 
109                             obj.data.list.au[0].id(), 
110                             util.functional.map_list(
111                                 obj[fm_type_plural],
112                                 function (o) {
113                                     var lib;
114                                     var cn_id = o.call_number();
115                                     if (cn_id == -1) {
116                                         lib = o.circ_lib(); // base perms on circ_lib instead of owning_lib if pre-cat
117                                     } else {
118                                         if (! obj.map_acn[ cn_id ]) {
119                                             var req = obj.network.simple_request('FM_ACN_RETRIEVE.authoritative',[ cn_id ]);
120                                             if (typeof req.ilsevent == 'undefined') {
121                                                 obj.map_acn[ cn_id ] = req;
122                                                 lib = obj.map_acn[ cn_id ].owning_lib();
123                                             } else {
124                                                 lib = o.circ_lib();
125                                             }
126                                         } else {
127                                             lib = obj.map_acn[ cn_id ].owning_lib();
128                                         }
129                                     }
130                                     return typeof lib == 'object' ? lib.id() : lib;
131                                 }
132                             ),
133                             obj[fm_type_plural].length == 1 ? [ 'UPDATE_COPY' ] : [ 'UPDATE_COPY', 'UPDATE_BATCH_COPY' ]
134                         ]
135                     ); */
136                     var check = [];
137                     obj.do_edit = check.length == 0;
138                 } catch(E) {
139                     obj.error.standard_unexpected_error_alert('batch permission check',E);
140                 }
141
142                 if (obj.do_edit) {
143                     $(obj.xul_id_prefix + '_save').setAttribute('hidden','false'); 
144                 } else {
145                     $('top_nav').setAttribute('hidden','true');
146                 }
147             } else {
148                 $('top_nav').setAttribute('hidden','true');
149             }
150
151
152             if (obj[fm_type_plural].length > 0 && obj[fm_type_plural][0].isnew()) {
153                 obj.mode = 'create';
154                 if (obj.can_have_notes) $(obj.xul_id_prefix + '_notes').setAttribute('hidden','true');
155                 $(obj.xul_id_prefix + '_save').setAttribute('label', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.create.label'));
156                 $(obj.xul_id_prefix + '_save').setAttribute('accesskey', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.create.accesskey'));
157             } else if (obj.mode == 'create') { // switching from create to modify
158                 obj.mode = 'modify';
159                 if (obj.can_have_notes) $(obj.xul_id_prefix + '_notes').setAttribute('hidden','false');
160                 $(obj.xul_id_prefix + '_save').setAttribute('label', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.modify.label'));
161                 $(obj.xul_id_prefix + '_save').setAttribute('accesskey', $('serialStrings').getString('staff.serial.' + fm_type + '_editor.modify.accesskey'));
162             }
163 /*else {
164                 obj.panes_and_field_names.left_pane = 
165                     [
166                         [
167                             $('catStrings').getString('staff.cat.copy_editor.status'),
168                             { 
169                                 render: 'typeof fm.status() == "object" ? fm.status().name() : obj.data.hash.ccs[ fm.status() ].name()', 
170                                 input: obj.safe_to_edit_copy_status() ? 'c = function(v){ obj.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( obj.data.list.ccs, function(obj) { return [ obj.name(), obj.id(), typeof my_constants.magical_statuses[obj.id()] != "undefined" ? true : false ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);' : undefined,
171                                 //input: 'c = function(v){ obj.apply("status",v); if (typeof post_c == "function") post_c(v); }; x = util.widgets.make_menulist( util.functional.map_list( util.functional.filter_list( obj.data.list.ccs, function(obj) { return typeof my_constants.magical_statuses[obj.id()] == "undefined"; } ), function(obj) { return [ obj.name(), obj.id() ]; } ).sort() ); x.addEventListener("apply",function(f){ return function(ev) { f(ev.target.value); } }(c), false);',
172                             }
173                         ]
174                     ].concat(obj.panes_and_field_names.left_pane);
175             }*/
176
177             if (obj[fm_type_plural].length != 1) {
178                 if (obj.can_have_notes) $(obj.xul_id_prefix + '_notes').setAttribute('hidden','true');
179             }
180
181             // clear change markers
182             obj.changed = {};
183
184             /******************************************************************************************************/
185             /* Show the Record Details? (only for 'in_modal' mode)*/
186
187             var bdb;
188             if (xul_param('in_modal') && obj.docid) {
189                 bdb = document.getElementById('brief_display_box'); while(bdb.firstChild) bdb.removeChild(bdb.lastChild);
190                 var brief_display = document.createElement('iframe'); bdb.appendChild(brief_display); 
191                 brief_display.setAttribute( 'src', urls.XUL_BIB_BRIEF + '?docid=' + obj.docid); // this is a modal window, so can't push in xulG
192                 brief_display.setAttribute( 'flex','1' );
193             }
194
195             /******************************************************************************************************/
196             /* Backup copies :) */
197
198             obj['original_' + fm_type_plural] = js2JSON( obj[fm_type_plural] );
199
200         } catch(E) {
201             var err_msg = $("commonStrings").getFormattedString('common.exception', ['serial/' + fm_type +'_editor.js - init', E]);
202             try { obj.error.sdump('D_ERROR',err_msg); } catch(E) { dump(err_msg); dump(js2JSON(E)); }
203             alert(err_msg);
204         }
205     },
206
207     /******************************************************************************************************/
208     /* Restore backup copies */
209
210     'editor_base_reset' : function() {
211         var obj = this;
212         var fm_type_plural = obj.fm_type_plural;
213
214         obj.changed = {};
215         obj[fm_type_plural] = JSON2js( obj['original_' + fm_type_plural] );
216         obj.summarize( obj[fm_type_plural] );
217         obj.render();
218     },
219
220     /******************************************************************************************************/
221     /* Apply a value to a specific field on all the copies being edited */
222     /* Don't forget to use util.money.sanitize if dealing with money values */
223
224     'editor_base_apply' : function(field, value, loop_func) {
225         var obj = this;
226         var fm_type_plural = obj.fm_type_plural;
227
228         var do_loop_func = (typeof loop_func == 'function');
229
230         obj.error.sdump('D_TRACE','applying field = <' + field + '>  value = <' + value + '>\n');
231         if (value == '<HACK:KLUDGE:NULL>') value = null;
232         for (var i = 0; i < obj[fm_type_plural].length; i++) {
233             var fm = obj[fm_type_plural][i];
234             try {
235                 fm[field]( value ); fm.ischanged('1');
236                 if (do_loop_func) {
237                     loop_func(fm);
238                 }
239             } catch(E) {
240                 alert(E);
241             }
242         }
243     },
244
245
246     /******************************************************************************************************/
247     /* This loops through all our fieldnames and all the copies, tallying up counts for the different values */
248
249     'editor_base_summarize' : function(my_fms) {
250         var obj = this;
251         var fm_type = obj.fm_type;
252         /******************************************************************************************************/
253         /* Setup */
254
255         JSAN.use('util.date'); JSAN.use('util.money');
256         obj.summary = {};
257         obj.field_names = [];
258         for (var i in obj.panes_and_field_names) {
259             obj.field_names = obj.field_names.concat( obj.panes_and_field_names[i] );
260         }
261
262         /******************************************************************************************************/
263         /* Loop through the field names */
264
265         obj.missing_required = [];
266
267         for (var i = 0; i < obj.field_names.length; i++) {
268
269             var field_name = obj.field_names[i][0];
270             var render = obj.field_names[i][1].render;
271             var attr = obj.field_names[i][1].attr;
272             var value_key = obj.field_names[i][1].value_key;
273             var dropdown_key = obj.field_names[i][1].dropdown_key;
274             var required = obj.field_names[i][1].required;
275             obj.summary[ field_name ] = {};
276
277             /******************************************************************************************************/
278             /* Loop through the copies */
279
280             for (var j = 0; j < my_fms.length; j++) {
281
282                 var fm = my_fms[j];
283                 var cmd = render || ('fm.' + field_name + '();');
284                 var value = '???';
285
286                 /**********************************************************************************************/
287                 /* Try to retrieve the value for this field for this copy */
288
289                 try { 
290                     value = eval( cmd );
291                     if (value == null) { // true for both 'null' and undefined
292                         value = "";
293                     }
294                     if (dropdown_key) {
295                         obj.editor_values[value_key] = eval(dropdown_key);
296                     } else if (value_key) {
297                         obj.editor_values[value_key] = value;
298                     }
299                     if (required && value == "") {
300                         obj.missing_required.push(fieldmapper.IDL.fmclasses[fm_type].field_map[field_name].label); //TODO: consider applying a style
301                     }
302
303                     if (value == "") {
304                         value = $('serialStrings').getString('serial.editor_base.unset');
305                     }
306
307                 } catch(E) { 
308                     obj.error.sdump('D_ERROR','Attempted ' + cmd + '\n' +  E + '\n'); 
309                 }
310                 if (typeof value == 'object' && value != null) {
311                     alert('FIXME: field_name = <' + field_name + '>  value = <' + js2JSON(value) + '>\n');
312                 }
313
314                 /**********************************************************************************************/
315                 /* Tally the count */
316
317                 if (obj.summary[ field_name ][ value ]) {
318                     obj.summary[ field_name ][ value ]++;
319                 } else {
320                     obj.summary[ field_name ][ value ] = 1;
321                 }
322             }
323         }
324
325         obj.error.sdump('D_TRACE','summary = ' + js2JSON(obj.summary) + '\n');
326     },
327
328     /******************************************************************************************************/
329     /* Display the summarized data and inputs for editing */
330
331     'editor_base_render' : function() {
332         var obj = this;
333         var fm_type = obj.fm_type;
334
335         /******************************************************************************************************/
336         /* Library setup and clear any existing interface */
337
338         JSAN.use('util.widgets'); JSAN.use('util.date'); JSAN.use('util.money'); JSAN.use('util.functional');
339
340         for (var i in obj.panes_and_field_names) {
341             var p = document.getElementById(i);
342             if (p) util.widgets.remove_children(p);
343         }
344
345         /******************************************************************************************************/
346         /* Prepare the panes */
347
348         var groupbox; var caption; var vbox; var grid; var rows;
349         
350         /******************************************************************************************************/
351         /* Loop through the field names */
352
353         for (h in obj.panes_and_field_names) {
354             if (!document.getElementById(h)) continue;
355             for (var i = 0; i < obj.panes_and_field_names[h].length; i++) {
356                 try {
357                     var f = obj.panes_and_field_names[h][i]; var fn = f[0]; var attr = f[1].attr;
358                     groupbox = document.createElement('groupbox'); document.getElementById(h).appendChild(groupbox);
359                     if (attr) {
360                         for (var a in attr) {
361                             groupbox.setAttribute(a,attr[a]);
362                         }
363                     }
364                     if (typeof obj.changed[fn] != 'undefined') {
365                         groupbox.setAttribute('class','copy_editor_field_changed');
366                     }
367                     caption = document.createElement('caption'); groupbox.appendChild(caption);
368                     if (f[1].label) {
369                         caption.setAttribute('label',f[1].label);
370                     } else {
371                         caption.setAttribute('label',fieldmapper.IDL.fmclasses[fm_type].field_map[fn].label);
372                     }
373                     caption.setAttribute('id','caption_'+fn);
374                     vbox = document.createElement('vbox'); groupbox.appendChild(vbox);
375                     grid = util.widgets.make_grid( [ { 'flex' : 1 }, {}, {} ] ); vbox.appendChild(grid);
376                     grid.setAttribute('flex','1');
377                     rows = grid.lastChild;
378                     var row;
379                     
380                     /**************************************************************************************/
381                     /* Loop through each value for the field */
382
383                     for (var j in obj.summary[fn]) {
384                         var value = j; var count = obj.summary[fn][j];
385                         row = document.createElement('row'); rows.appendChild(row);
386                         var label1 = document.createElement('description'); row.appendChild(label1);
387                         label1.setAttribute('id',fn + '_label');
388                         //if (obj.special_exception[ fn ]) {
389                         //      obj.special_exception[ fn ]( label1, value );
390                         //} else {
391                             label1.appendChild( document.createTextNode(value) );
392                         //}
393                         var label2 = document.createElement('description'); row.appendChild(label2);
394                         var fm_count;
395                         if (count == 1) {
396                             fm_count = $('serialStrings').getString('staff.serial.' + fm_type +'_editor.count');
397                         } else {
398                             fm_count = $('serialStrings').getFormattedString('staff.serial.' + fm_type +'_editor.count.plural', [count]);
399                         }
400                         label2.appendChild( document.createTextNode(fm_count) );
401                     }
402                     var hbox = document.createElement('hbox'); 
403                     hbox.setAttribute('id',fn);
404                     groupbox.appendChild(hbox);
405                     var hbox2 = document.createElement('hbox');
406                     groupbox.appendChild(hbox2);
407
408                     /**************************************************************************************/
409                     /* Render the input widget */
410
411                     if (f[1].input && obj.do_edit) {
412                         obj.render_input(hbox,f[1]);
413                     }
414
415                 } catch(E) {
416                     obj.error.sdump('D_ERROR','copy editor: ' + E + '\n');
417                 }
418             }
419         }
420         
421         
422         /******************************************************************************************************/
423         /* Synchronize stat cat visibility with library filter menu, and default template selection */
424         JSAN.use('util.file'); 
425         var file = new util.file(fm_type + '_editor_prefs.'+obj.data.server_unadorned);
426         obj[fm_type + '_editor_prefs'] = util.widgets.load_attributes(file);
427         for (var i in obj[fm_type + '_editor_prefs']) {
428             if (i.match(/filter_/) && obj[fm_type + '_editor_prefs'][i].checked == '') {
429                 try { 
430                     obj.toggle_stat_cat_display( document.getElementById(i) ); 
431                 } catch(E) { alert(E); }
432             }
433         }
434         if (obj.template_menu) obj.template_menu.value = obj.template_menu.getAttribute('value');
435
436     },
437
438     /******************************************************************************************************/
439     /* This actually draws the change button and input widget for a given field */
440     'editor_base_render_input' : function(node, blob) {
441         var obj = this;
442         var fm_type_plural = obj.fm_type_plural;
443
444         try {
445             // node = hbox ;    groupbox ->  hbox, hbox
446
447             var groupbox = node.parentNode;
448             var caption = groupbox.firstChild;
449             var vbox = node.previousSibling;
450             var hbox = node;
451             var hbox2 = node.nextSibling;
452
453             var input_cmd = blob.input;
454             var render_cmd = blob.render;
455             var attr = blob.attr;
456
457             var block = false; var first = true;
458
459             function on_mouseover(ev) {
460                 groupbox.setAttribute('style','background: white');
461             }
462
463             function on_mouseout(ev) {
464                 groupbox.setAttribute('style','');
465             }
466
467             vbox.addEventListener('mouseover',on_mouseover,false);
468             vbox.addEventListener('mouseout',on_mouseout,false);
469             groupbox.addEventListener('mouseover',on_mouseover,false);
470             groupbox.addEventListener('mouseout',on_mouseout,false);
471             groupbox.firstChild.addEventListener('mouseover',on_mouseover,false);
472             groupbox.firstChild.addEventListener('mouseout',on_mouseout,false);
473
474             function on_click(ev){
475                 try {
476                     if (block) return; block = true;
477
478                     function post_c(v, unchanged) {
479                         try {
480                             /* dbw2 not needed?
481                             var t = input_cmd.match('apply_stat_cat') ? 'stat_cat' : ( input_cmd.match('apply_owning_lib') ? 'owning_lib' : 'attribute' );
482                             var f;
483                             switch(t) {
484                                 case 'attribute' :
485                                     f = input_cmd.match(/apply.?\("(.+?)",/)[1];
486                                 break;
487                                 case 'stat_cat' :
488                                     f = input_cmd.match(/apply_stat_cat\((.+?),/)[1];
489                                 break;
490                                 case 'owning_lib' :
491                                     f = null;
492                                 break;
493                             }
494                             obj.changed[ hbox.id ] = { 'type' : t, 'field' : f, 'value' : v }; */
495                             if (!unchanged) {
496                                 obj.changed[ hbox.id ] = true;
497                             }
498                             block = false;
499                             setTimeout(
500                                 function() {
501                                     obj.summarize( obj[fm_type_plural] );
502                                     obj.render();
503                                     document.getElementById(caption.id).focus();
504                                 }, 0
505                             );
506                         } catch(E) {
507                             obj.error.standard_unexpected_error_alert('post_c',E);
508                         }
509                     }
510                     var x; var c; eval( input_cmd );
511                     if (x) {
512                         util.widgets.remove_children(vbox);
513                         util.widgets.remove_children(hbox);
514                         util.widgets.remove_children(hbox2);
515                         hbox.appendChild(x);
516                         var apply = document.createElement('button');
517                         apply.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.apply.label'));
518                         apply.setAttribute('accesskey', $('catStrings').getString('staff.cat.copy_editor.apply.accesskey'));
519                         hbox2.appendChild(apply);
520                         apply.addEventListener('command',function() { c(x.value); },false);
521                         var cancel = document.createElement('button');
522                         cancel.setAttribute('label', $('catStrings').getString('staff.cat.copy_editor.cancel.label'));
523                         cancel.addEventListener('command',function() { setTimeout( function() { obj.summarize( obj[fm_type_plural] ); obj.render(); document.getElementById(caption.id).focus(); }, 0); }, false);
524                         hbox2.appendChild(cancel);
525                         setTimeout( function() { x.focus(); }, 0 );
526                     }
527                 } catch(E) {
528                     obj.error.standard_unexpected_error_alert('render_input',E);
529                 }
530             }
531             vbox.addEventListener('click',on_click, false);
532             hbox.addEventListener('click',on_click, false);
533             caption.addEventListener('click',on_click, false);
534             caption.addEventListener('keypress',function(ev) {
535                 if (ev.keyCode == 13 /* enter */ || ev.keyCode == 77 /* mac enter */) on_click();
536             }, false);
537             caption.setAttribute('style','-moz-user-focus: normal');
538             caption.setAttribute('onfocus','this.setAttribute("class","outline_me")');
539             caption.setAttribute('onblur','this.setAttribute("class","")');
540
541         } catch(E) {
542             obj.error.sdump('D_ERROR',E + '\n');
543         }
544     },
545
546     /******************************************************************************************************/
547     /* save or store the updated fms as appropriate */
548
549     'editor_base_save' : function(update_method) {
550         var obj = this;
551         var fm_type_plural = obj.fm_type_plural;
552         var fm_type = obj.fm_type;
553
554         try {
555             if (obj.handle_update) {
556                 try {
557                     if (obj.missing_required.length > 0) {
558                         alert($('serialStrings').getString('staff.serial.required_fields_alert') + obj.missing_required.join(', '));
559                         return; //stop submission
560                     }
561
562                     //send fms to the update function
563                     var r = obj.network.request(
564                         'open-ils.serial',
565                         update_method,
566                         [ ses(), obj[fm_type_plural] ]
567                     );
568                     if (typeof r.ilsevent != 'undefined') {
569                         obj.error.standard_unexpected_error_alert('serial ' + fm_type + ' update',r);
570                     } else {
571                         alert($('serialStrings').getString('staff.serial.editor_base.handle_update.success'));
572                         obj.changed = {};
573                         if (obj.trigger_refresh) {
574                             obj.refresh_command();
575                         } else {
576                             obj.render();
577                         }
578                     }
579                     /* FIXME -- revisit the return value here */
580                 } catch(E) {
581                     alert($('serialStrings').getString('staff.serial.editor_base.handle_update.error') + ' ' + js2JSON(E));
582                 }
583             } else if (xul_param('in_modal')) {
584                 // TODO: this is to perhaps allow this editor to be called
585                 // in a modal window, but is unfinished functionality
586                 var xulG = {};
587                 xulG[fm_type_plural] = obj[fm_type_plural];
588             } else {
589                 obj.data['temp_' + fm_type_plural] = js2JSON( obj[fm_type_plural] );
590                 obj.data.stash('temp_' + fm_type_plural);
591             }
592
593             if (xul_param('in_modal')) {
594                 window.close();
595             }
596         } catch(E) {
597             obj.error.standard_unexpected_error_alert(fm_type + '_editor save',E);
598         }
599     },
600
601     /******************************************************************************************************/
602     'editor_base_save_attributes' : function() {
603         var obj = this;
604         var fm_type = obj.fm_type;
605
606         JSAN.use('util.widgets'); JSAN.use('util.file'); var file = new util.file(fm_type + '_editor_prefs.'+obj.data.server_unadorned);
607         var what_to_save = {};
608         for (var i in obj[fm_type + '_editor_prefs']) {
609             what_to_save[i] = [];
610             for (var j in obj[fm_type + '_editor_prefs'][i]) what_to_save[i].push(j);
611         }
612         util.widgets.save_attributes(file, what_to_save );
613     }
614 };
615
616 dump('exiting serial/editor_base.js\n');