Diff for /loncom/interface/lonnavmaps.pm between versions 1.92 and 1.98

version 1.92, 2002/10/29 20:20:45 version 1.98, 2002/11/08 18:35:37
Line 837  sub new_handle { Line 837  sub new_handle {
                                           ''));                                            ''));
     $r->print('<script>window.focus();</script>');      $r->print('<script>window.focus();</script>');
     my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'};      my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'};
     if (defined($desc)) { $r->print("<h2>$desc</h2>\n"); }      $r->print('<table border="0" cellpadding="2" cellspacing="0">');
     my $date=localtime;      my $date=localtime;
     $r->print("<h3>$date</h3>\n");      $r->print('<tr><td align="right" valign="bottom">Key:&nbsp;&nbsp;</td>');
     $r->rflush();  
     if ($navmap->{LAST_CHECK}) {      if ($navmap->{LAST_CHECK}) {
         $r->print('<img src="/adm/lonMisc/chat.gif"> New discussion since '.          $r->print('<td align="center" valign="bottom">&nbsp;&nbsp;'.
                     '<img src="/adm/lonMisc/chat.gif"> New discussion since '.
                   strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).                    strftime("%A, %b %e at %I:%M %P", localtime($navmap->{LAST_CHECK})).
                   '<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>');                     '</td><td align="center" valign="bottom">&nbsp;&nbsp;'.
                     '<img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>'.
                     '</td>'); 
     } else {      } else {
         $r->print('<img src="/adm/lonMisc/chat.gif"> Discussions'.          $r->print('<td align="center" valign="bottom">&nbsp;&nbsp;'.
                   '<br><img src="/adm/lonMisc/feedback.gif"> New message (click to open)<p>');                     '<img src="/adm/lonMisc/chat.gif"> Discussions</td><td align="center" valign="bottom">'.
                     '&nbsp;&nbsp;<img src="/adm/lonMisc/feedback.gif"> New message (click to open)'.
                     '</td>'); 
       }
       $r->print('</tr></table>');
       my $condition = 0;
       if ($ENV{'form.condition'}) {
           $condition = 1;
     }      }
   
       if ($condition) {
           $r->print('<a href="navmaps?condition=0&filter=">Close All Folders</a>');
       } else {
           $r->print('<a href="navmaps?condition=1&filter=">Open All Folders</a>');
       }
   
       $r->print('<br>&nbsp;');
       $r->rflush();
   
     # Now that we've displayed some stuff to the user, init the navmap      # Now that we've displayed some stuff to the user, init the navmap
     $navmap->init();      $navmap->init();
   
Line 929  sub new_handle { Line 947  sub new_handle {
     my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl) .      my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl) .
         "&alreadyHere=1";          "&alreadyHere=1";
   
     my $condition = 0;  
     if ($ENV{'form.condition'}) {  
         $condition = 1;  
     }  
   
     if ($condition) {  
         $r->print('<a href="navmaps?condition=0&filter=">Close All Folders</a><br /><br />');  
     } else {  
         $r->print('<a href="navmaps?condition=1&filter=">Open All Folders</a><br /><br />');  
     }  
   
     # Begin the HTML table      # Begin the HTML table
     # four cols: resource + indent, chat+feedback, icon, text string      # four cols: resource + indent, chat+feedback, icon, text string
     $r->print('<table cellspacing="0" cellpadding="3" border="0" bgcolor="#FFFFFF">' ."\n");      $r->print('<table cellspacing="0" cellpadding="3" border="0" bgcolor="#FFFFFF">' ."\n");
