Browse Source

findFormNode(), getTagNodeNameValue(), buildFormData(), handleResource(), submit button, form handling

master
Odilitime 5 years ago
parent
commit
5030659646
  1. 230
      src/graphics/elements/INPUTElement.cpp
  2. 9
      src/graphics/elements/INPUTElement.h

230
src/graphics/elements/INPUTElement.cpp

@ -1,15 +1,241 @@ @@ -1,15 +1,241 @@
#include "INPUTElement.h"
#include "../components/InputComponent.h"
#include "../components/ButtonComponent.h"
#include "../components/DocumentComponent.h"
#include "../../Log.h"
#include "../../html/HTMLParser.h"
INPUTElement::INPUTElement() {
isInline = true;
}
void handleResource(WebResource &res, std::string url, DocumentComponent *docComponent) {
if (res.resourceType == ResourceType::INVALID) {
logError() << "Invalid resource type: " << res.raw << std::endl;
return;
}
std::cout << "body: " << res.raw << std::endl;
//std::cout << "type: " << res.resourceType << std::endl;
// parse HTML
if (res.resourceType == ResourceType::HTML) {
HTMLParser parser;
const std::clock_t begin = clock();
std::shared_ptr<Node> rootNode = parser.parse(res.raw);
const std::clock_t end = clock();
logDebug() << "main::setWindowContent - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
// send NodeTree to window
//this->win->setDOM(rootNode);
//printNode(rootNode, 0);
// we need a way to communicate with our tabComponent
// maybe an event is best
if (docComponent->onBeforeLoad) {
docComponent->onBeforeLoad(url);
}
docComponent->setDOM(rootNode);
} else if (res.resourceType == ResourceType::TXT) {
std::cout << "Rendering text document" << std::endl;
std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
tagNode->tag="p";
// bind tag to root
tagNode->parent = rootNode;
rootNode->children.push_back(tagNode);
std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
textNode->text = res.raw;
// bind text to tag
textNode->parent = tagNode;
tagNode->children.push_back(textNode);
// send NodeTree to window
//this->win->setDOM(rootNode);
docComponent->setDOM(rootNode);
} else {
std::cout << "setWindowContent() - I don't know how to render non-html files" << std::endl;
}
}
std::shared_ptr<Node> findFormNode(std::shared_ptr<Node> node) {
if (!node.get()) {
// found root?
return nullptr;
}
TagNode *tagNode = dynamic_cast<TagNode*>(node.get());
if (tagNode) {
if (tagNode->tag == "form") {
return node;
}
}
return findFormNode(node->parent);
}
std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue(TagNode &tagNode) {
auto namePropIter = tagNode.properties.find("name");
if (namePropIter == tagNode.properties.end()) {
return nullptr;
}
auto valuePropIter = tagNode.properties.find("value");
if (valuePropIter == tagNode.properties.end()) {
return nullptr;
}
return std::make_unique<std::pair<std::string, std::string>>(namePropIter->second, valuePropIter->second);
}
// how do we match up nodes to components to extract the current values out of the components?
// well we link input components back to node (and/or nodes back to components)
// or we create a form UI component
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result) {
if (!result) {
result = std::make_unique<std::map<std::string, std::string>>();
}
// check this node
TagNode *tagNode = dynamic_cast<TagNode*>(formNode.get());
if (tagNode) {
auto typePropIter = tagNode->properties.find("type");
bool skip = false;
if (typePropIter != tagNode->properties.end()) {
if (typePropIter->second == "submit") {
skip = true;
}
}
auto namePropIter = tagNode->properties.find("name");
if (!skip && namePropIter != tagNode->properties.end()) {
auto valuePropIter = tagNode->properties.find("value");
if (valuePropIter != tagNode->properties.end()) {
auto it = result->find(namePropIter->second);
if (it == result->end()) {
std::cout << "INPUTElement.buildFormData - setting " << namePropIter->second << " to [" << valuePropIter->second << "]" << std::endl;
result->insert(std::pair<std::string, std::string>(namePropIter->second, valuePropIter->second));
} else {
(*result)[namePropIter->second] = valuePropIter->second;
}
}
}
}
// check children noedes
for (auto &child : formNode->children) {
//std::shared_ptr<TagNode> tagNode = std::dynamic_pointer_cast<TagNode>(child);
//if (tagNode) {
result = buildFormData(child, std::move(result));
//}
}
return result;
}
std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &request) {
// const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int windowWidth, const int windowHeight
// what should our default size be?
//std::cout << "INPUTElement::renderer - creating InputComponent at " << x << "x" << y << std::endl;
std::unique_ptr<Component> component = std::make_unique<InputComponent>(0, 0, 125.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
return component;
TagNode *tagNode = dynamic_cast<TagNode*>(request.node.get());
if (tagNode) {
std::string type = "text";
auto propIter = tagNode->properties.find("type");
if (propIter != tagNode->properties.end()) {
//std::cout << "input is of type: " << propIter->second << std::endl;
type = propIter->second;
}
//std::cout << "type is " << type << std::endl;
if (type == "text") {
std::unique_ptr<InputComponent> inputComponent = std::make_unique<InputComponent>(0, 0, 125.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
inputComponent->node = tagNode;
inputComponent->name = "textInput";
auto propIter = tagNode->properties.find("value");
if (propIter != tagNode->properties.end()) {
inputComponent->value = propIter->second;
}
return std::move(inputComponent);
} else if (type == "submit") {
//std::cout << "Creating submit" << std::endl;
std::unique_ptr<ButtonComponent> butComponent = std::make_unique<ButtonComponent>(0, 0, 62.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
auto propIter = tagNode->properties.find("value");
if (propIter != tagNode->properties.end()) {
butComponent->value = propIter->second;
} else {
butComponent->value = "submit";
}
butComponent->onClick=[tagNode, request]() {
// recurse up to find oug form tag
std::shared_ptr<Node> formNode = findFormNode(request.node);
if (!formNode) {
std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
return;
}
if (!request.docComponent) {
std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find documentComponent for submit" << std::endl;
return;
}
TagNode *formTagNode = dynamic_cast<TagNode*>(formNode.get());
auto formNodeActionIter = formTagNode->properties.find("action");
if (formNodeActionIter == formTagNode->properties.end()) {
std::cout << "Form has no action" << std::endl;
return;
}
auto formNodeMethodIter = formTagNode->properties.find("method");
std::string method="GET";
if (formNodeMethodIter != formTagNode->properties.end()) {
method=formNodeMethodIter->second;
}
std::cout << "Form method is " << method << std::endl;
auto formData = buildFormData(formNode, nullptr);
// add our name/value (because we skip all submit buttons, there can only be one)
auto submitButtonNameValue = getTagNodeNameValue(*tagNode);
if (submitButtonNameValue) {
formData->insert(*submitButtonNameValue);
}
if (method=="POST" || method=="post") {
// need documentComponent
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, std::move(formData));
handleResource(res, uAction.toString(), request.docComponent);
} else {
// need documentComponent
std::string queryString = "";
if (formData) {
// iterator over formData
for (auto &entry : *formData) {
queryString += entry.first + "=" + entry.second + "&";
}
// strip last & off
queryString = queryString.substr(0, queryString.size() - 1);
std::cout << "queryString is " << queryString << std::endl;
// The moral of the story is, if you have binary (non-alphanumeric) data (or a significantly sized payload) to transmit, use multipart/form-data
auto search = queryString.find(" ");
if (search != std::string::npos) {
queryString.replace(search, 1, "+");
}
}
URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second+"?"+queryString));
std::cout << "Action URL is " << uAction.toString() << std::endl;
// download URL
WebResource res = postWebResource(uAction, nullptr);
handleResource(res, uAction.toString(), request.docComponent);
}
};
return std::move(butComponent);
} else if (type == "hidden") {
// we don't need a UI component but we do belong in the form
} else {
// submit, button, checkbox, file
//std::cout << "INPUTElement::renderer - ignoring input is of type: " << type << std::endl;
}
}
/*
TextNode *textNode = dynamic_cast<TextNode*>(node.get());

9
src/graphics/elements/INPUTElement.h

@ -3,8 +3,15 @@ @@ -3,8 +3,15 @@
#include "Element.h"
#include "../components/Component.h"
#include "../components/InputComponent.h"
#include "../../html/TextNode.h"
#include "../../WebResource.h"
class DocumentComponent;
std::shared_ptr<Node> findFormNode(std::shared_ptr<Node> node);
std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue(TagNode &tagNode);
std::unique_ptr<std::map<std::string, std::string>> buildFormData(std::shared_ptr<Node> formNode, std::unique_ptr<std::map<std::string, std::string>> result);
void handleResource(WebResource &res, std::string url, DocumentComponent *docComponent);
class INPUTElement : public Element {
public:

Loading…
Cancel
Save