diff --git a/connex.pl b/connex.pl index 3c470f9..29618fd 100755 --- a/connex.pl +++ b/connex.pl @@ -1,32 +1,40 @@ #!/usr/bin/perl -w - +#connex.pl Nightfall Express (nex://) browser +#TODO: Some branding (status popup on load?, Connex menu item?), About dialog +#TODO: Help dialog use strict; use warnings; use strict; use Curses::UI; use Net::Telnet; -use URI::Split; - +use URI::Split qw(uri_split uri_join); +use URI (); my $HOST_DEFAULT = "nightfall.city"; my $PATHSPEC_DEFAULT = ''; my $PORT_DEFAULT = 1900; -my $PROT_NEX = "nex://"; +my $SCHEME_NEX = "nex"; -my $home_default= $PROT_NEX . $HOST_DEFAULT; +my $home_default= $SCHEME_NEX . $HOST_DEFAULT; my $host = "nightfall.city"; my $port = "1900"; my $pathspec = ""; -my $docname = ""; +my $docname; # = ""; my $doctype = "txt"; my $dot_ext = "."; -my @history; +my $HOME_URL = uri_join($SCHEME_NEX,$HOST_DEFAULT); + +my $full_url = $HOME_URL; + +my @history; +my @page_links; my $statusbar; +my $navwindow; my $win1; my $cui = new Curses::UI( -color_support => 1 ); my $connect = new Net::Telnet (Timeout => 10, @@ -34,10 +42,11 @@ my $connect = new Net::Telnet (Timeout => 10, my @menu = ( { -label => 'File', -submenu => [ - { -label => 'Go to Link ^G', -value => \&goto_link_dialog }, - { -label => 'Change Site ^C', -value => \&goto_site_dialog }, + { -label => 'Go to Link ^G', -value => \&navigate_link_dialog }, + { -label => 'Choose Link >', -value => \&goto_link_dialog }, { -label => 'Back ^B', -value => \&goto_back }, - { -label => 'History ^H', -value => "\&history_status_dialog" }, + { -label => 'History ^H', -value => \&history_status_dialog }, + { -label => 'Page Links ^P', -value => \&page_links_dialog }, { -label => 'Exit ^Q', -value => \&exit_dialog } ] }, @@ -47,8 +56,8 @@ my @menu = ( sub exit_dialog() { my $return = $cui->dialog( - -message => "Really quit?", - -title => "Are you sure?", + -title => "Quit Connex?", + -message => "Are you sure?", -buttons => ['yes', 'no'], ); @@ -56,60 +65,102 @@ sub exit_dialog() exit(0) if $return; } -sub goto_link_dialog() + +sub links_dialog() { - my $return = $cui->question(-question => "This is [$host/ $pathspec]. Enter destination link:", - -answer => $pathspec, - ); - navigate($return); + my $return = $cui->dialog( + -message => page_links_list(), + -title => "Page Links", + -buttons => ['ok'], + + ); + } - -sub goto_site_dialog() +sub unsupported_dialog { + my $scheme = shift; + my $return = $cui->dialog( + -message => "$scheme protocol not supported.", + -title => "Bad Navigation", + -buttons => ['ok'], + + ); - my $return = $cui->question(-question => "This is [$host/ $pathspec]. Visit site:", - -answer => $host, - ); - if ($return){ - my $site = $return; - if($site ne ''){ - $host = $site; - add_history($host); - load($pathspec); - update_status_bar(); - } - history_status_dialog(); +} + +sub navigate_link_dialog() +{ + my $return = $cui->question(-question => "This is [$full_url]. Enter destination link:", + -answer => $full_url, + ); + #if not user canceled then navigate + if($return){ + navigate($return); + update_status_bar(); } } - + + +sub goto_link_dialog() +{ + my $return = $cui->question(-question => "Enter link #:", + ); + #if not canceled, or too big or too small, goto link + if($return){ + my $linkcount = scalar @page_links; + if($return <= $linkcount && $return >0){ + goto_link($return); + update_status_bar(); + }else{ + #$browser->focus(); + my $return1 = $cui->status("there is no link # " . $return); + } + #do nothing on cancel + } +} + + + + sub goto_back() { my $fetched = fetch_history(); #if($fetched) { - $pathspec =$fetched; - load($pathspec); + $full_url =$fetched; + load($full_url,0); #don't move back to top update_status_bar(); history_status_dialog(); - #} + #} } +sub goto_link{ + my $linknum = shift; + my $linkcount = scalar @page_links; + + #my $element=$linknum -1; #offset for array element + #if ($element <= $#page_links){ #such a perlism, this is highest INDEX of array + + if ($linknum <= $linkcount){ #such a perlism, this is highest INDEX of array + navigate($page_links[$linknum-1]); #offset from count to index + }else{ + #my $browser = $win1->getobj("browser"); + #$browser->focus(); + my $return = $cui->status("no link # " . $linknum); + } + +} + + sub navigate{ my $link = shift; - #if ($link){ - # if($link ne ''){ - add_history($pathspec); #add last link to history before going forward! - $pathspec = $link; - load($pathspec); - # }else { - # $pathspec = ''; - # } - update_status_bar(); - #} + add_history($full_url); #add last link to history before going forward! + $full_url = $link; + load($full_url, 1); #new URL go to top of page } @@ -118,7 +169,8 @@ sub update_status_bar { my $browser = $win1->getobj("browser"); my $statusbar = $win1->getobj("status"); - $statusbar->text("Current site: $host, current link: [$pathspec]"); + #$full_url = construct_valid_url($SCHEME_NEX, $host, $pathspec, $docname); + $statusbar->text("$full_url" . " | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back."); $statusbar->draw(); $browser->focus(); @@ -129,7 +181,6 @@ sub update_status_bar sub add_history { my $link = shift; - print ("link $link"); push(@history, $link); history_status_dialog(); return; @@ -142,7 +193,7 @@ sub fetch_history $latest = pop(@history); return $latest; }else{ - return($PATHSPEC_DEFAULT); + return($HOME_URL); } } @@ -152,12 +203,43 @@ sub history_status_dialog if(@history){ my $browser = $win1->getobj("browser"); $browser->focus(); - my $history_list = join(", ", @history); - my $return = $cui->status("$history_list"); + my $history_list = join("\n", @history); + my $return = $cui->status("history:\n$history_list"); } } +sub page_links_dialog +{ + if(@page_links){ + my $browser = $win1->getobj("browser"); + $browser->focus(); + #my $link_list = join("\n", @page_links); + my $link_list = page_links_list(); + my $return = $cui->status("links on this page:\n$link_list"); + } +} + + +sub page_links_list +{ + if(@page_links){ + my $browser = $win1->getobj("browser"); + $browser->focus(); + #my $link_list = join("\n", @page_links); + my $link_list=""; + my $link; + my $count=0; + foreach $link (@page_links){ + $count+=1; + $link_list = $link_list . "[$count] $link\n"; + } + return $link_list; + } +} + + + my $menu = $cui->add( 'menu','Menubar', -menu => \@menu, @@ -173,6 +255,9 @@ $win1 = $cui->add( -vscrollbar => 'right', ); + + + my $texteditor = $win1->add("browser", "TextViewer", -text => "Start Page", -border => 1, @@ -195,7 +280,7 @@ $statusbar = $win1->add("status", "TextViewer", -width => -1, -reverse => 1, -paddingspaces => 1, - -text => "Current site: $HOST_DEFAULT, current link: [/]", + -text => "$HOME_URL | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back.", ); @@ -204,78 +289,120 @@ $statusbar = $win1->add("status", "TextViewer", #key bindings, should match menu items $cui->set_binding(sub {$menu->focus()}, "\cX"); $cui->set_binding( \&exit_dialog , "\cQ"); -$cui->set_binding( \&goto_link_dialog , "\cG"); +$cui->set_binding( \&navigate_link_dialog , "\cG"); $cui->set_binding( \&goto_back , "\cB"); -$cui->set_binding( \&goto_site_dialog , "\cC"); +$cui->set_binding( \&goto_back , "<"); +$cui->set_binding( \&goto_link_dialog , ">"); +$cui->set_binding(sub { + my $cui = shift; + $cui->layout; + $cui->draw; +}, "\cL"); + + + +# There is no need for the editor widget to loose focus, so +# the "loose-focus" binding is disabled here. This also enables the +# use of the "TAB" key in the editor, which is nice to have. +$texteditor->clear_binding('loose-focus'); #start up -$texteditor->focus(); +#$texteditor->focus(); -navigate(''); +navigate($HOME_URL); $cui->mainloop(); -#pick apart nex:// urls, which we should be using -#sub parse_url #need to pass in url; and host, pathspec, file variables to be filled -#{ -# my $url = @_[0]; -# my $host = url; -# host =~ s/^nex://([a-z0-9]*)\/[a-z0-9\/\.]*$/$1/; -# my $pathspec = url; -# pathspec =~ s/^nex://[a-z0-9]*(\/[a-z0-9\/]*)[[a-z0-9\.]*$)/$1/; -# my $file = $url -# -#} - - #make nex:// urls from parts, relative urls -#sub construct_valid_url #protocol, host, pathspec -#{ -# my $protocol = shift; -# my $host = shift; -# my $pathspec = shift; -# my $file = shift; -# my $url = $protocol; -#need error handling -#what about the end slash and files vs index? -# $url = $protocol . $host; -# if ($pathspec){ -# $url = $url . '/' . $pathspec; -# if ($file){ -# $url = $url . '.' $file; -# } -# } -# return $url; -#} +sub construct_valid_url #scheme, host, pathspec, docname +{ + my $scheme = shift; + my $host = shift; + my $pathspec = shift; + my $docname = shift; + if (defined($docname)){ + $pathspec= $pathspec . '/' . $docname; #this is just local pathspec + } + my $url = uri_join($scheme, $host, $pathspec); + return $url; +} +sub is_path_index{ + my $path = shift; + my $result = 1; + if ($path !~ /\/$/ && $path =~ /\.[a-zA-Z]*$/){ + $result =0; + } + return($result); +} + +sub add_page_link{ + my $linkline = shift; + my $base_url = shift; + my $uri_object; + #my $link= $linkline=~ s/^=>[ ]*(.*$)/$1/r; + local $URI::ABS_ALLOW_RELATIVE_SCHEME = 1; + local $URI::ABS_REMOTE_LEADING_DOTS = 1; + $uri_object=URI->new_abs($linkline=~ /^=>[ ]+([^ ]*)/,$base_url); + + push(@page_links, $uri_object->as_string); + my $count = scalar @page_links; #scalar is size/count of links, but $#page_links is highest INDEX + return $linkline =~ s/(^=>.*$)/$1 [\>$count\]/r; + +} sub load { - my $pathspec = shift; my @lines; my $ok; - $connect->host($host); - $connect->port($port); - $ok= $connect->open($host); - $ok= $connect->print($pathspec); - @lines =$connect->getlines(ErrMode=> 'return'); - print $connect->eof(); -#handle non-existant request? - die unless $connect->eof(); - $connect->close(); - my $widget= $texteditor; - my $loaded_text =""; - my $currentline; - my $count=0; - foreach $currentline (@lines){ - if ($currentline =~ m/^=>/){ - $count+=1; - $currentline =~ s/(^=>.*$)/$1 [\>$count\]/; - } - $loaded_text= $loaded_text . $currentline; - } - $widget->text($loaded_text); + my $url = shift; + my $top = shift; + #my $scheme, $host, $path, $query, $frag; + my ($scheme, $host, $path, $query, $frag) = uri_split($url); + #what happens with different scheme? + if ($scheme eq $SCHEME_NEX){ + $connect->host($host); + $connect->port($port); + $ok= $connect->open($host); + $ok= $connect->print($path); + @lines =$connect->getlines(ErrMode=> 'return'); + print $connect->eof(); + #handle non-existant request? + die unless $connect->eof(); + $connect->close(); + my $widget= $texteditor; + + #loop through nex response and load into text editor. Identify, mark and store link lines for index pages + #TODO: store link lines for index pages + my $page_contents =""; #this is the page contents + my $currentline; + my $count=0; + my $is_index= is_path_index($path); + undef @page_links; #clear list + foreach $currentline (@lines){ + if ($currentline =~ m/^=>/){ + if ($is_index){ + $currentline = add_page_link($currentline, $url); + } + } + $page_contents= $page_contents . $currentline; + } + $widget->text($page_contents); + $widget->draw(); + if ($top){ + $widget->pos(0); + } + }else{ + #can't load non-nex as of now + my $browser = $win1->getobj("browser"); + $browser->focus(); + my $return = unsupported_dialog($scheme); + #pop and trash history + my $fetched = fetch_history(); + + } + }