root/ui/web/htdocs/graph_panel.inc

Revision d68066045f9337867d309cae485e1e618be9f41d, 21.6 kB (checked in by Umar Farooq <umar@omniti.com>, 5 years ago)

new streaming controls for modal graph viewing, aside from future styling, such as icons for play/pause/stop buttons, closes #146

  • Property mode set to 100644
Line 
1 <script type="text/javascript">
2 <!--
3 var maingraph;
4 var current_graph_id = '';
5 var debug_graph_edit = false;
6 var graphinfo= {};
7 var displayinfo = { start : 14*86400, cnt: '', end: '' };
8 var color_pool = Array('#33aa33','#4a00dc','#caac00','#3377cc','#00acfa','#cc3333')
9 var color_sel = 0;
10 var locked = true;
11
12 function graph_locked(warning) {               
13   if(locked) {
14     modal_warning("Graph Locked!", warning);
15     return locked;
16   }
17   return locked;
18 }
19
20 function lock_forms() {
21         $("input[@name='math1']").attr("disabled", "true");
22         $("input[@name='math2']").attr("disabled", "true");
23         $("input[@name='view']").attr("disabled", "true");
24         $("input[@class='graphType']").attr("disabled", "true");
25         $("select[@name='derive']").attr("disabled", "true");
26         $("select[@class='av_stacks']").attr("disabled", "true");
27
28         $("h2#graphTitle").unbind();
29         $(".datatitle").unbind();
30 }
31
32 function unlock_forms() {
33
34         $("input[@name='math1']").removeAttr("disabled");
35         $("input[@name='math2']").removeAttr("disabled");
36         $("input[@name='view']").removeAttr("disabled");
37 //        $("input[@class='graphType']").removeAttr("disabled");
38         $("select[@name='derive']").removeAttr("disabled");
39         $("select[@class='av_stacks']").removeAttr("disabled");
40
41         $("h2#graphTitle").editable(function(value, settings) {
42            graphinfo.title = value;
43            update_current_graph(false);
44            return(value);
45            }, { });
46
47         $(".datatitle").editable(function(value, settings) {     
48            graphinfo.datapoints[$(".datatitle").index(this)].name = value;
49            update_current_graph(true);
50            return(value);
51         }, { });
52 }
53
54 function update_graph_permalink(id, start, end, gran){
55  $("#gpermalink a").attr("href", "drawing_board.php?otype=graph&id="+id+"&start="+start+"&end="+end+"&gran="+gran);
56 }
57
58 function stop_streaming_graph (){
59          streaming = false;
60          $('#streambox').html('');
61          $(".stream-log").attr("style", "display:none;");
62          $("#graph_datetool").css("display", "");
63          $("#play_pause").html('PLAY');
64 }
65 //overlay mode: stackadd=false: we just add new stacks for any the incoming graph has
66 //stack mode: stackadd=trule, push new stacks onto matching stack indexes, else append
67 function aggregate_graph(id, stackadd) {
68   if(!graph_locked("Click 'Edit Graph' to unlock.")){
69          if(id) $.getJSON("json/graph/info/" + id, function(g) {
70
71                 var max_old = (graphinfo.datapoints.length) ?  graphinfo.datapoints.length : 0;
72
73                 for(var i=0; i<g.datapoints.length; i++) {
74                         graphinfo.datapoints.push(g.datapoints[i]);
75                 }       
76
77                 //we must use indexes for datapoints that are updated
78                 for(var i=0; i<g.stacks.length; i++) {
79                   for(var j=0; j<g.stacks[i].length; j++) {
80                     g.stacks[i][j] += max_old;
81                   }
82                 }
83
84                 if(stackadd) {
85                   for(var i=0; i<g.stacks.length; i++){
86                           if(graphinfo.stacks[i]) graphinfo.stacks[i] = graphinfo.stacks[i].concat(g.stacks[i]);
87                           else graphinfo.stacks.push(g.stacks[i]);
88                   }                       
89                 }       
90                 else {
91                    for(var i=0; i<g.stacks.length; i++){
92                            graphinfo.stacks.push(g.stacks[i]);
93                    }
94                 }       
95                 update_current_graph(true);
96                 refresh_graph_from_json(graphinfo);
97               });
98   }
99 }
100
101 function set_current_graph_id(id) {
102   streaming = false; //precautionary
103   $(".rememberGraph").fadeOut('fast');
104   if(id!='')  {
105     graphinfo.saved = true;
106     locked = true;
107     lock_forms();
108     $(".editGraph").html('Edit Graph').fadeIn('slow');   
109   }
110   else {       
111     locked = false;
112     unlock_forms();   
113     $(".editGraph").html('Editing!').fadeIn('slow');
114     stop_streaming_graph();
115   }
116   current_graph_id = id;
117   update_graph_permalink(current_graph_id, "", "", "");
118   fetch_graph_info(current_graph_id);
119 }
120 var recurse = 0;
121 function update_current_graph(redraw, f) {
122   if(recurse > 0) return;
123   stop_streaming_graph();
124   if(graphinfo.id) update_graph_permalink(current_graph_id, "", "", "");
125   var str = JSON.stringify(graphinfo);
126   if(debug_graph_edit) $("#payload").html(str);
127   $.post("json/graph/store",
128          {'json':str},
129          function(d) {
130            recurse++;
131            graphinfo.id = current_graph_id = d.id;
132            
133            if(d.error) $("#gtool-error").html(d.error).fadeIn('fast');
134            else $("#gtool-error").fadeOut('fast');     
135            if(graphinfo.id && graphinfo.saved != true &&
136               $(".rememberGraph:visible").length == 0) {
137               graphinfo.saved=false;
138              $(".rememberGraph").html('"Remember" this graph.').fadeIn('slow');
139              $(".rememberGraph").click(function() {
140                graphinfo.saved = true;
141                update_current_graph(false, function(r) {
142                  if(r.error) {
143                    graphinfo.saved = false;
144                     $("#gtool-error").html(r.error).fadeIn('fast');
145                  }
146                  else $(".rememberGraph").html('Remebered').fadeOut('slow');
147                });
148              });
149            }
150            if(redraw && maingraph) maingraph.ReconGraphRefresh({graphid: graphinfo.id, type: graphinfo.type});
151            if(f) f(d);
152            recurse--;
153          }, 'json');
154 }
155
156 function graph_add_datapoint(d) {
157   if(!graph_locked("Click 'Edit Graph' to unlock.")){
158     if(d.axis == undefined) { d.axis = 'l'; }
159     if(d.name == undefined) { d.name = d.target + '`' + d.metric_name; }
160     graphinfo.datapoints.push(d);
161     gtool_add_datapoint(d);
162     update_current_graph(true);
163   }
164 }
165 function gtool_add_guide() {
166   var d = {};
167   d.metric_type = 'guide';
168   graphinfo.datapoints.push(d);
169   gtool_add_datapoint(d);
170   update_current_graph(true);
171 }
172
173 function gtool_add_datapoint(d) {
174   var o;
175   if(d.metric_type == 'guide') {
176     o = $("#guideeditor").clone();
177   }
178   else {
179     o = $("#datapointeditor").clone();
180   }
181   recurse++;
182  
183   if(d.color == null) {
184     d.color = color_pool[color_sel];
185     color_sel = (color_sel + 1) % color_pool.length;
186   }
187   o.find('.colorPickerHolder').ColorPicker({
188     flat: true,
189     color: '#ffffff',
190     onSubmit: (function(dpo,dp) {
191       var cs = dpo.find('.colorSelector');
192       return function(hsb, hex, rgb) {
193         dp.color = '#' + hex;
194         cs.css('border', '1px solid ' + dp.color);
195         cs.find('div').css('backgroundColor', dp.color)
196                       .css('opacity', 0.2);
197         cs.parent().find('.colorPickerHolder').stop().animate({height:0},500);
198         update_current_graph(true);
199       }
200     })(o,d)
201   });
202   o.find('.colorPickerHolder > div').css('position', 'absolute');
203   o.find('.colorSelector').css('border', '1px solid ' + d.color);
204   o.find('.colorSelector div').css('backgroundColor', d.color)
205                               .css('opacity',
206                                    (d.opacity == null) ? 0.2 : d.opacity);
207   o.find('.colorSelector').bind('click', function() {
208     if(!locked){
209         var activecp = $(this).parent().find('.colorPickerHolder');
210         var h = activecp.height();
211         $("#gtool #dataPoints .colorPickerHolder").filter(function(index) {
212              return ($(this) != activecp);
213         }).stop().animate({height:0}, 0);;
214         $(this).parent().find('.colorPickerHolder').stop().animate({height: h != 0 ? 0 : 173}, 500);
215     }
216   });
217
218   o.find(d.axis == "l" ? ".axisl" : ".axisr").addClass("axison");
219   o.find("span.axis").click(function(){
220     if(!locked) {
221        $(this).siblings().removeClass("axison");
222        $(this).addClass("axison");
223        d.axis = $(this).hasClass("axisl") ? "l" : "r";
224        update_current_graph(true);
225     }
226   });
227
228   if(!d.hidden){o.find('input[@name="view"]').attr("checked","checked");}
229   o.find('input[@name="view"]').change(function(){
230     d.hidden = !($(this).attr("checked"));
231     update_current_graph(true);
232   }).change();
233
234   o.find('select[@id="datastack"]').change(function(){
235       var si = find_in_stacks($(".av_stacks").index(this));
236        if(si != -1) graphinfo.stacks[si.i].splice(si.j, 1);
237
238        if(graphinfo.stacks[ $(this).val() ]) {
239          graphinfo.stacks[ $(this).val()].push($(".av_stacks").index(this));
240          graphinfo.stacks[ $(this).val()].sort(function(a,b){return a - b});
241        }
242        update_current_graph(true);
243
244       $(".graphStacks").empty();
245
246       for(var i=0; i<graphinfo.stacks.length; i++) {
247         $(".graphStacks").append('<br>Stack '+i+': ');
248         for(var j=0; j<graphinfo.stacks[i].length; j++){
249             $(".graphStacks").append(graphinfo.datapoints[graphinfo.stacks[i][j]].name + ",");
250         }
251       }
252     }).change();
253
254
255   o.find('.deletedatapoint').click(function(){
256     if(!locked){
257        for(var i=0; i<graphinfo.datapoints.length; i++) {
258          if(graphinfo.datapoints[i] == d) {
259            graphinfo.datapoints.splice(i,1);
260            var si = find_in_stacks(i);
261            if(si != -1) graphinfo.stacks[si.i].splice(si.j, 1);
262
263            //we must decrement the datapoint indexes in the stack array as well
264            for(var k=0; k<graphinfo.stacks.length; k++) {
265              for(var j=0; j<graphinfo.stacks[k].length; j++) {
266                if(graphinfo.stacks[k][j]>i) graphinfo.stacks[k][j]-=1;
267              }
268            }       
269
270            break;
271          }
272        }         
273           update_current_graph(true);
274           refresh_graph_from_json(graphinfo);
275        }
276     return false;
277   });
278
279   if(d.metric_type == 'text') {
280     o.find('tr.mathbox').remove();
281     o.find('select[@name="derive"]').val('false');
282   }
283   else {
284     if(d.derive){
285       o.find('select[@name="derive"]').val(d.derive);
286     }
287     o.find('select[@name="derive"]').change(function(){
288       d.derive = $(this).find(":selected").val();
289       update_current_graph(true);
290     }).change();
291
292     o.find('input[@name="math1"]').val(d.math1);
293     o.find('input[@name="math1"]').change(function(){
294       d.math1 = $(this).val();
295       update_current_graph(true);
296     });
297     o.find('input[@name="math2"]').val(d.math2);
298     o.find('input[@name="math2"]').change(function(){
299       d.math2 = $(this).val();
300       update_current_graph(true);
301     });
302   }
303
304   d.mouse_title = ( (d.target!=undefined) ? d.target+"`": '') +
305                         ( (d.module!=undefined) ? d.module+"`": '') +
306                         ( (d.orig_name!=undefined)? d.orig_name+"`": '') +
307                         ( (d.metric_name!=undefined)? "`"+d.metric_name: '');
308
309   o.find(".datatitle").html(d.name);
310   o.find(".datatitle").attr("title", d.mouse_title);
311
312   if (!locked) {
313       o.find(".datatitle").editable(function(value, settings) {
314       graphinfo.datapoints[$(".datatitle").index(this)].name = value;
315       update_current_graph(true);
316       return(value);
317     }, { });
318  }
319
320   recurse--;
321   $("#gtool #dataPoints").append(o.children());
322 }
323
324 function find_in_stacks(dnum) {
325   for(var i=0; i<graphinfo.stacks.length; i++) {
326       for(var j=0; j<graphinfo.stacks[i].length; j++) {
327          if(graphinfo.stacks[i][j] == dnum) {           
328             var r = { i: i, j: j}; return r;
329          }       
330       }
331   }
332   return -1;
333 }
334 //this function works on the maingraph dom element, which is used througout in graph editing
335 function refresh_graph_from_json(j) {
336   graphinfo = j;
337   if(graphinfo.stacks == undefined) graphinfo.stacks = Array();
338   if(graphinfo.datapoints == undefined) graphinfo.datapoints = Array();
339   $("h2#graphTitle").html(graphinfo.title ? graphinfo.title : 'Graph Title (click to edit)');
340
341   $("input[@name='graphtype']:checked").removeAttr("checked");
342   $("input[@name='graphtype']")
343     .filter(function(i){return $(this).val() == graphinfo.type;})
344     .attr("checked","checked");
345   $("#dataPoints").empty();
346
347   for(var i=0; i<graphinfo.datapoints.length; i++) {
348     gtool_add_datapoint(graphinfo.datapoints[i]);
349   }
350
351   $(".graphStacks").empty();
352   $('.av_stacks').find('option').remove().end().append('<option value="-1">select </option>')
353
354   for(var i=0; i<graphinfo.stacks.length; i++) {
355     $(".graphStacks").append('<br>Stack '+i+': ');     
356     $('.av_stacks').append('<option value="'+i+'">Stack '+i+'</option>');
357
358     for(var j=0; j<graphinfo.stacks[i].length; j++){
359         $(".graphStacks").append(graphinfo.datapoints[graphinfo.stacks[i][j]].name + ",");
360     }
361   }
362
363   $('select[@id="datastack"]').each( function()  {   
364     var si = find_in_stacks($(".av_stacks").index(this));
365     if(si != -1) $(this).val(si.i);
366   });
367
368   if(maingraph) {
369     if(graphinfo.id)
370       maingraph.ReconGraphRefresh({graphid: graphinfo.id, type: graphinfo.type, stacks: graphinfo.stacks});
371     else
372       maingraph.ReconGraphReset();
373   }
374 }
375 function fetch_graph_info(id) {
376   graphinfo.id = id;
377   if(id) $.getJSON("json/graph/info/" + id, refresh_graph_from_json);
378   else refresh_graph_from_json({});
379 }
380 -->
381 </script>
382 <!--<p><a href="">username</a> / <a href="#">worksheet</a></p>-->
383 <p/>
384 <span class="rememberGraph"></span>
385 <span class="blankGraph">New Blank</span>
386 <span class="stream_controls">
387 <span id="play_pause">PLAY</span>
388 <span id="stopstream">STOP</span>
389 </span>
390 <span class="editGraph">Edit Graph</span>
391 <span class="permalink" id="gpermalink"><a href="">Link</a></span>
392
393 <h2 id="graphTitle"></h2>
394 <!-- date range box -->
395 <script type="text/javascript">
396 $(document).ready(function(){
397
398         lock_forms();
399
400         var time_windows = { '2d' : 86400*2,
401                               '1w' : 86400*7,
402                               '2w' : 86400*14,
403                               '4w' : 86400*28,
404                               '1y' : 86400*365,
405                             };
406         var state = false;
407
408         //only allow graph editing if not streaming
409         $(".editGraph").click(function() {     
410             if(!streaming){
411                 if(locked){
412                     locked = false;                 
413                     unlock_forms();
414                     $(".editGraph").html('Editing!').fadeIn('slow');
415                 }
416                 else if(!locked){
417                     locked = true;     
418                     lock_forms();
419                     $(".editGraph").html('Edit Graph').fadeIn('slow');
420                 }
421             }
422         });
423
424         $(".editStacks").click(function() {
425                if(!graph_locked("Unlock by clicking 'Edit Graph'.") && graphinfo.id) {                         
426                  graphinfo.stacks.push([]);
427                  var ns = graphinfo.stacks.length - 1;
428                  $(".graphStacks").append('<p>Stack '+ns+'</p>');
429                  $('.av_stacks').append('<option value="'+ns+'">Stack '+ns+'</option>');
430                  update_current_graph(true);
431                }
432        });                                             
433        
434        //only allow stream toggling if the graph is locked and saved
435        $("#play_pause").click(function() {
436          if(locked && graphinfo.saved) {
437           if($(this).html() == 'PLAY') {
438             $(this).html('PAUSE');
439             //if we are playing for the frist time
440             if(!streaming) {
441               $('#graph_datetool').css("display", "none");
442               $(".stream-log").removeAttr("style").html("stream log_");
443             }
444             //setup/restart the plotting
445             stream_data(graphinfo.id, maingraph, $('#streambox'));
446           }
447           else if($(this).html() == 'PAUSE') {
448              $(this).html('PLAY');
449              //this is where we pause for a bit
450              maingraph.stopTime();
451           }
452         }
453        });
454
455        $("#stopstream").click(function() {
456         stop_streaming_graph();
457         update_current_graph(true);
458        });
459
460         $("#graph_datetool .btn-slide").click(function(){
461                 $("#graph_widgetCalendar").stop().animate({
462                      height: state ? 0 :
463                        $('#graph_widgetCalendar div.datepicker').get(0).offsetHeight
464                   }, 500);
465                 state = !state;
466                 $(this).toggleClass("active");
467                 return false;
468         });
469         $("#graph_datetool .datechoice").click(function(){
470                 $("#graph_datetool .range a.btn-slide").html("YYYY/MM/DD - YYYY/MM/DD");
471                 $("#graph_widgetCalendar").slideUp("slow");
472                 $("#graph_datetool .datechoice").removeClass("selected");
473
474                 //start here will be in seconds
475                 displayinfo.start = time_windows[$(this).html()];
476                 displayinfo.end = '';
477                 $(this).addClass("selected");
478                 var cdate = new Date();
479                 update_graph_permalink(graphinfo.id, parseInt(cdate.getTime() - displayinfo.start*1000), "", "");
480                 maingraph.ReconGraphRefresh({graphid: graphinfo.id, start: time_windows[$(this).html()], end: '', type: graphinfo.type});
481                 return false;
482         });
483         $('#graph_widgetCalendar').DatePicker({
484                 flat: true,
485                 format: 'Y/m/d',
486                 date: [new Date(), new Date()],
487                 calendars: 3,
488                 mode: 'range',
489                 starts: 1,
490                 onChange: function(formated) {
491                         var dates;
492                         dates = formated[0].split('/');
493                         var start = new Date(dates[0], dates[1]-1, dates[2], 0, 0, 0);
494                         dates = formated[1].split('/');
495                         var end = new Date((new Date(dates[0], dates[1]-1, dates[2], 0, 0, 0)).getTime() + 86400000);
496                         displayinfo.start = start.toUTCString();
497                         displayinfo.end = end.toUTCString();
498                         update_graph_permalink(graphinfo.id, parseInt(start.getTime()), parseInt(end.getTime()), "");
499                         maingraph.ReconGraphRefresh({graphid: graphinfo.id, start: displayinfo.start, end: displayinfo.end, type: graphinfo.type});
500                         $('#graph_datetool .range a.btn-slide').get(0).innerHTML = formated.join(' - ');
501                 }
502         });
503         $(".graphType").change(function(){
504                 graphinfo.type = $(this).val();
505                 update_current_graph(true);
506         });
507         $("#gtool-error").click(function(){
508           $("#gtool-error").fadeOut("slow");
509         });
510         $(".addGuide").click(function() {
511           if(!locked){
512             gtool_add_guide();
513           }
514         });
515
516         $("span.blankGraph").click(function() {
517
518           stop_streaming_graph();
519
520           // current graph is saved, so just give a new one.
521           if(graphinfo.saved) set_current_graph_id('');
522
523           // otherwise make sure the user wants to move on, and if so, give a new graph and orphan the unsaved one.
524           else if(graphinfo.saved!=undefined && graphinfo.saved==false) {
525             confirm("I will forget the current graph.  Are you sure?", function() {
526               set_current_graph_id('');
527             });
528           }
529
530           return false;
531         });
532
533         set_current_graph_id('');
534         maingraph = $('<div></div>').attr("id", "maingraph").ReconGraph({graphid: graphinfo.id, width: 780, height: 400});
535         $("#maingraphHolder").append(maingraph);
536 });
537 </script>
538
539 <div id="graph_datetool">
540         <div class="zoom">
541                 <dl>
542                         <dt>Zoom:</dt>
543                         <dd><a href="#" class="first datechoice">2d</a></dd>
544                         <dd><a href="#" class="datechoice">1w</a></dd>
545                         <dd><a href="#" class="selected datechoice">2w</a></dd>
546                         <dd><a href="#" class="datechoice">4w</a></dd>
547                         <dd><a href="#" class="datechoice">1y</a></dd>
548                 </dl>
549         </div>
550         <div class="range">
551                 <dl>
552                         <dt>Date Range:</dt>
553                         <dd><a href="" class="btn-slide">YYYY/MM/DD - YYYY/MM/DD</a></dd>
554                 </dl>
555         </div>
556 <br/>
557         <div id="graph_widgetCalendar" class="widgetCalendar"></div>
558 </div>
559                        
560 <!-- confirm box -->
561 <div id="confirm" style="display:none">
562         <a href="#" title="Close" class="modalCloseX modalClose">x</a>
563         <div class="header"><span>Confirm</span></div>
564         <p class="message"></p>
565         <div class="buttons">
566                 <div class="no modalClose">No</div><div class="yes">Yes</div>
567         </div>
568 </div>
569
570 <div>
571         <div id="maingraphHolder">
572         </div>
573         <div class='stream-log' style='display:none'></div>
574         <form action="#" name="form4" id="form4" style="margin:1em 0;text-align:center;">
575         <fieldset>
576         <legend style="display:none;">View</legend>
577         <label for="std_view"><input class="graphType" type="radio" name="graphtype" id="std_view" value="standard"/> Standard View</label> &nbsp;&nbsp;&nbsp;
578         <label for="stacked_view"><input class="graphType" type="radio" name="graphtype" id="stacked_view" value="stacked" /> Stack Left Axis</label>
579         </fieldset>
580         </form>
581        
582 </div>
583 <span class="graphStacks"></span>
584 <div><span class="editStacks">Add Stack Set</span></div>
585 <div style="float:right"><span class="addGuide">Add Guide</span></div>
586 <br style="clear:right" />
587 <div class="error"><p class="error" id="gtool-error"></p></div>
588 <table id="gtool">
589         <tr>
590                 <th></th>
591                
592                 <th class="data">Data Points</th>
593                 <th>Stacking</th>
594                 <th>Color</th>
595                 <th>Derivative</th>
596                 <th>Axis</th>
597                 <th></th>
598                 <th></th>
599         </tr>
600         <tbody id="dataPoints">
601         </tbody>
602 </table>
603
604 <div style="display:none">
605 <form id="hiddeneditor">
606         <table>
607         <tbody id="guideeditor">
608         <tr>           
609                 <td><input name="view" type="checkbox" value="1" /></td>
610                 <td class="data datatitle"></td>
611                 <td><div class="colorPicker"><div class="colorSelector"><div style="background-color: #fff"></div></div><div class="colorPickerHolder"></div></div></td>
612                 <td></td>
613                 <td></td>
614                 <td><a href="#" class="deletedatapoint"><span>delete</span></a></td>
615                 <td class="math">math</td>
616         </tr>
617         <tr class="mathbox">
618                 <td colspan="7">
619                 <div>
620                         <label for="math">Display Math</label> <input name="math1" type="text" value="" style="width:380px;" />
621                 </div>
622                 <div>
623                         <label for="math">Percentile</label> <input name="math2" type="text" value="" style="width:380px;" />
624                 </div>
625                 </td>
626         </tr>
627         </tbody>
628         <tbody id="datapointeditor">
629         <tr>
630                 <td><input name="view" type="checkbox" value="1"/></td>
631                 <td class="data datatitle"></td>
632                 <td><select id="datastack" class="av_stacks"><option value='na'>select</option></select></td>
633                 <td><div class="colorPicker"><div class="colorSelector"><div style="background-color: #fff"></div></div><div class="colorPickerHolder"></div></div></td>
634                 <td><select name="derive"><option value="false">no</option><option value="derive">derive</option><option value="counter">counter</option></select></td>
635                 <td><span class="axis axisl"> L</span> <span class="axis axisr"> R</span></td>
636                 <td><a href="#" class="deletedatapoint"><span>delete</span></a></td>
637                 <td class="math">math</td>
638         </tr>
639         <tr class="mathbox">
640                 <td colspan="7">
641                 <div>
642                         <label for="math">Display Math</label> <input name="math1" type="text" value="" style="width:380px;" />
643                 </div>
644                 <div>   
645                         <label for="math">Source Math</label> <input name="math2" type="text" value="" style="width:380px;" />
646                 </div>
647                 </td>
648         </tr>
649         </tbody>
650         </table>
651 </form>
652 </div>
653
654 <div id="payload">
655 </div>
Note: See TracBrowser for help on using the browser.