You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

INPUTElement.cpp 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. #include "INPUTElement.h"
  2. #include "../components/InputComponent.h"
  3. #include "../components/ButtonComponent.h"
  4. #include "../components/DocumentComponent.h"
  5. #include "../../Log.h"
  6. #include "../../html/HTMLParser.h"
  7. #include <ctime>
  8. INPUTElement::INPUTElement() {
  9. isInline = true;
  10. }
  11. void handleResource(WebResource &res, std::string url, DocumentComponent *docComponent) {
  12. if (res.resourceType == ResourceType::INVALID) {
  13. logError() << "Invalid resource type: " << res.raw << std::endl;
  14. return;
  15. }
  16. //std::cout << "body: " << res.raw << std::endl;
  17. //std::cout << "type: " << res.resourceType << std::endl;
  18. // parse HTML
  19. if (res.resourceType == ResourceType::HTML) {
  20. HTMLParser parser;
  21. const std::clock_t begin = clock();
  22. std::shared_ptr<Node> rootNode = parser.parse(res.raw);
  23. const std::clock_t end = clock();
  24. logDebug() << "main::setWindowContent - Parsed document in: " << std::fixed << ((static_cast<double>(end - begin)) / CLOCKS_PER_SEC) << std::scientific << " seconds" << std::endl;
  25. // send NodeTree to window
  26. //this->win->setDOM(rootNode);
  27. //printNode(rootNode, 0);
  28. // we need a way to communicate with our tabComponent
  29. // maybe an event is best
  30. if (docComponent->onBeforeLoad) {
  31. docComponent->onBeforeLoad(url);
  32. }
  33. docComponent->setDOM(rootNode);
  34. } else if (res.resourceType == ResourceType::TXT) {
  35. std::cout << "Rendering text document" << std::endl;
  36. std::shared_ptr<Node> rootNode = std::make_shared<Node>(NodeType::ROOT);
  37. std::shared_ptr<TagNode> tagNode = std::make_shared<TagNode>();
  38. tagNode->tag="p";
  39. // bind tag to root
  40. tagNode->parent = rootNode;
  41. rootNode->children.push_back(tagNode);
  42. std::shared_ptr<TextNode> textNode = std::make_shared<TextNode>();
  43. textNode->text = res.raw;
  44. // bind text to tag
  45. textNode->parent = tagNode;
  46. tagNode->children.push_back(textNode);
  47. // send NodeTree to window
  48. //this->win->setDOM(rootNode);
  49. docComponent->setDOM(rootNode);
  50. } else {
  51. std::cout << "setWindowContent() - I don't know how to render non-html files" << std::endl;
  52. }
  53. }
  54. std::shared_ptr<Node> findFormNode(std::shared_ptr<Node> node) {
  55. if (!node.get()) {
  56. // found root?
  57. return nullptr;
  58. }
  59. TagNode *tagNode = dynamic_cast<TagNode*>(node.get());
  60. if (tagNode) {
  61. if (tagNode->tag == "form") {
  62. return node;
  63. }
  64. }
  65. return findFormNode(node->parent);
  66. }
  67. std::unique_ptr<std::pair<std::string, std::string>> getTagNodeNameValue(TagNode &tagNode) {
  68. auto namePropIter = tagNode.properties.find("name");
  69. if (namePropIter == tagNode.properties.end()) {
  70. return nullptr;
  71. }
  72. auto valuePropIter = tagNode.properties.find("value");
  73. if (valuePropIter == tagNode.properties.end()) {
  74. return nullptr;
  75. }
  76. return std::make_unique<std::pair<std::string, std::string>>(namePropIter->second, valuePropIter->second);
  77. }
  78. // how do we match up nodes to components to extract the current values out of the components?
  79. // well we link input components back to node (and/or nodes back to components)
  80. // or we create a form UI component
  81. 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) {
  82. if (!result) {
  83. result = std::make_unique<std::map<std::string, std::string>>();
  84. }
  85. // check this node
  86. TagNode *tagNode = dynamic_cast<TagNode*>(formNode.get());
  87. if (tagNode) {
  88. auto typePropIter = tagNode->properties.find("type");
  89. bool skip = false;
  90. if (typePropIter != tagNode->properties.end()) {
  91. if (typePropIter->second == "submit") {
  92. skip = true;
  93. }
  94. if (typePropIter->second == "checkbox") {
  95. // FIXME: see if it's checked or not
  96. skip = true;
  97. }
  98. if (typePropIter->second == "radio") {
  99. // FIXME: see if it's checked or not
  100. skip = true;
  101. }
  102. }
  103. auto namePropIter = tagNode->properties.find("name");
  104. if (!skip && namePropIter != tagNode->properties.end()) {
  105. auto valuePropIter = tagNode->properties.find("value");
  106. if (valuePropIter != tagNode->properties.end()) {
  107. auto it = result->find(namePropIter->second);
  108. if (it == result->end()) {
  109. std::cout << "INPUTElement.buildFormData - setting " << namePropIter->second << " to [" << valuePropIter->second << "]" << std::endl;
  110. result->insert(std::pair<std::string, std::string>(namePropIter->second, valuePropIter->second));
  111. } else {
  112. (*result)[namePropIter->second] = valuePropIter->second;
  113. }
  114. }
  115. }
  116. }
  117. // check children noedes
  118. for (auto &child : formNode->children) {
  119. //std::shared_ptr<TagNode> tagNode = std::dynamic_pointer_cast<TagNode>(child);
  120. //if (tagNode) {
  121. result = buildFormData(child, std::move(result));
  122. //}
  123. }
  124. return result;
  125. }
  126. std::unique_ptr<Component> INPUTElement::renderer(const ElementRenderRequest &request) {
  127. // const float rawX, const float rawY, const float rawWidth, const float rawHeight, const int windowWidth, const int windowHeight
  128. // what should our default size be?
  129. //std::cout << "INPUTElement::renderer - creating InputComponent at " << x << "x" << y << std::endl;
  130. TagNode *tagNode = dynamic_cast<TagNode*>(request.node.get());
  131. if (tagNode) {
  132. std::string type = "text";
  133. auto propIter = tagNode->properties.find("type");
  134. if (propIter != tagNode->properties.end()) {
  135. //std::cout << "input is of type: " << propIter->second << std::endl;
  136. type = propIter->second;
  137. }
  138. //std::cout << "type is " << type << std::endl;
  139. if (type == "text") {
  140. std::unique_ptr<InputComponent> inputComponent = std::make_unique<InputComponent>(0, 0, 125.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
  141. inputComponent->node = tagNode;
  142. inputComponent->name = "textInput";
  143. auto propIter2 = tagNode->properties.find("value");
  144. if (propIter2 != tagNode->properties.end()) {
  145. inputComponent->value = propIter2->second;
  146. }
  147. return std::move(inputComponent);
  148. } else if (type == "submit") {
  149. //std::cout << "Creating submit" << std::endl;
  150. std::unique_ptr<ButtonComponent> butComponent = std::make_unique<ButtonComponent>(0, 0, 62.0f, 13.0f, request.parentComponent->win->windowWidth, request.parentComponent->win->windowHeight);
  151. auto propIter3 = tagNode->properties.find("value");
  152. if (propIter3 != tagNode->properties.end()) {
  153. butComponent->value = propIter3->second;
  154. } else {
  155. butComponent->value = "submit";
  156. }
  157. butComponent->onClick=[tagNode, request]() {
  158. // recurse up to find oug form tag
  159. std::shared_ptr<Node> formNode = findFormNode(request.node);
  160. if (!formNode) {
  161. std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find form parent for submit" << std::endl;
  162. return;
  163. }
  164. if (!request.docComponent) {
  165. std::cout << "INPUTElement::renderer:butComponent->onClick - Can't find documentComponent for submit" << std::endl;
  166. return;
  167. }
  168. TagNode *formTagNode = dynamic_cast<TagNode*>(formNode.get());
  169. auto formNodeActionIter = formTagNode->properties.find("action");
  170. if (formNodeActionIter == formTagNode->properties.end()) {
  171. std::cout << "Form has no action" << std::endl;
  172. return;
  173. }
  174. auto formNodeMethodIter = formTagNode->properties.find("method");
  175. std::string method="GET";
  176. if (formNodeMethodIter != formTagNode->properties.end()) {
  177. method=formNodeMethodIter->second;
  178. }
  179. std::cout << "Form method is " << method << std::endl;
  180. auto formData = buildFormData(formNode, nullptr);
  181. // add our name/value (because we skip all submit buttons, there can only be one)
  182. auto submitButtonNameValue = getTagNodeNameValue(*tagNode);
  183. if (submitButtonNameValue) {
  184. formData->insert(*submitButtonNameValue);
  185. }
  186. if (method=="POST" || method=="post") {
  187. // need documentComponent
  188. URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second));
  189. std::cout << "Action URL is " << uAction.toString() << std::endl;
  190. // download URL
  191. WebResource res = postWebResource(uAction, std::move(formData));
  192. handleResource(res, uAction.toString(), request.docComponent);
  193. } else {
  194. // need documentComponent
  195. std::string queryString = "";
  196. if (formData) {
  197. // iterator over formData
  198. for (auto &entry : *formData) {
  199. queryString += entry.first + "=" + entry.second + "&";
  200. }
  201. // strip last & off
  202. queryString = queryString.substr(0, queryString.size() - 1);
  203. std::cout << "queryString is " << queryString << std::endl;
  204. // The moral of the story is, if you have binary (non-alphanumeric) data (or a significantly sized payload) to transmit, use multipart/form-data
  205. auto search = queryString.find(" ");
  206. if (search != std::string::npos) {
  207. queryString.replace(search, 1, "+");
  208. }
  209. }
  210. URL uAction = request.docComponent->currentURL.merge(URL(formNodeActionIter->second+"?"+queryString));
  211. std::cout << "Action URL is " << uAction.toString() << std::endl;
  212. // download URL
  213. WebResource res = postWebResource(uAction, nullptr);
  214. handleResource(res, uAction.toString(), request.docComponent);
  215. }
  216. };
  217. return std::move(butComponent);
  218. } else if (type == "hidden") {
  219. // we don't need a UI component but we do belong in the form
  220. } else {
  221. // submit, button, checkbox, file
  222. //std::cout << "INPUTElement::renderer - ignoring input is of type: " << type << std::endl;
  223. }
  224. }
  225. /*
  226. TextNode *textNode = dynamic_cast<TextNode*>(node.get());
  227. if (textNode) {
  228. if (node->parent->children.size() == 1) {
  229. std::unique_ptr<Component> component = std::make_unique<InputComponent>(textNode->text, x, y, 12, false, 0x000000FF, windowWidth, windowHeight);
  230. return component;
  231. }
  232. }
  233. */
  234. return nullptr;
  235. }