HTMLIngredient.mjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295
  1. /**
  2. * @author n1474335 [n1474335@gmail.com]
  3. * @copyright Crown Copyright 2016
  4. * @license Apache-2.0
  5. */
  6. import Utils from "../core/Utils";
  7. /**
  8. * Object to handle the creation of operation ingredients.
  9. */
  10. class HTMLIngredient {
  11. /**
  12. * HTMLIngredient constructor.
  13. *
  14. * @param {Object} config - The configuration object for this ingredient.
  15. * @param {App} app - The main view object for CyberChef.
  16. * @param {Manager} manager - The CyberChef event manager.
  17. */
  18. constructor(config, app, manager) {
  19. this.app = app;
  20. this.manager = manager;
  21. this.name = config.name;
  22. this.type = config.type;
  23. this.value = config.value;
  24. this.disabled = config.disabled || false;
  25. this.hint = config.hint || false;
  26. this.rows = config.rows || false;
  27. this.target = config.target;
  28. this.defaultIndex = config.defaultIndex || 0;
  29. this.toggleValues = config.toggleValues;
  30. this.id = "ing-" + this.app.nextIngId();
  31. this.min = (typeof config.min === "number") ? config.min : "";
  32. this.max = (typeof config.max === "number") ? config.max : "";
  33. this.step = config.step || 1;
  34. }
  35. /**
  36. * Renders the ingredient in HTML.
  37. *
  38. * @returns {string}
  39. */
  40. toHtml() {
  41. let html = "",
  42. i, m;
  43. switch (this.type) {
  44. case "string":
  45. case "binaryString":
  46. case "byteArray":
  47. html += `<div class="form-group">
  48. <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
  49. <input type="text"
  50. class="form-control arg"
  51. id="${this.id}"
  52. arg-name="${this.name}"
  53. value="${this.value}"
  54. ${this.disabled ? "disabled" : ""}>
  55. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  56. </div>`;
  57. break;
  58. case "shortString":
  59. case "binaryShortString":
  60. html += `<div class="form-group inline">
  61. <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
  62. <input type="text"
  63. class="form-control arg inline"
  64. id="${this.id}"
  65. arg-name="${this.name}"
  66. value="${this.value}"
  67. ${this.disabled ? "disabled" : ""}>
  68. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  69. </div>`;
  70. break;
  71. case "toggleString":
  72. html += `<div class="form-group input-group">
  73. <div class="toggle-string">
  74. <label for="${this.id}" class="bmd-label-floating toggle-string">${this.name}</label>
  75. <input type="text"
  76. class="form-control arg toggle-string"
  77. id="${this.id}"
  78. arg-name="${this.name}"
  79. value="${this.value}"
  80. ${this.disabled ? "disabled" : ""}>
  81. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  82. </div>
  83. <div class="input-group-append">
  84. <button class="btn btn-secondary dropdown-toggle" type="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">${this.toggleValues[0]}</button>
  85. <div class="dropdown-menu toggle-dropdown">`;
  86. for (i = 0; i < this.toggleValues.length; i++) {
  87. html += `<a class="dropdown-item" href="#">${this.toggleValues[i]}</a>`;
  88. }
  89. html += `</div>
  90. </div>
  91. </div>`;
  92. break;
  93. case "number":
  94. html += `<div class="form-group inline">
  95. <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
  96. <input type="number"
  97. class="form-control arg inline"
  98. id="${this.id}"
  99. arg-name="${this.name}"
  100. value="${this.value}"
  101. min="${this.min}"
  102. max="${this.max}"
  103. step="${this.step}"
  104. ${this.disabled ? "disabled" : ""}>
  105. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  106. </div>`;
  107. break;
  108. case "boolean":
  109. html += `<div class="form-group inline boolean-arg">
  110. <div class="checkbox">
  111. <label>
  112. <input type="checkbox"
  113. class="arg"
  114. id="${this.id}"
  115. arg-name="${this.name}"
  116. ${this.value ? " checked" : ""}
  117. ${this.disabled ? " disabled" : ""}
  118. value="${this.name}"> ${this.name}
  119. </label>
  120. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  121. </div>
  122. </div>`;
  123. break;
  124. case "option":
  125. html += `<div class="form-group inline">
  126. <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
  127. <select
  128. class="form-control arg inline"
  129. id="${this.id}"
  130. arg-name="${this.name}"
  131. ${this.disabled ? "disabled" : ""}>`;
  132. for (i = 0; i < this.value.length; i++) {
  133. if ((m = this.value[i].match(/\[([a-z0-9 -()^]+)\]/i))) {
  134. html += `<optgroup label="${m[1]}">`;
  135. } else if ((m = this.value[i].match(/\[\/([a-z0-9 -()^]+)\]/i))) {
  136. html += "</optgroup>";
  137. } else {
  138. html += `<option ${this.defaultIndex === i ? "selected" : ""}>${this.value[i]}</option>`;
  139. }
  140. }
  141. html += `</select>
  142. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  143. </div>`;
  144. break;
  145. case "populateOption":
  146. html += `<div class="form-group">
  147. <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
  148. <select
  149. class="form-control arg"
  150. id="${this.id}"
  151. arg-name="${this.name}"
  152. ${this.disabled ? "disabled" : ""}>`;
  153. for (i = 0; i < this.value.length; i++) {
  154. if ((m = this.value[i].name.match(/\[([a-z0-9 -()^]+)\]/i))) {
  155. html += `<optgroup label="${m[1]}">`;
  156. } else if ((m = this.value[i].name.match(/\[\/([a-z0-9 -()^]+)\]/i))) {
  157. html += "</optgroup>";
  158. } else {
  159. html += `<option populate-value="${Utils.escapeHtml(this.value[i].value)}">${this.value[i].name}</option>`;
  160. }
  161. }
  162. html += `</select>
  163. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  164. </div>`;
  165. this.manager.addDynamicListener("#" + this.id, "change", this.populateOptionChange, this);
  166. break;
  167. case "editableOption":
  168. html += `<div class="form-group input-group">
  169. <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
  170. <input type="text"
  171. class="form-control arg"
  172. id="${this.id}"
  173. arg-name="${this.name}"
  174. value="${this.value[this.defaultIndex].value}"
  175. ${this.disabled ? "disabled" : ""}>
  176. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  177. <div class="input-group-append">
  178. <button type="button"
  179. class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
  180. data-toggle="dropdown"
  181. data-boundary="scrollParent"
  182. aria-haspopup="true"
  183. aria-expanded="false">
  184. <span class="sr-only">Toggle Dropdown</span>
  185. </button>
  186. <div class="dropdown-menu editable-option-menu">`;
  187. for (i = 0; i < this.value.length; i++) {
  188. html += `<a class="dropdown-item" href="#" value="${this.value[i].value}">${this.value[i].name}</a>`;
  189. }
  190. html += `</div>
  191. </div>
  192. </div>`;
  193. this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this);
  194. break;
  195. case "editableOptionShort":
  196. html += `<div class="form-group input-group inline">
  197. <label for="${this.id}" class="bmd-label-floating inline">${this.name}</label>
  198. <input type="text"
  199. class="form-control arg inline"
  200. id="${this.id}"
  201. arg-name="${this.name}"
  202. value="${this.value[this.defaultIndex].value}"
  203. ${this.disabled ? "disabled" : ""}>
  204. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  205. <div class="input-group-append inline">
  206. <button type="button"
  207. class="btn btn-secondary dropdown-toggle dropdown-toggle-split"
  208. data-toggle="dropdown"
  209. data-boundary="scrollParent"
  210. aria-haspopup="true"
  211. aria-expanded="false">
  212. <span class="sr-only">Toggle Dropdown</span>
  213. </button>
  214. <div class="dropdown-menu editable-option-menu">`;
  215. for (i = 0; i < this.value.length; i++) {
  216. html += `<a class="dropdown-item" href="#" value="${this.value[i].value}">${this.value[i].name}</a>`;
  217. }
  218. html += `</div>
  219. </div>
  220. </div>`;
  221. this.manager.addDynamicListener(".editable-option-menu a", "click", this.editableOptionClick, this);
  222. break;
  223. case "text":
  224. html += `<div class="form-group">
  225. <label for="${this.id}" class="bmd-label-floating">${this.name}</label>
  226. <textarea
  227. class="form-control arg"
  228. id="${this.id}"
  229. arg-name="${this.name}"
  230. rows="${this.rows ? this.rows : 3}"
  231. ${this.disabled ? "disabled" : ""}>${this.value}</textarea>
  232. ${this.hint ? "<span class='bmd-help'>" + this.hint + "</span>" : ""}
  233. </div>`;
  234. break;
  235. default:
  236. break;
  237. }
  238. return html;
  239. }
  240. /**
  241. * Handler for populate option changes.
  242. * Populates the relevant argument with the specified value.
  243. *
  244. * @param {event} e
  245. */
  246. populateOptionChange(e) {
  247. const el = e.target;
  248. const op = el.parentNode.parentNode;
  249. const target = op.querySelectorAll(".arg")[this.target];
  250. target.value = el.childNodes[el.selectedIndex].getAttribute("populate-value");
  251. const evt = new Event("change");
  252. target.dispatchEvent(evt);
  253. this.manager.recipe.ingChange();
  254. }
  255. /**
  256. * Handler for editable option clicks.
  257. * Populates the input box with the selected value.
  258. *
  259. * @param {event} e
  260. */
  261. editableOptionClick(e) {
  262. e.preventDefault();
  263. e.stopPropagation();
  264. const link = e.target,
  265. input = link.parentNode.parentNode.parentNode.querySelector("input");
  266. input.value = link.getAttribute("value");
  267. const evt = new Event("change");
  268. input.dispatchEvent(evt);
  269. this.manager.recipe.ingChange();
  270. }
  271. }
  272. export default HTMLIngredient;