page = $page; // Init container $this->gtk = new \GtkScrolledWindow; $this->gtk->set_margin_start( $this->_margin ); $this->gtk->set_margin_end( $this->_margin ); $this->gtk->set_margin_bottom( $this->_margin ); // Init viewport // to integrate scrolled window features for data label $this->viewport = new Viewport( $this ); // Init data label $this->data = new Data( $this ); $this->viewport->gtk->add( $this->data->gtk ); $this->gtk->add( $this->viewport->gtk ); // Render $this->gtk->show(); } public function refresh() { // @TODO } public function update( bool $history = true ): void { // Parse address $address = new \Yggverse\Net\Address( $this->page->navbar->request->getValue() ); // Init new title $this->page->title->setValue( $address->getHost(), 'loading...' ); if ($history) { // Remember address in the navigation memory $this->page->navbar->history->add( $address->get() ); // Update history in database $this->page->container->browser->database->renewHistory( $address->get(), // @TODO title ); } // Detect protocol switch ($address->getScheme()) { case 'file': if (file_exists($address->getPath()) && is_readable($address->getPath())) { switch ($address->getPath()) { case is_dir($address->getPath()): // @TODO build fs listing break; case str_ends_with($address->getPath(), '.gmi'): $title = null; $this->data->setGemtext( file_get_contents( // @TODO format relative links $address->getPath() ), $title ); if ($title) // detect title by document h1 { $this->page->title->setValue( $title, $this->page->title->getSubtitle() ); } break; default: $this->page->title->setValue( 'Oops!', 'file extension not supported' ); $this->data->setPlain( 'File extension not supported' ); } } else { $this->page->title->setValue( 'Failure', 'resource not found or not readable' ); $this->data->setPlain( 'Could not open file' ); } break; case 'nex': $client = new \Yggverse\Nex\Client; if ($response = $client->request($address->get())) { // Detect content type switch (true) { case in_array( pathinfo( strval( $address->getPath() ), PATHINFO_EXTENSION ), [ 'gmi', 'gemini' ] ): $title = null; $this->data->setGemtext( $response, $title ); $this->page->title->setValue( $title ? sprintf( '%s - %s', $title, $address->getHost() ) : $address->getHost() ); break; default: $this->data->setMono( $response ); $this->page->title->setValue( $address->getHost() ); } } else { $this->page->title->setValue( 'Failure', 'could not open resource' ); $this->data->setPlain( 'Requested resource not available!' ); } break; case 'gemini': $request = new \Yggverse\Gemini\Client\Request( $address->get() ); $response = new \Yggverse\Gemini\Client\Response( $request->getResponse() ); // Route status code // https://geminiprotocol.net/docs/protocol-specification.gmi#status-codes switch ($response->getCode()) { case 10: // response expected case 11: // sensitive input $this->page->title->setValue( $address->getHost(), $response->getMeta() ? $response->getMeta() : 'response expected' ); $this->page->response->show( $response->getMeta(), // placeholder boolval(11 !== $response->getCode()) // input visibility ); break; case 20: // ok // Detect content type switch (true) { case str_contains( $response->getMeta(), 'text/gemini' ): case in_array( pathinfo( strval( $address->getPath() ), PATHINFO_EXTENSION ), [ 'gmi', 'gemini' ] ): $title = null; $this->data->setGemtext( $response->getBody(), $title ); $this->page->title->setValue( $title ? sprintf( '%s - %s', $title, $address->getHost() ) : $address->getHost(), // detect title by document h1 $response->getMeta() ); break; default: $this->data->setPlain( $response->getBody() ); $this->page->title->setValue( $address->getHost() ); } break; case 31: // redirect @TODO $this->data->setGemtext( sprintf( '=> %s', $response->getMeta() ) ); $this->page->title->setValue( $address->getHost(), sprintf( 'redirect (code %d)', intval( $response->getCode() ) ) ); break; default: $this->page->title->setValue( 'Failure', sprintf( '%s (code %d)', $response->getMeta() ? $response->getMeta() : 'could not open resource', intval( $response->getCode() ) ) ); $this->data->setPlain( 'Requested resource not available!' ); } break; case null: // Try gemini protocol $address = new \Yggverse\Net\Address( sprintf( 'gemini://%s', $this->page->navbar->request->getValue() ) ); // Is hostname request if (filter_var( $address->getHost(), FILTER_VALIDATE_DOMAIN, FILTER_FLAG_HOSTNAME ) ) { $this->page->navbar->request->setValue( $address->get() ); } // Is search request else { $this->page->navbar->request->setValue( sprintf( 'gemini://tlgs.one/search?%s', // @TODO custom provider urlencode( $this->page->navbar->request->getValue() ) ) ); } $this->update(); return; default: $this->page->title->setValue( 'Oops!', 'protocol not supported!' ); $this->data->setPlain( 'Protocol not supported!' ); } // Render content $this->gtk->show_all(); // Refresh page components $this->page->refresh(); // Update window header $this->page->container->browser->header->setTitle( $this->page->title->getValue(), $this->page->title->getSubtitle(), ); } }