--- loncom/interface/lonnavmaps.pm 2002/10/28 21:11:39 1.90 +++ loncom/interface/lonnavmaps.pm 2002/11/08 18:35:37 1.98 @@ -2,7 +2,7 @@ # The LearningOnline Network with CAPA # Navigate Maps Handler # -# $Id: lonnavmaps.pm,v 1.90 2002/10/28 21:11:39 bowersj2 Exp $ +# $Id: lonnavmaps.pm,v 1.98 2002/11/08 18:35:37 bowersj2 Exp $ # # Copyright Michigan State University Board of Trustees # @@ -820,7 +820,7 @@ sub new_handle { &Apache::loncommon::no_cache($r); $r->send_http_header; - # Initialize the nav map + # Create the nav map the nav map my $navmap = Apache::lonnavmaps::navmap->new( $ENV{"request.course.fn"}.".db", $ENV{"request.course.fn"}."_parms.db", 1, 1); @@ -837,19 +837,40 @@ sub new_handle { '')); $r->print(''); my $desc=$ENV{'course.'.$ENV{'request.course.id'}.'.description'}; - if (defined($desc)) { $r->print("
Key: | '); if ($navmap->{LAST_CHECK}) { - $r->print(' '.
+ '![]() ![]() '); + ' | '.
+ '![]() '. + ' | ');
+ } else {
+ $r->print(' '.
+ '![]() | '.
+ ' ![]() | ');
+ }
+ $r->print('
');
+ $r->print('Open All Folders');
}
+ $r->print('
');
+ $r->rflush();
+
+ # Now that we've displayed some stuff to the user, init the navmap
+ $navmap->init();
+
# Check that it's defined
if (!($navmap->courseMapDefined())) {
$r->print('Coursemap undefined.' .
@@ -926,36 +947,35 @@ sub new_handle {
my $queryAdd = "postdata=" . &Apache::lonnet::escape($currenturl) .
"&alreadyHere=1";
- $r->print('Show All Resources
');
-
# Begin the HTML table
# four cols: resource + indent, chat+feedback, icon, text string
$r->print('
\n");
+ # anchor for current resource... - 5 is deliberate: If it's that
+ # high on the screen, don't bother focusing on it. Also this will
+ # print multiple anchors if this is an expanded multi-part problem...
+ # who cares?
+ if ($counter == $currentUrlIndex - 5) {
+ $r->print('');
+ }
+
# print indentation
for (my $i = 0; $i < $indentLevel - $deltalevel + $deltadepth; $i++) {
$r->print($indentString);
@@ -1260,6 +1290,9 @@ sub new_handle {
}
$r->print(" $curMarkerBegin$title$partLabel $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}) {
$r->print(&Apache::loncommon::help_open_topic ("Navmap_Host_Down",
@@ -1616,6 +1649,14 @@ sub new {
$self->{PARM_HASH} = \%parmhash;
$self->{HASH_TIED} = 1;
+ bless($self);
+
+ return $self;
+}
+
+sub init {
+ my $self = shift;
+
# If the course opt hash and the user opt hash should be generated,
# generate them
if ($self->{GENERATE_COURSE_USER_OPT}) {
@@ -1720,10 +1761,6 @@ sub new {
}
$self->{PARM_CACHE} = {};
-
- bless($self);
-
- return $self;
}
# Checks to see if coursemap is defined, matching test in old lonnavmaps
@@ -1997,6 +2034,288 @@ sub END_BRANCH { return 4; } # end of
sub FORWARD { return 1; } # go forward
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 |