]> git.evergreen-ils.org Git - working/Evergreen.git/blob - Open-ILS/xul/staff_client/chrome/content/util/error.js
5ced634a757a80be5e60253898369515d06f1b3c
[working/Evergreen.git] / Open-ILS / xul / staff_client / chrome / content / util / error.js
1 dump('entering util/error.js\n');
2
3 if (typeof util == 'undefined') util = {};
4 util.error = function () {
5
6     try {
7
8         try {
9             netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
10             this.consoleService = Components.classes['@mozilla.org/consoleservice;1']
11                 .getService(Components.interfaces.nsIConsoleService);
12         } catch(E) {
13             this.consoleDump = false;
14             dump('util.error constructor: ' + E + '\n');
15         }
16
17         this.sdump_last_time = new Date();
18
19         this.OpenILS = {};
20
21         // Only use sounds if the context window has already created a sound object
22         if (typeof xulG != 'undefined' && xulG._sound) {
23             this.sound = xulG._sound;
24         }
25
26     } catch(E) {
27         alert('Error in util.error constructor: ' + E);
28     }
29
30     return this;
31 };
32
33 util.error.prototype = {
34
35     'allowPrintDebug' : true,
36     'allowConsoleDump' : true,
37     'allowDebugDump' : true,
38     'allowFileDump' : true,
39     'allowAlertDump' : true,
40
41     'forcePrintDebug' : false,
42     'forceConsoleDump' : false,
43     'forceDebugDump' : false,
44     'forceFileDump' : false,
45     'forceAlertDump' : false,
46
47     'arg_dump_full' : false,
48
49     'debug' : function(e){
50         dump('-----------------------------------------\n' 
51             + e + '\n-----------------------------------------\n' );
52     },
53
54     'obj_dump' : function(s,dobj) {
55         var o = 'typeof ' + dobj + ' = ' + typeof dobj + '\n';
56         for (var i in dobj) {
57             o += i + '\t' + typeof dobj[i] + '\n';
58         }
59         this.sdump(s,o);
60     },
61
62     'sdump_levels' : {
63
64         'D_NONE' : false, 
65         'D_ALL' : false,
66
67         'D_ERROR' : { 'debug' : true, 'console' : true }, 
68         'D_DEBUG' : { 'debug' : true, 'console' : true }, 
69         'D_TRACE' :  { 'debug' : false }, 
70         'D_ALERT' : { 'alert' : true, 'debug' : true },
71         'D_WARN' : { 'debug' : true }, 
72         'D_COLUMN_RENDER_ERROR' : { 'debug' : false }, 
73         'D_XULRUNNER' : { 'debug' : false }, 
74         'D_DECK' : { 'debug' : false },
75         'D_TRACE_ENTER' :  false, 
76         'D_TRACE_EXIT' :  false, 
77         'D_TIMEOUT' :  false, 
78         'D_FILTER' : { 'debug' : false },
79         'D_CONSTRUCTOR' : { 'debug' : false }, 
80         'D_FIREFOX' : { 'debug' : false }, 
81         'D_LEGACY' : { 'debug' : false }, 
82         'D_DATA_STASH' : { 'alert' : false }, 
83         'D_DATA_RETRIEVE' : { 'debug' : false },
84
85         'D_CLAM' : { 'debug' : false }, 
86         'D_PAGED_TREE' : { 'debug' : false }, 
87         'D_GRID_LIST' : { 'debug' : false }, 
88         'D_HTML_TABLE' : { 'debug' : false },
89         'D_TAB' : { 'debug' : false }, 
90         'D_LIST' : { 'debug' : false }, 
91         'D_LIST_DUMP_WITH_KEYS_ON_CLEAR' : { 'debug' : false }, 
92         'D_LIST_DUMP_ON_CLEAR' : { 'debug' : false },
93
94         'D_AUTH' : { 'debug' : false }, 
95         'D_OPAC' : { 'debug' : false }, 
96         'D_CAT' : { 'debug' : false }, 
97         'D_BROWSER' : { 'debug' : false },
98
99         'D_PATRON_SEARCH' : { 'debug' : false }, 
100         'D_PATRON_SEARCH_FORM' : { 'debug' : false }, 
101         'D_PATRON_SEARCH_RESULTS' : { 'debug' : false },
102
103         'D_PATRON_DISPLAY' : { 'debug' : false }, 
104         'D_PATRON_DISPLAY_STATUS' : { 'debug' : false }, 
105         'D_PATRON_DISPLAY_CONTACT' : { 'debug' : false },
106
107         'D_PATRON_ITEMS' : { 'debug' : false }, 
108         'D_PATRON_CHECKOUT_ITEMS' : { 'debug' : false }, 
109         'D_PATRON_HOLDS' : { 'debug' : false },
110         'D_PATRON_BILLS' : { 'debug' : false }, 
111         'D_PATRON_EDIT' : { 'debug' : false },
112
113         'D_CHECKIN' : { 'debug' : false }, 
114         'D_CHECKIN_ITEMS' : { 'debug' : false },
115
116         'D_HOLD_CAPTURE' : { 'debug' : false }, 
117         'D_HOLD_CAPTURE_ITEMS' : { 'debug' : false },
118
119         'D_PATRON_UTILS' : { 'debug' : false }, 
120         'D_CIRC_UTILS' : { 'debug' : false },
121
122         'D_FILE' : { 'debug' : false }, 
123         'D_EXPLODE' : { 'debug' : false }, 
124         'D_FM_UTILS' : { 'debug' : false }, 
125         'D_PRINT' : { 'debug' : false }, 
126         'D_OBSERVERS' : { 'debug' : false, 'console' : false, 'alert' : false },
127         'D_CACHE' : { 'debug' : false, 'console' : false, 'alert' : false },
128         'D_SES' : { 'debug' : false, 'console' : true },
129         'D_SES_FUNC' : { 'debug' : false }, 
130         'D_SES_RESULT' : { 'debug' : false }, 
131         'D_SES_ERROR' : { 'debug' : true, 'console' : true }, 
132         'D_SPAWN' : { 'debug' : false }, 
133         'D_STRING' : { 'debug' : false },
134         'D_UTIL' : { 'debug' : false }, 
135         'D_WIN' : { 'debug' : false }, 
136         'D_WIDGETS' : { 'debug' : false }
137     },
138
139     'filter_console_init' : function (p) {
140         this.sdump('D_FILTER',this.arg_dump(arguments,{0:true}));
141
142         var filterConsoleListener = {
143             observe: function( msg ) {
144                 try {
145                     p.observe_msg( msg );
146                 } catch(E) {
147                     alert(E);
148                 }
149             },
150             QueryInterface: function (iid) {
151                 if (!iid.equals(Components.interfaces.nsIConsoleListener) &&
152                     !iid.equals(Components.interfaces.nsISupports)) {
153                         throw Components.results.NS_ERROR_NO_INTERFACE;
154                 }
155                     return this;
156             }
157         };
158         try {
159             this.consoleService.registerListener(filterConsoleListener);    
160         } catch(E) {
161             alert(E);
162         }
163
164         this.sdump('D_TRACE_EXIT',this.arg_dump(arguments));
165     },
166
167     'sdump' : function (level,msg) {
168         try {
169             var now = new Date();
170             var message = now.valueOf() + '\tdelta = ' + (now.valueOf() - this.sdump_last_time.valueOf()) + '\t' + level + '\n' + msg;
171             if (this.sdump_levels['D_NONE']) return null;
172             if (this.sdump_levels[level]||this.sdump_levels['D_ALL']) {
173                 this.sdump_last_time = now;
174                 if (this.forceDebugDump || ( this.allowDebugDump && this.sdump_levels[level] && this.sdump_levels[level].debug ) ) this.debug(message);
175                 if (this.forceAlertDump || ( this.allowAlertDump && this.sdump_levels[level] && this.sdump_levels[level].alert ) ) alert(message);
176                 if (this.forceConsoleDump || ( this.allowConsoleDump && this.sdump_levels[level] && this.sdump_levels[level].console ) ) {
177                     netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
178                     if (level=='D_ERROR') {
179                         Components.utils.reportError(message);
180                     } else {
181                         this.consoleService.logStringMessage(message);
182                     }
183                 }
184                 if (this.forceFileDump || ( this.allowFileDump && this.sdump_levels[level] && this.sdump_levels[level].file ) ) {
185                     if (level!='D_FILE') {
186                         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
187                         JSAN.use('util.file'); var master_log = new util.file('log');
188                         master_log.write_content('append',message); master_log.close();
189                         var specific_log = new util.file('log_'+level);
190                         specific_log.write_content('append',message); specific_log.close();
191                     }
192                 }
193             }
194         } catch(E) {
195             dump('Calling sdump but ' + E + '\n');
196         }
197     },
198
199     'arg_dump' : function (args,dump_these) {
200         var s = '*>*>*> Called function ';
201         try {
202             if (!dump_these)
203                 dump_these = {};
204             s += args.callee.toString().match(/\w+/g)[1] + ' : ';
205             for (var i = 0; i < args.length; i++)
206                 s += typeof(args[i]) + ' ';
207             s += '\n';
208             for (var i = 0; i < args.length; i++)
209                 if (dump_these[i]) {
210
211                     var arg = args[i];
212                     //dump('dump_these[i] = ' + dump_these[i] + '  arg = ' + arg + '\n');
213
214                     if (typeof(dump_these[i])=='string') {
215
216                         if (dump_these[i].slice(0,1) == '.') {
217                             var cmd = 'arg' + dump_these[i];
218                             var result;
219                             try {
220                                 result = eval( cmd );
221                             } catch(E) {
222                                 result = cmd + ' ==> ' + E;
223                             }
224                             s += '\targ #' + i + ': ' + cmd + ' = ' + result;
225                         } else {
226                             var result;
227                             try {
228                                 result = eval( dump_these[i] );
229                             } catch(E) {
230                                 result = dump_these[i] + ' ==> ' + E;
231                             }
232                             s += '\targ #' + i + ': ' + result;
233                         }
234     
235                     } else {
236                         s += '\targ #' + i + ' = ';
237                         try {
238                             //s += js2JSON( arg );
239                             s += arg;
240                         } catch(E) {
241                             s += arg;
242                         }
243                     }
244     
245                     s += '\n';
246                     if (this.arg_dump_full)
247                         s += 'Definition: ' + args.callee.toString() + '\n';
248     
249                 }
250             return s;
251         } catch(E) {
252             return s + '\nDEBUG ME: ' + js2JSON(E) + '\n';
253         }
254     },
255
256     'handle_error' : function (E,annoy) {
257         var s = '';
258         if (instanceOf(E,ex)) {
259             s += E.err_msg();
260             //s += '\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n';
261             //s += 'This error was anticipated.\n\n';
262             //s += js2JSON(E).substr(0,200) + '...\n\n';
263             if (snd_bad) snd_bad();
264         } else {
265             s += '\n\n=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=\n\n';
266             s += 'This is a bug that we will fix later.\n\n';
267             try {
268                 s += js2JSON(E).substr(0,1024) + '\n\n';
269             } catch(E2) {
270                 try {
271                     s += E.substr(0,1024) + '\n\n';
272                 } catch(E3) {
273                     s += E + '\n\n';
274                 }
275             }
276             if (snd_really_bad) snd_really_bad();
277         }
278         sdump('D_ERROR',s);
279         if (annoy)
280             this.s_alert(s);
281         else
282             alert(s);
283     },
284
285     's_alert' : function (s) { alert(s); },
286
287     'standard_network_error_alert' : function(msg) {
288         var obj = this;
289         if (!msg) msg = '';
290         var alert_msg = 'We experienced a network/server communication failure.  Please check your internet connection and try this action again.  Repeated failures may require attention from your local IT staff or your friendly Evergreen developers.\n\n' + msg;
291         obj.yns_alert(
292             alert_msg,    
293             'Communication Failure',
294             'Ok', null, null, 'Check here to confirm this message'
295         );
296     },
297
298     'standard_unexpected_error_alert' : function(msg,E) {
299         var obj = this;
300         if (E != null && typeof E.ilsevent != 'undefined') {
301             if (E.ilsevent == 0 /* SUCCESS */ ) {
302                 msg = "The action involved likely succeeded, however, this part of the software needs to be updated to better understand success messages from the server, so please let us know about it.";
303             }
304             if (E.ilsevent == -1 /* Network/Server Problem */ ) {
305                 return obj.standard_network_error_alert(msg);
306             }
307             if (E.ilsevent == 5000 /* PERM_FAILURE */ ) {
308                 msg = "The action involved likely failed due to insufficient permissions.  However, this part of the software needs to be updated to better understand permission messages from the server, so please let us know about it.";
309             }
310         }
311         if (!msg) msg = '';
312         var alert_msg = 'FIXME:  If you encounter this alert, please inform your IT/ILS helpdesk staff or your friendly Evergreen developers.\n\n' + (new Date()) + '\n\n' + msg + '\n\n' + (typeof E.ilsevent != 'undefined' ? E.textcode + '\n' + (E.desc ? E.desc + '\n' : '') : '') + ( typeof E.status != 'undefined' ? 'Status: ' + E.status + '\n': '' ) + ( typeof E == 'string' ? E + '\n' : '' );
313         obj.sdump('D_ERROR',msg + ' : ' + js2JSON(E));
314         var r = obj.yns_alert(
315             alert_msg,    
316             'Unhandled Error',
317             'Ok', 'Debug Output to send to Helpdesk', null, 'Check here to confirm this message',
318             '/xul/server/skin/media/images/skull.png'
319         );
320         if (r == 1) {
321             JSAN.use('util.window'); var win = new util.window();
322             win.open(
323                 'data:text/plain,' + window.escape( 'Please open a helpdesk ticket and include the following text: \n\n' + (new Date()) + '\n\n' + msg + '\n\n' + obj.pretty_print(js2JSON(E)) ),
324                 'error_alert',
325                 'chrome,resizable,width=700,height=500'
326             );
327         }
328         if (r==2) {
329             alert('Not Yet Implemented');
330         }
331     },
332
333     'yns_alert' : function (s,title,b1,b2,b3,c,image) {
334
335         try {
336
337             if (location.href.match(/^chrome/)) return this.yns_alert_original(s,title,b1,b2,b3,c);
338
339         /* The original purpose of yns_alert was to prevent errors from being scanned through accidentally with a barcode scanner.  
340         However, this can be done in a less annoying manner by rolling our own dialog and not having any of the options in focus */
341
342         /*
343             s     = Message to display
344             title     = Text in Title Bar
345             b1    = Text for button 1
346             b2    = Text for button 2
347             b3    = Text for button 3
348             c    = Text for confirmation checkbox.  null for no confirm
349         */
350
351         dump('yns_alert:\n\ts = ' + s + '\n\ttitle = ' + title + '\n\tb1 = ' + b1 + '\n\tb2 = ' + b2 + '\n\tb3 = ' + b3 + '\n\tc = ' + c + '\n');
352         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect UniversalBrowserWrite");
353
354         //FIXME - is that good enough of an escape job?
355         s = s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
356
357         var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" flex="1">' 
358             + '<groupbox flex="1" style="overflow: auto; border: solid thin red;"><caption label="' + (title) + '"/>';
359
360         if (image) xml += '<hbox><image src="' + image + '"/><spacer flex="1"/></hbox>';
361         xml += '<description id="msg" style="-moz-user-select: text; -moz-user-focus: normal; font-size: large">' + (s)
362             + '</description></groupbox><groupbox><caption label="Options"/><hbox>';
363         var b1_key = b1 ? b1[0] : '';
364         var b2_key = b2 ? b2[0] : '';
365         var b3_key = b3 ? b3[0] : ''; /* FIXME - need to check for collisions */
366         if (b1) xml += '<button id="b1" accesskey="' + b1_key + '" label="' + (b1) + '" name="fancy_submit" value="b1"/>';
367         if (b2) xml += '<button id="b2" accesskey="' + b2_key + '" label="' + (b2) + '" name="fancy_submit" value="b2"/>';
368         if (b3) xml += '<button id="b3" accesskey="' + b3_key + '" label="' + (b3) + '" name="fancy_submit" value="b3"/>';
369         var copy_button_label = 'Copy Message'; /* default in case the I18N infrastructure is failing, yns_alert often gets used for errors */
370         var x= document.getElementById('offlineStrings');
371         if (x) {
372             if (typeof x.getString == 'function') {
373                 if (x.getString('common.error.copy_msg')) { copy_button_label = x.getString('common.error.copy_msg'); }
374             }
375         }
376         xml += '<spacer flex="1"/><button label="' + copy_button_label + '" oncommand="try { copy_to_clipboard( document.getElementById(' + "'msg'" + ').textContent ); } catch(E) { alert(E); }" />';
377         xml += '</hbox></groupbox></vbox>';
378         JSAN.use('OpenILS.data');
379         //var data = new OpenILS.data(); data.init({'via':'stash'});
380         //data.temp_yns_xml = xml; data.stash('temp_yns_xml');
381         var url = urls.XUL_FANCY_PROMPT; // + '?xml_in_stash=temp_yns_xml' + '&title=' + window.escape(title);
382         if (typeof xulG != 'undefined') if (typeof xulG.url_prefix == 'function') url = xulG.url_prefix( url );
383         JSAN.use('util.window'); var win = new util.window();
384         var fancy_prompt_data = win.open(
385             url, 'fancy_prompt', 'chrome,resizable,modal,width=700,height=500', { 'xml' : xml, 'title' : title, 'sound' : 'bad' }
386         );
387         if (fancy_prompt_data.fancy_status == 'complete') {
388             switch(fancy_prompt_data.fancy_submit) {
389                 case 'b1' : return 0; break;
390                 case 'b2' : return 1; break;
391                 case 'b3' : return 2; break;
392             }
393         } else {
394             //return this.yns_alert(s,title,b1,b2,b3,c,image);
395             return null;
396         }
397
398         } catch(E) {
399
400             dump('yns_alert failed: ' + E + '\ns = ' + s + '\ntitle = ' + title + '\nb1 = ' + b1 + '\nb2 = ' + b2 + '\nb3 = ' + b3 + '\nc = ' + c + '\nimage = ' + image + '\n');
401
402             this.yns_alert_original(s + '\n\nAlso, yns_alert failed: ' + E,title,b1,b2,b3,c);
403
404         }
405     },
406
407     'yns_alert_formatted' : function (s,title,b1,b2,b3,c,image) {
408
409         try {
410
411             if (location.href.match(/^chrome/)) return this.yns_alert_original(s,title,b1,b2,b3,c);
412
413         /* The original purpose of yns_alert was to prevent errors from being scanned through accidentally with a barcode scanner.  
414         However, this can be done in a less annoying manner by rolling our own dialog and not having any of the options in focus */
415
416         /*
417             s     = Message to display
418             title     = Text in Title Bar
419             b1    = Text for button 1
420             b2    = Text for button 2
421             b3    = Text for button 3
422             c    = Text for confirmation checkbox.  null for no confirm
423         */
424
425         dump('yns_alert_formatted:\n\ts = ' + s + '\n\ttitle = ' + title + '\n\tb1 = ' + b1 + '\n\tb2 = ' + b2 + '\n\tb3 = ' + b3 + '\n\tc = ' + c + '\n');
426         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect UniversalBrowserWrite");
427
428         //FIXME - is that good enough of an escape job?
429         s = s.replace(/&/g, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
430
431         var xml = '<vbox xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" xmlns:html="http://www.w3.org/1999/xhtml" flex="1">' 
432             + '<groupbox flex="1" style="overflow: auto; border: solid thin red;"><caption label="' + (title) + '"/>';
433
434         if (image) xml += '<hbox><image src="' + image + '"/><spacer flex="1"/></hbox>';
435         xml += '<description style="-moz-user-select: text; -moz-user-focus: normal; font-size: large"><html:pre id="msg" style="font-size: large">' + (s)
436             + '</html:pre></description></groupbox><groupbox><caption label="Options"/><hbox>';
437         var b1_key = b1 ? b1[0] : '';
438         var b2_key = b2 ? b2[0] : '';
439         var b3_key = b3 ? b3[0] : ''; /* FIXME - need to check for collisions */
440         if (b1) xml += '<button id="b1" accesskey="' + b1_key + '" label="' + (b1) + '" name="fancy_submit" value="b1"/>';
441         if (b2) xml += '<button id="b2" accesskey="' + b2_key + '" label="' + (b2) + '" name="fancy_submit" value="b2"/>';
442         if (b3) xml += '<button id="b3" accesskey="' + b3_key + '" label="' + (b3) + '" name="fancy_submit" value="b3"/>';
443         var copy_button_label = 'Copy Message'; /* default in case the I18N infrastructure is failing, yns_alert often gets used for errors */
444         var x= document.getElementById('offlineStrings');
445         if (x) {
446             if (typeof x.getString == 'function') {
447                 if (x.getString('common.error.copy_msg')) { copy_button_label = x.getString('common.error.copy_msg'); }
448             }
449         }
450         xml += '<spacer flex="1"/><button label="' + copy_button_label + '" oncommand="try { copy_to_clipboard( document.getElementById(' + "'msg'" + ').textContent ); } catch(E) { alert(E); }" />';
451         xml += '</hbox></groupbox></vbox>';
452         JSAN.use('OpenILS.data');
453         //var data = new OpenILS.data(); data.init({'via':'stash'});
454         //data.temp_yns_xml = xml; data.stash('temp_yns_xml');
455         var url = urls.XUL_FANCY_PROMPT; // + '?xml_in_stash=temp_yns_xml' + '&title=' + window.escape(title);
456         if (typeof xulG != 'undefined') if (typeof xulG.url_prefix == 'function') url = xulG.url_prefix( url );
457         JSAN.use('util.window'); var win = new util.window();
458         var fancy_prompt_data = win.open(
459             url, 'fancy_prompt', 'chrome,resizable,modal,width=700,height=500', { 'xml' : xml, 'title' : title, 'sound' : 'bad' }
460         );
461         if (fancy_prompt_data.fancy_status == 'complete') {
462             switch(fancy_prompt_data.fancy_submit) {
463                 case 'b1' : return 0; break;
464                 case 'b2' : return 1; break;
465                 case 'b3' : return 2; break;
466             }
467         } else {
468             //return this.yns_alert(s,title,b1,b2,b3,c,image);
469             return null;
470         }
471
472         } catch(E) {
473
474             alert('yns_alert_formatted failed: ' + E + '\ns = ' + s + '\ntitle = ' + title + '\nb1 = ' + b1 + '\nb2 = ' + b2 + '\nb3 = ' + b3 + '\nc = ' + c + '\nimage = ' + image + '\n');
475
476         }
477
478     },
479
480     'yns_alert_original' : function (s,title,b1,b2,b3,c) {
481
482         /*
483             s     = Message to display
484             title     = Text in Title Bar
485             b1    = Text for button 1
486             b2    = Text for button 2
487             b3    = Text for button 3
488             c    = Text for confirmation checkbox.  null for no confirm
489         */
490
491         dump('yns_alert_original:\n\ts = ' + s + '\n\ttitle = ' + title + '\n\tb1 = ' + b1 + '\n\tb2 = ' + b2 + '\n\tb3 = ' + b3 + '\n\tc = ' + c + '\n');
492         netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
493
494         if (this.sound) { this.sound.bad(); }
495
496         // get a reference to the prompt service component.
497         var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
498             .getService(Components.interfaces.nsIPromptService);
499
500         // set the buttons that will appear on the dialog. It should be
501         // a set of constants multiplied by button position constants. In this case,
502         // three buttons appear, Save, Cancel and a custom button.
503         //var flags=promptService.BUTTON_TITLE_OK * promptService.BUTTON_POS_0 +
504         //    promptService.BUTTON_TITLE_CANCEL * promptService.BUTTON_POS_1 +
505         //    promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2;
506         var flags = promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_0 +
507             promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_1 +
508             promptService.BUTTON_TITLE_IS_STRING * promptService.BUTTON_POS_2; 
509
510         // display the dialog box. The flags set above are passed
511         // as the fourth argument. The next three arguments are custom labels used for
512         // the buttons, which are used if BUTTON_TITLE_IS_STRING is assigned to a
513         // particular button. The last two arguments are for an optional check box.
514         var check = {};
515         
516         // promptService.confirmEx does not offer scrollbars for long
517         // content, so trim error lines to avoid spilling offscreen
518         //
519         // There's probably a better way of doing this.
520
521         var maxlines = 30;
522         var ss = '';
523         var linefeeds = 0;
524         for (var i=0, chr; linefeeds < maxlines && i < s.length; i++) {  
525             if ((chr = this.getWholeChar(s, i)) === false) {continue;}
526             if (chr == '\u000A') { // \n
527                 linefeeds++;
528             }    
529             ss = ss + chr;
530         }
531         
532         var rv = promptService.confirmEx(window,title, ss, flags, b1, b2, b3, c, check);
533         if (c && !check.value) {
534             return this.yns_alert_original(ss,title,b1,b2,b3,c);
535         }
536         return rv;
537     },
538
539     'print_tabs' : function(t) {
540         var r = '';
541         for (var j = 0; j < t; j++ ) { r = r + "\t"; }
542         return r;
543     },
544
545     'pretty_print' : function(s) {
546         var r = ''; var t = 0;
547         for (var i in s) {
548             if (s[i] == '{') {
549                 r = r + "\n" + this.print_tabs(t) + s[i]; t++;
550                 r = r + "\n" + this.print_tabs(t);
551             } else if (s[i] == '[') {
552                 r = r + "\n" + this.print_tabs(t) + s[i]; t++;
553                 r = r + "\n" + this.print_tabs(t);
554             } else if (s[i] == '}') {
555                 t--; r = r + "\n" + this.print_tabs(t) + s[i];
556                 r = r + "\n" + this.print_tabs(t);
557             } else if (s[i] == ']') {
558                 t--; r = r + "\n" + this.print_tabs(t) + s[i];
559                 r = r + "\n" + this.print_tabs(t);
560             } else if (s[i] == ',') {
561                 r = r + s[i];
562                 r = r + "\n" + this.print_tabs(t);
563             } else {
564                 r = r + s[i];
565             }
566         }
567         return r;
568     },
569
570     // Copied from https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Global_Objects/String/charCodeAt
571     'getWholeChar' : function(str, i) {  
572         var code = str.charCodeAt(i);  
573         if (0xD800 <= code && code <= 0xDBFF) { // High surrogate(could change last hex to 0xDB7F to treat high private surrogates as single characters)  
574             if (str.length <= (i+1))  {  
575                 throw 'High surrogate without following low surrogate';  
576             }  
577             var next = str.charCodeAt(i+1);  
578             if (0xDC00 > next || next > 0xDFFF) {  
579                 throw 'High surrogate without following low surrogate';  
580             }  
581             return str[i]+str[i+1];  
582         }  
583         else if (0xDC00 <= code && code <= 0xDFFF) { // Low surrogate  
584             if (i === 0) {  
585                 throw 'Low surrogate without preceding high surrogate';  
586             }  
587             var prev = str.charCodeAt(i-1);  
588             if (0xD800 > prev || prev > 0xDBFF) { //(could change last hex to 0xDB7F to treat high private surrogates as single characters)  
589                 throw 'Low surrogate without preceding high surrogate';  
590             }  
591             return false; // We can pass over low surrogates now as the second component in a pair which we have already processed  
592         }  
593         return str[i];  
594     },
595
596     'work_log' : function(msg,row_data) {
597         try {
598             JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
599             var max_entries = data.hash.aous['ui.admin.work_log.max_entries'] || 20;
600             if (! data.work_log) data.work_log = [];
601             if (! row_data) row_data = {};
602             row_data.message = msg;
603             row_data.when = new Date();
604             var ds = { 
605                 retrieve_id: js2JSON( { 'au_id' : row_data.au_id, 'au_barcode' : row_data.au_barcode, 'au_family_name' : row_data.au_family_name, 'acp_id' : row_data.acp_id, 'acp_barcode' : row_data.acp_barcode } ), 
606                 row: { my: row_data },
607                 to_top: true
608             };
609             data.work_log.push( ds );
610             if (data.work_log.length > max_entries) data.work_log.shift();
611             data.stash('work_log');
612             if (row_data.au_id) {
613                this.patron_log(msg,row_data); 
614             }
615         } catch(E) {
616             try { this.standard_unexpected_error_alert('error in error.js, work_log(): ',E); } catch(F) { alert(E); }
617         }
618     },
619
620     'patron_log' : function(msg,row_data) {
621         try {
622             JSAN.use('OpenILS.data'); var data = new OpenILS.data(); data.stash_retrieve();
623             var max_entries = data.hash.aous['ui.admin.patron_log.max_entries'] || 10;
624             if (! data.patron_log) data.patron_log = [];
625             if (! row_data) row_data = {};
626             row_data.message = msg;
627             row_data.when = new Date();
628             var ds = { 
629                 retrieve_id: js2JSON( { 'au_id' : row_data.au_id, 'au_barcode' : row_data.au_barcode, 'au_family_name' : row_data.au_family_name, 'acp_id' : row_data.acp_id, 'acp_barcode' : row_data.acp_barcode } ), 
630                 row: { my: row_data },
631                 to_top: true
632             };
633             if (data.patron_log.length > 0) {
634                 var temp = [];
635                 for (var i = 0; i < data.patron_log.length; i++) {
636                     if (data.patron_log[ i ].row.my.au_id != row_data.au_id) temp.push( data.patron_log[i] );
637                 } 
638                 data.patron_log = temp;
639             }
640             data.patron_log.push( ds );
641             if (data.patron_log.length > max_entries) data.patron_log.shift();
642             data.stash('patron_log');
643         } catch(E) {
644             try { this.standard_unexpected_error_alert('error in error.js, patron_log(): ',E); } catch(F) { alert(E); }
645         }
646     } 
647 }
648
649 dump('exiting util/error.js\n');