![]() ![]() | ![]() |
Bug 3123: Change wording on tic mark entry.
1: # The LearningOnline Network with CAPA 2: # Dynamic plot 3: # 4: # $Id: lonplot.pm,v 1.98 2004/06/28 15:42:49 matthew Exp $ 5: # 6: # Copyright Michigan State University Board of Trustees 7: # 8: # This file is part of the LearningOnline Network with CAPA (LON-CAPA). 9: # 10: # LON-CAPA is free software; you can redistribute it and/or modify 11: # it under the terms of the GNU General Public License as published by 12: # the Free Software Foundation; either version 2 of the License, or 13: # (at your option) any later version. 14: # 15: # LON-CAPA is distributed in the hope that it will be useful, 16: # but WITHOUT ANY WARRANTY; without even the implied warranty of 17: # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18: # GNU General Public License for more details. 19: # 20: # You should have received a copy of the GNU General Public License 21: # along with LON-CAPA; if not, write to the Free Software 22: # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 23: # 24: # /home/httpd/html/adm/gpl.txt 25: # 26: # http://www.lon-capa.org/ 27: # 28: 29: package Apache::lonplot; 30: 31: use strict; 32: use warnings FATAL=>'all'; 33: no warnings 'uninitialized'; 34: use Apache::File; 35: use Apache::response; 36: use Apache::lonxml; 37: use Apache::edit; 38: 39: use vars qw/$weboutputformat $versionstring/; 40: 41: BEGIN { 42: &Apache::lonxml::register('Apache::lonplot',('gnuplot')); 43: # 44: # Determine the version of GNUPLOT 45: $weboutputformat = 'gif'; 46: $versionstring = `gnuplot --version`; 47: if ($versionstring =~ /^gnuplot 4/) { 48: $weboutputformat = 'png'; 49: } 50: } 51: 52: ## 53: ## Description of data structures: 54: ## 55: ## %plot %key %axis 56: ## -------------------------- 57: ## height title color 58: ## width box xmin 59: ## bgcolor pos xmax 60: ## fgcolor ymin 61: ## transparent ymax 62: ## grid 63: ## border 64: ## font 65: ## align 66: ## 67: ## @labels: $labels[$i] = \%label 68: ## %label: text, xpos, ypos, justify 69: ## 70: ## @curves: $curves[$i] = \%curve 71: ## %curve: name, linestyle, ( function | data ) 72: ## 73: ## $curves[$i]->{'data'} = [ [x1,x2,x3,x4], 74: ## [y1,y2,y3,y4] ] 75: ## 76: 77: ################################################################### 78: ## ## 79: ## Tests used in checking the validitity of input ## 80: ## ## 81: ################################################################### 82: 83: my $max_str_len = 50; # if a label, title, xlabel, or ylabel text 84: # is longer than this, it will be truncated. 85: 86: my %linestyles = 87: ( 88: lines => 2, # Maybe this will be used in the future 89: linespoints => 2, # to check on whether or not they have 90: dots => 2, # supplied enough <data></data> fields 91: points => 2, # to use the given line style. But for 92: steps => 2, # now there are more important things 93: fsteps => 2, # for me to deal with. 94: histeps => 2, 95: errorbars => 3, 96: xerrorbars => [3,4], 97: yerrorbars => [3,4], 98: xyerrorbars => [4,6], 99: boxes => 3, 100: vector => 4 101: ); 102: 103: my $int_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^\d+$/}; 104: my $real_test = 105: sub {$_[0]=~s/\s+//g;$_[0]=~/^[+-]?\d*\.?\d*([eE][+-]\d+)?$/}; 106: my $pos_real_test = 107: sub {$_[0]=~s/\s+//g;$_[0]=~/^[+]?\d*\.?\d*([eE][+-]\d+)?$/}; 108: my $color_test = sub {$_[0]=~s/\s+//g;$_[0]=~/^x[\da-fA-F]{6}$/}; 109: my $onoff_test = sub {$_[0]=~/^(on|off)$/}; 110: my $key_pos_test = sub {$_[0]=~/^(top|bottom|right|left|outside|below| )+$/}; 111: my $sml_test = sub {$_[0]=~/^(small|medium|large)$/}; 112: my $linestyle_test = sub {exists($linestyles{$_[0]})}; 113: my $words_test = sub {$_[0]=~s/\s+/ /g;$_[0]=~/^([\w~!\@\#\$\%^&\*\(\)-=_\+\[\]\{\}:\;\'<>,\.\/\?\\]+ ?)+$/}; 114: 115: ################################################################### 116: ## ## 117: ## Attribute metadata ## 118: ## ## 119: ################################################################### 120: my @gnuplot_edit_order = 121: qw/alttag bgcolor fgcolor height width font transparent grid samples 122: border align texwidth texfont plottype/; 123: 124: my %gnuplot_defaults = 125: ( 126: alttag => { 127: default => 'dynamically generated plot', 128: test => $words_test, 129: description => 'brief description of the plot', 130: edit_type => 'entry', 131: size => '40' 132: }, 133: height => { 134: default => 300, 135: test => $int_test, 136: description => 'height of image (pixels)', 137: edit_type => 'entry', 138: size => '10' 139: }, 140: width => { 141: default => 400, 142: test => $int_test, 143: description => 'width of image (pixels)', 144: edit_type => 'entry', 145: size => '10' 146: }, 147: bgcolor => { 148: default => 'xffffff', 149: test => $color_test, 150: description => 'background color of image (xffffff)', 151: edit_type => 'entry', 152: size => '10' 153: }, 154: fgcolor => { 155: default => 'x000000', 156: test => $color_test, 157: description => 'foreground color of image (x000000)', 158: edit_type => 'entry', 159: size => '10' 160: }, 161: transparent => { 162: default => 'off', 163: test => $onoff_test, 164: description => 'Transparent image', 165: edit_type => 'onoff' 166: }, 167: grid => { 168: default => 'on', 169: test => $onoff_test, 170: description => 'Display grid', 171: edit_type => 'onoff' 172: }, 173: border => { 174: default => 'on', 175: test => $onoff_test, 176: description => 'Draw border around plot', 177: edit_type => 'onoff' 178: }, 179: font => { 180: default => 'medium', 181: test => $sml_test, 182: description => 'Size of font to use', 183: edit_type => 'choice', 184: choices => ['small','medium','large'] 185: }, 186: samples => { 187: default => '100', 188: test => $int_test, 189: description => 'Number of samples for non-data plots', 190: edit_type => 'choice', 191: choices => ['100','200','500','1000','2000','5000'] 192: }, 193: align => { 194: default => 'center', 195: test => sub {$_[0]=~/^(left|right|center)$/}, 196: description => 'alignment for image in html', 197: edit_type => 'choice', 198: choices => ['left','right','center'] 199: }, 200: texwidth => { 201: default => '93', 202: test => $int_test, 203: description => 'Width of plot when printed (mm)', 204: edit_type => 'entry', 205: size => '5' 206: }, 207: texfont => { 208: default => '22', 209: test => $int_test, 210: description => 'Font size to use in TeX output (pts):', 211: edit_type => 'choice', 212: choices => [qw/8 10 12 14 16 18 20 22 24 26 28 30 32 34 36/], 213: }, 214: plottype => { 215: default => 'Cartesian', 216: test => sub {$_[0]=~/^(Polar|Cartesian)$/}, 217: description => 'Plot type:', 218: edit_type => 'choice', 219: choices => ['Cartesian','Polar'] 220: }, 221: ); 222: 223: my %key_defaults = 224: ( 225: title => { 226: default => '', 227: test => $words_test, 228: description => 'Title of key', 229: edit_type => 'entry', 230: size => '40' 231: }, 232: box => { 233: default => 'off', 234: test => $onoff_test, 235: description => 'Draw a box around the key?', 236: edit_type => 'onoff' 237: }, 238: pos => { 239: default => 'top right', 240: test => $key_pos_test, 241: description => 'position of the key on the plot', 242: edit_type => 'choice', 243: choices => ['top left','top right','bottom left','bottom right', 244: 'outside','below'] 245: } 246: ); 247: 248: my %label_defaults = 249: ( 250: xpos => { 251: default => 0, 252: test => $real_test, 253: description => 'x position of label (graph coordinates)', 254: edit_type => 'entry', 255: size => '10' 256: }, 257: ypos => { 258: default => 0, 259: test => $real_test, 260: description => 'y position of label (graph coordinates)', 261: edit_type => 'entry', 262: size => '10' 263: }, 264: justify => { 265: default => 'left', 266: test => sub {$_[0]=~/^(left|right|center)$/}, 267: description => 'justification of the label text on the plot', 268: edit_type => 'choice', 269: choices => ['left','right','center'] 270: } 271: ); 272: 273: my @tic_edit_order = ('location','mirror','start','increment','end', 274: 'minorfreq'); 275: my %tic_defaults = 276: ( 277: location => { 278: default => 'border', 279: test => sub {$_[0]=~/^(border|axis)$/}, 280: description => 'Location of major tic marks', 281: edit_type => 'choice', 282: choices => ['border','axis'] 283: }, 284: mirror => { 285: default => 'on', 286: test => $onoff_test, 287: description => 'mirror tics on opposite axis?', 288: edit_type => 'onoff' 289: }, 290: start => { 291: default => '-10.0', 292: test => $real_test, 293: description => 'Start major tics at', 294: edit_type => 'entry', 295: size => '10' 296: }, 297: increment => { 298: default => '1.0', 299: test => $real_test, 300: description => 'Place a major tic every', 301: edit_type => 'entry', 302: size => '10' 303: }, 304: end => { 305: default => ' 10.0', 306: test => $real_test, 307: description => 'Stop major tics at ', 308: edit_type => 'entry', 309: size => '10' 310: }, 311: minorfreq => { 312: default => '0', 313: test => $int_test, 314: description => 'Number of minor tics per major tic mark', 315: edit_type => 'entry', 316: size => '10' 317: }, 318: ); 319: 320: my @axis_edit_order = ('color','xmin','xmax','ymin','ymax'); 321: my %axis_defaults = 322: ( 323: color => { 324: default => 'x000000', 325: test => $color_test, 326: description => 'color of grid lines (x000000)', 327: edit_type => 'entry', 328: size => '10' 329: }, 330: xmin => { 331: default => '-10.0', 332: test => $real_test, 333: description => 'minimum x-value shown in plot', 334: edit_type => 'entry', 335: size => '10' 336: }, 337: xmax => { 338: default => ' 10.0', 339: test => $real_test, 340: description => 'maximum x-value shown in plot', 341: edit_type => 'entry', 342: size => '10' 343: }, 344: ymin => { 345: default => '-10.0', 346: test => $real_test, 347: description => 'minimum y-value shown in plot', 348: edit_type => 'entry', 349: size => '10' 350: }, 351: ymax => { 352: default => ' 10.0', 353: test => $real_test, 354: description => 'maximum y-value shown in plot', 355: edit_type => 'entry', 356: size => '10' 357: } 358: ); 359: 360: my @curve_edit_order = ('color','name','linestyle','pointtype','pointsize'); 361: 362: my %curve_defaults = 363: ( 364: color => { 365: default => 'x000000', 366: test => $color_test, 367: description => 'color of curve (x000000)', 368: edit_type => 'entry', 369: size => '10' 370: }, 371: name => { 372: default => '', 373: test => $words_test, 374: description => 'name of curve to appear in key', 375: edit_type => 'entry', 376: size => '20' 377: }, 378: linestyle => { 379: default => 'lines', 380: test => $linestyle_test, 381: description => 'Line style', 382: edit_type => 'choice', 383: choices => [keys(%linestyles)] 384: }, 385: # gnuplots term=gif driver does not handle linewidth :( 386: # linewidth => { 387: # default => 1, 388: # test => $int_test, 389: # description => 'Line width (may not apply to all line styles)', 390: # edit_type => 'choice', 391: # choices => [1,2,3,4,5,6,7,8,9,10] 392: # }, 393: pointsize => { 394: default => 1, 395: test => $pos_real_test, 396: description => 'point size (may not apply to all line styles)', 397: edit_type => 'entry', 398: size => '5' 399: }, 400: pointtype => { 401: default => 1, 402: test => $int_test, 403: description => 'point type (may not apply to all line styles)', 404: edit_type => 'choice', 405: choices => [0,1,2,3,4,5,6] 406: } 407: ); 408: 409: ################################################################### 410: ## ## 411: ## parsing and edit rendering ## 412: ## ## 413: ################################################################### 414: my (%plot,%key,%axis,$title,$xlabel,$ylabel,@labels,@curves,%xtics,%ytics); 415: 416: sub start_gnuplot { 417: undef(%plot); undef(%key); undef(%axis); 418: undef($title); undef($xlabel); undef($ylabel); 419: undef(@labels); undef(@curves); 420: undef(%xtics); undef(%ytics); 421: # 422: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 423: my $result=''; 424: &Apache::lonxml::register('Apache::lonplot', 425: ('title','xlabel','ylabel','key','axis','label','curve', 426: 'xtics','ytics')); 427: push (@Apache::lonxml::namespace,'lonplot'); 428: if ($target eq 'web' || $target eq 'tex') { 429: &get_attributes(\%plot,\%gnuplot_defaults,$parstack,$safeeval, 430: $tagstack->[-1]); 431: } elsif ($target eq 'edit') { 432: $result .= &Apache::edit::tag_start($target,$token,'GnuPlot'); 433: $result .= &edit_attributes($target,$token,\%gnuplot_defaults, 434: \@gnuplot_edit_order); 435: } elsif ($target eq 'modified') { 436: my $constructtag=&Apache::edit::get_new_args 437: ($token,$parstack,$safeeval,keys(%gnuplot_defaults)); 438: if ($constructtag) { 439: $result = &Apache::edit::rebuild_tag($token); 440: } 441: } 442: return $result; 443: } 444: 445: sub end_gnuplot { 446: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 447: pop @Apache::lonxml::namespace; 448: &Apache::lonxml::deregister('Apache::lonplot', 449: ('title','xlabel','ylabel','key','axis','label','curve')); 450: my $result = ''; 451: my $randnumber; 452: # need to call rand everytime start_script would evaluate, as the 453: # safe space rand number generator and the global rand generator 454: # are not separate 455: if ($target eq 'web' || $target eq 'tex' || $target eq 'grade' || 456: $target eq 'answer') { 457: $randnumber=int(rand(1000)); 458: } 459: if ($target eq 'web' || $target eq 'tex') { 460: &check_inputs(); # Make sure we have all the data we need 461: ## 462: ## Determine filename 463: my $tmpdir = '/home/httpd/perl/tmp/'; 464: my $filename = $ENV{'user.name'}.'_'.$ENV{'user.domain'}. 465: '_'.time.'_'.$$.$randnumber.'_plot'; 466: ## Write the plot description to the file 467: &write_gnuplot_file($tmpdir,$filename,$target); 468: $filename = &Apache::lonnet::escape($filename); 469: ## return image tag for the plot 470: if ($target eq 'web') { 471: $result .= <<"ENDIMAGE"; 472: <img src = "/cgi-bin/plot.gif?file=$filename.data&output=$weboutputformat" 473: width = "$plot{'width'}" 474: height = "$plot{'height'}" 475: align = "$plot{'align'}" 476: alt = "$plot{'alttag'}" /> 477: ENDIMAGE 478: } elsif ($target eq 'tex') { 479: #might be inside the safe space, register the URL for later 480: &Apache::lonxml::register_ssi("/cgi-bin/plot.gif?file=$filename.data&output=eps"); 481: $result = '\graphicspath{{/home/httpd/perl/tmp/}}\includegraphics[width='.$plot{'texwidth'}.' mm]{'.&Apache::lonnet::unescape($filename).'.eps}'; 482: } 483: } elsif ($target eq 'edit') { 484: $result.=&Apache::edit::tag_end($target,$token); 485: } 486: return $result; 487: } 488: 489: 490: ##--------------------------------------------------------------- xtics 491: sub start_xtics { 492: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 493: my $result=''; 494: if ($target eq 'web' || $target eq 'tex') { 495: &get_attributes(\%xtics,\%tic_defaults,$parstack,$safeeval, 496: $tagstack->[-1]); 497: } elsif ($target eq 'edit') { 498: $result .= &Apache::edit::tag_start($target,$token,'xtics'); 499: $result .= &edit_attributes($target,$token,\%tic_defaults, 500: \@tic_edit_order); 501: } elsif ($target eq 'modified') { 502: my $constructtag=&Apache::edit::get_new_args 503: ($token,$parstack,$safeeval,keys(%tic_defaults)); 504: if ($constructtag) { 505: $result = &Apache::edit::rebuild_tag($token); 506: } 507: } 508: return $result; 509: } 510: 511: sub end_xtics { 512: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 513: my $result = ''; 514: if ($target eq 'web' || $target eq 'tex') { 515: } elsif ($target eq 'edit') { 516: $result.=&Apache::edit::tag_end($target,$token); 517: } 518: return $result; 519: } 520: 521: ##--------------------------------------------------------------- ytics 522: sub start_ytics { 523: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 524: my $result=''; 525: if ($target eq 'web' || $target eq 'tex') { 526: &get_attributes(\%ytics,\%tic_defaults,$parstack,$safeeval, 527: $tagstack->[-1]); 528: } elsif ($target eq 'edit') { 529: $result .= &Apache::edit::tag_start($target,$token,'ytics'); 530: $result .= &edit_attributes($target,$token,\%tic_defaults, 531: \@tic_edit_order); 532: } elsif ($target eq 'modified') { 533: my $constructtag=&Apache::edit::get_new_args 534: ($token,$parstack,$safeeval,keys(%tic_defaults)); 535: if ($constructtag) { 536: $result = &Apache::edit::rebuild_tag($token); 537: } 538: } 539: return $result; 540: } 541: 542: sub end_ytics { 543: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 544: my $result = ''; 545: if ($target eq 'web' || $target eq 'tex') { 546: } elsif ($target eq 'edit') { 547: $result.=&Apache::edit::tag_end($target,$token); 548: } 549: return $result; 550: } 551: 552: 553: ##----------------------------------------------------------------- key 554: sub start_key { 555: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 556: my $result=''; 557: if ($target eq 'web' || $target eq 'tex') { 558: &get_attributes(\%key,\%key_defaults,$parstack,$safeeval, 559: $tagstack->[-1]); 560: } elsif ($target eq 'edit') { 561: $result .= &Apache::edit::tag_start($target,$token,'Plot Key'); 562: $result .= &edit_attributes($target,$token,\%key_defaults); 563: } elsif ($target eq 'modified') { 564: my $constructtag=&Apache::edit::get_new_args 565: ($token,$parstack,$safeeval,keys(%key_defaults)); 566: if ($constructtag) { 567: $result = &Apache::edit::rebuild_tag($token); 568: } 569: } 570: return $result; 571: } 572: 573: sub end_key { 574: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 575: my $result = ''; 576: if ($target eq 'web' || $target eq 'tex') { 577: } elsif ($target eq 'edit') { 578: $result.=&Apache::edit::tag_end($target,$token); 579: } 580: return $result; 581: } 582: 583: ##------------------------------------------------------------------- title 584: sub start_title { 585: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 586: my $result=''; 587: if ($target eq 'web' || $target eq 'tex') { 588: $title = &Apache::lonxml::get_all_text("/title",$parser); 589: $title=&Apache::run::evaluate($title,$safeeval,$$parstack[-1]); 590: $title =~ s/\n/ /g; 591: if (length($title) > $max_str_len) { 592: $title = substr($title,0,$max_str_len); 593: } 594: } elsif ($target eq 'edit') { 595: $result.=&Apache::edit::tag_start($target,$token,'Plot Title'); 596: my $text=&Apache::lonxml::get_all_text("/title",$parser); 597: $result.=&Apache::edit::end_row(). 598: &Apache::edit::start_spanning_row(). 599: &Apache::edit::editline('',$text,'',60); 600: } elsif ($target eq 'modified') { 601: $result.=&Apache::edit::rebuild_tag($token); 602: $result.=&Apache::edit::modifiedfield("/title",$parser); 603: } 604: return $result; 605: } 606: 607: sub end_title { 608: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 609: my $result = ''; 610: if ($target eq 'web' || $target eq 'tex') { 611: } elsif ($target eq 'edit') { 612: $result.=&Apache::edit::tag_end($target,$token); 613: } 614: return $result; 615: } 616: ##------------------------------------------------------------------- xlabel 617: sub start_xlabel { 618: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 619: my $result=''; 620: if ($target eq 'web' || $target eq 'tex') { 621: $xlabel = &Apache::lonxml::get_all_text("/xlabel",$parser); 622: $xlabel=&Apache::run::evaluate($xlabel,$safeeval,$$parstack[-1]); 623: $xlabel =~ s/\n/ /g; 624: if (length($xlabel) > $max_str_len) { 625: $xlabel = substr($xlabel,0,$max_str_len); 626: } 627: } elsif ($target eq 'edit') { 628: $result.=&Apache::edit::tag_start($target,$token,'Plot Xlabel'); 629: my $text=&Apache::lonxml::get_all_text("/xlabel",$parser); 630: $result.=&Apache::edit::end_row(). 631: &Apache::edit::start_spanning_row(). 632: &Apache::edit::editline('',$text,'',60); 633: } elsif ($target eq 'modified') { 634: $result.=&Apache::edit::rebuild_tag($token); 635: $result.=&Apache::edit::modifiedfield("/xlabel",$parser); 636: } 637: return $result; 638: } 639: 640: sub end_xlabel { 641: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 642: my $result = ''; 643: if ($target eq 'web' || $target eq 'tex') { 644: } elsif ($target eq 'edit') { 645: $result.=&Apache::edit::tag_end($target,$token); 646: } 647: return $result; 648: } 649: 650: ##------------------------------------------------------------------- ylabel 651: sub start_ylabel { 652: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 653: my $result=''; 654: if ($target eq 'web' || $target eq 'tex') { 655: $ylabel = &Apache::lonxml::get_all_text("/ylabel",$parser); 656: $ylabel = &Apache::run::evaluate($ylabel,$safeeval,$$parstack[-1]); 657: $ylabel =~ s/\n/ /g; 658: if (length($ylabel) > $max_str_len) { 659: $ylabel = substr($ylabel,0,$max_str_len); 660: } 661: } elsif ($target eq 'edit') { 662: $result .= &Apache::edit::tag_start($target,$token,'Plot Ylabel'); 663: my $text = &Apache::lonxml::get_all_text("/ylabel",$parser); 664: $result .= &Apache::edit::end_row(). 665: &Apache::edit::start_spanning_row(). 666: &Apache::edit::editline('',$text,'',60); 667: } elsif ($target eq 'modified') { 668: $result.=&Apache::edit::rebuild_tag($token); 669: $result.=&Apache::edit::modifiedfield("/ylabel",$parser); 670: } 671: return $result; 672: } 673: 674: sub end_ylabel { 675: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 676: my $result = ''; 677: if ($target eq 'web' || $target eq 'tex') { 678: } elsif ($target eq 'edit') { 679: $result.=&Apache::edit::tag_end($target,$token); 680: } 681: return $result; 682: } 683: 684: ##------------------------------------------------------------------- label 685: sub start_label { 686: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 687: my $result=''; 688: if ($target eq 'web' || $target eq 'tex') { 689: my %label; 690: &get_attributes(\%label,\%label_defaults,$parstack,$safeeval, 691: $tagstack->[-1]); 692: my $text = &Apache::lonxml::get_all_text("/label",$parser); 693: $text = &Apache::run::evaluate($text,$safeeval,$$parstack[-1]); 694: $text =~ s/\n/ /g; 695: $text = substr($text,0,$max_str_len) if (length($text) > $max_str_len); 696: $label{'text'} = $text; 697: push(@labels,\%label); 698: } elsif ($target eq 'edit') { 699: $result .= &Apache::edit::tag_start($target,$token,'Plot Label'); 700: $result .= &edit_attributes($target,$token,\%label_defaults); 701: my $text = &Apache::lonxml::get_all_text("/label",$parser); 702: $result .= &Apache::edit::end_row(). 703: &Apache::edit::start_spanning_row(). 704: &Apache::edit::editline('',$text,'',60); 705: } elsif ($target eq 'modified') { 706: &Apache::edit::get_new_args 707: ($token,$parstack,$safeeval,keys(%label_defaults)); 708: $result.=&Apache::edit::rebuild_tag($token); 709: $result.=&Apache::edit::modifiedfield("/label",$parser); 710: } 711: return $result; 712: } 713: 714: sub end_label { 715: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 716: my $result = ''; 717: if ($target eq 'web' || $target eq 'tex') { 718: } elsif ($target eq 'edit') { 719: $result.=&Apache::edit::tag_end($target,$token); 720: } 721: return $result; 722: } 723: 724: ##------------------------------------------------------------------- curve 725: sub start_curve { 726: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 727: my $result=''; 728: &Apache::lonxml::register('Apache::lonplot',('function','data')); 729: push (@Apache::lonxml::namespace,'curve'); 730: if ($target eq 'web' || $target eq 'tex') { 731: my %curve; 732: &get_attributes(\%curve,\%curve_defaults,$parstack,$safeeval, 733: $tagstack->[-1]); 734: push (@curves,\%curve); 735: } elsif ($target eq 'edit') { 736: $result .= &Apache::edit::tag_start($target,$token,'Curve'); 737: $result .= &edit_attributes($target,$token,\%curve_defaults, 738: \@curve_edit_order); 739: } elsif ($target eq 'modified') { 740: my $constructtag=&Apache::edit::get_new_args 741: ($token,$parstack,$safeeval,keys(%curve_defaults)); 742: if ($constructtag) { 743: $result = &Apache::edit::rebuild_tag($token); 744: $result.= &Apache::edit::handle_insert(); 745: } 746: } 747: return $result; 748: } 749: 750: sub end_curve { 751: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 752: my $result = ''; 753: pop @Apache::lonxml::namespace; 754: &Apache::lonxml::deregister('Apache::lonplot',('function','data')); 755: if ($target eq 'web' || $target eq 'tex') { 756: } elsif ($target eq 'edit') { 757: $result.=&Apache::edit::tag_end($target,$token); 758: } 759: return $result; 760: } 761: 762: ##------------------------------------------------------------ curve function 763: sub start_function { 764: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 765: my $result=''; 766: if ($target eq 'web' || $target eq 'tex') { 767: if (exists($curves[-1]->{'data'})) { 768: &Apache::lonxml::warning 769: ('Use of the <b>curve function</b> tag precludes use of '. 770: ' the <b>curve data</b> tag. '. 771: 'The curve data tag will be omitted in favor of the '. 772: 'curve function declaration.'); 773: delete $curves[-1]->{'data'} ; 774: } 775: my $function = &Apache::lonxml::get_all_text("/function",$parser); 776: $function = &Apache::run::evaluate($function,$safeeval,$$parstack[-1]); 777: $curves[-1]->{'function'} = $function; 778: } elsif ($target eq 'edit') { 779: $result .= &Apache::edit::tag_start($target,$token,'Gnuplot compatible curve function'); 780: my $text = &Apache::lonxml::get_all_text("/function",$parser); 781: $result .= &Apache::edit::end_row(). 782: &Apache::edit::start_spanning_row(). 783: &Apache::edit::editline('',$text,'',60); 784: } elsif ($target eq 'modified') { 785: $result.=&Apache::edit::rebuild_tag($token); 786: $result.=&Apache::edit::modifiedfield("/function",$parser); 787: } 788: return $result; 789: } 790: 791: sub end_function { 792: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 793: my $result = ''; 794: if ($target eq 'web' || $target eq 'tex') { 795: } elsif ($target eq 'edit') { 796: $result .= &Apache::edit::end_table(); 797: } 798: return $result; 799: } 800: 801: ##------------------------------------------------------------ curve data 802: sub start_data { 803: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 804: my $result=''; 805: if ($target eq 'web' || $target eq 'tex') { 806: if (exists($curves[-1]->{'function'})) { 807: &Apache::lonxml::warning 808: ('Use of the <b>curve function</b> tag precludes use of '. 809: ' the <b>curve data</b> tag. '. 810: 'The curve function tag will be omitted in favor of the '. 811: 'curve data declaration.'); 812: delete($curves[-1]->{'function'}); 813: } 814: my $datatext = &Apache::lonxml::get_all_text("/data",$parser); 815: $datatext=&Apache::run::evaluate($datatext,$safeeval,$$parstack[-1]); 816: # Deal with cases where we're given an array... 817: if ($datatext =~ /^\@/) { 818: $datatext = &Apache::run::run('return "'.$datatext.'"', 819: $safeeval,1); 820: } 821: $datatext =~ s/\s+/ /g; 822: # Need to do some error checking on the @data array - 823: # make sure it's all numbers and make sure each array 824: # is of the same length. 825: my @data; 826: if ($datatext =~ /,/) { # comma deliminated 827: @data = split /,/,$datatext; 828: } else { # Assume it's space separated. 829: @data = split / /,$datatext; 830: } 831: for (my $i=0;$i<=$#data;$i++) { 832: # Check that it's non-empty 833: if (! defined($data[$i])) { 834: &Apache::lonxml::warning( 835: 'undefined curve data value. Replacing with '. 836: ' pi/e = 1.15572734979092'); 837: $data[$i] = 1.15572734979092; 838: } 839: # Check that it's a number 840: if (! &$real_test($data[$i]) & ! &$int_test($data[$i])) { 841: &Apache::lonxml::warning( 842: 'Bad curve data value of '.$data[$i].' Replacing with '. 843: ' pi/e = 1.15572734979092'); 844: $data[$i] = 1.15572734979092; 845: } 846: } 847: # complain if the number of data points is not the same as 848: # in previous sets of data. 849: if (($curves[-1]->{'data'}) && ($#data != $#{@{$curves[-1]->{'data'}->[0]}})){ 850: &Apache::lonxml::warning 851: ('Number of data points is not consistent with previous '. 852: 'number of data points'); 853: } 854: push @{$curves[-1]->{'data'}},\@data; 855: } elsif ($target eq 'edit') { 856: $result .= &Apache::edit::tag_start($target,$token,'Comma or space deliminated curve data'); 857: my $text = &Apache::lonxml::get_all_text("/data",$parser); 858: $result .= &Apache::edit::end_row(). 859: &Apache::edit::start_spanning_row(). 860: &Apache::edit::editline('',$text,'',60); 861: } elsif ($target eq 'modified') { 862: $result.=&Apache::edit::rebuild_tag($token); 863: $result.=&Apache::edit::modifiedfield("/data",$parser); 864: } 865: return $result; 866: } 867: 868: sub end_data { 869: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 870: my $result = ''; 871: if ($target eq 'web' || $target eq 'tex') { 872: } elsif ($target eq 'edit') { 873: $result .= &Apache::edit::end_table(); 874: } 875: return $result; 876: } 877: 878: ##------------------------------------------------------------------- axis 879: sub start_axis { 880: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 881: my $result=''; 882: if ($target eq 'web' || $target eq 'tex') { 883: &get_attributes(\%axis,\%axis_defaults,$parstack,$safeeval, 884: $tagstack->[-1]); 885: } elsif ($target eq 'edit') { 886: $result .= &Apache::edit::tag_start($target,$token,'Plot Axes'); 887: $result .= &edit_attributes($target,$token,\%axis_defaults, 888: \@axis_edit_order); 889: } elsif ($target eq 'modified') { 890: my $constructtag=&Apache::edit::get_new_args 891: ($token,$parstack,$safeeval,keys(%axis_defaults)); 892: if ($constructtag) { 893: $result = &Apache::edit::rebuild_tag($token); 894: } 895: } 896: return $result; 897: } 898: 899: sub end_axis { 900: my ($target,$token,$tagstack,$parstack,$parser,$safeeval,$style)=@_; 901: my $result = ''; 902: if ($target eq 'web' || $target eq 'tex') { 903: } elsif ($target eq 'edit') { 904: $result.=&Apache::edit::tag_end($target,$token); 905: } elsif ($target eq 'modified') { 906: } 907: return $result; 908: } 909: 910: ################################################################### 911: ## ## 912: ## Utility Functions ## 913: ## ## 914: ################################################################### 915: 916: ##----------------------------------------------------------- set_defaults 917: sub set_defaults { 918: my ($var,$defaults) = @_; 919: my $key; 920: foreach $key (keys(%$defaults)) { 921: $var->{$key} = $defaults->{$key}->{'default'}; 922: } 923: } 924: 925: ##------------------------------------------------------------------- misc 926: sub get_attributes{ 927: my ($values,$defaults,$parstack,$safeeval,$tag) = @_; 928: foreach my $attr (keys(%{$defaults})) { 929: if ($attr eq 'texwidth' || $attr eq 'texfont') { 930: $values->{$attr} = 931: &Apache::lonxml::get_param($attr,$parstack,$safeeval,undef,1); 932: } else { 933: $values->{$attr} = 934: &Apache::lonxml::get_param($attr,$parstack,$safeeval); 935: } 936: if ($values->{$attr} eq '' | !defined($values->{$attr})) { 937: $values->{$attr} = $defaults->{$attr}->{'default'}; 938: next; 939: } 940: my $test = $defaults->{$attr}->{'test'}; 941: if (! &$test($values->{$attr})) { 942: &Apache::lonxml::warning 943: ($tag.':'.$attr.': Bad value.'.'Replacing your value with : ' 944: .$defaults->{$attr}->{'default'} ); 945: $values->{$attr} = $defaults->{$attr}->{'default'}; 946: } 947: } 948: return ; 949: } 950: 951: ##------------------------------------------------------- write_gnuplot_file 952: sub write_gnuplot_file { 953: my ($tmpdir,$filename,$target)= @_; 954: my $gnuplot_input = ''; 955: my $curve; 956: my $pt = $plot{'texfont'}; 957: # Collect all the colors 958: my @Colors; 959: push @Colors, $plot{'bgcolor'}; 960: push @Colors, $plot{'fgcolor'}; 961: push @Colors, (defined($axis{'color'})?$axis{'color'}:$plot{'fgcolor'}); 962: foreach $curve (@curves) { 963: push @Colors, ($curve->{'color'} ne '' ? 964: $curve->{'color'} : 965: $plot{'fgcolor'} ); 966: } 967: # set term 968: if ($target eq 'web') { 969: $gnuplot_input .= 'set term gif '; 970: $gnuplot_input .= 'transparent ' if ($plot{'transparent'} eq 'on'); 971: $gnuplot_input .= $plot{'font'} . ' '; 972: $gnuplot_input .= 'size '.$plot{'width'}.','.$plot{'height'}.' '; 973: $gnuplot_input .= "@Colors\n"; 974: # set output 975: $gnuplot_input .= "set output\n"; 976: } elsif ($target eq 'tex') { 977: $gnuplot_input .= "set term postscript eps monochrome solid \"Helvetica\" $pt \n"; 978: $gnuplot_input .= "set output \"/home/httpd/perl/tmp/". 979: &Apache::lonnet::unescape($filename).".eps\"\n"; 980: } 981: # cartesian or polar? 982: if (lc($plot{'plottype'}) eq 'polar') { 983: $gnuplot_input .= 'set polar'.$/; 984: } else { 985: # Assume Cartesian 986: } 987: # grid 988: $gnuplot_input .= 'set grid'.$/ if ($plot{'grid'} eq 'on'); 989: # border 990: $gnuplot_input .= ($plot{'border'} eq 'on'? 991: 'set border'.$/ : 992: 'set noborder'.$/ ); 993: # sampling rate for non-data curves 994: $gnuplot_input .= "set samples $plot{'samples'}\n"; 995: # title, xlabel, ylabel 996: # titles 997: if ($target eq 'tex') { 998: $gnuplot_input .= "set title \"$title\" font \"Helvetica,".$pt."pt\"\n" if (defined($title)) ; 999: $gnuplot_input .= "set xlabel \"$xlabel\" font \"Helvetica,".$pt."pt\" \n" if (defined($xlabel)); 1000: $gnuplot_input .= "set ylabel \"$ylabel\" font \"Helvetica,".$pt."pt\"\n" if (defined($ylabel)); 1001: } else { 1002: $gnuplot_input .= "set title \"$title\" \n" if (defined($title)) ; 1003: $gnuplot_input .= "set xlabel \"$xlabel\" \n" if (defined($xlabel)); 1004: $gnuplot_input .= "set ylabel \"$ylabel\" \n" if (defined($ylabel)); 1005: } 1006: # tics 1007: if (%xtics) { 1008: $gnuplot_input .= "set xtics $xtics{'location'} "; 1009: $gnuplot_input .= ( $xtics{'mirror'} eq 'on'?"mirror ":"nomirror "); 1010: $gnuplot_input .= "$xtics{'start'}, "; 1011: $gnuplot_input .= "$xtics{'increment'}, "; 1012: $gnuplot_input .= "$xtics{'end'}\n"; 1013: if ($xtics{'minorfreq'} != 0) { 1014: $gnuplot_input .= "set mxtics ".$xtics{'minorfreq'}."\n"; 1015: } 1016: } 1017: if (%ytics) { 1018: $gnuplot_input .= "set ytics $ytics{'location'} "; 1019: $gnuplot_input .= ( $ytics{'mirror'} eq 'on'?"mirror ":"nomirror "); 1020: $gnuplot_input .= "$ytics{'start'}, "; 1021: $gnuplot_input .= "$ytics{'increment'}, "; 1022: $gnuplot_input .= "$ytics{'end'}\n"; 1023: if ($ytics{'minorfreq'} != 0) { 1024: $gnuplot_input .= "set mytics ".$ytics{'minorfreq'}."\n"; 1025: } 1026: } 1027: # axis 1028: if (%axis) { 1029: $gnuplot_input .= "set xrange \[$axis{'xmin'}:$axis{'xmax'}\]\n"; 1030: $gnuplot_input .= "set yrange \[$axis{'ymin'}:$axis{'ymax'}\]\n"; 1031: } 1032: # Key 1033: if (%key) { 1034: $gnuplot_input .= 'set key '.$key{'pos'}.' '; 1035: if ($key{'title'} ne '') { 1036: $gnuplot_input .= 'title "'.$key{'title'}.'" '; 1037: } 1038: $gnuplot_input .= ($key{'box'} eq 'on' ? 'box ' : 'nobox ').$/; 1039: } else { 1040: $gnuplot_input .= 'set nokey'.$/; 1041: } 1042: # labels 1043: my $label; 1044: foreach $label (@labels) { 1045: $gnuplot_input .= 'set label "'.$label->{'text'}.'" at '. 1046: $label->{'xpos'}.','.$label->{'ypos'}.' '.$label->{'justify'}.' font "Helvetica,'.$pt.'pt"'.$/ ; 1047: } 1048: if ($target eq 'tex') { 1049: $gnuplot_input .="set size 1,".$plot{'height'}/$plot{'width'}*1.38; 1050: $gnuplot_input .="\n"; 1051: } 1052: # curves 1053: $gnuplot_input .= 'plot '; 1054: for (my $i = 0;$i<=$#curves;$i++) { 1055: $curve = $curves[$i]; 1056: $gnuplot_input.= ', ' if ($i > 0); 1057: if (exists($curve->{'function'})) { 1058: $gnuplot_input.= 1059: $curve->{'function'}.' title "'. 1060: $curve->{'name'}.'" with '. 1061: $curve->{'linestyle'}; 1062: $gnuplot_input.= ' linewidth 4 ' if ($target eq 'tex'); 1063: if (($curve->{'linestyle'} eq 'points') || 1064: ($curve->{'linestyle'} eq 'linespoints') || 1065: ($curve->{'linestyle'} eq 'errorbars') || 1066: ($curve->{'linestyle'} eq 'xerrorbars') || 1067: ($curve->{'linestyle'} eq 'yerrorbars') || 1068: ($curve->{'linestyle'} eq 'xyerrorbars')) { 1069: $gnuplot_input.=' pointtype '.$curve->{'pointtype'}; 1070: $gnuplot_input.=' pointsize '.$curve->{'pointsize'}; 1071: } 1072: } elsif (exists($curve->{'data'})) { 1073: # Store data values in $datatext 1074: my $datatext = ''; 1075: # get new filename 1076: my $datafilename = "$tmpdir/$filename.data.$i"; 1077: my $fh=Apache::File->new(">$datafilename"); 1078: # Compile data 1079: my @Data = @{$curve->{'data'}}; 1080: my @Data0 = @{$Data[0]}; 1081: for (my $i =0; $i<=$#Data0; $i++) { 1082: my $dataset; 1083: foreach $dataset (@Data) { 1084: $datatext .= $dataset->[$i] . ' '; 1085: } 1086: $datatext .= $/; 1087: } 1088: # write file 1089: print $fh $datatext; 1090: close ($fh); 1091: # generate gnuplot text 1092: $gnuplot_input.= '"'.$datafilename.'" title "'. 1093: $curve->{'name'}.'" with '. 1094: $curve->{'linestyle'}; 1095: $gnuplot_input.= ' linewidth 4 ' if ($target eq 'tex'); 1096: if (($curve->{'linestyle'} eq 'points') || 1097: ($curve->{'linestyle'} eq 'linespoints') || 1098: ($curve->{'linestyle'} eq 'errorbars') || 1099: ($curve->{'linestyle'} eq 'xerrorbars') || 1100: ($curve->{'linestyle'} eq 'yerrorbars') || 1101: ($curve->{'linestyle'} eq 'xyerrorbars')) { 1102: $gnuplot_input.=' pointtype '.$curve->{'pointtype'}; 1103: $gnuplot_input.=' pointsize '.$curve->{'pointsize'}; 1104: } 1105: } 1106: } 1107: # Write the output to a file. 1108: my $fh=Apache::File->new(">$tmpdir$filename.data"); 1109: print $fh $gnuplot_input; 1110: close($fh); 1111: # That's all folks. 1112: return ; 1113: } 1114: 1115: #---------------------------------------------- check_inputs 1116: sub check_inputs { 1117: ## Note: no inputs, no outputs - this acts only on global variables. 1118: ## Make sure we have all the input we need: 1119: if (! %plot) { &set_defaults(\%plot,\%gnuplot_defaults); } 1120: if (! %key ) {} # No key for this plot, thats okay 1121: # if (! %axis) { &set_defaults(\%axis,\%axis_defaults); } 1122: if (! defined($title )) {} # No title for this plot, thats okay 1123: if (! defined($xlabel)) {} # No xlabel for this plot, thats okay 1124: if (! defined($ylabel)) {} # No ylabel for this plot, thats okay 1125: if ($#labels < 0) { } # No labels for this plot, thats okay 1126: if ($#curves < 0) { 1127: &Apache::lonxml::warning("No curves specified for plot!!!!"); 1128: return ''; 1129: } 1130: my $curve; 1131: foreach $curve (@curves) { 1132: if (!defined($curve->{'function'})&&!defined($curve->{'data'})){ 1133: &Apache::lonxml::warning("One of the curves specified did not contain any curve data or curve function declarations\n"); 1134: return ''; 1135: } 1136: } 1137: } 1138: 1139: #------------------------------------------------ make_edit 1140: sub edit_attributes { 1141: my ($target,$token,$defaults,$keys) = @_; 1142: my ($result,@keys); 1143: if ($keys && ref($keys) eq 'ARRAY') { 1144: @keys = @$keys; 1145: } else { 1146: @keys = sort(keys(%$defaults)); 1147: } 1148: foreach my $attr (@keys) { 1149: # append a ' ' to the description if it doesn't have one already. 1150: my $description = $defaults->{$attr}->{'description'}; 1151: $description .= ' ' if ($description !~ / $/); 1152: if ($defaults->{$attr}->{'edit_type'} eq 'entry') { 1153: $result .= &Apache::edit::text_arg 1154: ($description,$attr,$token, 1155: $defaults->{$attr}->{'size'}); 1156: } elsif ($defaults->{$attr}->{'edit_type'} eq 'choice') { 1157: $result .= &Apache::edit::select_arg 1158: ($description,$attr,$defaults->{$attr}->{'choices'},$token); 1159: } elsif ($defaults->{$attr}->{'edit_type'} eq 'onoff') { 1160: $result .= &Apache::edit::select_arg 1161: ($description,$attr,['on','off'],$token); 1162: } 1163: $result .= '<br />'; 1164: } 1165: return $result; 1166: } 1167: 1168: 1169: ################################################################### 1170: ## ## 1171: ## Insertion functions for editing plots ## 1172: ## ## 1173: ################################################################### 1174: 1175: sub insert_gnuplot { 1176: my $result = ''; 1177: # plot attributes 1178: $result .= "\n<gnuplot "; 1179: foreach my $attr (keys(%gnuplot_defaults)) { 1180: $result .= "\n $attr=\"$gnuplot_defaults{$attr}->{'default'}\""; 1181: } 1182: $result .= ">"; 1183: # Add the components (most are commented out for simplicity) 1184: # $result .= &insert_key(); 1185: # $result .= &insert_axis(); 1186: # $result .= &insert_title(); 1187: # $result .= &insert_xlabel(); 1188: # $result .= &insert_ylabel(); 1189: $result .= &insert_curve(); 1190: # close up the <gnuplot> 1191: $result .= "\n</gnuplot>"; 1192: return $result; 1193: } 1194: 1195: sub insert_tics { 1196: my $result; 1197: $result .= &insert_xtics() . &insert_ytics; 1198: return $result; 1199: } 1200: 1201: sub insert_xtics { 1202: my $result; 1203: $result .= "\n <xtics "; 1204: foreach my $attr (keys(%tic_defaults)) { 1205: $result .= "\n $attr=\"$tic_defaults{$attr}->{'default'}\" "; 1206: } 1207: $result .= "/>"; 1208: return $result; 1209: } 1210: 1211: sub insert_ytics { 1212: my $result; 1213: $result .= "\n <ytics "; 1214: foreach my $attr (keys(%tic_defaults)) { 1215: $result .= "\n $attr=\"$tic_defaults{$attr}->{'default'}\" "; 1216: } 1217: $result .= "/>"; 1218: return $result; 1219: } 1220: 1221: sub insert_key { 1222: my $result; 1223: $result .= "\n <key "; 1224: foreach my $attr (keys(%key_defaults)) { 1225: $result .= "\n $attr=\"$key_defaults{$attr}->{'default'}\""; 1226: } 1227: $result .= " />"; 1228: return $result; 1229: } 1230: 1231: sub insert_axis{ 1232: my $result; 1233: $result .= "\n <axis "; 1234: foreach my $attr (keys(%axis_defaults)) { 1235: $result .= "\n $attr=\"$axis_defaults{$attr}->{'default'}\""; 1236: } 1237: $result .= " />"; 1238: return $result; 1239: } 1240: 1241: sub insert_title { return "\n <title></title>"; } 1242: sub insert_xlabel { return "\n <xlabel></xlabel>"; } 1243: sub insert_ylabel { return "\n <ylabel></ylabel>"; } 1244: 1245: sub insert_label { 1246: my $result; 1247: $result .= "\n <label "; 1248: foreach my $attr (keys(%label_defaults)) { 1249: $result .= "\n $attr=\"". 1250: $label_defaults{$attr}->{'default'}."\""; 1251: } 1252: $result .= "></label>"; 1253: return $result; 1254: } 1255: 1256: sub insert_curve { 1257: my $result; 1258: $result .= "\n <curve "; 1259: foreach my $attr (keys(%curve_defaults)) { 1260: $result .= "\n $attr=\"". 1261: $curve_defaults{$attr}->{'default'}."\""; 1262: } 1263: $result .= " >"; 1264: $result .= &insert_data().&insert_data()."\n </curve>"; 1265: } 1266: 1267: sub insert_function { 1268: my $result; 1269: $result .= "\n <function></function>"; 1270: return $result; 1271: } 1272: 1273: sub insert_data { 1274: my $result; 1275: $result .= "\n <data></data>"; 1276: return $result; 1277: } 1278: 1279: ##---------------------------------------------------------------------- 1280: 1; 1281: __END__ 1282: 1283: