HTMLIngredient.mjs 12 KB

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