Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add URL demo source code #69

Merged
merged 3 commits into from
Sep 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/url-demo/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Ignore compiler output
/www/url-api.*
8 changes: 8 additions & 0 deletions examples/url-demo/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# Upa URL demo source code

The Upa URL demo is available online at https://upa-url.github.io/demo/

How to prepare for the Web:
1. Install and activate Emscripten, more information: https://emscripten.org/docs/getting_started/downloads.html
2. Build `url-api.cpp` with `build-url-api.sh` (`build-url-api.bat` on Windows) script. Output files `url-api.js` and `url-api.wasm` will be written to the `www` directory..
3. Publish contents of `www` directory to web server.
22 changes: 22 additions & 0 deletions examples/url-demo/build-url-api.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
@echo off

REM the directory path of this file
set p=%~dp0

REM CD to repository root folder
%~d0
cd %p%../..

REM Compile
call emcc examples/url-demo/url-api.cpp ^
src/url.cpp ^
src/url_ip.cpp ^
src/url_percent_encode.cpp ^
src/url_search_params.cpp ^
src/url_utf.cpp ^
src/idna.cpp ^
-Iinclude -std=c++17 -O3 ^
-lembind ^
-o examples/url-demo/www/url-api.js

cd examples/url-demo
22 changes: 22 additions & 0 deletions examples/url-demo/build-url-api.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/bin/sh

# the directory path of this file
# https://stackoverflow.com/q/59895
p="$(dirname "$0")"

# CD to repository root folder
cd $p/../..

# Compile
emcc examples/url-demo/url-api.cpp \
src/url.cpp \
src/url_ip.cpp \
src/url_percent_encode.cpp \
src/url_search_params.cpp \
src/url_utf.cpp \
src/idna.cpp \
-Iinclude -std=c++17 -O3 \
-lembind \
-o examples/url-demo/www/url-api.js

cd examples/url-demo
135 changes: 135 additions & 0 deletions examples/url-demo/url-api.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// Copyright 2024 Rimas Misevičius
// Distributed under the BSD-style license that can be
// found in the LICENSE file.
//
#include "upa/url.h"

#include <emscripten/bind.h>

using namespace emscripten;

class URL {
public:
URL(const std::string& url_str) {
m_url.parse(url_str, nullptr);
m_base_valid = true;
}

URL(const std::string& url_str, const std::string& base_str) {
upa::url base;
base.parse(base_str, nullptr);
m_url.parse(url_str, &base);
m_base_valid = base.is_valid();
}

std::string get_href() const {
return std::string{ m_url.href() };
}
void set_href(const std::string& val) {
m_url.href(val);
}

std::string get_origin() const {
return m_url.origin();
}

std::string get_protocol() const {
return std::string{ m_url.protocol() };
}
void set_protocol(const std::string& val) {
m_url.protocol(val);
}

std::string get_username() const {
return std::string{ m_url.username() };
}
void set_username(const std::string& val) {
m_url.username(val);
}

std::string get_password() const {
return std::string{ m_url.password() };
}
void set_password(const std::string& val) {
m_url.password(val);
}

std::string get_host() const {
return std::string{ m_url.host() };
}
void set_host(const std::string& val) {
m_url.host(val);
}

std::string get_hostname() const {
return std::string{ m_url.hostname() };
}
void set_hostname(const std::string& val) {
m_url.hostname(val);
}

std::string get_port() const {
return std::string{ m_url.port() };
}
void set_port(const std::string& val) {
m_url.port(val);
}

std::string get_path() const {
return std::string{ m_url.path() };
}

std::string get_pathname() const {
return std::string{ m_url.pathname() };
}
void set_pathname(const std::string& val) {
m_url.pathname(val);
}

std::string get_search() const {
return std::string{ m_url.search() };
}
void set_search(const std::string& val) {
m_url.search(val);
}

std::string get_hash() const {
return std::string{ m_url.hash() };
}
void set_hash(const std::string& val) {
m_url.hash(val);
}

bool valid() const {
return m_url.is_valid();
}
bool base_valid() const {
return m_base_valid;
}
private:
upa::url m_url;
bool m_base_valid;
};


// Binding code
EMSCRIPTEN_BINDINGS(url_api) {
class_<URL>("URL")
.constructor<std::string>()
.constructor<std::string, std::string>()
.property("href", &URL::get_href, &URL::set_href)
.property("origin", &URL::get_origin)
.property("protocol", &URL::get_protocol, &URL::set_protocol)
.property("username", &URL::get_username, &URL::set_username)
.property("password", &URL::get_password, &URL::set_password)
.property("host", &URL::get_host, &URL::set_host)
.property("hostname", &URL::get_hostname, &URL::set_hostname)
.property("port", &URL::get_port, &URL::set_port)
.property("path", &URL::get_path)
.property("pathname", &URL::get_pathname, &URL::set_pathname)
.property("search", &URL::get_search, &URL::set_search)
.property("hash", &URL::get_hash, &URL::set_hash)
// is valid?
.property("valid", &URL::valid)
.property("base_valid", &URL::base_valid);
}
48 changes: 48 additions & 0 deletions examples/url-demo/www/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Upa URL demo</title>
<link rel="stylesheet" href="url-demo.css">
<script src="url-api.js"></script>
<script type="module" src="url-demo.js"></script>
</head>
<body>
<h1>Upa URL demo</h1>
<p><i>Try the <a href="https://github.com/upa-url/upa">Upa URL library</a> that runs in the browser.</i></p>

