Manager.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import WindowWaiter from "./WindowWaiter.js";
  2. import ControlsWaiter from "./ControlsWaiter.js";
  3. import RecipeWaiter from "./RecipeWaiter.js";
  4. import OperationsWaiter from "./OperationsWaiter.js";
  5. import InputWaiter from "./InputWaiter.js";
  6. import OutputWaiter from "./OutputWaiter.js";
  7. import OptionsWaiter from "./OptionsWaiter.js";
  8. import HighlighterWaiter from "./HighlighterWaiter.js";
  9. import SeasonalWaiter from "./SeasonalWaiter.js";
  10. /**
  11. * This object controls the Waiters responsible for handling events from all areas of the app.
  12. *
  13. * @author n1474335 [n1474335@gmail.com]
  14. * @copyright Crown Copyright 2016
  15. * @license Apache-2.0
  16. *
  17. * @constructor
  18. * @param {App} app - The main view object for CyberChef.
  19. */
  20. const Manager = function(app) {
  21. this.app = app;
  22. // Define custom events
  23. /**
  24. * @event Manager#appstart
  25. */
  26. this.appstart = new CustomEvent("appstart", {bubbles: true});
  27. /**
  28. * @event Manager#operationadd
  29. */
  30. this.operationadd = new CustomEvent("operationadd", {bubbles: true});
  31. /**
  32. * @event Manager#operationremove
  33. */
  34. this.operationremove = new CustomEvent("operationremove", {bubbles: true});
  35. /**
  36. * @event Manager#oplistcreate
  37. */
  38. this.oplistcreate = new CustomEvent("oplistcreate", {bubbles: true});
  39. /**
  40. * @event Manager#statechange
  41. */
  42. this.statechange = new CustomEvent("statechange", {bubbles: true});
  43. // Define Waiter objects to handle various areas
  44. this.window = new WindowWaiter(this.app);
  45. this.controls = new ControlsWaiter(this.app, this);
  46. this.recipe = new RecipeWaiter(this.app, this);
  47. this.ops = new OperationsWaiter(this.app, this);
  48. this.input = new InputWaiter(this.app, this);
  49. this.output = new OutputWaiter(this.app, this);
  50. this.options = new OptionsWaiter(this.app);
  51. this.highlighter = new HighlighterWaiter(this.app);
  52. this.seasonal = new SeasonalWaiter(this.app, this);
  53. // Object to store dynamic handlers to fire on elements that may not exist yet
  54. this.dynamicHandlers = {};
  55. this.initialiseEventListeners();
  56. };
  57. /**
  58. * Sets up the various components and listeners.
  59. */
  60. Manager.prototype.setup = function() {
  61. this.recipe.initialiseOperationDragNDrop();
  62. this.controls.autoBakeChange();
  63. this.seasonal.load();
  64. };
  65. /**
  66. * Main function to handle the creation of the event listeners.
  67. */
  68. Manager.prototype.initialiseEventListeners = function() {
  69. // Global
  70. window.addEventListener("resize", this.window.windowResize.bind(this.window));
  71. window.addEventListener("blur", this.window.windowBlur.bind(this.window));
  72. window.addEventListener("focus", this.window.windowFocus.bind(this.window));
  73. window.addEventListener("statechange", this.app.stateChange.bind(this.app));
  74. window.addEventListener("popstate", this.app.popState.bind(this.app));
  75. // Controls
  76. document.getElementById("bake").addEventListener("click", this.controls.bakeClick.bind(this.controls));
  77. document.getElementById("auto-bake").addEventListener("change", this.controls.autoBakeChange.bind(this.controls));
  78. document.getElementById("step").addEventListener("click", this.controls.stepClick.bind(this.controls));
  79. document.getElementById("clr-recipe").addEventListener("click", this.controls.clearRecipeClick.bind(this.controls));
  80. document.getElementById("clr-breaks").addEventListener("click", this.controls.clearBreaksClick.bind(this.controls));
  81. document.getElementById("save").addEventListener("click", this.controls.saveClick.bind(this.controls));
  82. document.getElementById("save-button").addEventListener("click", this.controls.saveButtonClick.bind(this.controls));
  83. document.getElementById("save-link-recipe-checkbox").addEventListener("change", this.controls.slrCheckChange.bind(this.controls));
  84. document.getElementById("save-link-input-checkbox").addEventListener("change", this.controls.sliCheckChange.bind(this.controls));
  85. document.getElementById("load").addEventListener("click", this.controls.loadClick.bind(this.controls));
  86. document.getElementById("load-delete-button").addEventListener("click", this.controls.loadDeleteClick.bind(this.controls));
  87. document.getElementById("load-name").addEventListener("change", this.controls.loadNameChange.bind(this.controls));
  88. document.getElementById("load-button").addEventListener("click", this.controls.loadButtonClick.bind(this.controls));
  89. document.getElementById("support").addEventListener("click", this.controls.supportButtonClick.bind(this.controls));
  90. this.addMultiEventListener("#save-text", "keyup paste", this.controls.saveTextChange, this.controls);
  91. // Operations
  92. this.addMultiEventListener("#search", "keyup paste search", this.ops.searchOperations, this.ops);
  93. this.addDynamicListener(".op-list li.operation", "dblclick", this.ops.operationDblclick, this.ops);
  94. document.getElementById("edit-favourites").addEventListener("click", this.ops.editFavouritesClick.bind(this.ops));
  95. document.getElementById("save-favourites").addEventListener("click", this.ops.saveFavouritesClick.bind(this.ops));
  96. document.getElementById("reset-favourites").addEventListener("click", this.ops.resetFavouritesClick.bind(this.ops));
  97. this.addDynamicListener(".op-list .op-icon", "mouseover", this.ops.opIconMouseover, this.ops);
  98. this.addDynamicListener(".op-list .op-icon", "mouseleave", this.ops.opIconMouseleave, this.ops);
  99. this.addDynamicListener(".op-list", "oplistcreate", this.ops.opListCreate, this.ops);
  100. this.addDynamicListener("li.operation", "operationadd", this.recipe.opAdd.bind(this.recipe));
  101. // Recipe
  102. this.addDynamicListener(".arg", "keyup", this.recipe.ingChange, this.recipe);
  103. this.addDynamicListener(".arg", "change", this.recipe.ingChange, this.recipe);
  104. this.addDynamicListener(".disable-icon", "click", this.recipe.disableClick, this.recipe);
  105. this.addDynamicListener(".breakpoint", "click", this.recipe.breakpointClick, this.recipe);
  106. this.addDynamicListener("#rec-list li.operation", "dblclick", this.recipe.operationDblclick, this.recipe);
  107. this.addDynamicListener("#rec-list li.operation > div", "dblclick", this.recipe.operationChildDblclick, this.recipe);
  108. this.addDynamicListener("#rec-list .input-group .dropdown-menu a", "click", this.recipe.dropdownToggleClick, this.recipe);
  109. this.addDynamicListener("#rec-list", "operationremove", this.recipe.opRemove.bind(this.recipe));
  110. // Input
  111. this.addMultiEventListener("#input-text", "keyup paste", this.input.inputChange, this.input);
  112. document.getElementById("reset-layout").addEventListener("click", this.app.resetLayout.bind(this.app));
  113. document.getElementById("clr-io").addEventListener("click", this.input.clearIoClick.bind(this.input));
  114. document.getElementById("input-text").addEventListener("dragover", this.input.inputDragover.bind(this.input));
  115. document.getElementById("input-text").addEventListener("dragleave", this.input.inputDragleave.bind(this.input));
  116. document.getElementById("input-text").addEventListener("drop", this.input.inputDrop.bind(this.input));
  117. document.getElementById("input-text").addEventListener("scroll", this.highlighter.inputScroll.bind(this.highlighter));
  118. document.getElementById("input-text").addEventListener("mouseup", this.highlighter.inputMouseup.bind(this.highlighter));
  119. document.getElementById("input-text").addEventListener("mousemove", this.highlighter.inputMousemove.bind(this.highlighter));
  120. this.addMultiEventListener("#input-text", "mousedown dblclick select", this.highlighter.inputMousedown, this.highlighter);
  121. // Output
  122. document.getElementById("save-to-file").addEventListener("click", this.output.saveClick.bind(this.output));
  123. document.getElementById("switch").addEventListener("click", this.output.switchClick.bind(this.output));
  124. document.getElementById("undo-switch").addEventListener("click", this.output.undoSwitchClick.bind(this.output));
  125. document.getElementById("maximise-output").addEventListener("click", this.output.maximiseOutputClick.bind(this.output));
  126. document.getElementById("output-text").addEventListener("scroll", this.highlighter.outputScroll.bind(this.highlighter));
  127. document.getElementById("output-text").addEventListener("mouseup", this.highlighter.outputMouseup.bind(this.highlighter));
  128. document.getElementById("output-text").addEventListener("mousemove", this.highlighter.outputMousemove.bind(this.highlighter));
  129. document.getElementById("output-html").addEventListener("mouseup", this.highlighter.outputHtmlMouseup.bind(this.highlighter));
  130. document.getElementById("output-html").addEventListener("mousemove", this.highlighter.outputHtmlMousemove.bind(this.highlighter));
  131. this.addMultiEventListener("#output-text", "mousedown dblclick select", this.highlighter.outputMousedown, this.highlighter);
  132. this.addMultiEventListener("#output-html", "mousedown dblclick select", this.highlighter.outputHtmlMousedown, this.highlighter);
  133. // Options
  134. document.getElementById("options").addEventListener("click", this.options.optionsClick.bind(this.options));
  135. document.getElementById("reset-options").addEventListener("click", this.options.resetOptionsClick.bind(this.options));
  136. $(document).on("switchChange.bootstrapSwitch", ".option-item input:checkbox", this.options.switchChange.bind(this.options));
  137. $(document).on("switchChange.bootstrapSwitch", ".option-item input:checkbox", this.options.setWordWrap.bind(this.options));
  138. this.addDynamicListener(".option-item input[type=number]", "keyup", this.options.numberChange, this.options);
  139. this.addDynamicListener(".option-item input[type=number]", "change", this.options.numberChange, this.options);
  140. this.addDynamicListener(".option-item select", "change", this.options.selectChange, this.options);
  141. // Misc
  142. document.getElementById("alert-close").addEventListener("click", this.app.alertCloseClick.bind(this.app));
  143. };
  144. /**
  145. * Adds an event listener to each element in the specified group.
  146. *
  147. * @param {string} selector - A selector string for the element group to add the event to, see
  148. * this.getAll()
  149. * @param {string} eventType - The event to listen for
  150. * @param {function} callback - The function to execute when the event is triggered
  151. * @param {Object} [scope=this] - The object to bind to the callback function
  152. *
  153. * @example
  154. * // Calls the clickable function whenever any element with the .clickable class is clicked
  155. * this.addListeners(".clickable", "click", this.clickable, this);
  156. */
  157. Manager.prototype.addListeners = function(selector, eventType, callback, scope) {
  158. scope = scope || this;
  159. [].forEach.call(document.querySelectorAll(selector), function(el) {
  160. el.addEventListener(eventType, callback.bind(scope));
  161. });
  162. };
  163. /**
  164. * Adds multiple event listeners to the specified element.
  165. *
  166. * @param {string} selector - A selector string for the element to add the events to
  167. * @param {string} eventTypes - A space-separated string of all the event types to listen for
  168. * @param {function} callback - The function to execute when the events are triggered
  169. * @param {Object} [scope=this] - The object to bind to the callback function
  170. *
  171. * @example
  172. * // Calls the search function whenever the the keyup, paste or search events are triggered on the
  173. * // search element
  174. * this.addMultiEventListener("search", "keyup paste search", this.search, this);
  175. */
  176. Manager.prototype.addMultiEventListener = function(selector, eventTypes, callback, scope) {
  177. const evs = eventTypes.split(" ");
  178. for (let i = 0; i < evs.length; i++) {
  179. document.querySelector(selector).addEventListener(evs[i], callback.bind(scope));
  180. }
  181. };
  182. /**
  183. * Adds multiple event listeners to each element in the specified group.
  184. *
  185. * @param {string} selector - A selector string for the element group to add the events to
  186. * @param {string} eventTypes - A space-separated string of all the event types to listen for
  187. * @param {function} callback - The function to execute when the events are triggered
  188. * @param {Object} [scope=this] - The object to bind to the callback function
  189. *
  190. * @example
  191. * // Calls the save function whenever the the keyup or paste events are triggered on any element
  192. * // with the .saveable class
  193. * this.addMultiEventListener(".saveable", "keyup paste", this.save, this);
  194. */
  195. Manager.prototype.addMultiEventListeners = function(selector, eventTypes, callback, scope) {
  196. const evs = eventTypes.split(" ");
  197. for (let i = 0; i < evs.length; i++) {
  198. this.addListeners(selector, evs[i], callback, scope);
  199. }
  200. };
  201. /**
  202. * Adds an event listener to the global document object which will listen on dynamic elements which
  203. * may not exist in the DOM yet.
  204. *
  205. * @param {string} selector - A selector string for the element(s) to add the event to
  206. * @param {string} eventType - The event(s) to listen for
  207. * @param {function} callback - The function to execute when the event(s) is/are triggered
  208. * @param {Object} [scope=this] - The object to bind to the callback function
  209. *
  210. * @example
  211. * // Pops up an alert whenever any button is clicked, even if it is added to the DOM after this
  212. * // listener is created
  213. * this.addDynamicListener("button", "click", alert, this);
  214. */
  215. Manager.prototype.addDynamicListener = function(selector, eventType, callback, scope) {
  216. const eventConfig = {
  217. selector: selector,
  218. callback: callback.bind(scope || this)
  219. };
  220. if (this.dynamicHandlers.hasOwnProperty(eventType)) {
  221. // Listener already exists, add new handler to the appropriate list
  222. this.dynamicHandlers[eventType].push(eventConfig);
  223. } else {
  224. this.dynamicHandlers[eventType] = [eventConfig];
  225. // Set up listener for this new type
  226. document.addEventListener(eventType, this.dynamicListenerHandler.bind(this));
  227. }
  228. };
  229. /**
  230. * Handler for dynamic events. This function is called for any dynamic event and decides which
  231. * callback(s) to execute based on the type and selector.
  232. *
  233. * @param {Event} e - The event to be handled
  234. */
  235. Manager.prototype.dynamicListenerHandler = function(e) {
  236. const { type, target } = e;
  237. const handlers = this.dynamicHandlers[type];
  238. const matches = target.matches ||
  239. target.webkitMatchesSelector ||
  240. target.mozMatchesSelector ||
  241. target.msMatchesSelector ||
  242. target.oMatchesSelector;
  243. for (let i = 0; i < handlers.length; i++) {
  244. if (matches && matches.call(target, handlers[i].selector)) {
  245. handlers[i].callback(e);
  246. }
  247. }
  248. };
  249. export default Manager;