Skip to content
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: 1 addition & 1 deletion docs/api/browser-compat-data/html/elements/input.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"spec_url": "https://html.spec.whatwg.org/multipage/input.html#the-input-element",
"support": {
"jsar": {
"version_added": false
"version_added": "0.9.0"
}
},
"status": {
Expand Down
89 changes: 89 additions & 0 deletions fixtures/html/htmlinputelement-test.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
<!DOCTYPE html>
<html>

<head>
<title>HTMLInputElement Test</title>
<meta charset="UTF-8">
<style>
input {
background-color: #f0f0f0;
}
</style>
</head>

<body style="background-color: #fff; padding: 20px;">
<h1>HTMLInputElement Test</h1>

<p>This page tests basic HTMLInputElement functionality in JSAR runtime:</p>

<!-- Text input -->
<label for="text-input">Text Input:</label>
<input id="text-input" type="text" placeholder="Enter text here" value="Default text">

<!-- Email input -->
<label for="email-input">Email Input:</label>
<input id="email-input" type="email" placeholder="Enter email" required>

<!-- Checkbox input -->
<label for="checkbox-input">Checkbox:</label>
<input id="checkbox-input" type="checkbox" checked>

<!-- Number input -->
<label for="number-input">Number Input:</label>
<input id="number-input" type="number" min="0" max="100" step="1" value="50">

<!-- Password input -->
<label for="password-input">Password Input:</label>
<input id="password-input" type="password" placeholder="Enter password">

<script>
// Test JavaScript interaction with HTMLInputElement
console.log('Testing HTMLInputElement...');

// Test constructor availability
if (typeof HTMLInputElement !== 'undefined') {
console.log('✓ HTMLInputElement constructor is available');

// Test creating an input element
try {
const input = document.createElement('input');
console.log('✓ HTMLInputElement can be instantiated');
console.log(' - tagName:', input.tagName);
console.log(' - type:', input.type);
console.log(' - value:', input.value);

// Test property setting
input.type = 'email';
input.value = 'test@example.com';
input.placeholder = 'Enter email';
input.required = true;

console.log('✓ Properties can be set');
console.log(' - type:', input.type);
console.log(' - value:', input.value);
console.log(' - placeholder:', input.placeholder);
console.log(' - required:', input.required);
} catch (e) {
console.error('✗ Error creating HTMLInputElement:', e);
}

} else {
console.error('✗ HTMLInputElement constructor is not available');
}

// Test DOM query for existing inputs
try {
const textInput = document.getElementById('text-input');
if (textInput) {
console.log('✓ Existing input elements found in DOM');
console.log(' - Text input value:', textInput.value);
console.log(' - Text input type:', textInput.type);
console.log(' - Text input placeholder:', textInput.placeholder);
}
} catch (e) {
console.error('✗ Error accessing DOM input elements:', e);
}
</script>
</body>

</html>
1 change: 1 addition & 0 deletions src/bindings/dom/all_html_elements.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include "./html_html_element.hpp"
#include "./html_iframe_element.hpp"
#include "./html_image_element.hpp"
#include "./html_input_element.hpp"
#include "./html_link_element.hpp"
#include "./html_media_element.hpp"
#include "./html_meta_element.hpp"
Expand Down
13 changes: 8 additions & 5 deletions src/bindings/dom/element.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,14 @@ namespace dombinding
Napi::Env env = info.Env();
Napi::HandleScope scope(env);

auto jsThis = info.This().As<Napi::Object>();
jsThis.Set("localName", Napi::String::New(env, this->node->localName));
jsThis.Set("namespaceURI", Napi::String::New(env, this->node->namespaceURI));
jsThis.Set("prefix", Napi::String::New(env, this->node->prefix));
jsThis.Set("tagName", Napi::String::New(env, this->node->tagName));
if (this->node != nullptr)
{
auto jsThis = info.This().As<Napi::Object>();
jsThis.Set("localName", Napi::String::New(env, this->node->localName));
jsThis.Set("namespaceURI", Napi::String::New(env, this->node->namespaceURI));
jsThis.Set("prefix", Napi::String::New(env, this->node->prefix));
jsThis.Set("tagName", Napi::String::New(env, this->node->tagName));
}
}
virtual ~ElementBase() = default;

Expand Down
254 changes: 254 additions & 0 deletions src/bindings/dom/html_input_element.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,254 @@
#include <assert.h>
#include "./html_input_element.hpp"