Line 950  sub new_handle { Line 957  sub new_handle {
     # Here's a simple example of the iterator.      # Here's a simple example of the iterator.
     # Preprocess the map: Look for current URL, force inlined maps to display      # Preprocess the map: Look for current URL, force inlined maps to display
   
       # This currently does very little...
       #my $mapEventualIterator = Apache::lonnavmaps::iterator->new($navmap, undef, undef, {},
       #undef, $condition);
   
     my $mapIterator = $navmap->getIterator(undef, undef, {}, 1);      my $mapIterator = $navmap->getIterator(undef, undef, {}, 1);
     my $found = 0;      my $found = 0;
     my $depth = 1;      my $depth = 1;
Line 961  sub new_handle { Line 972  sub new_handle {
     my $counter = 0;      my $counter = 0;
           
     while ($depth > 0) {      while ($depth > 0) {
         if ($curRes == $mapIterator->BEGIN_MAP()) {          if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }
             $depth++;          if ($curRes == $mapIterator->END_MAP()) { $depth--; }
         }  
         if ($curRes == $mapIterator->END_MAP()) {  
             $depth--;  
         }  
   
         if (ref($curRes)) { $counter++; }          if (ref($curRes)) { $counter++; }
   
Line 1051  sub new_handle { Line 1058  sub new_handle {
         if ($curRes == $mapIterator->BEGIN_BRANCH()) {          if ($curRes == $mapIterator->BEGIN_BRANCH()) {
             $isNewBranch = 1;              $isNewBranch = 1;
         }          }
         if ($curRes == $mapIterator->BEGIN_MAP()) {          if ($curRes == $mapIterator->BEGIN_MAP()) { $depth++; }
             $depth++;          if ($curRes == $mapIterator->END_MAP()) { $depth--; }
         }  
         if ($curRes == $mapIterator->END_MAP()) {  
             $depth--;  
         }  
   
         if (ref($curRes)) { $counter++; }          if (ref($curRes)) { $counter++; }
   
Line 1287  sub new_handle { Line 1290  sub new_handle {
                 }                  }
   
                 $r->print("  $curMarkerBegin<a href=\"$link\">$title$partLabel</a> $curMarkerEnd $nonLinkedText");                  $r->print("  $curMarkerBegin<a href=\"$link\">$title$partLabel</a> $curMarkerEnd $nonLinkedText");
                   #$r->print(" TDV:" . $curRes->{DATA}->{TOP_DOWN_VAL}); # temp
                   #$r->print(" BUV:" . $curRes->{DATA}->{BOT_UP_VAL}); # temp
                   #$r->print(" DD:" . $curRes->{DATA}->{DISPLAY_DEPTH}); # temp
   
                 if ($curRes->{RESOURCE_ERROR}) {                  if ($curRes->{RESOURCE_ERROR}) {
                     $r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down",                      $r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down",
Line 1649  sub new { Line 1655  sub new {
 }  }
   
 sub init {  sub init {
     my $self = $self;      my $self = shift;
   
     # If the course opt hash and the user opt hash should be generated,      # If the course opt hash and the user opt hash should be generated,
     # generate them      # generate them
Line 2028  sub END_BRANCH { return 4; }   # end of Line 2034  sub END_BRANCH { return 4; }   # end of
 sub FORWARD { return 1; }      # go forward  sub FORWARD { return 1; }      # go forward
 sub BACKWARD { return 2; }  sub BACKWARD { return 2; }
   
   sub min {
       (my $a, my $b) = @_;
       if ($a < $b) { return $a; } else { return $b; }
   }
   
   sub new {
       # magic invocation to create a class instance
       my $proto = shift;
       my $class = ref($proto) || $proto;
       my $self = {};
   
       $self->{NAV_MAP} = shift;
       return undef unless ($self->{NAV_MAP});
   
       # Handle the parameters
       $self->{FIRST_RESOURCE} = shift || $self->{NAV_MAP}->firstResource();
       $self->{FINISH_RESOURCE} = shift || $self->{NAV_MAP}->finishResource();
   
       # If the given resources are just the ID of the resource, get the
       # objects
       if (!ref($self->{FIRST_RESOURCE})) { $self->{FIRST_RESOURCE} = 
                $self->{NAV_MAP}->getById($self->{FIRST_RESOURCE}); }
       if (!ref($self->{FINISH_RESOURCE})) { $self->{FINISH_RESOURCE} = 
                $self->{NAV_MAP}->getById($self->{FINISH_RESOURCE}); }
   
       $self->{FILTER} = shift;
   
       # A hash, used as a set, of resource already seen
       $self->{ALREADY_SEEN} = shift;
       if (!defined($self->{ALREADY_SEEN})) { $self->{ALREADY_SEEN} = {} };
       $self->{CONDITION} = shift;
   
       # Now, we need to pre-process the map, by walking forward and backward
       # over the parts of the map we're going to look at.
   
       # The processing steps are exactly the same, except for a few small 
       # changes, so I bundle those up in the following list of two elements:
       # (direction_to_iterate, VAL_name, next_resource_method_to_call,
       # first_resource).
       # This prevents writing nearly-identical code twice.
       my @iterations = ( [FORWARD(), 'TOP_DOWN_VAL', 'getNext', 
                           'FIRST_RESOURCE'],
                          [BACKWARD(), 'BOT_UP_VAL', 'getPrevious', 
                           'FINISH_RESOURCE'] );
   
       my $maxDepth = 0; # tracks max depth
   
       foreach my $pass (@iterations) {
           my $direction = $pass->[0];
           my $valName = $pass->[1];
           my $nextResourceMethod = $pass->[2];
           my $firstResourceName = $pass->[3];
   
           my $iterator = Apache::lonnavmaps::DFSiterator->new($self->{NAV_MAP}, 
                                                               $self->{FIRST_RESOURCE},
                                                               $self->{FINISH_RESOURCE},
                                                               {}, undef, 0, $direction);
       
           # prime the recursion
           $self->{$firstResourceName}->{DATA}->{$valName} = 0;
           my $depth = 0;
           $iterator->next();
           my $curRes = $iterator->next();
           while ($depth > -1) {
               if ($curRes == $iterator->BEGIN_MAP()) { $depth++; }
               if ($curRes == $iterator->END_MAP()) { $depth--; }
           
               if (ref($curRes)) {
                   my $resultingVal = $curRes->{DATA}->{$valName};
                   my $nextResources = $curRes->$nextResourceMethod();
                   my $resourceCount = scalar(@{$nextResources});
               
                   if ($resourceCount == 1) {
                       my $current = $nextResources->[0]->{DATA}->{$valName} || 999999999;
                       $nextResources->[0]->{DATA}->{$valName} = min($resultingVal, $current);
                   }
                   
                   if ($resourceCount > 1) {
                       foreach my $res (@{$nextResources}) {
                           my $current = $res->{DATA}->{$valName} || 999999999;
                           $res->{DATA}->{$valName} = min($current, $resultingVal + 1);
                       }
                   }
               }
               if (ref($curRes) && $curRes->is_map() && $direction == FORWARD()) {
                   my $firstResource = $curRes->map_start();
                   my $finishResource = $curRes->map_finish();
                   my $newIterator = Apache::lonnavmaps::iterator->new($self->{NAV_MAP},
                                                                       $firstResource,
                                                                       $finishResource,
                                                                       $self->{FILTER},
                                                                       $self->{ALREADY_SEEN},
                                                                       $self->{CONDITION});
               }
               
               # Assign the final val
               if (ref($curRes) && $direction == BACKWARD()) {
                   my $finalDepth = min($curRes->{DATA}->{TOP_DOWN_VAL},
                                        $curRes->{DATA}->{BOT_UP_VAL});
                   
                   $curRes->{DATA}->{DISPLAY_DEPTH} = $finalDepth;
                   if ($finalDepth > $maxDepth) {$maxDepth = $finalDepth;}
                   }
               $curRes = $iterator->next();
           }
       }
   
       # Set up some bookkeeping information.
       $self->{CURRENT_DEPTH} = 0;
       $self->{MAX_DEPTH} = $maxDepth;
       $self->{STACK} = [];
       $self->{RECURSIVE_ITERATOR_FLAG} = 0;
   
       for (my $i = 0; $i <= $self->{MAX_DEPTH}; $i++) {
           push @{$self->{STACK}}, [];
       }
   
       # Prime the recursion w/ the first resource
       push @{$self->{STACK}->[0]}, $self->{FIRST_RESOURCE};
       $self->{ALREADY_SEEN}->{$self->{FIRST_RESOURCE}->{ID}} = 1;
   
       bless ($self);
   
       return $self;
   }
   
   sub next {
       my $self = shift;
   
       if ($self->{RECURSIVE_ITERATOR_FLAG}) {
           # grab the next from the recursive iterator 
           my $next = $self->{RECURSIVE_ITERATOR}->next();
   
           # is it a begin or end map? If so, update the depth
           if ($next == BEGIN_MAP() ) { $self->{RECURSIVE_DEPTH}++; }
           if ($next == END_MAP() ) { $self->{RECURSIVE_DEPTH}--; }
   
           # Are we back at depth 0? If so, stop recursing
           if ($self->{RECURSIVE_DEPTH} == 0) {
               $self->{RECURSIVE_ITERATOR_FLAG} = 0;
           }
   
           return $next;
       }
   
       if (defined($self->{FORCE_NEXT})) {
           my $tmp = $self->{FORCE_NEXT};
           $self->{FORCE_NEXT} = undef;
           return $tmp;
       }
   
       # Have we not yet begun? If not, return BEGIN_MAP and
       # remember we've started.
       if ( !$self->{STARTED} ) { 
           $self->{STARTED} = 1;
           return $self->BEGIN_MAP();
       }
   
       # Here's the guts of the iterator.
       
       # Find the next resource, if any.
       my $found = 0;
       my $i = $self->{MAX_DEPTH};
       my $newDepth;
       my $here;
       while ( $i >= 0 && !$found ) {
           if ( scalar(@{$self->{STACK}->[$i]}) > 0 ) {
               $here = $self->{HERE} = shift @{$self->{STACK}->[$i]};
               $found = 1;
               $newDepth = $i;
           }
           $i--;
       }
   
       # If we still didn't find anything, we're done.
       if ( !$found ) {
           # We need to get back down to the correct branch depth
           if ( $self->{CURRENT_DEPTH} > 0 ) {
               $self->{CURRENT_DEPTH}--;
               return END_BRANCH();
           } else {
               return END_MAP();
           }
       }
   
       # Get to the right level
       if ( $self->{CURRENT_DEPTH} > $newDepth ) {
           push @{$self->{STACK}->[$newDepth]}, $here;
           $self->{CURRENT_DEPTH}--;
           return END_BRANCH();
       }
       if ( $self->{CURRENT_DEPTH} < $newDepth) {
           push @{$self->{STACK}->[$newDepth]}, $here;
           $self->{CURRENT_DEPTH}++;
           return BEGIN_BRANCH();
       }
   
       # If we made it here, we have the next resource, and we're at the
       # right branch level. So let's examine the resource for where
       # we can get to from here.
   
       # So we need to look at all the resources we can get to from here,
       # categorize them if we haven't seen them, remember if we have a new
       my $nextUnfiltered = $here->getNext();
   
       for (@$nextUnfiltered) {
           if (!defined($self->{ALREADY_SEEN}->{$_->{ID}})) {
               push @{$self->{STACK}->[$_->{DATA}->{DISPLAY_DEPTH}]}, $_;
               $self->{ALREADY_SEEN}->{$_->{ID}} = 1;
           }
       }
       
       # That ends the main iterator logic. Now, do we want to recurse
       # down this map (if this resource is a map)?
       if ($self->{HERE}->is_map() &&
           (defined($self->{FILTER}->{$self->{HERE}->map_pc()}) xor $self->{CONDITION})) {
           $self->{RECURSIVE_ITERATOR_FLAG} = 1;
           my $firstResource = $self->{HERE}->map_start();
           my $finishResource = $self->{HERE}->map_finish();
   
           $self->{RECURSIVE_ITERATOR} = 
               Apache::lonnavmaps::iterator->new($self->{NAV_MAP}, $firstResource,
                                                 $finishResource, $self->{FILTER},
                                                 $self->{ALREADY_SEEN}, $self->{CONDITION});
       }
   
       return $self->{HERE};
   
   }
   
   =pod
   
   The other method available on the iterator is B<getStack>, which returns an array populated with the current 'stack' of maps, as references to the resource objects. Example: This is useful when making the navigation map, as we need to check whether we are under a page map to see if we need to link directly to the resource, or to the page. The first elements in the array will correspond to the top of the stack (most inclusive map).
   
   =cut
   
   sub getStack {
       my $self=shift;
   
       my @stack;
   
       $self->populateStack(\@stack);
   
       return \@stack;
   }
   
   # Private method: Calls the iterators recursively to populate the stack.
   sub populateStack {
       my $self=shift;
       my $stack = shift;
   
       push @$stack, $self->{HERE} if ($self->{HERE});
   
       if ($self->{RECURSIVE_ITERATOR_FLAG}) {
           $self->{RECURSIVE_ITERATOR}->populateStack($stack);
       }
   }
   
   1;
   
   package Apache::lonnavmaps::DFSiterator;
   
   # UNDOCUMENTED: This is a private library, it should not generally be used
   # by the outside world. What it does is walk through the nav map in a 
   # depth-first fashion. This is not appropriate for most uses, but it is
   # used by the main iterator for pre-processing. It also is able to isolate
   # much of the complexity of the main iterator, so the main iterator is much
   # simpler.
   # There is no real benefit in merging the main iterator and this one into one class...
   # all the logic in DFSiterator would need to be replicated, you gain no performance,
   # at best, you just make one massively complicated iterator in place of two 
   # somewhat complicated ones. ;-) - Jeremy
   
   # Here are the tokens for the iterator, replicated from iterator for convenience:
   
   sub BEGIN_MAP { return 1; }    # begining of a new map
   sub END_MAP { return 2; }      # end of the map
   sub BEGIN_BRANCH { return 3; } # beginning of a branch
   sub END_BRANCH { return 4; }   # end of a branch
   sub FORWARD { return 1; }      # go forward
   sub BACKWARD { return 2; }
   
 # Params: nav map, start resource, end resource, filter, condition,   # Params: nav map, start resource, end resource, filter, condition, 
 # already seen hash ref  # already seen hash ref
   
Line 2103  sub next { Line 2391  sub next {
   
     # Are we using a recursive iterator? If so, pull from that and      # Are we using a recursive iterator? If so, pull from that and
     # watch the depth; we want to resume our level at the correct time.      # watch the depth; we want to resume our level at the correct time.
     if ($self->{RECURSIVE_ITERATOR_FLAG})      if ($self->{RECURSIVE_ITERATOR_FLAG}) {
     {  
         # grab the next from the recursive iterator          # grab the next from the recursive iterator
         my $next = $self->{RECURSIVE_ITERATOR}->next();          my $next = $self->{RECURSIVE_ITERATOR}->next();
                   
Line 2168  sub next { Line 2455  sub next {
   
     # Are we at the right depth? If not, close a branch and return      # Are we at the right depth? If not, close a branch and return
     # the current resource onto the branch stack      # the current resource onto the branch stack
       # Note: There seems to be some bugs here, so don't rely
       # on this, use the real iterator instead.
     if (defined($self->{HERE}->{DATA}->{ITERATOR_DEPTH})      if (defined($self->{HERE}->{DATA}->{ITERATOR_DEPTH})
         && $self->{HERE}->{DATA}->{ITERATOR_DEPTH} <          && $self->{HERE}->{DATA}->{ITERATOR_DEPTH} <
         $self->{BRANCH_DEPTH} ) {          $self->{BRANCH_DEPTH} ) {
Line 2236  sub next { Line 2525  sub next {
         my $finishResource = $self->{HERE}->map_finish();          my $finishResource = $self->{HERE}->map_finish();
   
         $self->{RECURSIVE_ITERATOR} =          $self->{RECURSIVE_ITERATOR} =
           Apache::lonnavmaps::iterator->new ($self->{NAV_MAP}, $firstResource,             Apache::lonnavmaps::DFSiterator->new ($self->{NAV_MAP}, $firstResource, 
                      $finishResource, $self->{FILTER}, $self->{ALREADY_SEEN},                       $finishResource, $self->{FILTER}, $self->{ALREADY_SEEN},
                                              $self->{CONDITION}, $self->{DIRECTION});                                               $self->{CONDITION}, $self->{DIRECTION});
     }      }
Line 2244  sub next { Line 2533  sub next {
     return $self->{HERE};      return $self->{HERE};
 }  }
   
 =pod  
   
 The other method available on the iterator is B<getStack>, which returns an array populated with the current 'stack' of maps, as references to the resource objects. Example: This is useful when making the navigation map, as we need to check whether we are under a page map to see if we need to link directly to the resource, or to the page. The first elements in the array will correspond to the top of the stack (most inclusive map).  
   
 =cut  
   
 sub getStack {  sub getStack {
     my $self=shift;      my $self=shift;
   

Removed from v.1.92  
changed lines
  Added in v.1.98


FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>