<form>
<label class="label" for="url">URL input:</label>
<input id="url" class="field" value="https://example.org/" autofocus>

<label class="label" for="base">Base URL <i>(leave empty to not use base URL)</i>:</label>
<input id="base" class="field" value="https://example.org/">

<div class="label">
<select id="setter"><option value="">—</option></select>
<label for="setter-inp">setter:</label>
</div>
<input id="setter-inp" class="field" value="">
</form>

<h2>Parsing result:</h2>

<table id="url-output" class="output">
<tr class="href"><th>href</th><td></td></tr>
<tr class="protocol"><th>protocol</th><td></td></tr>
<tr class="username"><th>username</th><td></td></tr>
<tr class="password"><th>password</th><td></td></tr>
<tr class="hostname"><th>hostname</th><td></td></tr>
<tr class="port"><th>port</th><td></td></tr>
<tr class="pathname"><th>pathname</th><td></td></tr>
<tr class="search"><th>search</th><td></td></tr>
<tr class="hash"><th>hash</th><td></td></tr>
<tr class="origin"><th>origin</th><td></td></tr>
</table>

<div id="url-error" class="output error"></div>

<!-- GitHub Corner from https://tholman.com/github-corners/ -->
<a href="https://github.com/upa-url/upa/tree/url-demo/examples/url-demo" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#C2B8A3; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
</body>
</html>
47 changes: 47 additions & 0 deletions examples/url-demo/www/url-demo.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
* { box-sizing: border-box; }

body {
font-family: Arial;
max-width: 70em;
margin: 16px;
}

h2 {
font-size: 1.4em;
margin-top: 0.3em;
margin-bottom: 0.2em;
}

form .label {
display: block;
margin-bottom: 6px;
}

form .field {
width: 100%;
margin-bottom: 20px;
padding: 3px;
}

.output {
background-color: #F7F0E0;
border: 1px solid #D2691E;
width: 100%;
padding: 1em;
margin: 0;
}

.output th {
width: 80px;
text-align: right;
padding-right: 10px;
}

.output td {
font-family: 'Bitstream Vera Sans Mono', 'Andale Mono', 'Lucida Console', monospace, fixed;
white-space: pre-wrap;
}

.output.error {
color: #B90000;
}
81 changes: 81 additions & 0 deletions examples/url-demo/www/url-demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const component_list = [
"href",
"protocol",
"username",
"password",
"host",
"hostname",
"port",
"pathname",
"search",
"hash",
"origin"
];

function getUrl() {
// construct URL
const url = elemBase.value.length !== 0
? new Module.URL(elemUrl.value, elemBase.value)
: new Module.URL(elemUrl.value);
// apply setter
if (elemSetter.value.length !== 0)
url[elemSetter.value] = elemSetterInp.value;
return url;
}

function showResult(url) {
const output = document.getElementById("url-output");
const error = document.getElementById("url-error");
if (url.valid) {
output.hidden = false;
error.hidden = true;
for (const component of component_list) {
const trComponent = output.querySelector(`.${component}`);
if (trComponent) {
const elemComponent = trComponent.querySelector("td");
elemComponent.textContent = url[component];
}
}
} else {
output.hidden = true;
error.hidden = false;
error.textContent = url.base_valid ? "URL parse error" : "Base URL parse error";
}
}

function onInpChange() {
showResult(getUrl());
}

function onSetterChange() {
elemSetterInp.disabled = elemSetter.value.length === 0;
onInpChange();
}

// Input elements
const elemUrl = document.getElementById("url");
const elemBase = document.getElementById("base");
const elemSetter = document.getElementById("setter");
const elemSetterInp = document.getElementById("setter-inp");

// After text change
elemUrl.addEventListener("input", onInpChange);
elemBase.addEventListener("input", onInpChange);
elemSetter.addEventListener("change", onSetterChange);
elemSetterInp.addEventListener("input", onInpChange);

// Fill setters list
for (var component of component_list) {
if (component !== "origin") {
var option = document.createElement("option");
option.text = component;
option.value = component;
elemSetter.add(option);
}
}

// Parse initial URL
if (Module.URL)
onSetterChange();
else
Module['onRuntimeInitialized'] = onSetterChange;