2024-03-04 20:56:21 -05:00
|
|
|
#!/usr/bin/perl -w
|
2024-03-06 04:09:19 -05:00
|
|
|
#connex.pl Nightfall Express (nex://) browser
|
|
|
|
#TODO: Some branding (status popup on load?, Connex menu item?), About dialog
|
|
|
|
#TODO: Help dialog
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
use strict;
|
|
|
|
use warnings;
|
|
|
|
use strict;
|
|
|
|
use Curses::UI;
|
|
|
|
use Net::Telnet;
|
2024-03-06 04:09:19 -05:00
|
|
|
use URI::Split qw(uri_split uri_join);
|
|
|
|
use URI ();
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
my $HOST_DEFAULT = "nightfall.city";
|
|
|
|
my $PATHSPEC_DEFAULT = '';
|
|
|
|
my $PORT_DEFAULT = 1900;
|
2024-03-06 04:09:19 -05:00
|
|
|
my $SCHEME_NEX = "nex";
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
my $home_default= $SCHEME_NEX . $HOST_DEFAULT;
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
my $host = "nightfall.city";
|
|
|
|
my $port = "1900";
|
|
|
|
my $pathspec = "";
|
2024-03-06 04:09:19 -05:00
|
|
|
my $docname; # = "";
|
2024-03-04 20:56:21 -05:00
|
|
|
my $doctype = "txt";
|
|
|
|
my $dot_ext = ".";
|
|
|
|
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
my $HOME_URL = uri_join($SCHEME_NEX,$HOST_DEFAULT);
|
|
|
|
|
|
|
|
my $full_url = $HOME_URL;
|
|
|
|
|
|
|
|
my @history;
|
|
|
|
my @page_links;
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
my $statusbar;
|
2024-03-06 04:09:19 -05:00
|
|
|
my $navwindow;
|
2024-03-04 20:56:21 -05:00
|
|
|
my $win1;
|
|
|
|
my $cui = new Curses::UI( -color_support => 1 );
|
|
|
|
my $connect = new Net::Telnet (Timeout => 10,
|
|
|
|
Errmode => 'return');
|
|
|
|
my @menu = (
|
|
|
|
{ -label => 'File',
|
|
|
|
-submenu => [
|
2024-03-06 04:09:19 -05:00
|
|
|
{ -label => 'Go to Link ^G', -value => \&navigate_link_dialog },
|
|
|
|
{ -label => 'Choose Link >', -value => \&goto_link_dialog },
|
2024-03-04 20:56:21 -05:00
|
|
|
{ -label => 'Back ^B', -value => \&goto_back },
|
2024-03-06 04:09:19 -05:00
|
|
|
{ -label => 'History ^H', -value => \&history_status_dialog },
|
|
|
|
{ -label => 'Page Links ^P', -value => \&page_links_dialog },
|
2024-03-04 20:56:21 -05:00
|
|
|
{ -label => 'Exit ^Q', -value => \&exit_dialog }
|
|
|
|
]
|
|
|
|
},
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
sub exit_dialog()
|
|
|
|
{
|
|
|
|
my $return = $cui->dialog(
|
2024-03-06 04:09:19 -05:00
|
|
|
-title => "Quit Connex?",
|
|
|
|
-message => "Are you sure?",
|
2024-03-04 20:56:21 -05:00
|
|
|
-buttons => ['yes', 'no'],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
exit(0) if $return;
|
|
|
|
}
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
|
|
|
|
sub links_dialog()
|
2024-03-04 20:56:21 -05:00
|
|
|
{
|
2024-03-06 04:09:19 -05:00
|
|
|
my $return = $cui->dialog(
|
|
|
|
-message => page_links_list(),
|
|
|
|
-title => "Page Links",
|
|
|
|
-buttons => ['ok'],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
}
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
sub unsupported_dialog
|
|
|
|
{
|
|
|
|
my $scheme = shift;
|
|
|
|
my $return = $cui->dialog(
|
|
|
|
-message => "$scheme protocol not supported.",
|
|
|
|
-title => "Bad Navigation",
|
|
|
|
-buttons => ['ok'],
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
sub navigate_link_dialog()
|
2024-03-04 20:56:21 -05:00
|
|
|
{
|
2024-03-06 04:09:19 -05:00
|
|
|
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();
|
|
|
|
}
|
|
|
|
}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
|
|
|
|
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);
|
2024-03-04 20:56:21 -05:00
|
|
|
update_status_bar();
|
2024-03-06 04:09:19 -05:00
|
|
|
}else{
|
|
|
|
#$browser->focus();
|
|
|
|
my $return1 = $cui->status("there is no link # " . $return);
|
2024-03-04 20:56:21 -05:00
|
|
|
}
|
2024-03-06 04:09:19 -05:00
|
|
|
#do nothing on cancel
|
2024-03-04 20:56:21 -05:00
|
|
|
}
|
|
|
|
}
|
2024-03-06 04:09:19 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
sub goto_back()
|
|
|
|
{
|
|
|
|
my $fetched = fetch_history();
|
2024-03-05 02:10:53 -05:00
|
|
|
#if($fetched) {
|
2024-03-06 04:09:19 -05:00
|
|
|
$full_url =$fetched;
|
|
|
|
load($full_url,0); #don't move back to top
|
2024-03-04 20:56:21 -05:00
|
|
|
update_status_bar();
|
2024-03-05 02:10:53 -05:00
|
|
|
history_status_dialog();
|
2024-03-06 04:09:19 -05:00
|
|
|
#}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
sub navigate{
|
|
|
|
my $link = shift;
|
2024-03-06 04:09:19 -05:00
|
|
|
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
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub update_status_bar
|
|
|
|
{
|
2024-03-05 02:10:53 -05:00
|
|
|
my $browser = $win1->getobj("browser");
|
2024-03-04 20:56:21 -05:00
|
|
|
my $statusbar = $win1->getobj("status");
|
2024-03-06 04:09:19 -05:00
|
|
|
#$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.");
|
2024-03-04 20:56:21 -05:00
|
|
|
$statusbar->draw();
|
2024-03-05 02:10:53 -05:00
|
|
|
$browser->focus();
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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{
|
2024-03-06 04:09:19 -05:00
|
|
|
return($HOME_URL);
|
2024-03-04 20:56:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
sub history_status_dialog
|
|
|
|
{
|
|
|
|
if(@history){
|
2024-03-05 02:10:53 -05:00
|
|
|
my $browser = $win1->getobj("browser");
|
|
|
|
$browser->focus();
|
2024-03-06 04:09:19 -05:00
|
|
|
my $history_list = join("\n", @history);
|
|
|
|
my $return = $cui->status("history:\n$history_list");
|
2024-03-04 20:56:21 -05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
my $menu = $cui->add(
|
|
|
|
'menu','Menubar',
|
|
|
|
-menu => \@menu,
|
|
|
|
-fg => "blue",
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
$win1 = $cui->add(
|
|
|
|
'win1', 'Window',
|
|
|
|
-border => 1,
|
|
|
|
-y => 1,
|
|
|
|
-bfg => 'red',
|
|
|
|
-vscrollbar => 'right',
|
|
|
|
);
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
|
|
|
|
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
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,
|
2024-03-06 04:09:19 -05:00
|
|
|
-text => "$HOME_URL | Press '>' key to enter link #. ctl-g to enter nex URL. '<' to go back.",
|
2024-03-04 20:56:21 -05:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#key bindings, should match menu items
|
|
|
|
$cui->set_binding(sub {$menu->focus()}, "\cX");
|
|
|
|
$cui->set_binding( \&exit_dialog , "\cQ");
|
2024-03-06 04:09:19 -05:00
|
|
|
$cui->set_binding( \&navigate_link_dialog , "\cG");
|
2024-03-04 20:56:21 -05:00
|
|
|
$cui->set_binding( \&goto_back , "\cB");
|
2024-03-06 04:09:19 -05:00
|
|
|
$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');
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-05 02:10:53 -05:00
|
|
|
#start up
|
2024-03-06 04:09:19 -05:00
|
|
|
#$texteditor->focus();
|
2024-03-05 02:10:53 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
navigate($HOME_URL);
|
2024-03-04 20:56:21 -05:00
|
|
|
$cui->mainloop();
|
|
|
|
|
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
#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;
|
|
|
|
}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
sub is_path_index{
|
|
|
|
my $path = shift;
|
|
|
|
my $result = 1;
|
|
|
|
if ($path !~ /\/$/ && $path =~ /\.[a-zA-Z]*$/){
|
|
|
|
$result =0;
|
|
|
|
}
|
|
|
|
return($result);
|
|
|
|
}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
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;
|
2024-03-04 20:56:21 -05:00
|
|
|
|
2024-03-06 04:09:19 -05:00
|
|
|
}
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
|
|
|
|
sub load
|
|
|
|
{
|
|
|
|
my @lines;
|
|
|
|
my $ok;
|
2024-03-06 04:09:19 -05:00
|
|
|
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();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2024-03-04 20:56:21 -05:00
|
|
|
|
|
|
|
}
|
|
|
|
|