1 # Vend::Menu - Interchange menu processing routines
3 # $Id: Menu.pm,v 2.53 2009-02-24 15:29:01 jon Exp $
5 # Copyright (C) 2002 Mike Heins, <mike@perusion.net>
7 # This program is free software; you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation; either version 2 of the License, or
10 # (at your option) any later version.
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
17 # You should have received a copy of the GNU General Public
18 # License along with this program; if not, write to the Free
19 # Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston,
24 $VERSION = substr(q$Revision: 2.53 $, 10);
28 no warnings qw(uninitialized numeric);
37 my ($row, $fields) = @_;
38 return 1 if ref($fields) ne 'ARRAY';
40 $row->{$_} =~ s/ / /g;
45 my ($row, $fields) = @_;
46 return 1 if ref($fields) ne 'ARRAY';
48 $row->{$_} = HTML::Entities::encode_entities($row->{$_});
53 my ($row, $fields) = @_;
54 return 1 if ref($fields) ne 'ARRAY';
56 $row->{$_} = errmsg($row->{$_});
61 my ($row, $fields) = @_;
62 return undef if ref($fields) ne 'ARRAY';
63 return 1 if $first_line;
67 $status = $status && ! $row->{$_};
70 $status = $status && $row->{$_};
73 return $first_line = $status;
76 my ($row, $fields) = @_;
77 #::logDebug("last_line transform, last_line=$last_line");
78 return 1 if ref($fields) ne 'ARRAY';
79 return 0 if $last_line;
82 #::logDebug("last_line transform checking field $_=$row->{$_}");
84 $status = ! $row->{$_};
89 #::logDebug("last_line transform checked field $_=$row->{$_}, status=$status");
92 #::logDebug("last_line transform returning last_line=$status");
94 #::logDebug("last_line transform returning status=" . ! $status);
98 my ($row, $fields) = @_;
99 return 1 if ref($fields) ne 'ARRAY';
103 $status = $status && ! $row->{$_};
106 $status = $status && $row->{$_};
112 my ($row, $fields) = @_;
113 return 1 if ref($fields) ne 'ARRAY';
117 $status = $status && $row->{$_};
120 $status = $status && ! $row->{$_};
126 my ($row, $fields) = @_;
127 return 1 if ref($fields) ne 'ARRAY';
131 $status = $status && ! $row->{$_};
134 $status = $status && $row->{$_};
140 my ($row, $fields) = @_;
141 return 1 if ref($fields) ne 'ARRAY';
144 next if ! length($row->{$_});
145 $status = $status && Vend::Tags->if_mm('advanced', $row->{$_});
149 full_interpolate => sub {
150 my ($row, $fields) = @_;
151 return 1 if ref($fields) ne 'ARRAY';
153 next unless $row->{$_} =~ /\[|__[A-Z]\w+__/;
154 $row->{$_} = Vend::Interpolate::interpolate_html($row->{$_});
159 my ($row, $fields) = @_;
160 return 1 unless $row->{indicated};
161 return 1 if $row->{mv_level};
162 return 1 if ref($fields) ne 'ARRAY';
165 my($f, $c) = split /[=~]+/, $_;
167 #::logDebug("setting scratch $f to row=$c=$row->{$c}");
168 $::Scratch->{$f} = $row->{$c};
174 my ($row, $fields) = @_;
175 return 1 if ref($fields) ne 'ARRAY';
179 my($f, $c) = split /[=~]+/, $_;
181 $status = $status && (
183 or $CGI::values{$c} =~ /$row->{$f}/i
190 my ($row, $fields) = @_;
191 return 1 if ref($fields) ne 'ARRAY';
194 $status = $status && (! $row->{$_} or Vend::Tags->if_mm('super'));
199 my ($row, $fields) = @_;
200 return 1 if ref($fields) ne 'ARRAY';
202 my $nitems = scalar(@{$Vend::Items}) ? 1 : 0;
204 next if ! length($row->{$_});
205 $status = $status && (! $nitems ^ $row->{$_});
210 my ($row, $fields) = @_;
211 return 1 if ref($fields) ne 'ARRAY';
214 next if ! length($row->{$_});
215 $status = $status && (! $::Vend::Session->{logged_in} ^ $row->{$_});
220 my ($row, $fields) = @_;
221 return 1 if ref($fields) ne 'ARRAY';
224 next if ! $row->{$_};
225 $status = $status && $CGI::values{$row->{$_}};
230 my ($row, $fields) = @_;
231 return 1 if ref($fields) ne 'ARRAY';
234 $status = $status && (! $CGI::values{$row->{$_}});
238 indicator_class => sub {
239 my ($row, $fields) = @_;
240 return 1 if ref($fields) ne 'ARRAY';
242 my ($indicator,$rev, $last, $status);
243 my($s,$r) = split /=/, $_;
244 $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
245 $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
246 #::logDebug("checking scratch $s=$::Scratch->{$s} eq row=$r=$row->{$r}");
247 $status = $::Scratch->{$s} eq $row->{$r};
248 if($rev xor $status) {
249 $row->{indicated} = 1;
253 if($row->{indicated}) {
254 $indicated = \$row->{indicated};
258 indicator_profile => sub {
259 my ($row, $fields) = @_;
260 return 1 if ref($fields) ne 'ARRAY';
262 my ($indicator,$rev, $last, $status);
263 next unless $indicator = $row->{$_};
264 $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
265 $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
266 $status = Vend::Tags->run_profile($indicator);
267 if($rev xor $status) {
268 $row->{indicated} = 1;
275 indicator_page => sub {
276 my ($row, $fields) = @_;
277 return 1 if ref($fields) ne 'ARRAY';
279 if ($::Scratch->{mv_logical_page} eq $row->{$_}) {
281 $::Scratch->{mv_logical_page_used}
282 and $::Scratch->{mv_logical_page_used}
284 $row->{$logical_field}
287 $row->{indicated} = 1;
288 $::Scratch->{mv_logical_page_used} = $row->{$logical_field};
292 ($row->{indicated} = 1, last)
293 if $Global::Variable->{MV_PAGE} eq $row->{$_}
294 and ! defined $row->{indicated};
299 my ($row, $fields) = @_;
300 return 1 if ref($fields) ne 'ARRAY';
302 my ($indicator,$rev, $last, $status);
303 next unless $indicator = $row->{$_};
304 $rev = $indicator =~ s/^\s*!\s*// ? 1 : 0;
305 $last = $indicator =~ s/\s*!\s*$// ? 1 : 0;
306 if($indicator =~ /^\s*([-\w.:][-\w.:]+)\s*$/) {
307 $status = $CGI::values{$1};
309 elsif ($indicator =~ /^\s*`(.*)`\s*$/s) {
310 $status = Vend::Interpolate::tag_calc($1);
312 elsif ($indicator =~ /\[/s) {
313 $status = Vend::Interpolate::interpolate_html($indicator);
316 if($rev xor $status) {
317 $row->{indicated} = 1;
320 $row->{indicated} = '';
326 expand_values_form => sub {
327 my ($row, $fields) = @_;
328 return 1 if ref($fields) ne 'ARRAY';
330 next unless $row->{$_} =~ /\%5b|\[/i;
331 my @parms = split $Global::UrlSplittor, $row->{$_};
334 my ($parm, $val) = split /=/, $p, 2;
335 $val = unhexify($val);
336 $val =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
337 $val =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
338 $val =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
339 push @out, join('=', $parm, hexify($val));
341 $row->{$_} = join $Global::UrlJoiner, @out;
345 expand_values => sub {
346 my ($row, $fields) = @_;
347 return 1 if ref($fields) ne 'ARRAY';
349 next unless $row->{$_} =~ /\[/;
350 $row->{$_} =~ s/\[cgi\s+([^\[]+)\]/$CGI::values{$1}/g;
351 $row->{$_} =~ s/\[var\s+([^\[]+)\]/$::Variable->{$1}/g;
352 $row->{$_} =~ s/\[value\s+([^\[]+)\]/$::Values->{$1}/g;
359 my ($extra, $row) = @_;
360 if(ref($extra) ne 'HASH') {
361 my ($k, $v) = split /=/, $extra, 2;
362 $extra = { $k => $v };
366 $row->{$_} = $extra->{$_}
367 if length($extra->{$_});
372 sub reset_transforms {
373 #::logDebug("resetting transforms");
376 $logical_field = $opt->{logical_page_field} || 'name';
384 my ($name, $opt, $template) = @_;
387 if(! $opt->{explode_url}) {
388 $u = Vend::Tags->history_scan( { var_exclude => 'toggle,collapse,expand' });
389 $opt->{explode_url} = $u;
390 $opt->{explode_url} .= $u =~ /\?/ ? $Global::UrlJoiner : "?";
391 $opt->{explode_url} .= 'explode=1';
393 if(! $opt->{collapse_url}) {
394 $u ||= Vend::Tags->history_scan( { var_exclude => 'toggle,collapse,expand' });
395 $opt->{collapse_url} = $u;
396 $opt->{collapse_url} .= $u =~ /\?/ ? $Global::UrlJoiner : "?";
397 $opt->{collapse_url} .= 'collapse=1';
400 my $explode_label = errmsg($opt->{explode_label} || 'Explode tree');
401 my $collapse_label = errmsg($opt->{collapse_label} || 'Collapse tree');
403 $opt->{header_template} ||= <<EOF;
405 <a href="{EXPLODE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$explode_label</a><br$Vend::Xtrailer>
406 <a href="{COLLAPSE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$collapse_label</a>
411 $header = ::interpolate_html($opt->{header_template})
412 if $opt->{header_template};
413 if($header =~ /\S/) {
414 $header = Vend::Tags->uc_attr_list($opt, $header);
419 start => $opt->{tree_selector} || 'Products',
420 table => $::Variable->{MV_TREE_TABLE} || 'tree',
421 master => $opt->{tree_master} || 'parent_fld',
422 subordinate => 'code',
425 iterator => \&tree_link,
430 collapse => 'collapse',
434 while( my ($k, $v) = each %defaults) {
435 next if defined $opt->{$k};
438 push @out, Vend::Tags->tree($opt);
441 $footer = ::interpolate_html($opt->{footer_template})
442 if $opt->{footer_template};
443 if($footer =~ /\S/) {
444 $footer = Vend::Tags->uc_attr_list($opt, $footer);
448 return join "\n", @out;
453 my ($name, $opt, $template) = @_;
461 while( my ($k, $v) = each %defaults) {
462 next if defined $opt->{$k};
469 if($opt->{iterator}) {
470 $main = Vend::Tags->loop(undef,$opt,$template);
473 $opt->{iterator} = \&transforms_only;
474 delete $opt->{_transforms};
475 Vend::Tags->loop(undef,$opt,'');
477 my $list = $opt->{object}{mv_results};
478 if(@$list and my $fn = $opt->{object}{mv_field_names}) {
479 push @$fn, 'mv_last_row';
480 $list->[-1][$#$fn] = 1;
482 $main = join($opt->{joiner}, map {menu_link($template, $_, $opt)} @$list);
485 # Prevent possibility of memory leak
489 $header = ::interpolate_html($opt->{header_template})
490 if $opt->{header_template};
491 if($header =~ /\S/) {
492 push @out, Vend::Tags->uc_attr_list($opt, $header);
499 $footer = ::interpolate_html($opt->{footer_template})
500 if $opt->{footer_template};
501 if($footer =~ /\S/) {
502 push @out, Vend::Tags->uc_attr_list($opt, $footer);
505 return join "\n", @out;
510 return old_simple(@_);
514 return dhtml_flyout(@_);
518 my($name, $opt, $template) = @_;
521 my $fdiv = $name . "_flyout";
523 my $vpf = $opt->{js_prefix} ||= 'mv_';
525 $template = <<EOF if $template !~ /\S/;
526 {MV_LEVEL:}<div>{PAGE?}{MV_SPACER}<a id="{CODE}" href="{PAGE}" onMouseOver="${vpf}mousein(this)" onMouseOut="${vpf}mouseout(this)" title="{DESCRIPTION}" class="$opt->{link_class}">{NAME}</a>{/PAGE?}{PAGE:}{MV_SPACER}{NAME}{/MV_SPACER}{/PAGE:}</div>{/MV_LEVEL:}
529 $opt->{cursor_type} ||= 'hand';
530 $opt->{flyout_style} ||= <<EOF;
534 font-family: verdana,arial;
536 text-decoration: none;
540 $opt->{anchor_down} = is_yes($opt->{anchor_down}) || 0;
541 my $top_timeout = $opt->{timeout} || 1000;
544 <script language="JavaScript1.3">
545 var ${vpf}timeoutCode = -1;
546 var ${vpf}mydiv = '$fdiv';
547 var ${vpf}lines = new Array;
551 start => $opt->{tree_selector} || $opt->{name},
552 file => $opt->{file},
553 table => $opt->{table} || $::Variable->{MV_TREE_TABLE} || 'tree',
554 master => $opt->{tree_master} || 'parent_fld',
555 subordinate => 'code',
559 sort => $opt->{sort} || 'code',
561 timed => $opt->{timed},
563 _transform => $opt->{_transform},
566 for(@{$opt->{_transform} || []}) {
572 if($opt->{iterator}) {
573 $o{iterator} = $opt->{iterator};
574 $main = Vend::Tags->tree(\%o);
575 $rows = $o{object}{mv_results};
578 $o{iterator} = \&transforms_only;
579 Vend::Tags->tree(\%o);
581 delete $o{_transform};
583 for(@{$o{object}{mv_results}}) {
584 next if $_->{deleted};
585 push @o, $_ unless $_->{deleted};
586 $main .= tree_line(undef, $_, \%o);
591 $rows->[-1]{mv_last_row} = 1 if @$rows;
593 # Prevent possibility of memory leak, reset last_line/first_line
599 my @levels = grep !$seen{$_}++, map { $_->{mv_level} } @$rows;
600 @levels = sort { $a <=> $b } @levels;
601 my $last = $#levels || 0;
605 var ${vpf}anchor_down = $opt->{anchor_down};
606 var ${vpf}link_prepend = '$opt->{link_prepend}';
607 var ${vpf}link_target = '$opt->{link_target}';
608 var ${vpf}last_level = $last;
609 var ${vpf}link_class = '$opt->{link_class}';
610 var ${vpf}link_class_open = '$opt->{link_class_open}';
611 var ${vpf}link_class_closed = '$opt->{link_class_closed}';
612 var ${vpf}link_style = '$opt->{link_style}';
613 var ${vpf}link_style_open = '$opt->{link_style_open}';
614 var ${vpf}link_style_closed = '$opt->{link_style_closed}';
615 var ${vpf}submenu_image_right = '$opt->{submenu_image_right}';
616 var ${vpf}submenu_image_left = '$opt->{submenu_image_left}';
618 push @out, <<EOF unless $opt->{no_emit_code};
621 // If you want to move these functions to the HEAD
622 function ${vpf}menu_link (idx) {
624 if( ${vpf}browserType() == "other" )
627 var l = ${vpf}lines[ idx ];
630 alert("Bad idx=" + idx + ", no line there.");
636 var out = '<tr><td id="' + l[0] + 'left"';
637 if(l[${vpf}MV_CHILDREN] > 0) {
639 mouseo_beg = ' onMouseOver="${vpf}mousein(this,';
640 mouseo_beg += l[${vpf}MV_LEVEL] + ',';
642 out += mouseo_beg + l[0] + mouseo_end;
647 if(${vpf}submenu_image_left && l[${vpf}MV_CHILDREN] > 0) {
648 if(${vpf}submenu_image_left.substr(0,1) == '<')
649 out += ${vpf}submenu_image_left;
651 out += '<img src="' + ${vpf}submenu_image_left + '" border="0"$Vend::Xtrailer>';
653 out += '</td><td><div';
655 if(l[${vpf}MV_CHILDREN] > 0) {
656 out += ' id="' + l[0] + '"' + mouseo_beg + "''" + mouseo_end;
659 var tstyle = ${vpf}link_style;
660 var tclass = ${vpf}link_class;
661 var ttarget = l[${vpf}TARGET];
663 ttarget = ${vpf}link_target;
664 var tprepend = ${vpf}link_prepend;
666 out = out + '<a href="' + tprepend + l[ ${vpf}PAGE ] + '"';
668 out = out + ' class="' + tclass + '"';
670 out = out + ' style="' + tstyle + '"';
672 out = out + ' target="' + ttarget + '"';
673 if(l[${vpf}DESCRIPTION])
674 out = out + ' title="' + l[ ${vpf}DESCRIPTION ] + '"';
676 out = out + l[ ${vpf}NAME ] + '</a>';
679 out = out + l[ ${vpf}NAME ];
681 // alert("build idx=" + idx + " into: " + out);
683 out += '</div></td><td id="' + l[0] + 'right"';
685 if(l[${vpf}MV_CHILDREN] > 0) {
686 out += mouseo_beg + l[0] + mouseo_end;
690 if(${vpf}submenu_image_right && l[${vpf}MV_CHILDREN] > 0) {
691 if(${vpf}submenu_image_right.substr(0,1) == '<')
692 out += ${vpf}submenu_image_right;
694 out += '<img src="' + ${vpf}submenu_image_right + '" border="0"$Vend::Xtrailer>';
701 function ${vpf}mousein (obj,level,otherid) {
702 if( ${vpf}browserType() == "other" )
705 if(otherid != '' && otherid != undefined)
706 obj = document.getElementById(otherid);
708 if(level == undefined)
712 var divname = ${vpf}mydiv + level;
714 var fod = document.getElementById( divname );
715 if(fod == undefined) {
718 fod.style.display = 'none';
719 clearTimeout( ${vpf}timeoutCode );
720 ${vpf}timeoutCode = -1;
722 var html = '<table cellpadding="0" cellspacing="0" border="0">';
726 digid = digid.replace(/^$vpf/, '');
727 for(var j = 0; j < ${vpf}lines.length; j++) {
728 if(${vpf}lines[j][0] == digid) {
737 var l = ${vpf}lines[idx];
738 var currentlevel = l[${vpf}MV_LEVEL];
739 if(currentlevel == undefined)
742 ${vpf}menuClear(currentlevel);
743 if(l[${vpf}MV_CHILDREN] < 1)
746 var x = ${vpf}getRightX( obj, currentlevel ) + 1;
747 var y = ${vpf}getTopX( obj, currentlevel );
748 var menu = fod.style;
749 menu.left = x + "px";
751 menu.display = 'block';
754 for( i = idx + 1; ; i++ )
756 var l = ${vpf}lines[i];
757 // alert("running link for level=" + l[${vpf}MV_LEVEL] + ", line=" + l);
758 if(l == undefined || l[${vpf}MV_LEVEL] < level)
760 if(l[${vpf}MV_LEVEL] == level)
761 html += ${vpf}menu_link(i);
764 fod.innerHTML = html;
767 function ${vpf}getRightX( obj, level )
769 if( ${vpf}browserType() == "other" )
773 var x = obj.offsetParent;
776 while(x.offsetParent != undefined) {
780 pos = n + obj.offsetLeft;
781 if(${vpf}anchor_down != 1 || level > 0)
782 pos += obj.offsetWidth;
786 function ${vpf}getTopX( obj, level )
788 if( ${vpf}browserType() == "other" )
794 while(x.offsetParent != undefined) {
795 n += x.offsetParent.offsetTop;
798 pos = n + obj.offsetTop;
799 if(${vpf}anchor_down && level == 0)
800 pos += obj.offsetHeight;
804 function ${vpf}mouseout( obj, level )
806 if( ${vpf}browserType() == "other" )
809 if(level == undefined)
812 ${vpf}timeoutCode = setTimeout( "${vpf}menuClear();", $top_timeout );
815 function ${vpf}menuClear(level)
817 if( ${vpf}browserType() == "other" )
820 if (level == undefined)
823 for( var i = level; i <= ${vpf}last_level; i++) {
824 var thisdiv = ${vpf}mydiv + i;
825 var fod = document.getElementById( thisdiv );
827 fod.style.display = 'none';
829 clearTimeout( ${vpf}timeoutCode );
830 ${vpf}timeoutCode = -1;
833 function ${vpf}menuBusy()
835 if( ${vpf}browserType() == "other" )
838 clearTimeout( ${vpf}timeoutCode );
839 ${vpf}timeoutCode = -1;
842 var ${vpf}clientType = "unknown";
844 function ${vpf}browserType()
846 if( ${vpf}clientType != "unknown" )
847 return ${vpf}clientType;
849 ${vpf}clientType = "other";
851 if( document.getElementById )
852 ${vpf}clientType = "ie";
854 else if (document.layers) {
856 else if (document.getElementById) {
857 ${vpf}clientType = "ns6";
863 return ${vpf}clientType;
875 <div class="$opt->{flyout_class}" id="$fdiv$_" style="
880 OnMouseOver="${vpf}menuBusy();" OnMouseOut="${vpf}mouseout();"></div>
885 $header = ::interpolate_html($opt->{header_template})
886 if $opt->{header_template};
887 if($header =~ /\S/) {
888 $header = Vend::Tags->uc_attr_list($opt, $header);
892 for my $row (@$rows) {
893 next if $row->{deleted};
894 extra_value($opt->{extra_value}, $row)
895 if $opt->{extra_value};
896 push @out, Vend::Tags->uc_attr_list($row, $template);
900 $footer = ::interpolate_html($opt->{footer_template})
901 if $opt->{footer_template};
902 if($footer =~ /\S/) {
903 $footer = Vend::Tags->uc_attr_list($opt, $footer);
907 return join "", @out;
911 my($name, $opt, $template) = @_;
915 my $vpf = $opt->{js_prefix} ||= 'mv_';
916 $opt->{toggle_class} ||= '';
917 $opt->{explode_url} ||= "javascript:${vpf}do_explode(); void(0)";
918 $opt->{collapse_url} ||= "javascript:${vpf}do_collapse(); void(0)";
919 my $explode_label = errmsg($opt->{explode_label} || 'Explode tree');
920 my $collapse_label = errmsg($opt->{collapse_label} || 'Collapse tree');
921 $opt->{header_template} ||= <<EOF;
923 <a href="{EXPLODE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$explode_label</a><br$Vend::Xtrailer>
924 <a href="{COLLAPSE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$collapse_label</a>
929 $header = ::interpolate_html($opt->{header_template})
930 if $opt->{header_template};
931 if($header =~ /\S/) {
932 $header = Vend::Tags->uc_attr_list($opt, $header);
936 $opt->{div_style} ||= '';
939 <div id="${vpf}treebox" style="visibility: Visible">
941 <script language="JavaScript1.3">
942 var ${vpf}lines = new Array;
943 var ${vpf}sary = new Array;
947 start => $opt->{tree_selector} || 'Products',
948 table => $opt->{table} || $::Variable->{MV_TREE_TABLE} || 'tree',
949 master => $opt->{tree_master} || 'parent_fld',
950 file => $opt->{file},
951 subordinate => 'code',
953 open_variable => $opt->{open_variable} || 'open',
954 sort => $opt->{sort} || 'code',
957 timed => $opt->{timed},
959 _transform => $opt->{_transform},
962 for(@{$opt->{_transform} || []}) {
968 if($opt->{iterator}) {
969 $o{iterator} = $opt->{iterator};
970 $main = Vend::Tags->tree(\%o);
971 $rows = $o{object}{mv_results};
974 $o{iterator} = \&transforms_only;
975 Vend::Tags->tree(\%o);
977 delete $o{_transform};
979 for(@{$o{object}{mv_results}}) {
980 next if $_->{deleted};
981 push @o, $_ unless $_->{deleted};
982 $main .= tree_line(undef, $_, \%o);
987 $rows->[-1]{mv_last_row} = 1 if @$rows;
989 my $openvar = $opt->{open_variable} || 'open';
992 if(defined $CGI::values{$openvar}) {
993 $::Scratch->{dhtml_tree_open} = $CGI::values{$openvar};
996 $CGI::values{$openvar} = $::Scratch->{dhtml_tree_open};
998 my $out = " var ${vpf}openstatus = [";
999 my @open = split /,/, $CGI::values{$openvar};
1002 my %hsh = (map { ($_, 1) } @open);
1004 for(0 .. $open[$#open]) {
1005 push @o, ($hsh{$_} ? 1 : 0);
1007 $out .= join ",", @o;
1009 $out .= " var ${vpf}explode = ";
1010 $out .= $CGI::values{$opt->{explode_variable} || 'explode'} ? 1 : 0;
1012 $out .= " var ${vpf}collapse = ";
1013 $out .= $CGI::values{$opt->{collapse_variable} || 'collapse'} ? 1 : 0;
1018 my $Tag = new Vend::Tags;
1020 if($opt->{specific_image_toggle}) {
1021 $opt->{specific_image_toggle} =~ s/\D+//;
1022 if(defined $opt->{specific_image_base}) {
1023 $opt->{specific_image_base} =~ s:/*$:/:;
1026 $opt->{specific_image_base} = $Vend::Cfg->{ImageDir};
1030 if($opt->{specific_image_link}) {
1031 if(defined $opt->{specific_image_base}) {
1032 $opt->{specific_image_base} =~ s:/*$:/:;
1035 $opt->{specific_image_base} = $Vend::Cfg->{ImageDir};
1039 $opt->{image_link_extra} = $Tag->jsq($opt->{image_link_extra});
1040 $opt->{image_link_extra} ||= qq{'border="0"'};
1042 $opt->{specific_image_toggle} ||= 0;
1044 $opt->{img_node} ||= 'node.gif';
1045 $opt->{img_lastnode} ||= 'lastnode.gif';
1046 $opt->{img_spacenode} ||= 'vertline.gif';
1047 my $node_extra = $opt->{img_clear_extra};
1048 $node_extra =~ s/\bheight\s*=\s*"?\d+"?\s*//;
1050 for(qw/img_node img_lastnode img_spacenode/) {
1051 $opt->{$_} = $Tag->image({
1054 extra => 'align=absbottom',
1056 #::logDebug("$_=$opt->{$_}");
1060 $opt->{img_node} =~ m{\bwidth\s*=\s*"?(\d+)"?}
1061 and $canonwidth = $1;
1064 $opt->{img_node} =~ m{\bheight\s*=\s*"?(\d+)"?}
1065 and $canonheight = $1;
1068 $opt->{toggle_anchor_clear} =~ s{\bwidth\s*=\s*"*\d+"*}{width="$canonwidth"};
1071 $opt->{toggle_anchor_clear} =~ s{\bheight\s*=\s*"*\d+"*}{height="$canonheight"};
1074 $opt->{toggle_anchor_clear} =~ s{\balign\s*=\s*"*\w+"*}{}i;
1075 $opt->{toggle_anchor_clear} =~ s{\s*>}{ align="absbottom">}i;
1077 #::logDebug("toggle_anchor_clear=$opt->{toggle_anchor_clear}");
1081 $opt->{icon_by_type} ||= qq{
1090 if($opt->{icon_by_type} and $ihash = get_option_hash($opt->{icon_by_type}) ) {
1092 var ${vpf}icon_by_type = 1;
1093 var ${vpf}icon = new Array;
1094 var ${vpf}img_node = '$opt->{img_node}';
1095 var ${vpf}img_lastnode = '$opt->{img_lastnode}';
1096 var ${vpf}img_spacenode = '$opt->{img_spacenode}';
1097 if(! ${vpf}img_lastnode)
1098 ${vpf}img_lastnode = ${vpf}img_node;
1101 my $img = $Tag->image({
1102 src => $ihash->{$_},
1105 push @out, qq{${vpf}icon['$_'] = '$img';\n};
1110 var ${vpf}icon_by_type = 0;
1114 $opt->{no_open} = $opt->{no_open} ? 1 : 0;
1117 var ${vpf}next_level = 0;
1118 var ${vpf}no_open = $opt->{no_open};
1119 var ${vpf}no_wrap = '$opt->{no_wrap}';
1120 var ${vpf}openstring = '';
1121 var ${vpf}link_prepend = '$opt->{link_prepend}';
1122 var ${vpf}link_target = '$opt->{link_target}';
1123 var ${vpf}link_class = '$opt->{link_class}';
1124 var ${vpf}link_class_open = '$opt->{link_class_open}';
1125 var ${vpf}link_class_closed = '$opt->{link_class_closed}';
1126 var ${vpf}link_style = '$opt->{link_style}';
1127 var ${vpf}link_style_open = '$opt->{link_style_open}';
1128 var ${vpf}link_style_closed = '$opt->{link_style_closed}';
1129 var ${vpf}specific_image_toggle = $opt->{specific_image_toggle};
1130 var ${vpf}specific_image_base = '$opt->{specific_image_base}';
1131 var ${vpf}specific_image_link;
1132 var ${vpf}image_link_extra = $opt->{image_link_extra};
1133 var ${vpf}toggle_class = '$opt->{toggle_class}';
1134 var ${vpf}toggle_anchor_clear = '$opt->{toggle_anchor_clear}';
1135 var ${vpf}toggle_anchor_closed = '$opt->{toggle_anchor_closed}';
1136 var ${vpf}toggle_anchor_open = '$opt->{toggle_anchor_open}';
1137 var ${vpf}treebox = document.getElementById('${vpf}treebox');
1138 if(${vpf}image_link_extra)
1139 ${vpf}image_link_extra = ' ' + ${vpf}image_link_extra;
1143 push @out, "${vpf}specific_image_link = 1;"
1144 if $opt->{specific_image_link};
1146 push @out, <<EOF unless $opt->{no_emit_code};
1148 function ${vpf}image_link (rec) {
1149 if(rec == undefined)
1152 if(rec[ ${vpf}IMG_UP ]) {
1154 out += ${vpf}specific_image_base;
1155 out += rec[ ${vpf}IMG_UP ];
1157 out += ${vpf}image_link_extra;
1158 out += '$Vend::Xtrailer>';
1159 // alert('img=' + out);
1162 out = rec[${vpf}NAME];
1169 function ${vpf}tree_link (idx) {
1174 out += '<div style="white-space: nowrap; margin: 0; padding: 0">';
1177 var l = ${vpf}lines[idx];
1178 var nxt_l = ${vpf}lines[idx + 1];
1182 if(l == undefined) {
1183 alert("Bad idx=" + idx + ", no line there.");
1187 if(l[${vpf}MV_LEVEL] > ${vpf}next_level)
1190 var spec_toggle = 0;
1191 if(${vpf}specific_image_toggle > 0) {
1192 var toglevel = ${vpf}specific_image_toggle - 1;
1193 // if(alert_shown == undefined) {
1194 // alert('specific image toggle triggered, toglevel=' + toglevel + ", mv_level=" + l[${vpf}MV_LEVEL]);
1197 if(l[${vpf}MV_LEVEL] <= toglevel) {
1202 var needed = l[${vpf}MV_LEVEL];
1205 if(l[${vpf}MV_LEVEL] && ${vpf}icon_by_type) {
1207 for(k = idx + 1; ${vpf}lines[k] && ${vpf}lines[k][${vpf}MV_LEVEL] > l[${vpf}MV_LEVEL]; k++) {
1210 if( ! ${vpf}lines[k] || ${vpf}lines[k][${vpf}MV_LEVEL] < l[${vpf}MV_LEVEL] ) {
1211 nodeimg = ${vpf}img_lastnode;
1212 ${vpf}sary[needed] = ${vpf}toggle_anchor_clear;
1215 nodeimg = ${vpf}img_node;
1216 ${vpf}sary[needed] = ${vpf}img_spacenode;
1220 ${vpf}sary[needed] = ' ';
1225 if(${vpf}icon_by_type && needed) {
1229 for(i = 1; i <= needed; i++)
1230 out += ${vpf}sary[i];
1232 var tstyle = ${vpf}link_style;
1233 var tclass = ${vpf}link_class;
1234 var ttarget = l[${vpf}TARGET];
1236 ttarget = ${vpf}link_target;
1237 var tprepend = ${vpf}link_prepend;
1238 if(l[${vpf}MV_CHILDREN] > 0) {
1239 if(l[${vpf}MV_LEVEL] && ${vpf}icon_by_type) {
1241 for(k = idx; ${vpf}lines[k][${vpf}MV_LEVEL] > l[${vpf}MV_LEVEL]; k++) {
1246 if(${vpf}openstatus[idx] == 1) {
1247 tclass = ${vpf}link_class_open;
1248 tstyle = ${vpf}link_style_open;
1249 if(spec_toggle > 0) {
1250 tanchor = '<img border="0" align="absbottom" src="' + ${vpf}specific_image_base + l[${vpf}IMG_DN] + '"$Vend::Xtrailer>';
1253 tanchor = ${vpf}toggle_anchor_open;
1255 ${vpf}next_level = l[${vpf}MV_LEVEL] + 1;
1258 tclass = ${vpf}link_class_closed;
1259 tstyle = ${vpf}link_style_closed;
1260 if(spec_toggle > 0) {
1261 tanchor = '<img border="0" align="absbottom" src="' + ${vpf}specific_image_base + l[${vpf}IMG_UP] + '"$Vend::Xtrailer>';
1262 // if(alert_shown < 2) {
1263 // alert('tanchor=' + tanchor);
1268 tanchor = ${vpf}toggle_anchor_closed;
1270 ${vpf}next_level = l[${vpf}MV_LEVEL];
1273 out = out + '<a href="javascript:${vpf}toggit(' + idx + ');void(0)"';
1275 out = out + ' class="' + tclass + '"';
1277 out = out + ' style="' + tstyle + '"';
1279 out = out + tanchor;
1283 if(! ${vpf}icon_by_type)
1284 out = out + ${vpf}toggle_anchor_clear;
1285 next_level = l[${vpf}MV_LEVEL];
1288 if(spec_toggle == 0) {
1290 out = out + '<a href="' + tprepend + l[${vpf}PAGE];
1293 out += ${vpf}openstring;
1298 out = out + ' class="' + tclass + '"';
1300 out = out + ' style="' + tstyle + '"';
1302 out = out + ' target="' + ttarget + '"';
1303 if(l[${vpf}DESCRIPTION])
1304 out = out + ' title="' + l[${vpf}DESCRIPTION] + '"';
1306 if(${vpf}icon_by_type) {
1307 var fn = l[ ${vpf}PAGE ];
1308 var fpos = fn.lastIndexOf('.');
1309 var ext = fn.substr(fpos + 1);
1311 if(${vpf}img_node) {
1315 if(${vpf}icon[ ext ]) {
1316 out += '<img border="0" align="absbottom" src="';
1317 out += ${vpf}icon[ ext ];
1318 out += '"$Vend::Xtrailer>';
1321 if(${vpf}specific_image_link)
1322 out += ${vpf}image_link(l);
1324 out += l[${vpf}NAME];
1328 if(tstyle || tclass) {
1331 out += ' class="' + tclass + '"';
1333 out += ' style="' + tstyle + '"';
1334 out += ">" + l[${vpf}NAME] + '</span>';
1337 out = out + l[${vpf}NAME];
1346 out += "<br$Vend::Xtrailer>";
1352 function ${vpf}toggit (idx) {
1354 var l = ${vpf}lines[idx];
1355 if(l == undefined) {
1356 alert("bad index " + idx);
1359 if(l[${vpf}MV_CHILDREN] < 1) {
1360 alert("nothing to toggle at index " + idx);
1364 ${vpf}openstatus[idx] = ${vpf}openstatus[idx] == 1 ? 0 : 1;
1365 ${vpf}gen_openstring();
1366 ${vpf}rewrite_tree();
1368 function ${vpf}gen_openstring () {
1369 ${vpf}openstring = '';
1371 for(var p = 0; p < ${vpf}openstatus.length; p++) {
1372 if(${vpf}openstatus[p])
1373 ${vpf}openstring += p + ',';
1375 ${vpf}openstring = ${vpf}openstring.replace(/,+\$/, '');
1378 function ${vpf}do_explode () {
1379 for(var i = 0; i < ${vpf}lines.length; i++)
1380 ${vpf}openstatus[i] = 1;
1381 ${vpf}gen_openstring();
1382 ${vpf}rewrite_tree();
1384 function ${vpf}do_collapse () {
1385 for(var i = 0; i < ${vpf}lines.length; i++)
1386 ${vpf}openstatus[i] = 0;
1387 ${vpf}gen_openstring();
1388 ${vpf}rewrite_tree();
1390 function ${vpf}rewrite_tree () {
1392 for(i = 0; i < ${vpf}lines.length; i++) {
1393 thing = thing + ${vpf}tree_link(i);
1395 ${vpf}treebox.innerHTML = thing;
1396 ${vpf}next_level = 0;
1405 if(${vpf}collapse == 1 || ${vpf}explode == 1) {
1406 ${vpf}openstatus.length = 0;
1408 for( var i = 0; i < ${vpf}lines.length; i++) {
1409 if(${vpf}openstatus[i] == undefined)
1410 ${vpf}openstatus[i] = ${vpf}explode;
1415 ${vpf}gen_openstring();
1416 ${vpf}rewrite_tree();
1421 $footer = ::interpolate_html($opt->{footer_template})
1422 if $opt->{footer_template};
1423 if($footer =~ /\S/) {
1424 $footer = Vend::Tags->uc_attr_list($opt, $footer);
1428 return join "\n", @out;
1432 my($name, $opt, $template) = @_;
1436 my $vpf = $opt->{js_prefix} ||= 'mv_';
1437 $opt->{toggle_class} ||= '';
1438 $opt->{explode_url} ||= "javascript:${vpf}do_explode(); void(0)";
1439 $opt->{collapse_url} ||= "javascript:${vpf}do_collapse(); void(0)";
1440 my $explode_label = errmsg($opt->{explode_label} || 'Explode tree');
1441 my $collapse_label = errmsg($opt->{collapse_label} || 'Collapse tree');
1442 $opt->{header_template} ||= <<EOF;
1444 <a href="{EXPLODE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$explode_label</a><br$Vend::Xtrailer>
1445 <a href="{COLLAPSE_URL}" {LINK_STYLE?} style="{LINK_STYLE}"{/LINK_STYLE?} {LINK_CLASS?} class="{LINK_CLASS}"{/LINK_CLASS?}>$collapse_label</a>
1450 $header = ::interpolate_html($opt->{header_template})
1451 if $opt->{header_template};
1452 if($header =~ /\S/) {
1453 $header = Vend::Tags->uc_attr_list($opt, $header);
1457 $opt->{div_style} ||= '';
1460 <div id="${vpf}treebox" style="visibility: Visible">
1462 <script language="JavaScript1.3">
1463 var ${vpf}lines = new Array;
1467 start => $opt->{tree_selector} || 'Products',
1468 table => $opt->{table} || $::Variable->{MV_TREE_TABLE} || 'tree',
1469 master => $opt->{tree_master} || 'parent_fld',
1470 file => $opt->{file},
1471 subordinate => 'code',
1473 open_variable => $opt->{open_variable} || 'open',
1474 sort => $opt->{sort} || 'code',
1477 timed => $opt->{timed},
1479 _transform => $opt->{_transform},
1482 for(@{$opt->{_transform} || []}) {
1483 $o{$_} = $opt->{$_};
1488 if($opt->{iterator}) {
1489 $o{iterator} = $opt->{iterator};
1490 $main = Vend::Tags->tree(\%o);
1491 $rows = $o{object}{mv_results};
1494 $o{iterator} = \&transforms_only;
1495 Vend::Tags->tree(\%o);
1497 delete $o{_transform};
1499 for(@{$o{object}{mv_results}}) {
1500 next if $_->{deleted};
1501 push @o, $_ unless $_->{deleted};
1502 $main .= tree_line(undef, $_, \%o);
1507 $rows->[-1]{mv_last_row} = 1 if @$rows;
1509 my $openvar = $opt->{open_variable} || 'open';
1512 if(defined $CGI::values{$openvar}) {
1513 $::Scratch->{dhtml_tree_open} = $CGI::values{$openvar};
1516 $CGI::values{$openvar} = $::Scratch->{dhtml_tree_open};
1518 my $out = " var ${vpf}openstatus = [";
1519 my @open = split /,/, $CGI::values{$openvar};
1522 my %hsh = (map { ($_, 1) } @open);
1524 for(0 .. $open[$#open]) {
1525 push @o, ($hsh{$_} ? 1 : 0);
1527 $out .= join ",", @o;
1529 $out .= " var ${vpf}explode = ";
1530 $out .= $CGI::values{$opt->{explode_variable} || 'explode'} ? 1 : 0;
1532 $out .= " var ${vpf}collapse = ";
1533 $out .= $CGI::values{$opt->{collapse_variable} || 'collapse'} ? 1 : 0;
1538 if($opt->{specific_image_toggle}) {
1539 $opt->{specific_image_toggle} =~ s/\D+//;
1540 if(defined $opt->{specific_image_base}) {
1541 $opt->{specific_image_base} =~ s:/*$:/:;
1544 $opt->{specific_image_base} = $Vend::Cfg->{ImageDir};
1548 if($opt->{specific_image_link}) {
1549 if(defined $opt->{specific_image_base}) {
1550 $opt->{specific_image_base} =~ s:/*$:/:;
1553 $opt->{specific_image_base} = $Vend::Cfg->{ImageDir};
1557 $opt->{image_link_extra} = Vend::Tags->jsq($opt->{image_link_extra});
1558 $opt->{image_link_extra} ||= qq{'border="0"'};
1560 $opt->{specific_image_toggle} ||= 0;
1563 var ${vpf}next_level = 0;
1564 var ${vpf}openstring = '';
1565 var ${vpf}link_class = '$opt->{link_class}';
1566 var ${vpf}link_class_open = '$opt->{link_class_open}';
1567 var ${vpf}link_class_closed = '$opt->{link_class_closed}';
1568 var ${vpf}link_style = '$opt->{link_style}';
1569 var ${vpf}link_style_open = '$opt->{link_style_open}';
1570 var ${vpf}link_style_closed = '$opt->{link_style_closed}';
1571 var ${vpf}specific_image_toggle = $opt->{specific_image_toggle};
1572 var ${vpf}specific_image_base = '$opt->{specific_image_base}';
1573 var ${vpf}specific_image_link;
1574 var ${vpf}image_link_extra = $opt->{image_link_extra};
1575 var ${vpf}toggle_class = '$opt->{toggle_class}';
1576 var ${vpf}toggle_anchor_clear = '$opt->{toggle_anchor_clear}';
1577 var ${vpf}toggle_anchor_closed = '$opt->{toggle_anchor_closed}';
1578 var ${vpf}toggle_anchor_open = '$opt->{toggle_anchor_open}';
1579 var ${vpf}treebox = document.getElementById('${vpf}treebox');
1580 if(${vpf}image_link_extra)
1581 ${vpf}image_link_extra = ' ' + ${vpf}image_link_extra;
1585 push @out, "${vpf}specific_image_link = 1;"
1586 if $opt->{specific_image_link};
1588 push @out, <<EOF unless $opt->{no_emit_code};
1590 function ${vpf}image_link (rec) {
1591 if(rec == undefined)
1594 if(rec[ ${vpf}IMG_UP ]) {
1596 out += ${vpf}specific_image_base;
1597 out += rec[ ${vpf}IMG_UP ];
1599 out += ${vpf}image_link_extra;
1600 out += '$Vend::Xtrailer>';
1601 // alert('img=' + out);
1604 out = rec[${vpf}NAME];
1609 function ${vpf}tree_link (idx) {
1613 var l = ${vpf}lines[idx];
1615 if(l == undefined) {
1616 alert("Bad idx=" + idx + ", no line there.");
1620 if(l[${vpf}MV_LEVEL] > ${vpf}next_level)
1623 var spec_toggle = 0;
1624 if(${vpf}specific_image_toggle > 0) {
1625 var toglevel = ${vpf}specific_image_toggle - 1;
1626 // if(alert_shown == undefined) {
1627 // alert('specific image toggle triggered, toglevel=' + toglevel + ", mv_level=" + l[${vpf}MV_LEVEL]);
1630 if(l[${vpf}MV_LEVEL] <= toglevel) {
1636 var needed = l[${vpf}MV_LEVEL];
1637 for(i = 1; i <= needed; i++)
1638 out = out + ' ';
1640 var tstyle = ${vpf}link_style;
1641 var tclass = ${vpf}link_class;
1642 if(l[${vpf}MV_CHILDREN] > 0) {
1643 if(${vpf}openstatus[idx] == 1) {
1644 tclass = ${vpf}link_class_open;
1645 tstyle = ${vpf}link_style_open;
1646 if(spec_toggle > 0) {
1647 tanchor = '<img border="0" src="' + ${vpf}specific_image_base + l[${vpf}IMG_DN] + '"$Vend::Xtrailer>';
1648 // if(alert_shown < 2) {
1649 // alert('tanchor=' + tanchor);
1654 tanchor = ${vpf}toggle_anchor_open;
1656 ${vpf}next_level = l[${vpf}MV_LEVEL] + 1;
1659 tclass = ${vpf}link_class_closed;
1660 tstyle = ${vpf}link_style_closed;
1661 if(spec_toggle > 0) {
1662 tanchor = '<img border="0" src="' + ${vpf}specific_image_base + l[${vpf}IMG_UP] + '"$Vend::Xtrailer>';
1663 // if(alert_shown < 2) {
1664 // alert('tanchor=' + tanchor);
1669 tanchor = ${vpf}toggle_anchor_closed;
1671 ${vpf}next_level = l[${vpf}MV_LEVEL];
1674 out = out + '<a href="javascript:${vpf}toggit(' + idx + ');void(0)"';
1676 out = out + ' class="' + tclass + '"';
1678 out = out + ' style="' + tstyle + '"';
1680 out = out + tanchor;
1684 out = out + ${vpf}toggle_anchor_clear;
1685 next_level = l[${vpf}MV_LEVEL];
1688 if(spec_toggle == 0) {
1690 out = out + '<a href="' + l[${vpf}PAGE] + ${vpf}openstring + '"';
1692 out = out + ' class="' + tclass + '"';
1694 out = out + ' style="' + tstyle + '"';
1695 if(l[${vpf}DESCRIPTION])
1696 out = out + ' title="' + l[${vpf}DESCRIPTION] + '"';
1698 if(${vpf}specific_image_link)
1699 out += ${vpf}image_link(l);
1701 out += l[${vpf}NAME];
1705 out = out + l[${vpf}NAME];
1708 out = out + "<br$Vend::Xtrailer>";
1713 function ${vpf}toggit (idx) {
1715 var l = ${vpf}lines[idx];
1716 if(l == undefined) {
1717 alert("bad index " + idx);
1720 if(l[${vpf}MV_CHILDREN] < 1) {
1721 alert("nothing to toggle at index " + idx);
1725 ${vpf}openstatus[idx] = ${vpf}openstatus[idx] == 1 ? 0 : 1;
1726 ${vpf}gen_openstring();
1727 ${vpf}rewrite_tree();
1729 function ${vpf}gen_openstring () {
1730 ${vpf}openstring = '';
1731 for(var p = 0; p < ${vpf}openstatus.length; p++) {
1732 if(${vpf}openstatus[p])
1733 ${vpf}openstring += p + ',';
1735 ${vpf}openstring = ${vpf}openstring.replace(/,+\$/, '');
1738 function ${vpf}do_explode () {
1739 for(var i = 0; i < ${vpf}lines.length; i++)
1740 ${vpf}openstatus[i] = 1;
1741 ${vpf}gen_openstring();
1742 ${vpf}rewrite_tree();
1744 function ${vpf}do_collapse () {
1745 for(var i = 0; i < ${vpf}lines.length; i++)
1746 ${vpf}openstatus[i] = 0;
1747 ${vpf}gen_openstring();
1748 ${vpf}rewrite_tree();
1750 function ${vpf}rewrite_tree () {
1752 for(i = 0; i < ${vpf}lines.length; i++) {
1753 thing = thing + ${vpf}tree_link(i);
1755 ${vpf}treebox.innerHTML = thing;
1756 ${vpf}next_level = 0;
1765 if(${vpf}collapse == 1 || ${vpf}explode == 1) {
1766 ${vpf}openstatus.length = 0;
1768 for( var i = 0; i < ${vpf}lines.length; i++) {
1769 if(${vpf}openstatus[i] == undefined)
1770 ${vpf}openstatus[i] = ${vpf}explode;
1775 ${vpf}gen_openstring();
1776 ${vpf}rewrite_tree();
1781 $footer = ::interpolate_html($opt->{footer_template})
1782 if $opt->{footer_template};
1783 if($footer =~ /\S/) {
1784 $footer = Vend::Tags->uc_attr_list($opt, $footer);
1788 return join "\n", @out;
1792 my %menu_default_img = (
1801 $regex = $::Variable->{MV_DHTML_BROWSER}
1802 and $regex = qr/$regex/;
1804 $regex ||= qr/MSIE [5-9].*Windows|Mozilla.*Gecko|Opera.*[7-9]/;
1805 return $Vend::Session->{browser} =~ $regex;
1808 ## Returns a link line for a tree walk without DHTML.
1810 my ($template, $row, $opt) = @_;
1812 for(@{$opt->{_transform}}) {
1813 return unless $transform{$_}->($row, $opt->{$_});
1817 {MV_SPACER}{MV_CHILDREN?}<a href="{TOGGLE_URL}" class="{TOGGLE_CLASS}" style="{TOGGLE_STYLE}">{TOGGLE_ANCHOR}</a>{PAGE?}<a href="{HREF}" class="{TOGGLE_CLASS}" style="{TOGGLE_STYLE}">{/PAGE?}{NAME}{PAGE?}</a>{/PAGE?}{/MV_CHILDREN?}{MV_CHILDREN:}{TOGGLE_ANCHOR}{PAGE?}<A href="{HREF}" class="{LINK_CLASS}" style="{LINK_STYLE}">{/PAGE?}{NAME}{PAGE?}</a>{/PAGE?}{/MV_CHILDREN:}<br$Vend::Xtrailer>
1820 if(! $row->{page}) {
1822 elsif ($row->{page} =~ /^\w+:/ or $row->{page} =~ m{^/}) {
1823 $row->{href} = $row->{page};
1826 unless($row->{form} =~ /[\r\n]/) {
1827 $row->{form} = join "\n", split $Global::UrlSplittor, $row->{form};
1829 my $add = ($::Scratch->{mv_add_dot_html} && $row->{page} !~ /\.\w+$/) || 0;
1830 $row->{href} = Vend::Tags->area({
1831 href => $row->{page},
1832 form => $row->{form},
1833 add_dot_html => $add,
1837 $row->{name} =~ s/ / /g;
1838 $opt->{toggle_base_url} ||= Vend::Tags->history_scan(
1839 { var_exclude => 'toggle,collapse,expand' }
1841 $row->{link_class} ||= $opt->{link_class};
1842 $row->{link_style} ||= $opt->{link_style};
1843 if($row->{mv_children}) {
1844 my $u = $opt->{toggle_base_url};
1845 $u .= $u =~ /\?/ ? $Global::UrlJoiner : "?";
1846 $u .= "toggle=$row->{code}";
1847 $row->{toggle_url} = $u;
1848 if($row->{mv_toggled}) {
1849 $row->{toggle_anchor} = $opt->{toggle_anchor_open};
1850 $row->{toggle_class} = $opt->{link_class_open};
1851 $row->{toggle_style} = $opt->{link_style_open};
1854 $row->{toggle_anchor} = $opt->{toggle_anchor_closed};
1855 $row->{toggle_class} = $opt->{link_class_closed};
1856 $row->{toggle_style} = $opt->{link_style_closed};
1860 $row->{toggle_anchor} = $opt->{toggle_anchor_clear};
1862 extra_value($opt->{extra_value}, $row)
1863 if $opt->{extra_value};
1864 return Vend::Tags->uc_attr_list($row, $template);
1867 ## Returns a javascript line from a tree walk.
1868 ## Designed as a [tree ..] iterator, first iteration
1869 ## returns UPPERCASE var name index defines for the fields.
1871 my($template, $row, $opt) = @_;
1876 if (! defined $opt->{loopinc}) {
1877 my $vpf = $opt->{js_prefix} || 'mv_';
1878 $opt->{loopinc} = 0;
1879 $opt->{loopname} ||= $vpf . 'lines';
1894 if($opt->{loopfields}) {
1895 if(! ref($opt->{loopfields})) {
1896 my $fstring = $opt->{loopfields};
1897 $fstring =~ s/^\s+//;
1898 @$fields = split /[\s,\0]+/, $fstring;
1901 $fields = $opt->{loopfields};
1905 if($opt->{fields_repository}) {
1906 $opt->{fields_repository} = [ @$fields ];
1908 push @$fields, 'open';
1909 for(my $i = 1; $i < @$fields; $i++) {
1910 push @out, "var $vpf\U$fields->[$i]\E = $i;";
1913 $opt->{loopfields} = $fields;
1916 $fields = $opt->{loopfields};
1918 if(defined $opt->{next_level}) {
1919 return if $row->{mv_level} > $opt->{next_level};
1920 undef $opt->{next_level};
1923 for(@{$opt->{_transform}}) {
1924 my $status = $transform{$_}->($row, $opt->{$_});
1925 $opt->{next_level} = $row->{mv_level}
1927 return unless $status;
1930 if($row->{page} and $row->{page} !~ m{^(\w+:)?/}) {
1931 my $form = $row->{form};
1932 if($form and $form !~ /[\r\n]/) {
1933 $form = join "\n", split $Global::UrlSplittor, $form;
1936 my $add = ($::Scratch->{mv_add_dot_html} && $row->{page} !~ /\.\w+$/) || 0;
1938 $row->{page} = Vend::Tags->area({
1939 href => $row->{page},
1941 no_count => $opt->{timed},
1942 add_dot_html => $add,
1943 no_session_id => $opt->{timed},
1947 unless($opt->{no_open}) {
1948 if($row->{page} =~ m{\?.+=}) {
1949 $row->{page} .= "$Global::UrlJoiner$opt->{open_variable}=";
1952 $row->{page} .= "?$opt->{open_variable}=";
1957 my @values = @{$row}{@$fields};
1960 $_ = Vend::Tags->jsq($_) unless $_ eq '0' || /^[1-9](?:\d*\.)?\d*$/;
1962 push @out, "$opt->{loopname}\[" . $opt->{loopinc}++ . "] = [" . join(", ", @values) . "];";
1963 return join "\n", @out, '';
1966 sub transforms_only {
1967 my ($template, $row, $opt) = @_;
1970 if(ref($row) eq 'ARRAY') {
1971 $opt->{_fa} ||= $opt->{object}{mv_field_names};
1972 @line{@{$opt->{_fa}}} = @$row;
1976 for(@{$opt->{_transform}}) {
1977 $row->{deleted} = 1, return unless $transform{$_}->($row, $opt->{$_});
1984 my ($template, $row, $opt) = @_;
1986 # Set to a default if not passed
1987 $template ||= <<EOF unless $template =~ /\S/;
1995 <a href="{HREF}"{DESCRIPTION?} title="{DESCRIPTION}"{/DESCRIPTION?}>{NAME}</a><br$Vend::Xtrailer>
2000 if(ref($row) eq 'ARRAY') {
2001 $opt->{_fa} ||= $opt->{object}{mv_field_names};
2002 @line{@{$opt->{_fa}}} = @$row;
2006 $row->{mv_ip} = $opt->{mv_ip}++ || 0;
2007 $row->{mv_increment} = ++$opt->{mv_increment};
2009 for(@{$opt->{_transform}}) {
2010 return unless $transform{$_}->($row, $opt->{$_});
2013 #return $row->{name} if ! $row->{page} and $row->{name} =~ /^\s*</;
2014 if(! $row->{page}) {
2016 elsif ($row->{page} =~ /^\w+:/) {
2017 $row->{href} = $row->{page};
2020 unless($row->{form} =~ /[\r\n]/) {
2021 $row->{form} = join "\n", split $Global::UrlSplittor, $row->{form};
2023 my $add = $::Scratch->{mv_add_dot_html} && $row->{page} !~ /\.\w+$/;
2025 $row->{href} = Vend::Tags->area(
2027 href => $row->{page},
2028 form => $row->{form},
2029 add_dot_html => $add,
2033 extra_value($opt->{extra_value}, $row)
2034 if $opt->{extra_value};
2035 return Vend::Tags->uc_attr_list($row, $template);
2041 $afn =~ s{(.*)/(.*)}{$2};
2045 $afn = join "/", $dir, $afn;
2050 return $base unless -f $afn and -r _;
2052 open AFILE, "< $afn"
2053 or die "Cannot open annotation file $afn: $!\n";
2054 my $text = join "", <AFILE>;
2061 sub make_tree_from_directory {
2062 my ($dir, $level, $prepend, $outfile) = @_;
2063 my @files = glob "$dir/*";
2069 $record{msort} = $level;
2070 $record{name} = annfile($_);
2071 $record{description} = $_;
2073 push @out, \%record;
2074 push @out, make_tree_from_directory($_, $level + 1, $prepend);
2079 $fn =~ s:^/*[^/]+?/::;
2080 $record{page} = "$prepend$fn";
2085 push @out, \%record;
2089 return @out unless $outfile;
2091 open OUT, "> $outfile"
2093 logError("Couldn't write outfile %s: %s", $outfile, $!);
2097 my @fields = qw/msort name page description/;
2098 print OUT join("\t", 'code', @fields);
2102 print OUT join "\t", $code++, @{$_}{@fields};
2110 my $vpf = $opt->{js_prefix} || 'mv_';
2112 my $out = "<script>\n${vpf}openstatus = [";
2113 my @open = split /,/, $CGI::values{$opt->{open_variable} || 'open'};
2116 my %hsh = (map { ($_, 1) } @open);
2118 for(0 .. $open[$#open]) {
2119 push @o, ($hsh{$_} ? 1 : 0);
2121 $out .= join ",", @o;
2123 $out .= "${vpf}explode = ";
2124 $out .= $CGI::values{$opt->{explode_variable} || 'explode'} ? 1 : 0;
2126 $out .= "${vpf}collapse = ";
2127 $out .= $CGI::values{$opt->{collapse_variable} || 'collapse'} ? 1 : 0;
2129 $out .= "${vpf}gen_openstring();\n";
2130 $out .= "${vpf}rewrite_tree();\n</script>";
2134 my ($name, $opt, $template) = @_;
2136 if($opt->{open_script}) {
2137 return open_script($opt);
2140 Vend::Tags->tmp('mv_logical_page_used', $::Scratch->{mv_logical_page_used});
2141 reset_transforms($opt);
2143 if(! $name and ! $opt->{list}) {
2144 # Auto menu for pages
2145 if($::Scratch->{mv_menu}) {
2146 my @names= qw/code page form anchor description/;
2148 my %hash = map { ( $_, $i++) } @names;
2151 my @items = split m{(?:</li\s*>)\s*<li>\s*}i, $::Scratch->{mv_menu};
2153 my ($page, $anchor, $form, $desc);
2158 (["']) # mandatory quote
2161 }isx and $desc = $2;
2166 (["']?) # possible quote
2169 }isx and $page = $2;
2170 ($page, $form) = split /\?/, $page, 2
2174 push @rows, [ $code++, $page, $form, $anchor, $desc ];
2176 $opt->{list} = [ \@rows, \%hash, \@names ];
2180 my $page_name = $Global::Variable->{MV_PAGE};
2181 my $dir = Vend::Tags->var('MV_MENU_DIRECTORY', 2) || 'include/menus';
2182 while($page_name =~ s:/[^/]+$::) {
2183 my $fn = "$dir/auto/$page_name.txt";
2184 #::logDebug("page name=$page_name, testing for $fn");
2190 if(! $opt->{file} and -f "$dir/default.txt") {
2191 $opt->{file} = "$dir/default.txt";
2196 $opt->{dhtml_browser} = dhtml_browser()
2197 unless defined $opt->{dhtml_browser};
2198 $opt->{menu_type} ||= 'simple';
2200 my $prefix = $opt->{prefix} || 'menu';
2201 $opt->{link_class} ||= $::Variable->{MV_DEFAULT_LINK_CLASS};
2203 $opt->{parse_header_footer} = 1 unless defined $opt->{parse_header_footer};
2205 if($opt->{parse_header_footer}) {
2206 $opt->{parse_header} = $opt->{parse_footer} = 1;
2208 if($template and $template =~ s:\[$prefix-header\](.*?)\[/$prefix-header\]::si) {
2209 $opt->{header_template} = $1;
2211 if($template and $template =~ s:\[$prefix-footer\](.*?)\[/$prefix-footer\]::si) {
2212 $opt->{footer_template} = $1;
2216 my @ordered_transform = qw/full_interpolate indicator_page page_class indicator_class localize entities nbsp/;
2218 @ordered{@ordered_transform} = @ordered_transform;
2220 for(keys %transform) {
2221 next if $ordered{$_};
2222 next unless $opt->{$_};
2223 my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
2224 $opt->{$_} = \@fields;
2225 push @transform, $_;
2227 for(@ordered_transform) {
2228 next unless $opt->{$_};
2229 my @fields = grep /\S/, split /[\s,\0]+/, $opt->{$_};
2230 $opt->{$_} = \@fields;
2231 push @transform, $_;
2233 $opt->{_transform} = \@transform;
2235 if($opt->{menu_type} eq 'tree') {
2236 $opt->{link_class_open} ||= $opt->{link_class};
2237 $opt->{link_class_closed} ||= $opt->{link_class};
2238 if(is_yes($opt->{no_image})) {
2239 $opt->{no_image} = 1;
2240 $opt->{toggle_anchor_clear} ||= ' ';
2241 $opt->{toggle_anchor_closed} ||= '+';
2242 $opt->{toggle_anchor_open} ||= '-';
2245 $opt->{no_image} = 0;
2247 if($opt->{file_tree}) {
2248 for(qw/ no_open no_wrap /) {
2249 $opt->{$_} = 1 unless defined $opt->{$_};
2252 $opt->{img_open} ||= 'openfolder.gif';
2253 $opt->{img_closed} ||= 'closedfolder.gif';
2256 $opt->{toggle_anchor_open} = Vend::Tags->image( {
2257 src => $opt->{img_open} || $menu_default_img{open},
2259 extra => $opt->{img_open_extra} || 'align=absbottom',
2261 $opt->{toggle_anchor_closed} = Vend::Tags->image( {
2262 src => $opt->{img_closed} || $menu_default_img{closed},
2264 extra => $opt->{img_closed_extra} || 'align=absbottom',
2266 if($opt->{toggle_anchor_closed} =~ /\s+width="?(\d+)/i) {
2267 $opt->{img_clear_extra} ||= "height=1 width=$1";
2269 $opt->{toggle_anchor_clear} = Vend::Tags->image( {
2270 src => $opt->{img_clear} || $menu_default_img{clear},
2273 extra => $opt->{img_clear_extra},
2275 #::logDebug("toggle_anchor_clear=$opt->{toggle_anchor_clear}");
2278 if($opt->{use_file}) {
2279 $opt->{file} = $::Variable->{MV_MENU_DIRECTORY} || 'include/menus';
2280 if(! $opt->{name}) {
2281 logError("No file or name specified for menu.");
2283 my $nm = escape_chars($opt->{name});
2284 $opt->{file} .= "/$nm.txt";
2285 undef $opt->{file} unless -f $opt->{file};
2287 elsif($opt->{directory}) {
2288 my $d = "$Vend::Cfg->{ScratchDir}/filetree";
2289 mkdir $d, 0777 unless -d $d;
2290 $opt->{file} = "$Vend::Cfg->{ScratchDir}/filetree/$Vend::SessionID.txt";
2291 make_tree_from_directory(
2294 delete $opt->{link_prepend},
2298 logError("Unable to make tree from directory %s", $opt->{directory});
2303 return old_tree($name,$opt,$template) unless $opt->{dhtml_browser};
2304 return file_tree($name,$opt,$template) if $opt->{file_tree};
2305 return dhtml_tree($name,$opt,$template);
2307 elsif($opt->{menu_type} eq 'flyout') {
2308 $opt->{link_class_open} ||= $opt->{link_class};
2309 $opt->{link_class_closed} ||= $opt->{link_class};
2310 if(is_yes($opt->{no_image})) {
2311 $opt->{no_image} = 1;
2312 $opt->{toggle_anchor_clear} ||= ' ';
2313 $opt->{toggle_anchor_closed} ||= '+';
2314 $opt->{toggle_anchor_open} ||= '-';
2317 $opt->{no_image} = 0;
2319 $opt->{toggle_anchor_open} = Vend::Tags->image( {
2320 src => $opt->{img_open} || $menu_default_img{open},
2322 extra => $opt->{img_open_extra} || 'align="absbottom"',
2324 $opt->{toggle_anchor_closed} = Vend::Tags->image( {
2325 src => $opt->{img_closed} || $menu_default_img{closed},
2327 extra => $opt->{img_closed_extra} || 'align="absbottom"',
2329 if($opt->{toggle_anchor_closed} =~ /\s+width="?(\d+)/i) {
2330 $opt->{img_clear_extra} ||= qq{height="1" width="$1"};
2332 $opt->{toggle_anchor_clear} = Vend::Tags->image( {
2333 src => $opt->{img_clear} || $menu_default_img{clear},
2336 extra => $opt->{img_clear_extra},
2339 if($opt->{use_file}) {
2340 $opt->{file} = $::Variable->{MV_MENU_DIRECTORY} || 'include/menus';
2341 if(! $opt->{name}) {
2342 logError("No file or name specified for menu.");
2344 my $nm = escape_chars($opt->{name});
2345 $opt->{file} .= "/$nm.txt";
2346 undef $opt->{file} unless -f $opt->{file};
2349 return old_flyout($name,$opt,$template) unless $opt->{dhtml_browser};
2350 return dhtml_flyout($name,$opt,$template);
2352 elsif($opt->{menu_type} eq 'simple') {
2353 if($opt->{search} || $opt->{list}) {
2356 elsif(! $opt->{file}) {
2357 $opt->{file} = $::Variable->{MV_MENU_DIRECTORY} || 'include/menus';
2358 if(! $opt->{name}) {
2359 logError("No file or name specified for menu.");
2361 my $nm = escape_chars($opt->{name});
2362 $opt->{file} .= "/$nm.txt";
2364 return old_simple($name, $opt, $template) unless $opt->{dhtml_browser};
2365 return dhtml_simple($name, $opt, $template);
2368 logError("unknown menu_type %s", $opt->{menu_type});