diff --git a/assets/stop-words-en.txt b/assets/stop-words-en.txt deleted file mode 100644 index cc09be2ec7..0000000000 --- a/assets/stop-words-en.txt +++ /dev/null @@ -1,1320 +0,0 @@ -# Stop words from https://github.com/Alir3z4/stop-words. - -'ll -'tis -'twas -'ve -a -a's -able -ableabout -about -above -abroad -abst -accordance -according -accordingly -across -act -actually -ad -added -adj -adopted -ae -af -affected -affecting -affects -after -afterwards -ag -again -against -ago -ah -ahead -ai -ain't -aint -al -all -allow -allows -almost -alone -along -alongside -already -also -although -always -am -amid -amidst -among -amongst -amoungst -amount -an -and -announce -another -any -anybody -anyhow -anymore -anyone -anything -anyway -anyways -anywhere -ao -apart -apparently -appear -appreciate -appropriate -approximately -aq -ar -are -area -areas -aren -aren't -arent -arise -around -arpa -as -aside -ask -asked -asking -asks -associated -at -au -auth -available -aw -away -awfully -az -b -ba -back -backed -backing -backs -backward -backwards -bb -bd -be -became -because -become -becomes -becoming -been -before -beforehand -began -begin -beginning -beginnings -begins -behind -being -beings -believe -below -beside -besides -best -better -between -beyond -bf -bg -bh -bi -big -bill -billion -biol -bj -bm -bn -bo -both -bottom -br -brief -briefly -bs -bt -but -buy -bv -bw -by -bz -c -c'mon -c's -ca -call -came -can -can't -cannot -cant -caption -case -cases -cause -causes -cc -cd -certain -certainly -cf -cg -ch -changes -ci -ck -cl -clear -clearly -click -cm -cmon -cn -co -co. -com -come -comes -computer -con -concerning -consequently -consider -considering -contain -containing -contains -copy -corresponding -could -could've -couldn -couldn't -couldnt -course -cr -cry -cs -cu -currently -cv -cx -cy -cz -d -dare -daren't -darent -date -de -dear -definitely -describe -described -despite -detail -did -didn -didn't -didnt -differ -different -differently -directly -dj -dk -dm -do -does -doesn -doesn't -doesnt -doing -don -don't -done -dont -doubtful -down -downed -downing -downs -downwards -due -during -dz -e -each -early -ec -ed -edu -ee -effect -eg -eh -eight -eighty -either -eleven -else -elsewhere -empty -end -ended -ending -ends -enough -entirely -er -es -especially -et -et-al -etc -even -evenly -ever -evermore -every -everybody -everyone -everything -everywhere -ex -exactly -example -except -f -face -faces -fact -facts -fairly -far -farther -felt -few -fewer -ff -fi -fifteen -fifth -fifty -fify -fill -find -finds -fire -first -five -fix -fj -fk -fm -fo -followed -following -follows -for -forever -former -formerly -forth -forty -forward -found -four -fr -free -from -front -full -fully -further -furthered -furthering -furthermore -furthers -fx -g -ga -gave -gb -gd -ge -general -generally -get -gets -getting -gf -gg -gh -gi -give -given -gives -giving -gl -gm -gmt -gn -go -goes -going -gone -good -goods -got -gotten -gov -gp -gq -gr -great -greater -greatest -greetings -group -grouped -grouping -groups -gs -gt -gu -gw -gy -h -had -hadn't -hadnt -half -happens -hardly -has -hasn -hasn't -hasnt -have -haven -haven't -havent -having -he -he'd -he'll -he's -hed -hell -hello -help -hence -her -here -here's -hereafter -hereby -herein -heres -hereupon -hers -herself -herse” -hes -hi -hid -high -higher -highest -him -himself -himse” -his -hither -hk -hm -hn -home -homepage -hopefully -how -how'd -how'll -how's -howbeit -however -hr -ht -htm -html -http -hu -hundred -i -i'd -i'll -i'm -i've -i.e. -id -ie -if -ignored -ii -il -ill -im -immediate -immediately -importance -important -in -inasmuch -inc -inc. -indeed -index -indicate -indicated -indicates -information -inner -inside -insofar -instead -int -interest -interested -interesting -interests -into -invention -inward -io -iq -ir -is -isn -isn't -isnt -it -it'd -it'll -it's -itd -itll -its -itself -itse” -ive -j -je -jm -jo -join -jp -just -k -ke -keep -keeps -kept -keys -kg -kh -ki -kind -km -kn -knew -know -known -knows -kp -kr -kw -ky -kz -l -la -large -largely -last -lately -later -latest -latter -latterly -lb -lc -least -length -less -lest -let -let's -lets -li -like -liked -likely -likewise -line -little -lk -ll -long -longer -longest -look -looking -looks -low -lower -lr -ls -lt -ltd -lu -lv -ly -m -ma -made -mainly -make -makes -making -man -many -may -maybe -mayn't -maynt -mc -md -me -mean -means -meantime -meanwhile -member -members -men -merely -mg -mh -microsoft -might -might've -mightn't -mightnt -mil -mill -million -mine -minus -miss -mk -ml -mm -mn -mo -more -moreover -most -mostly -move -mp -mq -mr -mrs -ms -msie -mt -mu -much -mug -must -must've -mustn't -mustnt -mv -mw -mx -my -myself -myse” -mz -n -na -name -namely -nay -nc -nd -ne -near -nearly -necessarily -necessary -need -needed -needing -needn't -neednt -needs -neither -net -netscape -never -neverf -neverless -nevertheless -new -newer -newest -next -nf -ng -ni -nine -ninety -nl -no -no-one -nobody -non -none -nonetheless -noone -nor -normally -nos -not -noted -nothing -notwithstanding -novel -now -nowhere -np -nr -nu -null -number -numbers -nz -o -obtain -obtained -obviously -of -off -often -oh -ok -okay -old -older -oldest -om -omitted -on -once -one -one's -ones -only -onto -open -opened -opening -opens -opposite -or -ord -order -ordered -ordering -orders -org -other -others -otherwise -ought -oughtn't -oughtnt -our -ours -ourselves -out -outside -over -overall -owing -own -p -pa -page -pages -part -parted -particular -particularly -parting -parts -past -pe -per -perhaps -pf -pg -ph -pk -pl -place -placed -places -please -plus -pm -pmid -pn -point -pointed -pointing -points -poorly -possible -possibly -potentially -pp -pr -predominantly -present -presented -presenting -presents -presumably -previously -primarily -probably -problem -problems -promptly -proud -provided -provides -pt -put -puts -pw -py -q -qa -que -quickly -quite -qv -r -ran -rather -rd -re -readily -really -reasonably -recent -recently -ref -refs -regarding -regardless -regards -related -relatively -research -reserved -respectively -resulted -resulting -results -right -ring -ro -room -rooms -round -ru -run -rw -s -sa -said -same -saw -say -saying -says -sb -sc -sd -se -sec -second -secondly -seconds -section -see -seeing -seem -seemed -seeming -seems -seen -sees -self -selves -sensible -sent -serious -seriously -seven -seventy -several -sg -sh -shall -shan't -shant -she -she'd -she'll -she's -shed -shell -shes -should -should've -shouldn -shouldn't -shouldnt -show -showed -showing -shown -showns -shows -si -side -sides -significant -significantly -similar -similarly -since -sincere -site -six -sixty -sj -sk -sl -slightly -sm -small -smaller -smallest -sn -so -some -somebody -someday -somehow -someone -somethan -something -sometime -sometimes -somewhat -somewhere -soon -sorry -specifically -specified -specify -specifying -sr -st -state -states -still -stop -strongly -su -sub -substantially -successfully -such -sufficiently -suggest -sup -sure -sv -sy -system -sz -t -t's -take -taken -taking -tc -td -tell -ten -tends -test -text -tf -tg -th -than -thank -thanks -thanx -that -that'll -that's -that've -thatll -thats -thatve -the -their -theirs -them -themselves -then -thence -there -there'd -there'll -there're -there's -there've -thereafter -thereby -thered -therefore -therein -therell -thereof -therere -theres -thereto -thereupon -thereve -these -they -they'd -they'll -they're -they've -theyd -theyll -theyre -theyve -thick -thin -thing -things -think -thinks -third -thirty -this -thorough -thoroughly -those -thou -though -thoughh -thought -thoughts -thousand -three -throug -through -throughout -thru -thus -til -till -tip -tis -tj -tk -tm -tn -to -today -together -too -took -top -toward -towards -tp -tr -tried -tries -trillion -truly -try -trying -ts -tt -turn -turned -turning -turns -tv -tw -twas -twelve -twenty -twice -two -tz -u -ua -ug -uk -um -un -under -underneath -undoing -unfortunately -unless -unlike -unlikely -until -unto -up -upon -ups -upwards -us -use -used -useful -usefully -usefulness -uses -using -usually -uucp -uy -uz -v -va -value -various -vc -ve -versus -very -vg -vi -via -viz -vn -vol -vols -vs -vu -w -want -wanted -wanting -wants -was -wasn -wasn't -wasnt -way -ways -we -we'd -we'll -we're -we've -web -webpage -website -wed -welcome -well -wells -went -were -weren -weren't -werent -weve -wf -what -what'd -what'll -what's -what've -whatever -whatll -whats -whatve -when -when'd -when'll -when's -whence -whenever -where -where'd -where'll -where's -whereafter -whereas -whereby -wherein -wheres -whereupon -wherever -whether -which -whichever -while -whilst -whim -whither -who -who'd -who'll -who's -whod -whoever -whole -wholl -whom -whomever -whos -whose -why -why'd -why'll -why's -widely -width -will -willing -wish -with -within -without -won -won't -wonder -wont -words -work -worked -working -works -world -would -would've -wouldn -wouldn't -wouldnt -ws -www -x -y -ye -year -years -yes -yet -you -you'd -you'll -you're -you've -youd -youll -young -younger -youngest -your -youre -yours -yourself -yourselves -youve -yt -yu -z -za -zero -zm -zr - -# Additional specific stop words specific to POD and sample problem documentation. -constructor -description -error -errors -macro -macros -pod -podlink -problink -synopsis -usage -funciton -functions -method -methods -option -options -todo -fixme -_ diff --git a/bin/dev_scripts/PODtoHTML.pm b/bin/dev_scripts/PODtoHTML.pm deleted file mode 100644 index a922314011..0000000000 --- a/bin/dev_scripts/PODtoHTML.pm +++ /dev/null @@ -1,201 +0,0 @@ -package PODtoHTML; - -use strict; -use warnings; -use utf8; - -use Pod::Simple::Search; -use Mojo::Template; -use Mojo::DOM; -use Mojo::Collection qw(c); -use File::Path qw(make_path); -use File::Basename qw(dirname); -use IO::File; -use POSIX qw(strftime); - -use WeBWorK::Utils::PODParser; - -our @sections = ( - doc => 'Documentation', - bin => 'Scripts', - macros => 'Macros', - lib => 'Libraries', -); -our %macro_names = ( - answers => 'Answers', - contexts => 'Contexts', - core => 'Core', - deprecated => 'Deprecated', - graph => 'Graph', - math => 'Math', - misc => 'Miscellaneous', - parsers => 'Parsers', - ui => 'User Interface' -); - -sub new { - my ($invocant, %o) = @_; - my $class = ref $invocant || $invocant; - - my @section_list = ref($o{sections}) eq 'ARRAY' ? @{ $o{sections} } : @sections; - my $section_hash = {@section_list}; - my $section_order = [ map { $section_list[ 2 * $_ ] } 0 .. $#section_list / 2 ]; - delete $o{sections}; - - my $self = { - %o, - idx => {}, - section_hash => $section_hash, - section_order => $section_order, - macros_hash => {}, - }; - return bless $self, $class; -} - -sub convert_pods { - my $self = shift; - my $source_root = $self->{source_root}; - my $dest_root = $self->{dest_root}; - - my $regex = join('|', map {"^$_"} @{ $self->{section_order} }); - - my ($name2path, $path2name) = Pod::Simple::Search->new->inc(0)->limit_re(qr!$regex!)->survey($self->{source_root}); - for (keys %$path2name) { - print "Processing file: $_\n" if $self->{verbose} > 1; - $self->process_pod($_, $name2path); - } - - $self->write_index("$dest_root/index.html"); - - return; -} - -sub process_pod { - my ($self, $pod_path, $pod_files) = @_; - - my $pod_name; - - my ($subdir, $filename) = $pod_path =~ m|^$self->{source_root}/(?:(.*)/)?(.*)$|; - - my ($subdir_first, $subdir_rest) = ('', ''); - - if (defined $subdir) { - if ($subdir =~ m|/|) { - ($subdir_first, $subdir_rest) = $subdir =~ m|^([^/]*)/(.*)|; - } else { - $subdir_first = $subdir; - } - } - - $pod_name = (defined $subdir_rest ? "$subdir_rest/" : '') . $filename; - if ($filename =~ /\.pl$/) { - $filename =~ s/\.pl$/.html/; - } elsif ($filename =~ /\.pod$/) { - $pod_name =~ s/\.pod$//; - $filename =~ s/\.pod$/.html/; - } elsif ($filename =~ /\.pm$/) { - $pod_name =~ s/\.pm$//; - $pod_name =~ s|/+|::|g; - $filename =~ s/\.pm$/.html/; - } elsif ($filename !~ /\.html$/) { - $filename .= '.html'; - } - - $pod_name =~ s/^(\/|::)//; - - my $html_dir = $self->{dest_root} . (defined $subdir ? "/$subdir" : ''); - my $html_path = "$html_dir/$filename"; - my $html_rel_path = defined $subdir ? "$subdir/$filename" : $filename; - - $self->update_index($subdir, $html_rel_path, $pod_name); - make_path($html_dir); - my $html = $self->do_pod2html( - pod_path => $pod_path, - pod_name => $pod_name, - pod_files => $pod_files - ); - my $fh = IO::File->new($html_path, '>:encoding(UTF-8)') - or die "Failed to open file '$html_path' for writing: $!\n"; - print $fh $html; - - return; -} - -sub update_index { - my ($self, $subdir, $html_rel_path, $pod_name) = @_; - - $subdir =~ s|/.*$||; - my $idx = $self->{idx}; - my $sections = $self->{section_hash}; - if ($subdir eq 'macros') { - $idx->{macros} = []; - if ($pod_name =~ m!^(.+)/(.+)$!) { - push @{ $self->{macros_hash}{$1} }, [ $html_rel_path, $2 ]; - } else { - push @{ $idx->{doc} }, [ $html_rel_path, $pod_name ]; - } - } elsif (exists $sections->{$subdir}) { - push @{ $idx->{$subdir} }, [ $html_rel_path, $pod_name ]; - } else { - warn "no section for subdir '$subdir'\n"; - } - - return; -} - -sub write_index { - my ($self, $out_path) = @_; - - my $fh = IO::File->new($out_path, '>:encoding(UTF-8)') or die "Failed to open index '$out_path' for writing: $!\n"; - print $fh Mojo::Template->new(vars => 1)->render_file( - "$self->{template_dir}/category-index.mt", - { - title => 'POD for ' . ($self->{source_root} =~ s|^.*/||r), - base_url => $self->{dest_url}, - pod_index => $self->{idx}, - sections => $self->{section_hash}, - section_order => $self->{section_order}, - macros => $self->{macros_hash}, - macros_order => [ sort keys %{ $self->{macros_hash} } ], - macro_names => \%macro_names, - date => strftime('%a %b %e %H:%M:%S %Z %Y', localtime) - } - ); - - return; -} - -sub do_pod2html { - my ($self, %o) = @_; - - my $psx = WeBWorK::Utils::PODParser->new($o{pod_files}); - $psx->{source_root} = $self->{source_root}; - $psx->{verbose} = $self->{verbose}; - $psx->{assert_html_ext} = 1; - $psx->{base_url} = ($self->{dest_url} // '') . '/' . (($self->{source_root} // '') =~ s|^.*/||r); - $psx->output_string(\my $html); - $psx->html_header(''); - $psx->html_footer(''); - $psx->parse_file($o{pod_path}); - - my $dom = Mojo::DOM->new($html); - my $podIndexUL = $dom->at('ul[id="index"]'); - my $podIndex = $podIndexUL ? $podIndexUL->find('ul[id="index"] > li') : c(); - for (@$podIndex) { - $_->attr({ class => 'nav-item' }); - $_->at('a')->attr({ class => 'nav-link p-0' }); - for (@{ $_->find('ul') }) { - $_->attr({ class => 'nav flex-column w-100' }); - } - for (@{ $_->find('li') }) { - $_->attr({ class => 'nav-item' }); - $_->at('a')->attr({ class => 'nav-link p-0' }); - } - } - my $podHTML = $podIndexUL ? $podIndexUL->remove : $html; - - return Mojo::Template->new(vars => 1)->render_file("$self->{template_dir}/pod.mt", - { title => $o{pod_name}, base_url => dirname($psx->{base_url}), index => $podIndex, content => $podHTML }); -} - -1; diff --git a/bin/dev_scripts/generate-ww-pg-pod.pl b/bin/dev_scripts/generate-ww-pg-pod.pl index f1591ae485..d20902a171 100755 --- a/bin/dev_scripts/generate-ww-pg-pod.pl +++ b/bin/dev_scripts/generate-ww-pg-pod.pl @@ -9,17 +9,16 @@ =head1 SYNOPSIS generate-ww-pg-pod.pl [options] Options: - -p|--pg-root Directory containing a git clone of pg. - If this option is not set, then the environment - variable $PG_ROOT will be used if it is set. -o|--output-dir Directory to save the output files to. (required) -b|--base-url Base url location used on server. (default: /) This is needed for internal POD links to work correctly. -v|--verbose Increase the verbosity of the output. (Use multiple times for more verbosity.) -Note that --pg-root must be provided or the PG_ROOT environment variable set -if the POD for pg is desired. +Note that C must be set in the C file, or +if that file does not exist then the clone of the PG repository must be located +at C as defined in the C +file. =head1 DESCRIPTION @@ -30,37 +29,47 @@ =head1 DESCRIPTION use strict; use warnings; +my ($webwork_root, $pg_root); + +BEGIN { + use File::Basename qw(dirname); + use Cwd qw(abs_path); + use YAML::XS qw(LoadFile); + + $webwork_root = abs_path(dirname(dirname(dirname(__FILE__)))); + + # Load the configuration file to obtain the PG root directory. + my $config_file = "$webwork_root/conf/webwork2.mojolicious.yml"; + $config_file = "$webwork_root/conf/webwork2.mojolicious.dist.yml" unless -e $config_file; + my $config = LoadFile($config_file); + + $pg_root = $config->{pg_dir}; +} + use Getopt::Long qw(:config bundling); use Pod::Usage; -my ($pg_root, $output_dir, $base_url); +my ($output_dir, $base_url); my $verbose = 0; GetOptions( - 'p|pg-root=s' => \$pg_root, 'o|output-dir=s' => \$output_dir, 'b|base-url=s' => \$base_url, 'v|verbose+' => \$verbose ); -$pg_root = $ENV{PG_ROOT} if !$pg_root; - -pod2usage(2) unless $output_dir; +pod2usage(2) unless $output_dir && $pg_root && -d $pg_root; $base_url = "/" if !$base_url; use Mojo::Template; use IO::File; use File::Copy; -use File::Path qw(make_path remove_tree); -use File::Basename qw(dirname); -use Cwd qw(abs_path); - -use lib dirname(dirname(dirname(__FILE__))) . '/lib'; -use lib dirname(__FILE__); +use File::Path qw(make_path remove_tree); -use PODtoHTML; +use lib "$webwork_root/lib"; +use lib "$pg_root/lib"; -my $webwork_root = abs_path(dirname(dirname(dirname(__FILE__)))); +use WeBWorK::Utils::PODtoHTML; for my $dir ($webwork_root, $pg_root) { next unless $dir && -d $dir; @@ -73,10 +82,10 @@ =head1 DESCRIPTION write_index($index_fh); make_path("$output_dir/assets"); -copy("$webwork_root/htdocs/js/PODViewer/podviewer.css", "$output_dir/assets/podviewer.css"); -print "copying $webwork_root/htdocs/js/PODViewer/podviewer.css to $output_dir/assets/podviewer.css\n" if $verbose; -copy("$webwork_root/htdocs/js/PODViewer/podviewer.js", "$output_dir/assets/podviewer.js"); -print "copying $webwork_root/htdocs/js/PODViewer/podviewer.css to $output_dir/assets/podviewer.js\n" if $verbose; +copy("$pg_root/htdocs/js/PODViewer/podviewer.css", "$output_dir/assets/podviewer.css"); +print "copying $pg_root/htdocs/js/PODViewer/podviewer.css to $output_dir/assets/podviewer.css\n" if $verbose; +copy("$pg_root/htdocs/js/PODViewer/podviewer.js", "$output_dir/assets/podviewer.js"); +print "copying $pg_root/htdocs/js/PODViewer/podviewer.css to $output_dir/assets/podviewer.js\n" if $verbose; sub process_dir { my $source_dir = shift; @@ -89,12 +98,15 @@ sub process_dir { remove_tree($dest_dir); make_path($dest_dir); - my $htmldocs = PODtoHTML->new( - source_root => $source_dir, - dest_root => $dest_dir, - template_dir => "$webwork_root/bin/dev_scripts/pod-templates", - dest_url => $base_url, - verbose => $verbose + my $htmldocs = WeBWorK::Utils::PODtoHTML->new( + source_root => $source_dir, + dest_root => $dest_dir, + template_dir => "$pg_root/assets/pod-templates", + dest_url => $base_url, + home_url => $base_url, + home_url_link_name => 'WeBWorK POD Home', + page_url => $base_url . ($source_dir =~ s|^.*/||r), + verbose => $verbose ); $htmldocs->convert_pods; diff --git a/bin/dev_scripts/pod-templates/category-index.mt b/bin/dev_scripts/pod-templates/category-index.mt deleted file mode 100644 index a5d980d599..0000000000 --- a/bin/dev_scripts/pod-templates/category-index.mt +++ /dev/null @@ -1,95 +0,0 @@ - - -% - - - - <%= $title %> - - - - - - -% - - - % - % my ($index, $macro_index, $content, $macro_content) = ('', '', '', ''); - % for my $macro (@$macros_order) { - % my $new_index = begin - <%= $macro_names->{$macro} // $macro %> - % end - % $macro_index .= $new_index->(); - % my $new_content = begin -

