peteyboy
e957438be6
code to modify links in read index pages only to add "[>#]" to identify link number. Recorded all links on a page in an array and used URI to handle relative links including "../index" type links (I hope, there were none to test). Added UI to navigate by link #s I made, went with kludgy dialog box. Had the idea to use > and < for navigation to copy the '[>#]' format I added to index page link lines, which works not bad. Added some help to status bar, other stuff. What needs to get added is some 'branding' (program name, about dialog) and a help dialog and to reorganize the menu so it's not crazy. Also could remove some of the status dialogs.
409 lines
9.2 KiB
Perl
Executable File
409 lines
9.2 KiB
Perl
Executable File
#!/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 qw(uri_split uri_join);
|
|
use URI ();
|
|
|
|
my $HOST_DEFAULT = "nightfall.city";
|
|
my $PATHSPEC_DEFAULT = '';
|
|
my $PORT_DEFAULT = 1900;
|
|
my $SCHEME_NEX = "nex";
|
|
|
|
my $home_default= $SCHEME_NEX . $HOST_DEFAULT;
|
|
|
|
my $host = "nightfall.city";
|
|
my $port = "1900";
|
|
my $pathspec = "";
|
|
my $docname; # = "";
|
|
my $doctype = "txt";
|
|
my $dot_ext = ".";
|
|
|
|
|
|
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,
|
|
Errmode => 'return');
|
|
my @menu = (
|
|
{ -label => 'File',
|
|
-submenu => [
|
|
{ -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 => 'Page Links ^P', -value => \&page_links_dialog },
|
|
{ -label => 'Exit ^Q', -value => \&exit_dialog }
|
|
]
|
|
},
|
|
|
|
);
|
|
|
|
sub exit_dialog()
|
|
{
|
|
my $return = $cui->dialog(
|
|
-title => "Quit Connex?",
|
|
-message => "Are you sure?",
|
|
-buttons => ['yes', 'no'],
|
|
|
|
);
|
|
|
|
exit(0) if $return;
|
|
}
|
|
|
|
|
|
sub links_dialog()
|
|
{
|
|
my $return = $cui->dialog(
|
|
-message => page_links_list(),
|
|
-title => "Page Links",
|
|
-buttons => ['ok'],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
sub unsupported_dialog
|
|
{
|
|
my $scheme = shift;
|
|
my $return = $cui->dialog(
|
|
-message => "$scheme protocol not supported.",
|
|
-title => "Bad Navigation",
|
|
-buttons => ['ok'],
|
|
|
|
);
|
|
|
|
}
|
|
|
|
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) {
|
|
$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;
|
|
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
|
|
|
|
}
|
|
|
|
|
|
sub update_status_bar
|
|
{
|
|
my $browser = $win1->getobj("browser");
|
|
my $statusbar = $win1->getobj("status");
|
|
#$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();
|
|
|
|
|
|
}
|
|
|
|
|
|
sub add_history
|
|
{
|
|
my $link = shift;
|
|
push(@history, $link);
|
|
history_status_dialog();
|
|
return;
|
|
}
|
|
|
|
sub fetch_history
|
|
{
|
|
my $latest;
|
|
if(@history) {
|
|
$latest = pop(@history);
|
|
return $latest;
|
|
}else{
|
|
return($HOME_URL);
|
|
}
|
|
}
|
|
|
|
|
|
sub history_status_dialog
|
|
{
|
|
if(@history){
|
|
my $browser = $win1->getobj("browser");
|
|
$browser->focus();
|
|
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,
|
|
-fg => "blue",
|
|
);
|
|
|
|
|
|
$win1 = $cui->add(
|
|
'win1', 'Window',
|
|
-border => 1,
|
|
-y => 1,
|
|
-bfg => 'red',
|
|
-vscrollbar => 'right',
|
|
);
|
|
|
|
|
|
|
|
|
|
my $texteditor = $win1->add("browser", "TextViewer",
|
|
-text => "Start Page",
|
|
-border => 1,
|
|
-padtop => 0,
|
|
-padbottom => 3,
|
|
-showlines => 0,
|
|
-sbborder => 0,
|
|
-vscrollbar => 1,
|
|
-hscrollbar => 1,
|
|
-showhardreturns => 0,
|
|
-wrapping => 0, # wrapping slows down the editor :-(
|
|
);
|
|
|
|
|
|
$statusbar = $win1->add("status", "TextViewer",
|
|
-border => 1,
|
|
-bfg => 'red',
|
|
-y => -1,
|
|
-height => 1,
|
|
-width => -1,
|
|
-reverse => 1,
|
|
-paddingspaces => 1,
|
|
-text => "$HOME_URL | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back.",
|
|
);
|
|
|
|
|
|
|
|
|
|
#key bindings, should match menu items
|
|
$cui->set_binding(sub {$menu->focus()}, "\cX");
|
|
$cui->set_binding( \&exit_dialog , "\cQ");
|
|
$cui->set_binding( \&navigate_link_dialog , "\cG");
|
|
$cui->set_binding( \&goto_back , "\cB");
|
|
$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();
|
|
|
|
navigate($HOME_URL);
|
|
$cui->mainloop();
|
|
|
|
|
|
#make nex:// urls from parts, relative urls
|
|
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 @lines;
|
|
my $ok;
|
|
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();
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|