cyberchef/src/js/views/html/Manager.js
Thomas Grainger 640f555b8a
fix eslint:recommended errors
this should find most of the imports
2016-11-29 18:36:38 +00:00

276 lines
14 KiB
JavaScript
Executable File

import $ from 'jquery';
import WindowWaiter from './WindowWaiter';
import ControlsWaiter from './ControlsWaiter';
import RecipeWaiter from './RecipeWaiter';
import OperationsWaiter from './OperationsWaiter';
import InputWaiter from './InputWaiter';
import OutputWaiter from './OutputWaiter';
import HighlighterWaiter from './HighlighterWaiter';
import SeasonalWaiter from './SeasonalWaiter';
import OptionsWaiter from './OptionsWaiter';
/**
* This object controls the Waiters responsible for handling events from all areas of the app.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @constructor
* @param {HTMLApp} app - The main view object for CyberChef.
*/
const Manager = function (app) {
this.app = app;
// Define custom events
/**
* @event Manager#appstart
*/
this.appstart = new CustomEvent('appstart', { bubbles: true });
/**
* @event Manager#operationadd
*/
this.operationadd = new CustomEvent('operationadd', { bubbles: true });
/**
* @event Manager#operationremove
*/
this.operationremove = new CustomEvent('operationremove', { bubbles: true });
/**
* @event Manager#oplistcreate
*/
this.oplistcreate = new CustomEvent('oplistcreate', { bubbles: true });
/**
* @event Manager#statechange
*/
this.statechange = new CustomEvent('statechange', { bubbles: true });
// Define Waiter objects to handle various areas
this.window = new WindowWaiter(this.app);
this.controls = new ControlsWaiter(this.app, this);
this.recipe = new RecipeWaiter(this.app, this);
this.ops = new OperationsWaiter(this.app, this);
this.input = new InputWaiter(this.app, this);
this.output = new OutputWaiter(this.app, this);
this.options = new OptionsWaiter(this.app);
this.highlighter = new HighlighterWaiter(this.app);
this.seasonal = new SeasonalWaiter(this.app, this);
// Object to store dynamic handlers to fire on elements that may not exist yet
this.dynamic_handlers = {};
this.initialise_event_listeners();
};
/**
* Sets up the various components and listeners.
*/
Manager.prototype.setup = function () {
this.recipe.initialise_operation_drag_n_drop();
this.controls.auto_bake_change();
this.seasonal.load();
};
/**
* Main function to handle the creation of the event listeners.
*/
Manager.prototype.initialise_event_listeners = function () {
// Global
window.addEventListener('resize', this.window.window_resize.bind(this.window));
window.addEventListener('blur', this.window.window_blur.bind(this.window));
window.addEventListener('focus', this.window.window_focus.bind(this.window));
window.addEventListener('statechange', this.app.state_change.bind(this.app));
window.addEventListener('popstate', this.app.pop_state.bind(this.app));
// Controls
document.getElementById('bake').addEventListener('click', this.controls.bake_click.bind(this.controls));
document.getElementById('auto-bake').addEventListener('change', this.controls.auto_bake_change.bind(this.controls));
document.getElementById('step').addEventListener('click', this.controls.step_click.bind(this.controls));
document.getElementById('clr-recipe').addEventListener('click', this.controls.clear_recipe_click.bind(this.controls));
document.getElementById('clr-breaks').addEventListener('click', this.controls.clear_breaks_click.bind(this.controls));
document.getElementById('save').addEventListener('click', this.controls.save_click.bind(this.controls));
document.getElementById('save-button').addEventListener('click', this.controls.save_button_click.bind(this.controls));
document.getElementById('save-link-recipe-checkbox').addEventListener('change', this.controls.slr_check_change.bind(this.controls));
document.getElementById('save-link-input-checkbox').addEventListener('change', this.controls.sli_check_change.bind(this.controls));
document.getElementById('load').addEventListener('click', this.controls.load_click.bind(this.controls));
document.getElementById('load-delete-button').addEventListener('click', this.controls.load_delete_click.bind(this.controls));
document.getElementById('load-name').addEventListener('change', this.controls.load_name_change.bind(this.controls));
document.getElementById('load-button').addEventListener('click', this.controls.load_button_click.bind(this.controls));
this.add_multi_event_listener('#save-text', 'keyup paste', this.controls.save_text_change, this.controls);
// Operations
this.add_multi_event_listener('#search', 'keyup paste search', this.ops.search_operations, this.ops);
this.add_dynamic_listener('.op_list li.operation', 'dblclick', this.ops.operation_dblclick, this.ops);
document.getElementById('edit-favourites').addEventListener('click', this.ops.edit_favourites_click.bind(this.ops));
document.getElementById('save-favourites').addEventListener('click', this.ops.save_favourites_click.bind(this.ops));
document.getElementById('reset-favourites').addEventListener('click', this.ops.reset_favourites_click.bind(this.ops));
this.add_dynamic_listener('.op_list .op-icon', 'mouseover', this.ops.op_icon_mouseover, this.ops);
this.add_dynamic_listener('.op_list .op-icon', 'mouseleave', this.ops.op_icon_mouseleave, this.ops);
this.add_dynamic_listener('.op_list', 'oplistcreate', this.ops.op_list_create, this.ops);
this.add_dynamic_listener('li.operation', 'operationadd', this.recipe.op_add.bind(this.recipe));
// Recipe
this.add_dynamic_listener('.arg', 'keyup', this.recipe.ing_change, this.recipe);
this.add_dynamic_listener('.arg', 'change', this.recipe.ing_change, this.recipe);
this.add_dynamic_listener('.disable-icon', 'click', this.recipe.disable_click, this.recipe);
this.add_dynamic_listener('.breakpoint', 'click', this.recipe.breakpoint_click, this.recipe);
this.add_dynamic_listener('#rec_list li.operation', 'dblclick', this.recipe.operation_dblclick, this.recipe);
this.add_dynamic_listener('#rec_list li.operation > div', 'dblclick', this.recipe.operation_child_dblclick, this.recipe);
this.add_dynamic_listener('#rec_list .input-group .dropdown-menu a', 'click', this.recipe.dropdown_toggle_click, this.recipe);
this.add_dynamic_listener('#rec_list', 'operationremove', this.recipe.op_remove.bind(this.recipe));
// Input
this.add_multi_event_listener('#input-text', 'keyup paste', this.input.input_change, this.input);
document.getElementById('reset-layout').addEventListener('click', this.app.reset_layout.bind(this.app));
document.getElementById('clr-io').addEventListener('click', this.input.clear_io_click.bind(this.input));
document.getElementById('input-text').addEventListener('dragover', this.input.input_dragover.bind(this.input));
document.getElementById('input-text').addEventListener('dragleave', this.input.input_dragleave.bind(this.input));
document.getElementById('input-text').addEventListener('drop', this.input.input_drop.bind(this.input));
document.getElementById('input-text').addEventListener('scroll', this.highlighter.input_scroll.bind(this.highlighter));
document.getElementById('input-text').addEventListener('mouseup', this.highlighter.input_mouseup.bind(this.highlighter));
document.getElementById('input-text').addEventListener('mousemove', this.highlighter.input_mousemove.bind(this.highlighter));
this.add_multi_event_listener('#input-text', 'mousedown dblclick select', this.highlighter.input_mousedown, this.highlighter);
// Output
document.getElementById('save-to-file').addEventListener('click', this.output.save_click.bind(this.output));
document.getElementById('switch').addEventListener('click', this.output.switch_click.bind(this.output));
document.getElementById('undo-switch').addEventListener('click', this.output.undo_switch_click.bind(this.output));
document.getElementById('output-text').addEventListener('scroll', this.highlighter.output_scroll.bind(this.highlighter));
document.getElementById('output-text').addEventListener('mouseup', this.highlighter.output_mouseup.bind(this.highlighter));
document.getElementById('output-text').addEventListener('mousemove', this.highlighter.output_mousemove.bind(this.highlighter));
document.getElementById('output-html').addEventListener('mouseup', this.highlighter.output_html_mouseup.bind(this.highlighter));
document.getElementById('output-html').addEventListener('mousemove', this.highlighter.output_html_mousemove.bind(this.highlighter));
this.add_multi_event_listener('#output-text', 'mousedown dblclick select', this.highlighter.output_mousedown, this.highlighter);
this.add_multi_event_listener('#output-html', 'mousedown dblclick select', this.highlighter.output_html_mousedown, this.highlighter);
// Options
document.getElementById('options').addEventListener('click', this.options.options_click.bind(this.options));
document.getElementById('reset-options').addEventListener('click', this.options.reset_options_click.bind(this.options));
$('.option-item input:checkbox').on('switchChange.bootstrapSwitch', this.options.switch_change.bind(this.options));
$('.option-item input:checkbox').on('switchChange.bootstrapSwitch', this.options.set_word_wrap.bind(this.options));
this.add_dynamic_listener('.option-item input[type=number]', 'keyup', this.options.number_change, this.options);
this.add_dynamic_listener('.option-item input[type=number]', 'change', this.options.number_change, this.options);
this.add_dynamic_listener('.option-item select', 'change', this.options.select_change, this.options);
// Misc
document.getElementById('alert-close').addEventListener('click', this.app.alert_close_click.bind(this.app));
};
/**
* Adds an event listener to each element in the specified group.
*
* @param {string} selector - A selector string for the element group to add the event to, see
* this.get_all()
* @param {string} event_type - The event to listen for
* @param {function} callback - The function to execute when the event is triggered
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
* // Calls the clickable function whenever any element with the .clickable class is clicked
* this.add_listeners(".clickable", "click", this.clickable, this);
*/
Manager.prototype.add_listeners = function (selector, event_type, callback, scope) {
scope = scope || this;
[].forEach.call(document.querySelectorAll(selector), (el) => {
el.addEventListener(event_type, callback.bind(scope));
});
};
/**
* Adds multiple event listeners to the specified element.
*
* @param {string} selector - A selector string for the element to add the events to
* @param {string} event_types - A space-separated string of all the event types to listen for
* @param {function} callback - The function to execute when the events are triggered
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
* // Calls the search function whenever the the keyup, paste or search events are triggered on the
* // search element
* this.add_multi_event_listener("search", "keyup paste search", this.search, this);
*/
Manager.prototype.add_multi_event_listener = function (selector, event_types, callback, scope) {
const evs = event_types.split(' ');
for (let i = 0; i < evs.length; i++) {
document.querySelector(selector).addEventListener(evs[i], callback.bind(scope));
}
};
/**
* Adds multiple event listeners to each element in the specified group.
*
* @param {string} selector - A selector string for the element group to add the events to
* @param {string} event_types - A space-separated string of all the event types to listen for
* @param {function} callback - The function to execute when the events are triggered
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
* // Calls the save function whenever the the keyup or paste events are triggered on any element
* // with the .saveable class
* this.add_multi_event_listener(".saveable", "keyup paste", this.save, this);
*/
Manager.prototype.add_multi_event_listeners = function (selector, event_types, callback, scope) {
const evs = event_types.split(' ');
for (let i = 0; i < evs.length; i++) {
this.add_listeners(selector, evs[i], callback, scope);
}
};
/**
* Adds an event listener to the global document object which will listen on dynamic elements which
* may not exist in the DOM yet.
*
* @param {string} selector - A selector string for the element(s) to add the event to
* @param {string} event_type - The event(s) to listen for
* @param {function} callback - The function to execute when the event(s) is/are triggered
* @param {Object} [scope=this] - The object to bind to the callback function
*
* @example
* // Pops up an alert whenever any button is clicked, even if it is added to the DOM after this
* // listener is created
* this.add_dynamic_listener("button", "click", alert, this);
*/
Manager.prototype.add_dynamic_listener = function (selector, event_type, callback, scope) {
const event_config = {
selector,
callback: callback.bind(scope || this),
};
if (this.dynamic_handlers.hasOwnProperty(event_type)) {
// Listener already exists, add new handler to the appropriate list
this.dynamic_handlers[event_type].push(event_config);
} else {
this.dynamic_handlers[event_type] = [event_config];
// Set up listener for this new type
document.addEventListener(event_type, this.dynamic_listener_handler.bind(this));
}
};
/**
* Handler for dynamic events. This function is called for any dynamic event and decides which
* callback(s) to execute based on the type and selector.
*
* @param {Event} e - The event to be handled
*/
Manager.prototype.dynamic_listener_handler = function (e) {
let handlers = this.dynamic_handlers[e.type],
matches = e.target.matches ||
e.target.webkitMatchesSelector ||
e.target.mozMatchesSelector ||
e.target.msMatchesSelector ||
e.target.oMatchesSelector;
for (let i = 0; i < handlers.length; i++) {
if (matches && e.target[matches.name](handlers[i].selector)) {
handlers[i].callback(e);
}
}
};