namespace dombinding
{
std::vector<Napi::ClassPropertyDescriptor<HTMLInputElement>> HTMLInputElement::GetClassProperties(Napi::Env env)
{
using T = HTMLInputElement;
auto props = HTMLElementBase<HTMLInputElement, dom::HTMLInputElement>::GetClassProperties(env);
auto added = std::vector<Napi::ClassPropertyDescriptor<HTMLInputElement>>(
{
// Properties
T::InstanceAccessor("type", &T::TypeGetter, &T::TypeSetter),
T::InstanceAccessor("value", &T::ValueGetter, &T::ValueSetter),
T::InstanceAccessor("checked", &T::CheckedGetter, &T::CheckedSetter),
T::InstanceAccessor("disabled", &T::DisabledGetter, &T::DisabledSetter),
T::InstanceAccessor("required", &T::RequiredGetter, &T::RequiredSetter),
T::InstanceAccessor("placeholder", &T::PlaceholderGetter, &T::PlaceholderSetter),
T::InstanceAccessor("name", &T::NameGetter, &T::NameSetter),
T::InstanceAccessor("valueAsNumber", &T::ValueAsNumberGetter, &T::ValueAsNumberSetter),

// Methods
T::InstanceMethod("checkValidity", &T::CheckValidity),
T::InstanceMethod("setCustomValidity", &T::SetCustomValidity),
T::InstanceMethod("reportValidity", &T::ReportValidity),
T::InstanceMethod("stepUp", &T::StepUp),
T::InstanceMethod("stepDown", &T::StepDown),
T::InstanceMethod("select", &T::Select),
T::InstanceMethod("setSelectionRange", &T::SetSelectionRange),
});
props.insert(props.end(), added.begin(), added.end());
return props;
}

thread_local Napi::FunctionReference *HTMLInputElement::constructor;
void HTMLInputElement::Init(Napi::Env env)
{
auto props = GetClassProperties(env);
Napi::Function func = DefineClass(env, "HTMLInputElement", props);
constructor = new Napi::FunctionReference();
*constructor = Napi::Persistent(func);
env.Global().Set("HTMLInputElement", func);
}

// Property implementations
Napi::Value HTMLInputElement::TypeGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::String::New(env, node->type());
}

void HTMLInputElement::TypeSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsString())
{
node->setType(value.As<Napi::String>().Utf8Value());
}
}

Napi::Value HTMLInputElement::ValueGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::String::New(env, node->value());
}

void HTMLInputElement::ValueSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsString())
{
node->setValue(value.As<Napi::String>().Utf8Value());
}
}

Napi::Value HTMLInputElement::CheckedGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Boolean::New(env, node->checked());
}

void HTMLInputElement::CheckedSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsBoolean())
{
node->setChecked(value.As<Napi::Boolean>().Value());
}
}

Napi::Value HTMLInputElement::DisabledGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Boolean::New(env, node->disabled());
}

void HTMLInputElement::DisabledSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsBoolean())
{
node->setDisabled(value.As<Napi::Boolean>().Value());
}
}

Napi::Value HTMLInputElement::RequiredGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Boolean::New(env, node->required());
}

void HTMLInputElement::RequiredSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsBoolean())
{
node->setRequired(value.As<Napi::Boolean>().Value());
}
}

Napi::Value HTMLInputElement::PlaceholderGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::String::New(env, node->placeholder());
}

void HTMLInputElement::PlaceholderSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsString())
{
node->setPlaceholder(value.As<Napi::String>().Utf8Value());
}
}

Napi::Value HTMLInputElement::NameGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::String::New(env, node->name());
}

void HTMLInputElement::NameSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsString())
{
node->setName(value.As<Napi::String>().Utf8Value());
}
}

Napi::Value HTMLInputElement::ValueAsNumberGetter(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Number::New(env, node->valueAsNumber());
}

void HTMLInputElement::ValueAsNumberSetter(const Napi::CallbackInfo &info, const Napi::Value &value)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (value.IsNumber())
{
node->setValueAsNumber(value.As<Napi::Number>().DoubleValue());
}
}

// Method implementations
Napi::Value HTMLInputElement::CheckValidity(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Boolean::New(env, node->checkValidity());
}

Napi::Value HTMLInputElement::SetCustomValidity(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (info.Length() > 0 && info[0].IsString())
{
node->setCustomValidity(info[0].As<Napi::String>().Utf8Value());
}
return env.Undefined();
}

Napi::Value HTMLInputElement::ReportValidity(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
return Napi::Boolean::New(env, node->reportValidity());
}

Napi::Value HTMLInputElement::StepUp(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
int steps = 1;
if (info.Length() > 0 && info[0].IsNumber())
{
steps = info[0].As<Napi::Number>().Int32Value();
}
node->stepUp(steps);
return env.Undefined();
}

Napi::Value HTMLInputElement::StepDown(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
int steps = 1;
if (info.Length() > 0 && info[0].IsNumber())
{
steps = info[0].As<Napi::Number>().Int32Value();
}
node->stepDown(steps);
return env.Undefined();
}

Napi::Value HTMLInputElement::Select(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
node->select();
return env.Undefined();
}

Napi::Value HTMLInputElement::SetSelectionRange(const Napi::CallbackInfo &info)
{
Napi::Env env = info.Env();
Napi::HandleScope scope(env);
if (info.Length() >= 2 && info[0].IsNumber() && info[1].IsNumber())
{
int start = info[0].As<Napi::Number>().Int32Value();
int end = info[1].As<Napi::Number>().Int32Value();
node->setSelectionRange(start, end);
}
return env.Undefined();
}
}
Loading