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