source: sage/server/notebook/js.py @ 5253:4be54a6a85a4

Revision 5253:4be54a6a85a4, 68.8 KB checked in by Tom Boothby <boothby@…>, 6 years ago (diff)

Added block-indenting capability to notebook.

Line 
1r"""
2Javascript (AJAX) Component of SAGE Notebook
3
4AUTHORS:
5    -- William Stein
6    -- Tom Boothby
7    -- Alex Clemesha
8
9
10This file is one big raw triple-quoted string that contains a bunch of
11javascript.  This javascript is inserted into the head of the notebook
12web page.
13"""
14
15from sage.misc.misc import SAGE_URL
16from compress.JavaScriptCompressor import JavaScriptCompressor
17import keyboards
18
19###########################################################################
20#       Copyright (C) 2006 William Stein <wstein@gmail.com>
21#                     2006 Tom Boothby <boothby@u.washington.edu>
22#
23#  Distributed under the terms of the GNU General Public License (GPL)
24#                  http://www.gnu.org/licenses/
25###########################################################################
26
27
28def javascript():
29    s = async_lib()
30    s += notebook_lib()
31
32    # TODO -- disabled while debuging.
33    #s = JavaScriptCompressor().getPacked(s)
34   
35    return s
36
37
38def async_lib():
39    s = r"""
40///////////////////////////////////////////////////////////////////
41// An AJAX framework for connections back to the
42// SAGE server (written by Tom Boothby and William Stein).
43///////////////////////////////////////////////////////////////////
44
45
46//globals
47
48var async_oblist = [null,null,null,null,null];
49var async_idstack= [0,1,2,3,4];
50
51function getAsyncObject(handler) {
52  var asyncObj;
53  try {
54    if (browser_ie) {
55      var s =browser_ie5?"Microsoft.XMLHTTP":"Msxml2.XMLHTTP";
56      asyncObj = new ActiveXObject(s);
57      asyncObj.onreadystatechange = handler;
58      return asyncObj;
59    } else {
60      asyncObj = new XMLHttpRequest();
61      asyncObj.onload  = handler;
62      asyncObj.onerror = handler;
63      return asyncObj;
64    }
65  } catch(e) {
66    no_async = true;
67    return null;
68  }
69}
70
71function generic_callback(status, response_text) {
72   /* do nothing */
73}
74
75function asyncCallbackHandler(id) {
76    //this was a one-liner, but Opera doesn't like to eval
77    // "function() {bla}" -- it needs to be part of an assignment
78    //Also, some versions of firefox don't like to see "function()"
79    //you need a space between the parentheses.  WTF?
80    var f;
81    eval("f = function( ) { async_callback("+id+"); }");
82    return f;
83}
84
85function async_callback(id) {
86    var asyncObj = async_oblist[id][0];
87    var callback = async_oblist[id][1];
88    try {
89        if( (asyncObj.readyState==4 || asyncObj.readyState=="complete")
90              && asyncObj.status == 200 )
91            try {
92                callback('success', asyncObj.responseText);
93                async_release(id);  //don't release the id until we've tried to capture output
94            } catch(e) {
95                async_release(id);  //release immediately in case exception was in the callback
96                callback('success', "empty");
97            }
98    } catch(e) {
99        if(async_oblist[id] != null) //release immediately
100            async_release(id);
101        callback("failure", e);
102    }
103}
104
105function async_request(url, callback, postvars) {
106  var id = async_id();
107  var f = asyncCallbackHandler(id);
108  var asyncObj = getAsyncObject(f);
109  async_oblist[id] = [asyncObj,callback];
110
111  if(postvars != null) {
112    asyncObj.open('POST',url,true);
113    asyncObj.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
114    asyncObj.send(postvars);
115  } else {
116    asyncObj.open('GET',url,true);
117    asyncObj.setRequestHeader('Content-Type',  "text/html");
118    asyncObj.send(null);
119  }
120}
121
122function async_id() {
123  if(async_idstack.length == 0) {
124    id = async_oblist.length;
125    async_oblist.push(null);
126  } else {
127    id = async_idstack.pop();
128  }
129  return id
130}
131
132function async_release(id) {
133  async_oblist[id] = null;
134  async_idstack.push(id);
135  if(async_idstack.length == async_oblist.length && async_oblist.length > 10) {
136    async_oblist = [null,null,null,null,null];
137    async_idstack= [0,1,2,3,4];
138  }
139}
140
141"""
142    return s
143
144
145def notebook_lib():
146    s= r"""
147
148///////////////////////////////////////////////////////////////////
149//
150// GLOBAL VARIABLES
151//
152// PLEASE define all global variables up here, and if you want to
153// set them with anything but simple assignment, 'var' them first,
154// and set them later.  Your code might work in your browser, but
155// it might break initial setup for other critical pieces in other
156// browsers.  Thanks. (and for the record, I'm guilty of this more
157// than anybody else here -- I figure a big block comment might
158// help keep me in check)
159//
160// Exception: keyboard globals are defined at the end
161//
162///////////////////////////////////////////////////////////////////
163
164
165// The active cell list.
166var active_cell_list = [];
167
168//Browser & OS identification
169var browser_op, browser_saf, browser_konq, browser_moz, browser_ie, browser_ie5;
170var os_mac, os_lin, os_win;
171
172var update_error_count = 0;
173var update_error_threshold = 30;
174
175// in milliseconds
176var update_error_delta = 1024;
177//var update_normal_delta = 256;
178var update_normal_delta = 512;
179var cell_output_delta = update_normal_delta;
180
181var server_ping_time = 10000;  /* Is once very 10 seconds way too fast?  Is it just right?  */
182
183
184var SEP = '___S_A_G_E___';   // this had better be the same as in the server
185var current_cell = -1;       // gets set on focus / blur
186var no_async = false; //this isn't really used -- should we think about dealing with this?
187var cell_has_changed = false;
188var cell_to_focus = -1;
189
190// introspection variables
191var introspection_loaded = false;
192var introspect_id;
193var introspection_text = "";
194var replacement_text = "";
195var replacement_row = 0;
196var replacement_col = 0;
197var replacing_word = "";
198var replacement_word = "";
199var replacing = false;
200var sub_introspecting = false;
201
202// Info about the current worksheet.  These get set in notebook.py
203var worksheet_id=0;
204var worksheet_filename='';
205var worksheet_name='';
206var user_name='';
207
208//regular expressions used to peek into the cell input for introspection
209var non_word = "[^a-zA-Z0-9_]"; //finds any character that doesn't belong in a variable name
210var command_pat = "([a-zA-Z_][a-zA-Z._0-9]*)$"; //identifies the command at the end of a string
211var function_pat = "([a-zA-Z_][a-zA-Z._0-9]*)\\([^()]*$";
212var one_word_pat = "([a-zA-Z_][a-zA-Z._0-9]*)";
213var unindent_pat = "^\\s{0,4}(.*)$";
214var whitespace_pat = "(\\s*)";
215
216try{
217  non_word = new RegExp(non_word);
218  command_pat = new RegExp(command_pat);
219  function_pat = new RegExp(function_pat);
220  one_word_pat = new RegExp(one_word_pat);
221  whitespace_pat = new RegExp(whitespace_pat);
222  unindent_pat = new RegExp(unindent_pat);
223} catch(e){}
224
225var after_cursor, before_cursor, before_replacing_word;
226
227var update_timeout = -1;
228
229var updating = false; var update_time = -1;
230
231var jsmath_font_msg = '<a href="SAGE_URL/jsmath">Click to download and install tex fonts.</a><br>';
232
233jsMath = {Font: {Message: function () {}}}
234
235var cell_id_list; // this gets set in worksheet.py
236
237var input_keypress; //this gets set to a function when we set up the keyboards
238
239var in_slide_mode = false; //whether or not we're in slideshow mode
240var slide_hidden = false; //whether the current slide has the hidden input class
241
242var worksheet_locked;
243
244var original_title;
245
246//var title_spinner = ['    ', '.   ', '..  ', '... '];
247//var title_spinner = ['[ ] ', '[.] ', '[:] ', '[.] '];
248//var title_spinner = ['S ', 'SA ', 'SAG ', 'SAGE '];
249var title_spinner = ['/ ', '\\ '];
250//var title_spinner = ['[   ] ', '[.  ] ', '[.. ] ', '[...] '];
251//var title_spinner = ['[-] ','[/] ','[|] ','[\\] '];
252var title_spinner_i = 0;
253try{
254    original_title = document.title;
255} catch(e) {}
256
257///////////////////////////////////////////////////////////////////
258//
259// Cross-Browser Stuff
260//
261///////////////////////////////////////////////////////////////////
262
263function true_function() {return true;}
264input_keypress = true_function;
265
266try{
267  var n=navigator;
268  var nav=n.appVersion;
269  var nan=n.appName;
270  var nua=n.userAgent;
271  browser_op=(nua.indexOf('Opera')!=-1);
272  browser_saf=(nua.indexOf('Safari')!=-1);
273  browser_konq=(!browser_saf && (nua.indexOf('Konqueror')!=-1) ) ? true : false;
274  browser_moz=( (!browser_saf && !browser_konq ) && ( nua.indexOf('Gecko')!=-1 ) ) ? true : false;
275  browser_ie=((nua.indexOf('MSIE')!=-1)&&!browser_op);
276  browser_ie5=(browser_ie&&(nua.indexOF('MSIE 5')!=-1));
277  os_mac=(nav.indexOf('Mac')!=-1);
278  os_win=( ( (nav.indexOf('Win')!=-1) || (nav.indexOf('NT')!=-1) ) && !os_mac)?true:false;
279  os_lin=(nua.indexOf('Linux')!=-1);
280
281  if(browser_ie) {
282      alert("Using SAGE with Microsoft Internet Explorer is currently not supported.");
283  }
284
285/*  line_height = 1.2;
286  if (os_mac) {
287     line_height = 1;
288  }
289  */
290  line_height = 1.2;
291  get_keyboard();
292} catch(e){}
293
294try{
295  [].indexOf || (Array.prototype.indexOf = function(v,n){
296    n = (n==null)?0:n; m = this.length;
297    for(var i = n; i < m; i++)
298      if(this[i] == v)
299         return i;
300    return -1;
301  });
302} catch(e){}
303
304
305function get_keyboard() {
306  var b,o,warn=false;
307
308  input_keypress = cell_input_key_event;
309  debug_keypress = debug_input_key_event;
310
311  if(browser_op) {
312    b = "o";
313  } else if(browser_ie) {
314    b = "i";
315    document.onkeydown = key_listen_ie;
316    input_keypress = true_function;
317    debug_keypress = true_function;
318//    warn = true;
319  } else if(browser_saf) {
320    b = "s";
321  } else if(browser_konq) {
322    b = "k";
323//    warn = true;
324  } else {
325    b = "m";
326  }
327
328  if(os_mac) {
329    o = "m";
330  } else if(os_lin) {
331    o = "l";
332  } else {
333    o = "w"
334  }
335
336  if(b == null || o == null || warn) {
337    alert("Your browser / OS combination is not supported.  \nPlease use Firefox or Opera under linux, windows, or mac OSX, or Safari.")
338  }
339
340  async_request('/javascript/keyboard/'+b+o, get_keyboard_callback, null);
341}
342
343function get_keyboard_callback(status, response_text) {
344  if(status == 'success') {
345    eval(response_text);
346  }
347}
348
349function get_element(id) {
350  if(document.getElementById)
351    return document.getElementById(id);
352  if(document.all)
353    return document.all[id];
354  if(document.layers)
355    return document.layers[id];
356}
357
358function set_class(id, cname) {
359  e = get_element(id);
360  if(e!=null) {
361      e.className = cname;
362  }
363}
364
365function get_class(id) {
366  e = get_element(id);
367  if(e!=null) {
368      return e.className;
369  }
370  return null
371}
372
373function set_html(id, html) {
374  e = get_element(id);
375  if(e!=null) {
376      e.innerHTML = html;
377  }
378}
379
380function get_event(e) {
381   return (e==null)?window.event:e;
382}
383
384function key_event(e) {
385   if(e==null) e = window.event;
386   if(e.modifiers) {
387     this.a = e.modifiers | 1;
388     this.c = e.modifiers | 2;
389     this.s = e.modifiers | 4;
390   } else {
391     this.a = e.altKey;
392     this.c = e.ctrlKey;
393     this.s = e.shiftKey;
394   }
395   this.k = e.keyCode + "," + e.which;
396   this.m = this.k + (this.s?'!':'');
397   return this;
398}
399
400function time_now() {
401  return (new Date()).getTime();
402}
403
404
405///////////////////////////////////////////////////////////////////
406//
407// Misc page functions -- for making the page work nicely
408// (this is a crappy descriptor)
409///////////////////////////////////////////////////////////////////
410
411
412// Replaces all instances of the given substring.
413// From http://www.bennadel.com/blog/142-Ask-Ben-Javascript-String-Replace-Method.htm
414
415String.prototype.replaceAll = function(strTarget, strSubString ) {
416        var strText = this;
417        var intIndexOfMatch = strText.indexOf( strTarget );
418        // Keep looping while an instance of the target string
419        // still exists in the string.
420        while (intIndexOfMatch != -1) {
421                // Replace out the current instance.
422                strText = strText.replace( strTarget, strSubString )
423                // Get the index of any next matching substring.
424                intIndexOfMatch = strText.indexOf( strTarget );
425        }
426        return( strText );
427}
428
429function is_whitespace(s) {
430    m = whitespace_pat.exec(s);
431    return (m[1] == s);
432}
433
434function trim(s) {
435    m = one_word_pat.exec(s);
436    if(m == null)
437        return s;
438    return m[1];
439}
440
441function body_load() {
442// init_menus();
443}
444
445function init_menus() {
446  for( i = 1; i <= 3; i++) {
447    menu = get_element("menu"+i);
448    menu.style.display="none";
449  }
450}
451
452function toggle_menu(name) {
453  if(get_class(name) == "hidden") {
454    set_class(name, name);
455    set_html(name+'_hider', '[-]');
456  } else {
457    set_class(name, 'hidden');
458    set_html(name+'_hider', '[+]');
459  }
460}
461
462function toggle_left_pane() {
463  if(get_class('left_pane') == "hidden") {
464    set_class('left_pane', 'pane');
465    set_class('worksheet', 'worksheet');
466  } else {
467    set_class('left_pane', 'hidden');
468  }
469}
470
471
472///////////////////////////////////////////////////////////////////
473//
474// Completions interface stuff
475//
476///////////////////////////////////////////////////////////////////
477
478function handle_replacement_controls(cell_input, event) {
479    deselect_replacement_element();
480    if(key_menu_up(event)) {
481        if(replacement_row <= 0) {
482            halt_introspection();
483        } else {
484            replacement_row--;
485        }
486    } else if(key_menu_down(event)) {
487        replacement_row++;
488        if(!replacement_element_exists())
489            replacement_row = 0;
490    } else if(key_menu_right(event)) {
491        replacement_col++;
492        if(!replacement_element_exists())
493            replacement_col = 0;
494    } else if(key_menu_left(event)) {
495        replacement_col--;
496        if(!replacement_element_exists()) {
497            replacement_col = 1;
498            while(replacement_element_exists())
499                replacement_col++;
500            replacement_col--;
501        }
502    } else if(key_menu_pick(event)) {
503        do_replacement(introspect_id, replacement_word, true);
504        return false;
505    } else if(key_request_introspections(event)) {
506        if(sub_introspecting) {
507            introspection_text = replacement_text;
508            introspection_loaded = true;
509            sub_introspecting = false;
510            update_introspection_text();
511        } else {
512            replacement_text = introspection_text;
513            introspection_loaded = false;
514            sub_introspecting = true;
515        }
516    } else {
517       halt_introspection();
518       return true;
519    }
520    select_replacement_element();
521
522    if(sub_introspecting) {
523        active_cell_list = active_cell_list.concat([introspect_id]);
524        evaluate_cell_introspection(introspect_id, before_replacing_word+replacement_word+'?', after_cursor);
525    }
526
527    return false;
528}
529
530function do_replacement(id, word,do_trim) {
531    var cell_input = get_cell(id);
532    cell_focus(id, false);
533
534    if(do_trim) //optimization 'cause Opera has a slow regexp engine
535        word = trim(word);
536
537    cell_input.value = before_replacing_word + word + after_cursor;
538
539    var pos = before_replacing_word.length + word.length;
540
541    //note for explorer:  may need to focus cell first.
542    if(cell_input.setSelectionRange) {
543        cell_input.setSelectionRange(pos,pos);
544    } else if (cell_input.createTextRange) {
545        var range = cell_input.createTextRange();
546        range.moveEnd('character', pos - cell_input.value.length);
547        range.collapse(false);
548        range.select();
549    } else {
550        //debug_append("crap");
551    }
552
553    halt_introspection();
554}
555
556function get_replacement_element() {
557    return get_element("completion"+introspect_id + "_" + replacement_row + "_" + replacement_col);
558}
559
560function replacement_element_exists() {
561    return get_replacement_element() != null;
562}
563
564function select_replacement(row, col) {
565    deselect_replacement_element();
566    replacement_row = row;
567    replacement_col = col;
568    select_replacement_element();
569}
570
571function deselect_replacement_element() {
572    e = get_replacement_element();
573    if(e==null) return;
574    e.className = 'completion_menu_two';
575}
576
577function select_replacement_element() {
578    var e = get_replacement_element();
579    if (e==null) return;
580    e.className = 'completion_menu_two completion_menu_selected';
581    var l = e.getElementsByTagName('a');
582    if(l.length && l[0]!=null) {
583        var h = l[0].innerHTML;
584        var i = h.indexOf('&nbsp')
585        if (i != -1) {
586            h = h.substr(0,i);
587        }
588        replacement_word = h;
589    }
590}
591
592function update_introspection_text(preserve_cursor) {
593  close_introspection_text();
594  d = get_element("introspect_div_"+introspect_id);
595  if(!d) return;
596
597  if(introspection_loaded) {
598    if(introspection_text == "") {
599        halt_introspection();
600        return;
601    }
602    d.innerHTML = introspection_text;
603    if(replacing)
604      select_replacement_element();
605  } else {
606    d.innerHTML = "loading..."
607  }
608}
609
610function close_introspection_text() {
611  d = get_element("introspect_div_"+introspect_id);
612  if(d!=null)
613    d.innerHTML = "";
614}
615
616function halt_introspection() {
617    close_introspection_text();
618    introspect_id = null;
619    replacing = false;
620    sub_introspecting = false;
621    introspection_loaded = false;
622    replacement_row = replacement_col = 0;
623}
624
625///////////////////////////////////////////////////////////////////
626//
627// OBJECT functions -- for managing saved objects
628//
629///////////////////////////////////////////////////////////////////
630
631function click_on_object(name) {
632   // o = document.open("/" + name + ".sobj");
633}
634
635
636///////////////////////////////////////////////////////////////////
637//
638// WORKSHEET functions -- for switching between and managing worksheets
639//
640///////////////////////////////////////////////////////////////////
641
642function new_worksheet() {
643    open("/new_worksheet")
644}
645
646function set_worksheet_list_checks() {
647    /* Go through and set all check boxes the same as they are in the control box */
648    var C, i, id, X;
649    C = get_element("controlbox");
650    for(i=0; i<worksheet_filenames.length; i++) {
651        id = worksheet_filenames[i];
652        X  = get_element(id);
653        X.checked = C.checked;
654    }
655}
656
657function worksheet_list_button(action, desc) {
658    /* For each filename listed in worksheet_filenames, look up the corresponding
659       input check box, see if it is checked, and if so, do the corresponding
660       action.
661     */
662    var i, id, X, filenames;
663    filenames = "";
664    for(i=0; i<worksheet_filenames.length; i++) {
665        id = worksheet_filenames[i];
666        X  = get_element(id);
667        if (X.checked) {
668            filenames = filenames + worksheet_filenames[i] + SEP;
669            X.checked = 0;
670        }
671    }
672    async_request(action, worksheet_list_button_callback, 'filenames='+filenames + '&sep='+SEP);
673}
674
675function worksheet_list_button_callback(status, response_text) {
676   if (status == 'success') {
677      if (response_text != '') {
678          alert(response_text);
679      }
680   } else {
681      alert("Failure deleting worksheet." + response_text);
682   }
683  window.location.reload(true);
684}
685
686function delete_button() {
687    worksheet_list_button("/send_to_trash", "--> trash");
688}     
689
690function make_active_button() {
691    worksheet_list_button("/send_to_active", "(--> active");
692}     
693
694function archive_button() {
695    worksheet_list_button("/send_to_archive", "--> archived");
696}     
697
698
699function history_window() {
700    window.open ("/history",
701      "", "menubar=1,scrollbars=1,width=800,height=600, toolbar=1,resizable=1");
702
703}
704
705function copy_worksheet() {
706    window.location.replace(worksheet_command("copy"));
707}
708
709function rate_worksheet(rating) {
710    comment = get_element("rating_comment").value;
711    window.location.replace(worksheet_command("rate?rating="+rating + "&comment="+escape0(comment)));
712}
713
714function download_worksheet(base_filename) {
715    open(worksheet_command("download/" + base_filename + '.sws'));
716}     
717
718function worksheet_settings() {
719    window.location.replace(worksheet_command("settings"));
720}
721
722function share_worksheet() {
723    window.location.replace(worksheet_command("share"));
724}
725
726function publish_worksheet() {
727    window.open(worksheet_command("publish"), "",
728      "menubar=1,location=1,scrollbars=1,width=800,height=600,toolbar=1,  resizable=1");
729}
730
731function save_as(typ) {
732    open(worksheet_command('save_as') + '?typ=' +typ);
733}
734
735function edit_worksheet() {
736    window.location.replace(worksheet_command(""));
737}
738
739function save_worksheet() {
740    async_request(worksheet_command('save_snapshot'), save_worksheet_callback, null);
741}
742
743function save_worksheet_callback(status, response_text) {
744   if (status != 'success') {
745       alert("Failed to save worksheet.");
746       return;
747   }
748}
749
750function close_callback(status, response_text) {
751   if (status != 'success') {
752       alert(response_text);
753       return;
754   }
755    window.location.replace('/');
756}
757
758function save_worksheet_and_close() {
759    async_request(worksheet_command('save_snapshot'), close_callback, null);
760}
761
762function worksheet_discard() {
763    async_request(worksheet_command('revert_to_last_saved_state'), close_callback, null);
764}
765
766function rename_worksheet() {
767   var new_worksheet_name = prompt('Enter new worksheet name:',worksheet_name);
768   if (new_worksheet_name == null) return;
769   var T = get_element("worksheet_title");
770   var set_name;
771   if (new_worksheet_name.length >= 30) {
772       set_name = new_worksheet_name.slice(0,30) + ' ...';
773   } else {
774       set_name = new_worksheet_name;
775   }
776   T.innerHTML = set_name;
777   worksheet_name = new_worksheet_name;
778   async_request(worksheet_command('rename'), null, 'name='+escape0(new_worksheet_name));
779}
780
781function entsub_ws(event, typ) {
782  if (event && event.which == 13)
783     search_worksheets(typ);
784  else
785     return true;
786}
787
788
789function search_worksheets(typ) {
790    X = get_element('search_worksheets');
791    url = '?typ=' + typ + '&search=' + escape0(X.value);
792    window.location.replace(url);
793}
794
795function go_system_select(theform, original_system) {
796   with(theform) {
797      var system = options[selectedIndex].value;
798      if (confirm("Are you sure you wish to change the evaluation system to " + system + "? All cells will be evaluted using " + system + " until you change the system back.")) {
799          system_select(system);
800      } else {
801          options[original_system].selected = 1;
802      }
803   }
804}
805
806function system_select(s) {
807    async_request(worksheet_command('system/'+s), null, null);
808}
809
810
811function go_data(theform) {
812   var value;
813   with(theform) {
814      value = options[selectedIndex].value;
815      if(value == "__upload_data_file__") {
816          window.location.replace(worksheet_command("upload_data"));
817      } else {
818          window.location.replace("/home/" + worksheet_filename + "/" + value);
819      }
820      options[0].selected = 1;
821   }
822}
823
824function add_worksheet(name) {
825    open("/home/" + user_name + "/" + name)
826}
827
828function add_worksheet_callback(status,response_text) {
829    if (status == "success") {
830        /* expect response_text to encode a pair consisting of
831           the HTML for the updated worksheet list and the
832           name of the new worksheet. */
833        var X = response_text.split(SEP);
834        if (X.length <= 1) {
835            alert("Unable to add worksheet.");
836        } else {
837            set_worksheet_list(X[0]);
838        }
839    } else {
840        alert("Possible failure adding worksheet.");
841    }
842}
843
844function delete_worksheet(name) {
845    async_request('/send_to_trash', delete_worksheet_callback, 'filename='+escape0(name))
846}
847
848function delete_worksheet_callback(status, response_text) {
849    if (status == "success") {
850        window.location.replace("/?typ=trash");
851         
852    } else {
853        alert("Possible failure deleting worksheet.");
854    }
855}
856
857function set_worksheet_list(worksheets) {
858    var wlist = get_element('worksheet_list');
859    wlist.innerHTML = worksheets;
860}
861
862function show_add_new_worksheet_menu() {
863    var add_worksheet_menu = get_element('add_worksheet_menu');
864    add_worksheet_menu.style.display = 'block';
865    get_element('new_worksheet_box').focus()
866}
867
868function hide_add_new_worksheet_menu() {
869    var add_worksheet_menu = get_element('add_worksheet_menu');
870    add_worksheet_menu.style.display = 'none';
871}
872
873function show_upload_worksheet_menu() {
874    window.open("__upload__.html","","location=1,menubar=1,scrollbars=0,width=800,height=700,toolbar=1,resizable=1");
875    if(w.focus)
876      w.focus();
877}
878
879
880function hide_upload_worksheet_menu() {
881    var upload_worksheet_menu = get_element('upload_worksheet_menu');
882    upload_worksheet_menu.style.display = 'none';
883}
884
885function process_upload_worksheet_menu_submit() {
886    hide_upload_worksheet_menu();
887    var box = get_element('upload_worksheet_filename');
888    var filename = box.value;
889    box.value = '';
890    upload_worksheet(filename);
891}
892
893function upload_worksheet(filename) {
894   async_request('/upload_worksheet', upload_worksheet_callback, 'filename='+filename)
895}
896
897function upload_worksheet_callback(status, response_text) {
898    if (status == "success") {
899        if (response_text.slice(0,5) == "Error") {
900            alert("Error uploading worksheet.");
901        } else {
902            set_worksheet_list(response_text);
903        }
904    } else {
905        alert("Possible problem uploading file.");
906    }
907}
908
909function show_delete_worksheet_menu() {
910    var delete_worksheet_menu = get_element('delete_worksheet_menu');
911    delete_worksheet_menu.style.display = 'block';
912    get_element('delete_worksheet_box').focus();
913}
914
915function hide_delete_worksheet_menu() {
916    var delete_worksheet_menu = get_element('delete_worksheet_menu');
917    delete_worksheet_menu.style.display = 'none';
918}
919
920function process_new_worksheet_menu_submit() {
921   /* hide_add_new_worksheet_menu(); */
922    var add_worksheet_box = get_element('new_worksheet_box');
923    name = add_worksheet_box.value;
924    if (name == '') {
925       alert("Enter a worksheet name in the box and click new to create a new worksheet.");
926       return;
927    }
928    add_worksheet_box.value = '';
929    add_worksheet(name);
930}
931
932function process_delete_worksheet_menu_submit() {
933    hide_delete_worksheet_menu();
934    var delete_worksheet_box = get_element('delete_worksheet_box');
935    name = delete_worksheet_box.value;
936    delete_worksheet_box.value = '';
937    delete_worksheet(name);
938}
939
940
941
942function unlock_worksheet() {
943    lock = get_element("worksheet_lock");
944    lock.innerHTML = 'Enter Passcode: <input onKeyPress="return unlock_worksheet_submit(event,value);" id="lock_input" type="password">';
945    lock.innerHTML+= '<span id="unlock_error" class="red"></span>';
946    lock_input = get_element("lock_input");
947    lock_input.focus();
948}
949
950function unlock_worksheet_submit(e,passcode) {
951    if(is_submit(e)) {
952        document.cookie = "ws_"+worksheet_filename+"_passcode="+passcode;
953        async_request('/unlock_worksheet', unlock_worksheet_callback, 'worksheet_id='+worksheet_id);
954        return false;
955    }
956    return true;
957}
958
959function unlock_worksheet_callback(status, response_text) {
960    if(status == 'success' && response_text == 'ok') {
961        lock = get_element("worksheet_lock");
962        lock.parentNode.removeChild(lock);
963        worksheet_locked = false;
964    } else {
965        lock_input = get_element("lock_input");
966        lock_input.value = "";
967        lock_input.focus();
968        txt = get_element('unlock_error');
969        if(txt)
970            txt.innerHTML = 'incorrect';
971    }
972}
973
974function sync_active_cell_list() {
975    async_request('/get_queue', sync_active_cell_list_callback, 'worksheet_id='+worksheet_id);
976}
977
978function sync_active_cell_list_callback(status, response_text) {
979    if(status == 'success') {
980        if(response_text == "")
981            return;
982        active_cell_list = response_text.split(",");
983        for(var i = 0; i < active_cell_list.length; i++)
984            cell_set_running(active_cell_list[i]);
985        start_update_check();
986    }
987}
988
989///////////////////////////////////////////////////////////////////
990//
991// WORKSHEET list functions -- i.e., functions on a specific
992// worksheet in the list of worksheets display.
993//
994///////////////////////////////////////////////////////////////////
995
996function refresh() {
997    window.location.replace(location.href);   
998}
999
1000function go_option(theform) {
1001   with(theform) {
1002      eval(options[selectedIndex].value);
1003      options[0].selected = 1;
1004   }
1005}
1006
1007function link_datafile(target_worksheet_filename, filename) {
1008   open(worksheet_command("link_datafile?filename=" + escape0(filename) +
1009         "&target="+escape0(target_worksheet_filename)));
1010}
1011
1012
1013function list_rename_worksheet(filename, curname) {
1014   var new_name = prompt('Enter new worksheet name:', curname);   
1015   async_request('/home/' + filename + '/' + 'rename',
1016            list_rename_worksheet_callback, 'name='+ escape0(new_name));
1017}
1018
1019function list_rename_worksheet_callback(status, response_text) {
1020   refresh();
1021}
1022
1023
1024function list_edit_worksheet(filename) {
1025    window.location.replace('/home/' + filename);
1026}
1027
1028function list_copy_worksheet(filename) {
1029    async_request('/home/' + filename + '/copy', list_copy_worksheet_callback, null);
1030}
1031
1032function list_copy_worksheet_callback(status, response_text) {
1033    window.location.replace('/');
1034}
1035
1036function list_share_worksheet(filename) {
1037   window.location.replace('/home/' + filename + '/share');
1038}
1039
1040function list_publish_worksheet(filename) {
1041   window.open('/home/' + filename + '/publish', "",
1042      "menubar=1,scrollbars=1,width=800,height=600,toolbar=1,  resizable=1");
1043}
1044
1045function list_revisions_of_worksheet(filename) {
1046   window.location.replace('/home/' + filename + '/revisions');
1047}
1048
1049function list_preview_worksheet(filename) {
1050   window.location.replace('/home/' + filename + '/preview');
1051}
1052
1053///////////////////////////////////////////////////////////////////
1054//
1055// Tell server I am alive
1056//
1057///////////////////////////////////////////////////////////////////
1058
1059/* This pings the server every 30 seconds to announce that we are
1060   still viewing this page.   If it fails, it should probably perform
1061   some action to indicate that there is no server running.
1062*/
1063   
1064function server_ping_while_alive() {
1065    async_request(worksheet_command('alive'), xx, null);
1066}
1067
1068
1069function xx(status, response_text) {
1070    if (status == "failure") {
1071        server_down();
1072    } else {
1073        server_up();
1074    }
1075    setTimeout("server_ping_while_alive();", server_ping_time);
1076}
1077
1078function server_down() {
1079   X = get_element("ping");
1080   X.className = "pingdown";
1081}
1082
1083function server_up() {
1084   X = get_element("ping");
1085   X.className = "ping";
1086}
1087
1088///////////////////////////////////////////////////////////////////
1089//
1090// CELL functions -- for the individual cells
1091//
1092///////////////////////////////////////////////////////////////////
1093
1094function get_cell(id) {
1095    return get_element('cell_input_'+ id);
1096}
1097
1098function cell_blur(id) {
1099    var cell = get_cell(id);
1100    if(cell == null) return;
1101
1102    /* Disable coloring and change to div for now */
1103    cell.className="cell_input";
1104    cell_input_minimize_size(cell);
1105    return true;  /* disable for now */
1106
1107
1108    cell.className="hidden";
1109
1110   /* if(!in_slide_mode)
1111        current_cell = -1; */
1112
1113    var t = cell.value.replaceAll("<","&lt;");
1114
1115    var display_cell = get_element('cell_display_' + id)
1116    if (t.indexOf('%hide') == -1) {
1117        set_class('cell_display_' + id, 'cell_input')
1118        // We do this so <'s don't result in being parsed as
1119        // special html tags.
1120        display_cell.innerHTML = t;
1121        setTimeout("prettify_cell("+id+")",10);
1122    } else {
1123        set_class('cell_display_' + id, 'cell_input_hide')
1124        display_cell.innerHTML = '<font color="grey">' + t + '</font>'
1125    }
1126
1127    if(cell_has_changed)
1128        send_cell_input(id);
1129    return true;
1130}
1131
1132function send_cell_input(id) {
1133    cell = get_cell(id)
1134    if(cell == null) return;
1135
1136    async_request("/set_cell_input", generic_callback, "cell_id="+id+"&input="+cell.value);
1137}
1138
1139function prettify_cell(id) {
1140    var cell = get_cell(id);
1141    var display_cell = get_element('cell_display_' + id)
1142    if(cell == null || display_cell == null) return;
1143    var t = cell.value.replaceAll("<","&lt;");
1144    display_cell.innerHTML = prettyPrintOne(t+' '); //add a space to keep the cell from being too skinny
1145}
1146
1147function debug_focus() {
1148    in_debug_input = true;
1149    w = get_element('debug_window');
1150    if(w)
1151       w.className = 'debug_window_active';
1152}
1153
1154function debug_blur() {
1155    in_debug_input = false;
1156    w = get_element('debug_window');
1157    if(w)
1158        w.className = 'debug_window_inactive';
1159}
1160
1161function refocus_cell() {
1162    if(cell_to_focus < 0) return;
1163    var c = cell_to_focus;  //make a temp variable so we don't trigger another body focus event
1164    cell_to_focus = -1;     //and cause an infinite loop.
1165    cell_focus(c);
1166}
1167
1168//set and_delay to true if you want to refocus the browser in a keyevent
1169//which expects a tab -- Opera apparently resists canceling the tab key
1170//event -- so we can subvert that by breaking out of the call stack with
1171//a little timeout.  Safari also has this problem.
1172function cell_focus(id, bottom) {
1173
1174    var cell = get_cell(id);
1175    if (cell) {
1176        cell.focus();
1177        set_class('cell_display_' + id, 'hidden');
1178        cell.className="cell_input_active";
1179        cell_input_resize(cell);
1180        if (!bottom)
1181            move_cursor_to_top_of_cell(cell);
1182        current_cell = id;
1183        cell.focus();
1184    }
1185    current_cell = id;
1186    cell_has_changed = false;
1187
1188    return true;
1189}
1190
1191function move_cursor_to_top_of_cell(cell) {
1192    try{ //firefox, et al.
1193        cell.selectionStart = 0;
1194        cell.selectionEnd = 0;
1195    } catch(e) {}
1196}
1197
1198function focus_delay(id,bottom) {
1199    if(!bottom)
1200         setTimeout('cell_focus('+id+',false)', 10);
1201    else
1202         setTimeout('cell_focus('+id+',true)', 10);
1203}
1204
1205function number_of_rows(txt, ncols) {
1206    var r;
1207    r = txt.split('\n');
1208    var e, i, k, nrows;
1209    nrows = r.length;
1210    for(i=0; i < nrows; i++) {
1211        try {
1212            nrows += Math.floor(r[i].length/ncols);
1213        } catch(e) {
1214
1215        };
1216    }
1217    return (nrows);
1218}
1219
1220
1221function cell_input_resize(cell_input) {
1222    var rows = number_of_rows(cell_input.value, cell_input.cols);
1223    if (rows <= 1) {
1224      rows = 1;
1225    }
1226    if (browser_saf) {
1227       rows += 1;
1228    }
1229    try {
1230        cell_input.style.height = 0.5 + rows*line_height + 'em';
1231    } catch(e) {}
1232    try{
1233        cell_input.rows = rows;
1234    } catch(e) {}
1235
1236    if(slide_hidden) {
1237        cell_input.className="cell_input_active";
1238        slide_hidden = false;
1239    }
1240}
1241
1242function lstrip(s) {
1243    var n = s.length;
1244    var i = 0;
1245    while (i < n && (s[i] == ' ' || s[i] == '\n' || s[i] == '\t')) {
1246        i = i + 1;
1247    }
1248    return s.slice(i);
1249}
1250
1251function cell_input_minimize_size(cell_input) {
1252    var v = cell_input.value;
1253    var w = lstrip(v);
1254    var sl = w.slice(0,5);
1255    if (sl == '%hide') {
1256        cell_input.className = 'cell_input_hide';
1257        cell_input.style.height = '1em';
1258        return;
1259    }
1260   
1261    cell_input.className = 'cell_input';
1262    var rows = number_of_rows(v, cell_input.cols);
1263    if (rows < 1) {
1264      rows = 1;
1265    }
1266    if (rows >= 25) {
1267      rows = 25;
1268    }
1269    cell_input.rows = rows;
1270    if (rows == 1) {
1271       // hack because of bug in firefox with 1-row textarea
1272       cell_input.style.height = '1.5em';
1273    }
1274}
1275
1276function cell_input_minimize_all() {
1277    var v = cell_id_list;
1278    var n = v.length;
1279    var i;
1280    for(i=0; i<n; i++) {
1281        var cell=get_cell(v[i]);
1282        cell_input_minimize_size(cell);
1283    }
1284}
1285
1286function cell_delete_callback(status, response_text) {
1287    if (status == "failure") {
1288     // cell = get_element('cell_outer_' + id_to_delete);
1289     // var worksheet = get_element('worksheet_cell_list');
1290     // worksheet.removeChild(cell);
1291     // jump_to_cell(id_to_delete,-1);
1292     // cell_id_list = delete_from_array(cell_id_list, id_to_delete);
1293     // id_to_delete = -1;
1294        return;
1295    }
1296    var X = response_text.split(SEP);
1297    if (X[0] == 'ignore') {
1298        return; /* do not delete, for some reason */
1299    }
1300    var cell = get_element('cell_outer_' + X[1]);
1301    var worksheet = get_element('worksheet_cell_list');
1302    worksheet.removeChild(cell);
1303    jump_to_cell(X[1],-1);
1304    cell_id_list = delete_from_array(cell_id_list, X[1]);
1305}
1306
1307
1308function cell_delete(id) {
1309   async_request(worksheet_command('delete_cell'), cell_delete_callback, 'id='+id)
1310}
1311
1312
1313function key_listen_ie() {
1314    var e = get_event(null);
1315    if(current_cell != -1) {
1316        k = new key_event(e);
1317        if(key_shift(k) || key_ctrl(k) || key_alt(k))
1318          return true;
1319
1320        if(!cell_input_key_event(current_cell, e)) {
1321            void(0);
1322            e.returnValue=false;
1323            e.cancelBubble=true;
1324            return false;
1325        }
1326        return true;
1327    }
1328    if(in_debug_input) {
1329        if(!debug_input_key_event(e)) {
1330            void(0);
1331            e.returnValue=false;
1332            e.cancelBubble=true;
1333            return false;
1334        }
1335        return true;
1336    }
1337    return true;
1338}
1339
1340function debug_input_key_event(e) {
1341    e = new key_event(e);
1342    debug_input = get_element('debug_input');
1343
1344    if (key_down_arrow(e)) {
1345        var after = text_cursor_split(debug_input)[1];
1346        var i = after.indexOf('\n');
1347        if (i == -1 || after == '') {
1348            jump_to_cell(cell_id_list[0],0)
1349            return false;
1350        } else {
1351            return true;
1352        }
1353    }
1354    if (key_send_input(e)) {
1355        var out = ""
1356        try {
1357          out = eval(debug_input.value);
1358        } catch(err) {
1359          out = "Error: " + err.description;
1360        } finally {
1361          debug_append(out);
1362          return false;
1363        }
1364    }
1365}
1366
1367function cell_input_key_event(id, e) {
1368    cell_input = get_cell(id);
1369    e = new key_event(e);
1370    if (e==null) return;
1371
1372    if (key_delete_cell(e) && is_whitespace(cell_input.value)) {
1373        cell_delete(id);
1374        return false;
1375    }
1376
1377    if((introspect_id == id) && introspection_loaded && replacing) {
1378        if(!handle_replacement_controls(cell_input, e)) {
1379            if(browser_op) focus_delay(id,true);
1380            return false; //otherwise, keep going
1381        }
1382        halt_introspection();
1383    }
1384
1385    cell_input_resize(cell_input);
1386    cell_input.focus();
1387/*    if (browser_saf)   {
1388        cell_input.scrollIntoView();
1389    }
1390    */
1391   
1392    // Will need IE version... if possible.
1393    if (!in_slide_mode && key_up_arrow(e)) {
1394        var before = text_cursor_split(cell_input)[0];
1395        var i = before.indexOf('\n');
1396        if (i == -1 || before == '') {
1397            jump_to_cell(id,-1, true);
1398            return false;
1399        } else {
1400            return true;
1401        }
1402    } else if (!in_slide_mode && key_down_arrow(e)) {
1403        var after = text_cursor_split(cell_input)[1];
1404        var i = after.indexOf('\n');
1405        if (i == -1 || after == '') {
1406            jump_to_cell(id,1);
1407            return false;
1408        } else {
1409            return true;
1410        }
1411    } else if (key_send_input(e)) {
1412       // User pressed shift-enter (or whatever the submit key is)
1413       evaluate_cell(id, 0);
1414       return false;
1415    } else if (key_send_input_newcell(e)) {
1416       evaluate_cell(id, 1);
1417       return false;
1418    } else if (key_unindent(e)) { //unfortunately, shift-tab needs to get caught before not-shift tab
1419       unindent_cell(cell_input);
1420       return false;
1421    } else if (key_request_introspections(e) && cell_input.selectionStart == cell_input.selectionEnd) {
1422       // command introspection (tab completion, ?, ??)
1423       evaluate_cell(id, 2);
1424       focus_delay(id,true);
1425       return false;
1426    } else if (key_indent(e)) {
1427       indent_cell(cell_input);
1428       return false;
1429    } else if (key_interrupt(e)) {
1430       interrupt();
1431       return false;
1432    } else if (key_page_down(e)) {
1433       if(in_slide_mode) {
1434           slide_next();
1435       } else {
1436           jump_to_cell(id, 5);
1437       }
1438       return false;
1439    } else if (key_page_up(e)) {
1440       if(in_slide_mode) {
1441           slide_prev();
1442       } else {
1443           jump_to_cell(id, -5);
1444       }
1445       return false;
1446    } else if (key_request_history(e)) {
1447       history_window();
1448    } else if (key_request_log(e)) {
1449       text_log_window(worksheet_filename);
1450    }
1451
1452    cell_has_changed = true;
1453    return true;
1454}
1455
1456function id_of_cell_delta(id, delta) {
1457    if (cell_id_list.length == 0) {
1458        alert("bug -- no cells.");
1459        return;
1460    }
1461    var i = cell_id_list.indexOf(eval(id));
1462    var new_id;
1463    if (i == -1) {
1464        return(id); /* Better not to move. */
1465    } else {
1466        i = i + delta;
1467        if (i < 0) {
1468            i = 0;
1469        } else if (i >= cell_id_list.length) {
1470            i = cell_id_list.length - 1;
1471        }
1472        return(cell_id_list[i]);
1473    }
1474}
1475
1476function debug_clear() {
1477    output = get_element("debug_output");
1478    if(output == null) return;
1479    output.innerHTML = "";
1480}
1481
1482function debug_append(txt) {
1483    output = get_element("debug_output");
1484    if(output == null) return;
1485    output.innerHTML = txt + "\n" + output.innerHTML;
1486}
1487
1488function jump_to_cell(id, delta, bottom) {
1489     if(delta != 0)
1490        id = id_of_cell_delta(id, delta)
1491    if(in_slide_mode) {
1492        jump_to_slide(id);
1493    } else {
1494        cell_focus(id, bottom);
1495    }
1496}
1497
1498function escape0(input) {
1499    input = escape(input);
1500    input = input.replace(/\+/g,"%2B");
1501    return input;
1502}
1503
1504function text_cursor_split(input) {
1505    if(browser_ie) {
1506        //for explorer, we call the
1507        // document.selection.createRange().duplicate()
1508        //to generate a selection range object which does not effect the
1509        //original input box.
1510        //Then, we rewind the start point of the range until we encounter
1511        //a non-word character, or we've rewound past the beginning of
1512        //the textarea).
1513        var range = document.selection.createRange().duplicate();
1514        var i = range.text.length;
1515        while((input.value.match(range.text) || i==0)
1516               && range.text.length == i) {
1517            range.moveStart('character', -1);
1518            i = i + 1;
1519        }
1520        if(!input.value.match(range.text))
1521            range.moveStart('character', 1);
1522        b = range.text;
1523    } else {
1524        b = input.value.substr(0,input.selectionEnd);
1525    }
1526
1527    a = input.value.substr(b.length);
1528    return new Array(b,a);
1529}
1530
1531function indent_cell(input) {
1532    if(browser_ie) {
1533    } else {
1534        var start = 1+input.value.lastIndexOf("\n", input.selectionStart);
1535        var a = input.value.substring(0, start);
1536        var b = input.value.substring(start, input.selectionEnd);
1537        var c = input.value.substring(input.selectionEnd);
1538        var lines = b.split("\n");
1539        for(var i = 0; i < lines.length; i++)
1540            lines[i] = "    "+lines[i];
1541        b = lines.join("\n");
1542        input.value = a+b+c;
1543        input.selectionStart = a.length;
1544        input.selectionEnd = a.length + b.length;;
1545    }
1546}
1547
1548function unindent_cell(input) {
1549    if(browser_ie) {
1550    } else {
1551        var start = 1+input.value.lastIndexOf("\n", input.selectionStart);
1552        var a = input.value.substring(0, start);
1553        var b = input.value.substring(start, input.selectionEnd);
1554        var c = input.value.substring(input.selectionEnd);
1555        var lines = b.split("\n");
1556        for(var i = 0; i < lines.length; i++)
1557            lines[i] = unindent_pat.exec(lines[i])[1];  //square brackets pull the captured pattern
1558        b = lines.join("\n");
1559        input.value = a+b+c;
1560        input.selectionStart = a.length;
1561        input.selectionEnd = a.length + b.length;;
1562    }
1563}
1564
1565function worksheet_command(cmd) {
1566   return ('/home/' + worksheet_filename + '/' + cmd);
1567}   
1568
1569function evaluate_cell(id, action) {
1570    if(worksheet_locked) {
1571        alert("This worksheet is read only.  Please make a copy or contact the owner to change it.")
1572        return;
1573    }
1574
1575    active_cell_list = active_cell_list.concat([id]);
1576
1577    if(action == 2) { // Introspection
1578       evaluate_cell_introspection(id,null,null);
1579       return;
1580    }
1581
1582    cell_has_changed = false; //stop from sending the input twice.
1583    if(!in_slide_mode) {
1584       jump_to_cell(id,1);
1585    }
1586    cell_set_running(id);
1587   
1588    var cell_input = get_cell(id);
1589    var I = cell_input.value;
1590    var input = escape0(I);
1591    async_request(worksheet_command('eval'), evaluate_cell_callback,
1592            'newcell=' + action + '&id=' + id + '&input='+input);
1593}
1594
1595function evaluate_cell_introspection(id, before, after) {
1596    var cell_input = get_cell(id);
1597
1598    replacing = false;
1599    if(before == null) {
1600        var in_text = text_cursor_split(cell_input);
1601        before_cursor = before = in_text[0];
1602        after_cursor = after = in_text[1];
1603        before_replacing_word = before;
1604
1605        m = command_pat.exec(before);
1606        f = function_pat.exec(before);
1607        if(introspect_id != null)
1608            halt_introspection();
1609        introspect_id = id;
1610
1611        var last_char_before = before.charAt(before.length-1);
1612        if(last_char_before == "?") {
1613        } else if(m) {
1614            replacing = true;
1615            replacing_word = m[1];
1616            before_replacing_word = before.substring(0, before.length-replacing_word.length);
1617        } else if(f != null) { //we're in an open function paren -- give info on the function
1618            before = f[1] + "?";
1619        } else { //just a tab
1620            cell_has_changed = true;
1621            do_replacement(id, '    ',false);
1622            return;
1623        }
1624    } else {
1625        sub_introspecting = true;
1626    }
1627    if(!replacing && (browser_op || browser_saf))
1628        focus_delay(id);
1629
1630    update_introspection_text();
1631    var before_cursor_e = escape0(before);
1632    var after_cursor_e = escape0(after);
1633    cell_set_running(id);
1634    async_request(worksheet_command('introspect'), evaluate_cell_callback,
1635          'id=' + id + '&before_cursor='+before_cursor_e + '&after_cursor='+after_cursor_e);
1636}
1637
1638function evaluate_cell_callback(status, response_text) {
1639    /* update focus and possibly add a new cell to the end */
1640    if (status == "failure") {
1641       /* alert("Failure evaluating a cell."); */
1642        return;
1643    }
1644    var X = response_text.split(SEP);
1645    if (X[0] == '-1') {
1646        /* something went wrong -- i.e., the requested cell doesn't exist. */
1647        alert("You requested to evaluate a cell that, for some reason, the server is unaware of.");
1648        return;
1649    }
1650    if (X[1] == 'append_new_cell') {
1651        // add a new cell to the very end
1652        append_new_cell(X[0],X[2]);
1653    } else if (X[1] == 'insert_cell') {
1654        // insert a new cell after the one with id X[3]
1655        do_insert_new_cell_after(X[3], X[0], X[2]);
1656        jump_to_cell(X[0],0);
1657    }
1658    start_update_check();
1659}
1660
1661function cell_output_set_type(id, typ, do_async) {
1662    set_class('cell_div_output_' + id,    'cell_output_' + typ)
1663    set_class('cell_output_' + id,        'cell_output_' + typ)
1664    set_class('cell_output_nowrap_' + id, 'cell_output_nowrap_' + typ)
1665    set_class('cell_output_html_' + id,   'cell_output_html_' + typ)
1666
1667    /* Do async request back to the server */
1668    if(do_async != false)
1669        async_request(worksheet_command('set_cell_output_type'), generic_callback, 'id='+id+'&type=' + typ)
1670}
1671
1672function cycle_cell_output_type(id) {
1673    var cell_div = get_element('cell_div_output_' + id);
1674
1675    if (cell_div.className == 'cell_output_hidden' || cell_div.className=='cell_output_running') {
1676        cell_output_set_type(id, 'wrap');
1677        return;
1678    }
1679
1680    if (cell_div.className == 'cell_output_wrap') {
1681        cell_output_set_type(id, 'nowrap');
1682    } else {
1683        cell_output_set_type(id, 'hidden');
1684    }
1685}
1686
1687function cell_set_evaluated(id) {
1688    var D = get_element('cell_'+id);
1689    D.className = "cell_evaluated";
1690}
1691
1692function cell_set_not_evaluated(id) {
1693    var D = get_element('cell_'+id);
1694    D.className = "cell_not_evaluated";
1695    cell_set_done(id);
1696}
1697
1698function cell_set_running(id) {
1699    set_output_text(id, '', '', '', '', '');
1700    cell_output_set_type(id, 'wrap');
1701    var cell_div = get_element('cell_div_output_' + id);
1702    cell_div.className = 'cell_output_running';
1703    var cell_number = get_element('cell_number_' + id);
1704    cell_number.className = 'cell_number_running';
1705}
1706
1707function cell_set_done(id) {
1708    var cell_div = get_element('cell_div_output_' + id)
1709    cell_div.className = 'cell_output_wrap';
1710    var cell_number = get_element('cell_number_' + id);
1711    cell_number.className = 'cell_number';
1712}
1713
1714function check_for_cell_update() {
1715    if (active_cell_list.length == 0) {
1716        cancel_update_check();
1717        return;
1718    }
1719    var cell_id = active_cell_list[0];
1720    update_time = time_now();
1721    async_request(worksheet_command('cell_update'),
1722                    check_for_cell_update_callback,
1723                    'id=' + cell_id);
1724    try{
1725        title_spinner_i = (title_spinner_i+1)%title_spinner.length;
1726        document.title = title_spinner[title_spinner_i] + original_title;
1727    } catch(e){}
1728}
1729
1730function start_update_check() {
1731    if(updating) return;
1732    updating = true;
1733    check_for_cell_update();
1734    set_class('interrupt', 'interrupt')
1735}
1736
1737function cancel_update_check() {
1738    updating = false;
1739    clearTimeout(update_timeout);
1740    set_class('interrupt', 'interrupt_grey')
1741    document.title = original_title;
1742}
1743
1744function set_output_text(id, text, wrapped_text, output_html, status, introspect_html) {
1745    /* fill in output text got so far */
1746    var cell_output = get_element('cell_output_' + id);
1747    var cell_output_nowrap = get_element('cell_output_nowrap_' + id);
1748    var cell_output_html = get_element('cell_output_html_' + id);
1749
1750    cell_output.innerHTML = wrapped_text;
1751    cell_output_nowrap.innerHTML = text;
1752    cell_output_html.innerHTML = output_html;
1753
1754    if (status == 'd') {
1755         cell_set_done(id);
1756         // TODO: should make this not case sensitive!!  how to .lower() in javascript?
1757         if (text.indexOf('class="math"') != -1 || text.indexOf("class='math'") != -1) {
1758             try {
1759                 /* jsMath.Process(cell_output); */
1760                 /* jsMath.ProcessBeforeShowing(cell_output_nowrap); */
1761                 jsMath.ProcessBeforeShowing(cell_output);
1762                 /* jsMath.ProcessBeforeShowing(cell_output_nowrap); */
1763             } catch(e) {
1764                 cell_output.innerHTML = jsmath_font_msg + cell_output.innerHTML;
1765                 cell_output_nowrap.innerHTML = jsmath_font_msg + cell_output_nowrap.innerHTML;
1766             }
1767         }
1768    } else {
1769    }
1770
1771    if(introspect_id == id) {
1772        if (status == 'd') {
1773            introspection_loaded = true;
1774            introspection_text = introspect_html;
1775        }
1776        update_introspection_text();
1777    } else if(introspect_html != '') {
1778        cell_output.innerHTML = '';
1779        cell_output_nowrap.innerHTML = '';
1780        cell_output_html.innerHTML = introspect_html;
1781    }
1782}
1783
1784function set_input_text(id, text) {
1785    /* fill in input text */
1786    var cell_input = get_cell(id);
1787    cell_input.value = text;
1788    jump_to_cell(id,0)
1789
1790    try {
1791        pos = text.length - after_cursor.length;
1792        cell_input.selectionStart = pos;
1793        cell_input.selectionEnd = pos;
1794    }catch(e){}
1795    try{
1796        var range = document.selection.createRange();
1797        range.moveStart('character', -after_cursor.length);
1798        range.moveEnd('character', -after_cursor.length);
1799    }catch(e){}
1800
1801    return false;
1802}
1803
1804function set_object_list(objects) {
1805    var objlist = get_element('object_list');
1806    objlist.innerHTML = objects;
1807}
1808
1809function set_attached_files_list(objects) {
1810    var objlist = get_element('attached_list');
1811    objlist.innerHTML = objects;
1812}
1813
1814/*
1815Could this be useful?
1816    source_code.match( new RegExp( "<script\\s+?type=['\"]text/javascript['\"]>([^]+?)</script>", "i" ) )
1817*/
1818
1819function eval_script_tags(text) {
1820   var s = text.replaceAll('\n','');
1821   var i = s.indexOf('<script>');
1822   while (i != -1) {
1823       var j = s.indexOf('</script>');
1824       var code = s.slice(8+i,j);
1825       try {
1826           window.eval(code);
1827       } catch(e) {
1828           alert(e);
1829       }
1830       s = s.slice(j+1);
1831       i = s.indexOf('<script>');       
1832   }
1833}
1834
1835function check_for_cell_update_callback(status, response_text) {
1836    // make sure the update happens again in a few hundred milliseconds,
1837    // unless a problem occurs below.
1838    if (status != "success") {
1839        if(update_error_count>update_error_threshold) {
1840            cancel_update_check();
1841            halt_active_cells();
1842            var elapsed_time = update_error_count*update_error_delta/1000;
1843            var msg = "Error updating cell output after " + elapsed_time + "s";
1844            msg += "(canceling further update checks).";
1845            alert(msg);
1846            return;
1847        }
1848        cell_output_delta = update_error_delta;
1849        update_error_count++;
1850        continue_update_check();
1851        return;
1852    } else {
1853        update_error_count = 0;
1854        cell_output_delta = update_normal_delta;
1855    }
1856
1857    var i = response_text.indexOf(' ');
1858    var id = response_text.substring(1, i);
1859    var stat = response_text.substring(0,1)
1860
1861    if(response_text == 'empty') {
1862    /* TODO  -- hack -- we are sometimes getting something nothing back from
1863       twisted for some reason.  Ignoring it seems to work....
1864       */
1865       continue_update_check();
1866       return;
1867  /*        cancel_update_check();
1868        return; */
1869    }
1870   
1871    if(stat == 'e') {
1872        cancel_update_check();
1873        halt_active_cells();
1874        return;
1875    }
1876   
1877    /* compute output for a cell */
1878    var D = response_text.slice(i+1).split(SEP);
1879    var output_text = D[0] + ' ';
1880    var output_text_wrapped = D[1] + ' ';
1881    eval_script_tags(output_text)
1882    var output_html = D[2];
1883    var new_cell_input = D[3];
1884    var interrupted = D[4];
1885    var introspect_html = D[5];
1886    var j = id_of_cell_delta(id,1);
1887   
1888    set_output_text(id, output_text, output_text_wrapped,
1889                    output_html, stat, introspect_html);
1890
1891    if (stat == 'd') {
1892        active_cell_list = delete_from_array(active_cell_list, id);
1893
1894        if (interrupted == 'restart') {
1895            restart_sage();
1896        } else if (interrupted == 'false') {
1897            cell_set_evaluated(id);
1898        } else {
1899            halt_active_cells();
1900            cancel_update_check();
1901        }
1902       
1903        if(active_cell_list.length == 0)
1904            cancel_update_check();
1905
1906        if (new_cell_input != '') {
1907            set_input_text(id, new_cell_input);
1908        }
1909
1910        set_object_list(object_list);
1911        set_attached_files_list(attached_files_list);
1912    }
1913
1914    continue_update_check();
1915}
1916
1917function continue_update_check() {
1918    var time_elapsed = time_now() - update_time;
1919    if(time_elapsed < cell_output_delta) {
1920        update_timeout = setTimeout('check_for_cell_update()', cell_output_delta-time_elapsed);
1921    } else {
1922        check_for_cell_update();
1923    }
1924}
1925
1926///////////////////////////////////////////////////////////////////
1927// Slideshow Functions
1928///////////////////////////////////////////////////////////////////
1929
1930/* Switch into slide mode. */
1931function slide_mode() {
1932    in_slide_mode = true;
1933    set_class('left_pane', 'hidden');
1934    set_class('cell_controls', 'hidden');
1935    set_class('slide_controls', 'slide_control_commands');
1936    set_class('left_pane_bar', 'hidden');
1937
1938    for(i = 0; i < cell_id_list.length ; i++) {
1939        set_class('cell_outer_'+cell_id_list[i], 'hidden');
1940    }
1941    slide_show();
1942}
1943
1944function cell_mode() {
1945    in_slide_mode = false;
1946    set_class('left_pane', 'pane');
1947    set_class('cell_controls', 'control_commands');
1948    set_class('slide_controls', 'hidden');
1949    set_class('worksheet', 'worksheet');
1950    set_class('left_pane_bar', 'left_pane_bar');
1951
1952    for(i = 0; i < cell_id_list.length ; i++) {
1953        set_class('cell_outer_'+cell_id_list[i], 'cell_visible');
1954    }
1955}
1956
1957function slide_hide() {
1958    set_class('cell_outer_' + current_cell, 'hidden');
1959}
1960
1961function slide_show() {
1962    if(current_cell != -1) {
1963        set_class('cell_outer_' + current_cell, 'cell_visible');
1964    } else {
1965        if(cell_id_list.length>0)
1966            current_cell = cell_id_list[0];
1967        set_class('cell_outer_' + current_cell, 'cell_visible');
1968    }
1969    if(current_cell != -1) {
1970        input = get_cell(current_cell);
1971        if(input != null) {
1972            s = lstrip(input.value).slice(0,5)
1973            cell_focus(current_cell, false);
1974            if (s == '%hide') {
1975                slide_hidden = true;
1976                input.className = 'cell_input_hide';
1977                input.style.height = '1.5em';
1978            }
1979        }
1980    }
1981    update_slideshow_progress();
1982}
1983
1984function slide_first() {
1985    jump_to_slide(cell_id_list[0]);
1986}
1987
1988function slide_last() {
1989    jump_to_slide(cell_id_list[cell_id_list.length-1]);
1990}
1991
1992
1993function slide_next() {
1994    jump_to_slide(id_of_cell_delta(current_cell, 1));
1995}
1996
1997function slide_prev() {
1998    jump_to_slide(id_of_cell_delta(current_cell, -1));
1999}
2000
2001function jump_to_slide(id) {
2002    slide_hide();
2003    current_cell = id;
2004    slide_show();
2005}
2006
2007
2008function update_slideshow_progress() {
2009    var i = cell_id_list.indexOf(current_cell) + 1;
2010    var n = cell_id_list.length;
2011    var bar = get_element("slideshow_progress_bar")
2012    if(bar != null)
2013        bar.style.width = "" + 100*i/n + "%";
2014    text = get_element("slideshow_progress_text")
2015    if(text != null)
2016        text.innerHTML = i + " / " + n;
2017}
2018
2019
2020///////////////////////////////////////////////////////////////////
2021// Insert and move cells
2022///////////////////////////////////////////////////////////////////
2023
2024function insertAfter(parent, node, referenceNode) {
2025        parent.insertBefore(node, referenceNode.nextSibling);
2026}
2027
2028function insert_into_array(v, i, x) {
2029    /* Return a new array with x inserted into position i of v. */
2030    return (v.slice(0,i).concat([x]).concat(v.slice(i,v.length)));
2031}
2032
2033function delete_from_array(v, x) {
2034    /* Delete first occurrence of x in v.
2035       Returns resulting array (creates a new array!).
2036       No error if x is not in v.
2037    */
2038    var i;
2039    for (i=0; i<v.length; i++)
2040        if (v[i] == x) {
2041            return v.slice(0,i).concat(v.slice(i+1,v.length));
2042        }
2043    return v;
2044}
2045
2046function make_new_cell(id, html) {
2047    var new_cell = document.createElement("div");
2048    var in_cell = document.createElement("div");
2049    new_cell.appendChild(in_cell);
2050    new_cell.id = 'cell_outer_' + id;
2051    in_cell.id = 'cell_' + id;
2052    in_cell.innerHTML = html;
2053    return new_cell;
2054}
2055
2056function do_insert_new_cell_before(id, new_id, new_html) {
2057  /* Insert a new cell with the given new_id and new_html
2058     before the cell with given id. */
2059    var new_cell = make_new_cell(new_id, new_html);
2060    var cell = get_element('cell_outer_' + id);
2061    var worksheet = get_element('worksheet_cell_list');
2062    worksheet.insertBefore(new_cell, cell);
2063    var i = cell_id_list.indexOf(eval(id));
2064    cell_id_list = insert_into_array(cell_id_list, i, eval(new_id));
2065}
2066
2067function insert_new_cell_after(id) {
2068    async_request(worksheet_command('new_cell_after'), insert_new_cell_after_callback, 'id='+id);
2069}
2070
2071function insert_new_cell_after_callback(status, response_text) {
2072    if (status == "failure") {
2073        alert("Problem inserting new input cell after current input cell.\n" + response_text);
2074        return ;
2075    }
2076    if (response_text == "locked") {
2077        alert("Worksheet is locked.  Cannot insert cells.");
2078        return;
2079    }
2080    /* Insert a new cell _after_ a cell. */
2081    var X = response_text.split(SEP);
2082    var new_id = eval(X[0]);
2083    var new_html = X[1];
2084    var id = eval(X[2]);
2085    do_insert_new_cell_after(id, new_id, new_html);
2086    jump_to_cell(new_id,0);
2087}
2088
2089
2090function do_insert_new_cell_after(id, new_id, new_html) {
2091  /* Insert a new cell with the given new_id and new_html
2092     after the cell with given id.      */
2093
2094    i = id_of_cell_delta(id,1);
2095    if(i == id) {
2096        append_new_cell(new_id,new_html);
2097    } else {
2098        do_insert_new_cell_before(i, new_id, new_html);
2099    }
2100}
2101
2102
2103
2104
2105function insert_new_cell_before(id) {
2106    async_request(worksheet_command('new_cell_before'), insert_new_cell_before_callback, 'id='+id);
2107}
2108
2109function insert_new_cell_before_callback(status, response_text) {
2110    if (status == "failure") {
2111        alert("Problem inserting new input cell before current input cell.");
2112        return ;
2113    }
2114    if (response_text == "locked") {
2115        alert("Worksheet is locked.  Cannot insert cells.");
2116        return;
2117    }
2118    /* Insert a new cell _before_ a cell. */
2119    var X = response_text.split(SEP);
2120    var new_id = eval(X[0]);
2121    var new_html = X[1];
2122    var id = eval(X[2]);
2123    do_insert_new_cell_before(id, new_id, new_html);
2124    jump_to_cell(new_id,0);
2125}
2126
2127
2128function append_new_cell(id, html) {
2129    var new_cell = make_new_cell(id, html);
2130    var worksheet = get_element('worksheet_cell_list');
2131    worksheet.appendChild(new_cell);
2132    cell_id_list = cell_id_list.concat([eval(id)]);
2133    if(in_slide_mode) {
2134        set_class('cell_outer_'+id, 'hidden');
2135        update_slideshow_progress();
2136    }else {
2137        jump_to_cell(id, 0);
2138    }
2139}
2140
2141
2142///////////////////////////////////////////////////////////////////
2143//
2144// CONTROL functions
2145//
2146///////////////////////////////////////////////////////////////////
2147
2148function interrupt_callback(status, response_text) {
2149    if (response_text == "failed") {
2150       alert('Unable to immediately interrupt calculation.');
2151       return;
2152    } else if(status == "success") {
2153        halt_active_cells()
2154    }
2155    var link = get_element("interrupt");
2156    link.className = "interrupt";
2157    link.innerHTML = "Interrupt"
2158}
2159
2160function interrupt() {
2161/*    var link = get_element("interrupt");
2162    if (link.className == "interrupt_grey") {
2163        return;
2164    }
2165    link.className = "interrupt_in_progress";
2166    link.innerHTML = "Interrupt"
2167*/
2168    async_request(worksheet_command('interrupt'), interrupt_callback);
2169}
2170
2171
2172function evaluate_all() {
2173    /* Iterate through every input cell in the document, in order,
2174       and evaluate.
2175    */
2176    var v = cell_id_list;
2177    var n = v.length;
2178    var i;
2179    for(i=0; i<n; i++) {
2180        var cell_input = get_cell(v[i]);
2181        var I = cell_input.value;
2182        if (trim(I).length > 0) {
2183            evaluate_cell(v[i],0);
2184        }
2185    }
2186}
2187
2188function hide_all_callback() {
2189}
2190
2191function hide_all() {
2192    var v = cell_id_list;
2193    var n = v.length;
2194    var i;
2195    for(i=0; i<n; i++) {
2196        cell_output_set_type(v[i],'hidden', false);
2197    }
2198    async_request(worksheet_command('hide_all'), hide_all_callback);
2199}
2200
2201function show_all_callback() {
2202}
2203
2204function show_all() {
2205    var v = cell_id_list;
2206    var n = v.length;
2207    var i;
2208    for(i=0; i<n; i++) {
2209        cell_output_set_type(v[i],'wrap', false);
2210    }
2211    async_request(worksheet_command('show_all'), show_all_callback);
2212}
2213
2214function halt_active_cells() {
2215    for(i = 0; i < active_cell_list.length; i++)
2216        cell_set_not_evaluated(active_cell_list[i]);
2217    active_cell_list = []
2218}
2219
2220function restart_sage_callback(status, response_text) {
2221    for(i = 0; i < cell_id_list.length; i++)
2222        cell_set_not_evaluated(cell_id_list[i]);
2223    active_cell_list = []
2224    var link = get_element("restart_sage");
2225    link.className = "restart_sage";
2226    link.innerHTML = "Restart";
2227    sync_active_cell_list();
2228}
2229
2230function restart_sage() {
2231/*    var link = get_element("restart_sage");
2232    link.className = "restart_sage_in_progress";
2233    link.innerHTML = "Restart";
2234    */
2235    async_request(worksheet_command('restart_sage'), restart_sage_callback);
2236}
2237
2238function login(username,password) {
2239  document.cookie="username="+username;
2240  document.cookie="password="+password;
2241  window.location="/";
2242}
2243
2244///////////////////////////////////////////////////////////////////
2245//
2246// HELP Window
2247//
2248///////////////////////////////////////////////////////////////////
2249
2250function show_help_window() {
2251    var help = get_element("help_window");
2252    help.style.display = "block";
2253}
2254
2255function hide_help_window() {
2256    var help = get_element("help_window");
2257    help.style.display = "none";
2258
2259}
2260
2261////////////////////////////////////////////
2262//
2263// doc-browser related
2264//
2265////////////////////////////////////////////
2266
2267function show_doc_browser() {
2268    window.open("/doc_browser?/?index.html","","location=1,menubar=1,scrollbars=0,width=700,height=600,toolbar=1,resizable=1");
2269}
2270
2271////////////////////////////////////////////
2272
2273
2274////////////////////////////////////////////
2275//
2276// wiki-window related stuff
2277//
2278///////////////////////////////////////////
2279
2280function insert_new_doc_html_after(id,doc_htmlin) {
2281    async_request('/new_doc_html_after', insert_doc_html_callback, 'id=' + id + '&doc_htmlin=' + doc_htmlin);
2282}
2283
2284//-------------------------------------------------
2285/* This is a early hack attempt at putting html into the worksheet cell area from the wiki window.*/
2286//
2287function insert_doc_html_callback(status, response_text) {
2288    if (status == "failure") {
2289        alert("Problem inserting new doc html after current cell.");
2290        return ;
2291    }
2292    var X = response_text.split(SEP);
2293    var new_id = eval(X[0]);
2294    var new_html = X[1];
2295    var id = eval(X[2]);
2296    do_insert_doc_html(id, new_id, new_html);
2297    jump_to_cell(new_id,0);
2298}
2299
2300function do_insert_doc_html(id,new_id,html) {
2301    var new_doc_html = make_new_doc_html(id, html);
2302    var worksheet = get_element('worksheet_cell_list');
2303    worksheet.appendChild(new_doc_html);
2304    cell_id_list = cell_id_list.concat([new_id]);
2305}
2306
2307function make_new_doc_html(id, html) {
2308    var new_doc_html = document.createElement("div");
2309    new_doc_html.id = 'doc_html_'+id;
2310    new_doc_html.innerHTML = html;
2311    return new_doc_html;
2312}
2313
2314function upload_doc_html(lastid,doc_html) {
2315    insert_new_doc_html_after(lastid,doc_html);
2316}
2317//
2318//------------------------------------------------------
2319
2320function get_cell_list() {
2321    return cell_id_list;
2322}
2323
2324function hide_wiki_window() {
2325    var wiki = get_element("wiki_window");
2326    wiki.style.display = "none";
2327}
2328
2329function show_wiki_window(worksheet) {
2330    window.open (worksheet+"__wiki__.html","", "location=1,menubar=1,scrollbars=1,width=750,height=700,toolbar=1,resizable=1");
2331}
2332
2333function insert_cells_from_wiki(text,do_eval) {
2334    var eval_param = "&eval=0";
2335    if(do_eval)
2336        eval_param = "&eval=1";
2337    async_request("/insert_wiki_cells", insert_cells_from_wiki_callback,
2338                  "worksheet_id="+worksheet_id+"&text="+escape0(text)+eval_param);
2339}
2340
2341function get_worksheet_id(){
2342    return worksheet_id;
2343}
2344
2345function insert_cells_from_wiki_callback(status, response_text) {
2346    if(status == "success") {
2347        var X = response_text.split(SEP);
2348        var do_eval = eval(X[0]);
2349        var new_cell_id_list = eval(X[1]);
2350        var old_first_cell = cell_id_list[0];
2351        for(var i = 2; i < X.length; i++)
2352            do_insert_new_cell_before(old_first_cell, new_cell_id_list[i-2], X[i]);
2353        if(do_eval)
2354            evaluate_all();
2355    }
2356}
2357
2358
2359///////////////////////////////////////////////////////////////////
2360//
2361// LOG windows
2362//
2363///////////////////////////////////////////////////////////////////
2364
2365function history_window() {
2366    history = window.open ("/history",
2367      "", "menubar=1,scrollbars=1,width=800,height=600, toolbar=1,resizable=1");
2368}
2369
2370
2371function doctest_window(worksheet) {
2372    log = window.open ("/home/" + worksheet+"/plain","",
2373    "menubar=1,scrollbars=1,width=800,height=600,toolbar=1, resizable=1");
2374}
2375
2376
2377function print_worksheet(worksheet) {
2378    log = window.open (worksheet_command("print"),"",
2379      "menubar=1,scrollbars=1,width=800,height=600,toolbar=1,  resizable=1");
2380}
2381
2382function help(worksheet) {
2383    log = window.open ("/help","",
2384    "menubar=1,location=1,scrollbars=1,width=800,height=650,toolbar=1,  resizable=1");
2385}
2386
2387
2388//////////////////////////////////
2389// HELP
2390/////////////////////////////////
2391
2392function show_help_window(worksheet) {
2393    help = window.open ("/help","",
2394    "menubar=1,scrollbars=1,width=800,height=600,resizable=1, toolbar=1");
2395}
2396
2397
2398/********************* js math ***************************/
2399
2400
2401function jsmath_init() {
2402    try {
2403    jsMath.Process();
2404    /*   jsMath.ProcessBeforeShowing(); */
2405    } catch(e) {
2406/*        font_warning(); */
2407    }
2408
2409}
2410
2411function font_warning() { /* alert(jsmath_font_msg); */
2412}
2413
2414"""
2415
2416    s = s.replace('SAGE_URL',SAGE_URL)
2417
2418    s += r"""
2419///////////////////////////////////////////////////////////////////
2420//
2421// KeyCodes (auto-generated from config.py and user's sage config
2422//
2423///////////////////////////////////////////////////////////////////
2424
2425//this one isn't auto-generated.  its only purpose is to make //onKeyPress stuff simpler for text inputs and the whatlike.
2426function is_submit(e) {
2427  e = new key_event(e);
2428  return key_generic_submit(e);
2429}
2430
2431%s
2432"""%keyhandler.all_tests()
2433    s += keyboards.get_keyboard('')
2434    return s
2435
2436
2437
2438
2439class JSKeyHandler:
2440    """This class is used to make javascript functions to check for specific keyevents."""
2441
2442    def __init__(self):
2443        self.key_codes = {};
2444
2445    def set(self, name, key='', alt=False, ctrl=False, shift=False):
2446        """Add a named keycode to the handler.  When built by \code{all_tests()}, it can be called in javascript by
2447\code{key_<key_name>(event_object)}.  The function returns true if the keycode numbered by the \code{key} parameter
2448was pressed with the appropriate modifier keys, false otherwise."""
2449        self.key_codes.setdefault(name,[])
2450        self.key_codes[name] = [JSKeyCode(key, alt, ctrl, shift)]
2451
2452    def add(self, name, key='', alt=False, ctrl=False, shift=False):
2453        """Similar to \code{set_key(...)}, but this instead checks if there is an existing keycode by the specified
2454name, and associates the specified key combination to that name in addition.  This way, if different browsers don't
2455catch one keycode, multiple keycodes can be assigned to the same test."""
2456        try: self.key_codes[name]
2457        except KeyError: self.key_codes.setdefault(name,[])
2458        self.key_codes[name].append(JSKeyCode(key,alt,ctrl,shift))
2459
2460    def all_tests(self):
2461        """Builds all tests currently in the handler.  Returns a string of javascript code which defines all
2462functions."""
2463        tests = ''
2464        for name, keys in self.key_codes.items():
2465            tests += """ function key_%s(e) {
2466  return %s;
2467}"""%(name, "\n || ".join([k.js_test() for k in keys]))
2468
2469        return tests;
2470
2471
2472class JSKeyCode:
2473    def __init__(self, key, alt, ctrl, shift):
2474        global key_codes
2475        self.key = key
2476        self.alt = alt
2477        self.ctrl = ctrl
2478        self.shift = shift
2479
2480    def js_test(self):
2481        t = "(((e.k == %s) || (e.m == %s))"%(self.key, self.key)
2482        if self.alt:
2483            t += " && e.a"
2484        if self.ctrl:
2485            t += " && e.c"
2486        if self.shift:
2487            t += " && e.s"
2488        t+= ")"
2489        return t
2490
2491
2492keyhandler = JSKeyHandler()
2493
2494
2495
Note: See TracBrowser for help on using the repository browser.