<%= $macro_names->{$macro} // $macro %>

-
- % for my $file (sort { $a->[1] cmp $b->[1] } @{ $macros->{$macro} }) { - <%= $file->[1] %> - % } -
- % end - % $macro_content .= $new_content->(); - % } - % for my $section (@$section_order) { - % next unless defined $pod_index->{$section}; - % my $new_index = begin - <%= $sections->{$section} %> - % if ($section eq 'macros') { - - % } - % end - % $index .= $new_index->(); - % my $new_content = begin -

<%= $sections->{$section} %>

-
- % if ($section eq 'macros') { - <%= $macro_content =%> - % } else { - % for my $file (sort { $a->[1] cmp $b->[1] } @{ $pod_index->{$section} }) { - - <%= $file->[1] %> - - % } - % } -
- % end - % $content .= $new_content->(); - % } - % - -
-
- <%= $content =%> -

Generated <%= $date %>

-
-
- -% - diff --git a/bin/dev_scripts/pod-templates/main-index.mt b/bin/dev_scripts/pod-templates/main-index.mt index d9bfe39e8d..9c8c711588 100644 --- a/bin/dev_scripts/pod-templates/main-index.mt +++ b/bin/dev_scripts/pod-templates/main-index.mt @@ -4,8 +4,7 @@ - - + WeBWorK/PG POD diff --git a/bin/dev_scripts/pod-templates/pod.mt b/bin/dev_scripts/pod-templates/pod.mt deleted file mode 100644 index 0ea8e6c5b4..0000000000 --- a/bin/dev_scripts/pod-templates/pod.mt +++ /dev/null @@ -1,55 +0,0 @@ - - -% - - - - <%= $title %> - - - - - - -% - - - -
-
- <%= $content =%> -
-
- -% - diff --git a/htdocs/js/PODViewer/podviewer.css b/htdocs/js/PODViewer/podviewer.css deleted file mode 100644 index e4f17811d2..0000000000 --- a/htdocs/js/PODViewer/podviewer.css +++ /dev/null @@ -1,66 +0,0 @@ -.main-index-header, -.pod-header { - height: 65px; - top: 0; - left: 0; - right: 0; - z-index: 2; -} - -#sidebar { - --bs-offcanvas-width: 300px; - overflow-y: auto; -} - -#sidebar ul.nav ul.nav li { - border-left: 1px solid #e1e4e8; - padding-left: 10px; -} - -#sidebar ul.nav ul.nav li:hover { - border-left: 6px solid #e1e4e8; - padding-left: 5px; -} - -.main-index-container, -.pod-page-container { - margin-top: 65px; -} - -@media only screen and (min-width: 768px) { - #sidebar { - height: calc(100vh - 65px); - width: 300px; - } - - .pod-page-container { - margin-left: 300px; - } -} - -#_podtop_ pre { - border: 1px solid #ccc; - border-radius: 5px; - background: #f6f6f6; - padding: 0.75rem; -} - -#_podtop_, -#_podtop_ *[id] { - scroll-margin-top: calc(65px + 1rem); -} - -@media only screen and (max-width: 768px) { - .pod-header { - height: 100px; - } - - .pod-page-container { - margin-top: 100px; - } - - #_podtop_, - #_podtop_ *[id] { - scroll-margin-top: calc(100px + 1rem); - } -} diff --git a/htdocs/js/PODViewer/podviewer.js b/htdocs/js/PODViewer/podviewer.js deleted file mode 100644 index 795093205a..0000000000 --- a/htdocs/js/PODViewer/podviewer.js +++ /dev/null @@ -1,8 +0,0 @@ -(() => { - const offcanvas = bootstrap.Offcanvas.getOrCreateInstance(document.getElementById('sidebar')); - for (const link of document.querySelectorAll('#sidebar .nav-link')) { - // The timeout is to workaround an issue in Chrome. If the offcanvas hides before the window scrolls to the - // fragment in the page, scrolling stops before it gets there. - link.addEventListener('click', () => setTimeout(() => offcanvas.hide(), 500)); - } -})(); diff --git a/htdocs/js/SampleProblemViewer/documentation-search.js b/htdocs/js/SampleProblemViewer/documentation-search.js deleted file mode 100644 index 5e61bc64ec..0000000000 --- a/htdocs/js/SampleProblemViewer/documentation-search.js +++ /dev/null @@ -1,76 +0,0 @@ -(async () => { - const searchBox = document.getElementById('search-box'); - const resultList = document.getElementById('result-list'); - if (!resultList || !searchBox) return; - - const webwork2URL = webworkConfig?.webwork_url ?? '/webwork2'; - - let searchData; - try { - const result = await fetch(`${webwork2URL}/sampleproblems/search_data`); - searchData = await result.json(); - } catch (e) { - console.log(e); - return; - } - - const miniSearch = new MiniSearch({ - fields: ['filename', 'name', 'description', 'terms', 'macros', 'subjects'], - storeFields: ['type', 'filename', 'dir', 'description'] - }); - miniSearch.addAll(searchData); - - const searchMacrosCheck = document.getElementById('search-macros'); - const searchSampleProblemsCheck = document.getElementById('search-sample-problems'); - - document.getElementById('clear-search-button')?.addEventListener('click', () => { - searchBox.value = ''; - while (resultList.firstChild) resultList.firstChild.remove(); - }); - - const searchDocumentation = () => { - const searchMacros = searchMacrosCheck?.checked; - const searchSampleProblems = searchSampleProblemsCheck?.checked; - - while (resultList.firstChild) resultList.firstChild.remove(); - - if (!searchBox.value) return; - - for (const result of miniSearch.search(searchBox.value, { prefix: true })) { - if ( - (searchSampleProblems && result.type === 'sample problem') || - (searchMacros && result.type === 'macro') - ) { - const link = document.createElement('a'); - link.classList.add('list-group-item', 'list-group-item-action'); - link.href = `${webwork2URL}/${ - result.type === 'sample problem' ? 'sampleproblems' : result.type === 'macro' ? 'pod' : '' - }/${result.dir}/${result.filename.replace('.pg', '')}`; - - const linkText = document.createElement('span'); - linkText.classList.add('h4'); - linkText.textContent = `${result.filename} (${result.type})`; - link.append(linkText); - - if (result.description) { - const summary = document.createElement('div'); - summary.textContent = result.description; - link.append(summary); - } - - resultList.append(link); - } - } - - if (resultList.children.length == 0) { - const item = document.createElement('div'); - item.classList.add('alert', 'alert-info'); - item.innerHTML = 'No results found'; - resultList.append(item); - } - }; - - searchBox.addEventListener('keyup', searchDocumentation); - searchMacrosCheck?.addEventListener('change', searchDocumentation); - searchSampleProblemsCheck?.addEventListener('change', searchDocumentation); -})(); diff --git a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm index 0703a78623..76e5b3118e 100644 --- a/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm +++ b/lib/WeBWorK/ContentGenerator/Instructor/PGProblemEditor.pm @@ -108,12 +108,12 @@ not exist. The path to the actual file being edited is stored in inputFilePath. use Mojo::File; use XML::LibXML; -use WeBWorK::Utils qw(not_blank x max); -use WeBWorK::Utils::Files qw(surePathToFile readFile path_is_subdir); -use WeBWorK::Utils::Instructor qw(assignProblemToAllSetUsers addProblemToSet); -use WeBWorK::Utils::JITAR qw(seq_to_jitar_id jitar_id_to_seq); -use WeBWorK::Utils::Sets qw(format_set_name_display); -use SampleProblemParser qw(getSampleProblemCode generateMetadata); +use WeBWorK::Utils qw(not_blank x max); +use WeBWorK::Utils::Files qw(surePathToFile readFile path_is_subdir); +use WeBWorK::Utils::Instructor qw(assignProblemToAllSetUsers addProblemToSet); +use WeBWorK::Utils::JITAR qw(seq_to_jitar_id jitar_id_to_seq); +use WeBWorK::Utils::Sets qw(format_set_name_display); +use WeBWorK::PG::SampleProblemParser qw(getSampleProblemCode generateMetadata); use constant DEFAULT_SEED => 123456; diff --git a/lib/WeBWorK/ContentGenerator/SampleProblemViewer.pm b/lib/WeBWorK/ContentGenerator/SampleProblemViewer.pm index 3ee248c2b0..d4d4ef44a0 100644 --- a/lib/WeBWorK/ContentGenerator/SampleProblemViewer.pm +++ b/lib/WeBWorK/ContentGenerator/SampleProblemViewer.pm @@ -2,14 +2,10 @@ package WeBWorK::ContentGenerator::SampleProblemViewer; use Mojo::Base 'WeBWorK::ContentGenerator', -signatures; use File::Basename qw(basename); -use Mojo::File; -use Mojo::JSON qw(decode_json encode_json); -use File::Find; use Pod::Simple::Search; -use Pod::Simple::SimpleTree; -use WeBWorK::Utils::Files qw(path_is_subdir); -use SampleProblemParser qw(parseSampleProblem generateMetadata getSampleProblemCode); +use WeBWorK::Utils::Files qw(path_is_subdir); +use WeBWorK::PG::SampleProblemParser qw(parseSampleProblem generateMetadata getSampleProblemCode getSearchData); =head1 NAME @@ -92,10 +88,10 @@ sub renderSampleProblem ($c) { %{ parseSampleProblem( $problemFile, - metadata => $metadata, - pod_root => $c->url_for('pod_viewer', filePath => 'macros'), - pg_doc_home => $c->url_for('sample_problem_index'), - macro_locations => \%macro_locations, + metadata => $metadata, + pod_base_url => $c->url_for('pod_viewer', filePath => 'macros'), + sample_problem_base_url => $c->url_for('sample_problem_index'), + macro_locations => \%macro_locations, ) }, metadata => $metadata, @@ -106,195 +102,7 @@ sub renderSampleProblem ($c) { } sub searchData ($c) { - my $sampleProblemDir = $c->ce->{pg_dir} . '/tutorial/sample-problems'; - - my $searchDataFile = Mojo::File->new($c->ce->{webworkDirs}{DATA})->child('sample-problem-search-data.json'); - my %files = map { $_->{filename} => $_ } @{ (eval { decode_json($searchDataFile->slurp('UTF-8')) } // []) }; - my @updatedFiles; - - # Process the sample problems in the sample problem directory. - find( - { - wanted => sub { - return unless $_ =~ /\.pg$/; - - my $file = Mojo::File->new($File::Find::name); - my $lastModified = $file->stat->mtime; - - if ($files{$_}) { - push(@updatedFiles, $files{$_}); - return if $files{$_}{lastModified} >= $lastModified; - } - - my @fileContents = eval { split("\n", $file->slurp('UTF-8')) }; - return if $@; - - if (!$files{$_}) { - $files{$_} = { - type => 'sample problem', - filename => $_, - dir => $file->dirname->basename - }; - push(@updatedFiles, $files{$_}); - } - $files{$_}{lastModified} = $lastModified; - - my (%words, @kw, @macros, @subjects, $description); - - while (@fileContents) { - my $line = shift @fileContents; - if ($line =~ /^#:%\s*(\w+)\s*=\s*(.*)\s*$/) { - # Store the name and subjects. - $files{$_}{name} = $2 if $1 eq 'name'; - if ($1 eq 'subject') { - @subjects = split(',\s*', $2 =~ s/\[(.*)\]/$1/r); - } - } elsif ($line =~ /^#:\s*(.*)?/) { - my @newWords = $c->processLine($1); - @words{@newWords} = (1) x @newWords if @newWords; - } elsif ($line =~ /loadMacros\(/) { - my $macros = $line; - while ($line && $line !~ /\);\s*$/) { - $line = shift @fileContents; - $macros .= $line; - } - my @usedMacros = - map {s/['"\s]//gr} split(/\s*,\s*/, $macros =~ s/loadMacros\((.*)\)\;$/$1/r); - - # Get the macros other than PGML.pl, PGstandard.pl, and PGcourse.pl. - for my $m (@usedMacros) { - push(@macros, $m) unless $m =~ /^(PGML|PGstandard|PGcourse)\.pl$/; - } - } elsif ($line =~ /##\s*KEYWORDS\((.*)\)/) { - @kw = map {s/^'(.*)'$/$1/r} split(/,\s*/, $1); - } elsif ($line =~ /^##\s*DESCRIPTION/) { - $line = shift(@fileContents); - while ($line && $line !~ /^##\s*ENDDESCRIPTION/) { - $description .= ($line =~ s/^##\s+//r) . ' '; - $line = shift(@fileContents); - } - $description =~ s/\s+$//; - } - } - - $files{$_}{description} = $description; - $files{$_}{subjects} = \@subjects; - $files{$_}{terms} = [ keys %words ]; - $files{$_}{keywords} = \@kw; - $files{$_}{macros} = \@macros; - - return; - } - }, - $sampleProblemDir - ); - - # Process the POD in macros in the macros dir. - (undef, my $macro_files) = Pod::Simple::Search->new->inc(0)->survey($c->ce->{pg_dir} . "/macros"); - for my $macroFile (sort keys %$macro_files) { - next if $macroFile =~ /deprecated/; - - my $file = Mojo::File->new($macroFile); - my $fileName = $file->basename; - my $lastModified = $file->stat->mtime; - - if ($files{$fileName}) { - push(@updatedFiles, $files{$fileName}); - next if $files{$fileName}{lastModified} >= $lastModified; - } - - if (!$files{$fileName}) { - $files{$fileName} = { - type => 'macro', - id => scalar(keys %files) + 1, - filename => $fileName, - dir => $file->dirname->to_rel($c->ce->{pg_dir})->to_string - }; - push(@updatedFiles, $files{$fileName}); - } - $files{$fileName}{lastModified} = $lastModified; - - my $root = Pod::Simple::SimpleTree->new->parse_file($file->to_string)->root; - - $files{$fileName}{terms} = $c->extractHeaders($root); - - if (my $nameDescription = extractHeadText($root, 'NAME')) { - (undef, my $description) = split(/\s*-\s*/, $nameDescription, 2); - $files{$fileName}{description} = $description if $description; - } - } - - # Redindex in case files were added or removed. - my $count = 0; - $_->{id} = ++$count for @updatedFiles; - - $searchDataFile->spew(encode_json(\@updatedFiles), 'UTF-8'); - - return $c->render(json => \@updatedFiles); -} - -# Get the stop words. The stop words file is loaded the first time this method is called, -# and is stashed and returned in later calls. -sub stopWords ($c) { - return $c->stash->{stopWords} if $c->stash->{stopWords}; - $c->stash->{stopWords} = {}; - - my $contents = eval { $c->app->home->child('assets', 'stop-words-en.txt')->slurp('UTF-8') }; - return $c->stash->{stopWords} if $@; - - for my $line (split("\n", $contents)) { - chomp $line; - next if $line =~ /^#/ || !$line; - $c->stash->{stopWords}{$line} = 1; - } - - return $c->stash->{stopWords}; -} - -sub processLine ($c, $line) { - my %words; - - # Extract linked macros and problems. - my @linkedFiles = $line =~ /(?:PODLINK|PROBLINK)\('([\w.]+)'\)/g; - $words{$_} = 1 for @linkedFiles; - - # Replace any non-word characters with spaces. - $line =~ s/\W/ /g; - - for my $word (split(/\s+/, $line)) { - next if $word =~ /^\d*$/; - $word = lc($word); - $words{$word} = 1 if !$c->stopWords->{$word}; - } - return keys %words; -} - -# Extract the text for a section from the given POD with a section header title. -sub extractHeadText ($root, $title) { - my @index = grep { ref($root->[$_]) eq 'ARRAY' && $root->[$_][2] eq $title } 0 .. $#$root; - return unless @index == 1; - - my $node = $root->[ $index[0] + 1 ]; - my $str = ''; - for (2 .. $#$node) { - $str .= ref($node->[$_]) eq 'ARRAY' ? $node->[$_][2] : $node->[$_]; - } - return $str; -} - -# Extract terms form POD headers. -sub extractHeaders ($c, $root) { - my %terms = - map { $_ => 1 } - grep { $_ && !$c->stopWords->{$_} } - map { split(/\s+/, $_) } - map { lc($_) =~ s/\W/ /gr } - map { - grep { !ref($_) } - @$_[ 2 .. $#$_ ] - } - grep { ref($_) eq 'ARRAY' && $_->[0] =~ /^head\d+$/ } @$root; - return [ keys %terms ]; + return $c->render(json => getSearchData($c->ce->{webworkDirs}{DATA} . '/sample-problem-search-data.json')); } 1; diff --git a/lib/WeBWorK/Utils/PODParser.pm b/lib/WeBWorK/Utils/PODParser.pm deleted file mode 100644 index e11b5899a3..0000000000 --- a/lib/WeBWorK/Utils/PODParser.pm +++ /dev/null @@ -1,66 +0,0 @@ -package WeBWorK::Utils::PODParser; -use parent qw(Pod::Simple::XHTML); - -use strict; -use warnings; - -use Pod::Simple::XHTML; -use File::Basename qw(basename); - -# $podFiles must be provided in order for pod links to local files to work. It should be the -# first return value of the POD::Simple::Search survey method. -sub new { - my ($invocant, $podFiles) = @_; - my $class = ref $invocant || $invocant; - my $self = $class->SUPER::new(@_); - $self->perldoc_url_prefix("https://metacpan.org/pod/"); - $self->index(1); - $self->backlink(1); - $self->html_charset('UTF-8'); - $self->{podFiles} = $podFiles // {}; - return bless $self, $class; -} - -# Attempt to resolve links to local files. If a local file is not found, then -# let Pod::Simple::XHTML resolve to a cpan link. -sub resolve_pod_page_link { - my ($self, $target, $section) = @_; - - unless (defined $target) { - print "Using internal page link.\n" if $self->{verbose} > 2; - return $self->SUPER::resolve_pod_page_link($target, $section); - } - - my $podFound; - for (keys %{ $self->{podFiles} }) { - if ($target eq $_ =~ s/lib:://r || $target eq basename($self->{podFiles}{$_}) =~ s/\.pod$//r) { - $podFound = - $self->{assert_html_ext} ? ($self->{podFiles}{$_} =~ s/\.(pm|pl|pod)$/.html/r) : $self->{podFiles}{$_}; - last; - } - } - - if ($podFound) { - my $pod_url = $self->encode_entities($podFound =~ s/^$self->{source_root}/$self->{base_url}/r) - . ($section ? '#' . $self->idify($self->encode_entities($section), 1) : ''); - print "Resolved local pod link for $target" . ($section ? "/$section" : '') . " to $pod_url\n" - if $self->{verbose} > 2; - return $pod_url; - } - - print "Using cpan pod link for $target" . ($section ? "/$section" : '') . "\n" if $self->{verbose} > 2; - return $self->SUPER::resolve_pod_page_link($target, $section); -} - -# Trim spaces from the beginning of each line in code blocks. This attempts to -# trim spaces from all lines in the code block in the same amount as there are -# spaces at the beginning of the first line. Note that Pod::Simple::XHTML has -# already converted tab characters into 8 spaces. -sub handle_code { - my ($self, $code) = @_; - my $start_spaces = length(($code =~ /^( *)/)[0]) || ''; - $self->SUPER::handle_code($code =~ s/^( {1,$start_spaces})//gmr); - return; -} - -1; diff --git a/templates/ContentGenerator/SampleProblemViewer.html.ep b/templates/ContentGenerator/SampleProblemViewer.html.ep index 6837417146..1729c3fbc8 100644 --- a/templates/ContentGenerator/SampleProblemViewer.html.ep +++ b/templates/ContentGenerator/SampleProblemViewer.html.ep @@ -14,7 +14,14 @@ <%= javascript $c->url({ type => 'webwork', name => 'htdocs', file => 'node_modules/minisearch/dist/umd/index.js' }), defer => undef =%> - + <%= javascript $c->url({ type => 'webwork', name => 'htdocs', file => 'js/SampleProblemViewer/documentation-search.js' }), defer => undef =%> @@ -83,7 +90,9 @@ <%= link_to 'sample_problem_viewer', { filePath => 'macros' }, class => 'list-group-item list-group-item-action', begin =%> <%= maketext('Sample Problems by Macro') %> -
<%= maketext('For many macros, this lists all sample problems used by the macro.') %>
+
+ <%= maketext('For many macros, this lists all sample problems that use the macro.') %> +
% end