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

424 lines
11 KiB
JavaScript
Executable file

import $ from 'jquery';
import HTMLOperation from './HTMLOperation';
/* globals Sortable */
/**
* Waiter to handle events related to the recipe.
*
* @author n1474335 [n1474335@gmail.com]
* @copyright Crown Copyright 2016
* @license Apache-2.0
*
* @constructor
* @param {HTMLApp} app - The main view object for CyberChef.
* @param {Manager} manager - The CyberChef event manager.
*/
export default function RecipeWaiter(app, manager) {
this.app = app;
this.manager = manager;
this.remove_intent = false;
}
/**
* Sets up the drag and drop capability for operations in the operations and recipe areas.
*/
RecipeWaiter.prototype.initialise_operation_drag_n_drop = function () {
let rec_list = document.getElementById('rec_list'),
op_lists = document.querySelectorAll('.category .op_list');
// Recipe list
Sortable.create(rec_list, {
group: 'recipe',
sort: true,
animation: 0,
delay: 0,
filter: '.arg-input,.arg', // Relies on commenting out a line in Sortable.js which calls evt.preventDefault()
setData(dataTransfer, drag_el) {
dataTransfer.setData('Text', drag_el.querySelector('.arg-title').textContent);
},
onEnd: function (evt) {
if (this.remove_intent) {
evt.item.remove();
evt.target.dispatchEvent(this.manager.operationremove);
}
}.bind(this),
});
Sortable.utils.on(rec_list, 'dragover', () => {
this.remove_intent = false;
});
Sortable.utils.on(rec_list, 'dragleave', () => {
this.remove_intent = true;
this.app.progress = 0;
});
// Favourites category
document.querySelector('#categories a').addEventListener('dragover', this.fav_dragover.bind(this));
document.querySelector('#categories a').addEventListener('dragleave', this.fav_dragleave.bind(this));
document.querySelector('#categories a').addEventListener('drop', this.fav_drop.bind(this));
};
/**
* Creates a drag-n-droppable seed list of operations.
*
* @param {element} list_el - The list the initialise
*/
RecipeWaiter.prototype.create_sortable_seed_list = function (list_el) {
Sortable.create(list_el, {
group: {
name: 'recipe',
pull: 'clone',
put: false,
},
sort: false,
setData(dataTransfer, drag_el) {
dataTransfer.setData('Text', drag_el.textContent);
},
onStart(evt) {
$(evt.item).popover('destroy');
evt.item.setAttribute('data-toggle', 'popover-disabled');
},
onEnd: this.op_sort_end.bind(this),
});
};
/**
* Handler for operation sort end events.
* Removes the operation from the list if it has been dropped outside. If not, adds it to the list
* at the appropriate place and initialises it.
*
* @fires Manager#operationadd
* @param {event} evt
*/
RecipeWaiter.prototype.op_sort_end = function (evt) {
if (this.remove_intent) {
if (evt.item.parentNode.id == 'rec_list') {
evt.item.remove();
}
return;
}
// Reinitialise the popover on the original element in the ops list because for some reason it
// gets destroyed and recreated.
$(evt.clone).popover();
$(evt.clone).children('[data-toggle=popover]').popover();
if (evt.item.parentNode.id !== 'rec_list') {
return;
}
this.build_recipe_operation(evt.item);
evt.item.dispatchEvent(this.manager.operationadd);
};
/**
* Handler for favourite dragover events.
* If the element being dragged is an operation, displays a visual cue so that the user knows it can
* be dropped here.
*
* @param {event} e
*/
RecipeWaiter.prototype.fav_dragover = function (e) {
if (e.dataTransfer.effectAllowed !== 'move') {
return false;
}
e.stopPropagation();
e.preventDefault();
if (e.target.className && e.target.className.indexOf('category-title') > -1) {
// Hovering over the a
e.target.classList.add('favourites-hover');
} else if (e.target.parentNode.className && e.target.parentNode.className.indexOf('category-title') > -1) {
// Hovering over the Edit button
e.target.parentNode.classList.add('favourites-hover');
} else if (e.target.parentNode.parentNode.className && e.target.parentNode.parentNode.className.indexOf('category-title') > -1) {
// Hovering over the image on the Edit button
e.target.parentNode.parentNode.classList.add('favourites-hover');
}
};
/**
* Handler for favourite dragleave events.
* Removes the visual cue.
*
* @param {event} e
*/
RecipeWaiter.prototype.fav_dragleave = function (e) {
e.stopPropagation();
e.preventDefault();
document.querySelector('#categories a').classList.remove('favourites-hover');
};
/**
* Handler for favourite drop events.
* Adds the dragged operation to the favourites list.
*
* @param {event} e
*/
RecipeWaiter.prototype.fav_drop = function (e) {
e.stopPropagation();
e.preventDefault();
e.target.classList.remove('favourites-hover');
const op_name = e.dataTransfer.getData('Text');
this.app.add_favourite(op_name);
};
/**
* Handler for ingredient change events.
*
* @fires Manager#statechange
*/
RecipeWaiter.prototype.ing_change = function () {
window.dispatchEvent(this.manager.statechange);
};
/**
* Handler for disable click events.
* Updates the icon status.
*
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.disable_click = function (e) {
const icon = e.target;
if (icon.getAttribute('disabled') == 'false') {
icon.setAttribute('disabled', 'true');
icon.classList.add('disable-icon-selected');
icon.parentNode.parentNode.classList.add('disabled');
} else {
icon.setAttribute('disabled', 'false');
icon.classList.remove('disable-icon-selected');
icon.parentNode.parentNode.classList.remove('disabled');
}
this.app.progress = 0;
window.dispatchEvent(this.manager.statechange);
};
/**
* Handler for breakpoint click events.
* Updates the icon status.
*
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.breakpoint_click = function (e) {
const bp = e.target;
if (bp.getAttribute('break') == 'false') {
bp.setAttribute('break', 'true');
bp.classList.add('breakpoint-selected');
} else {
bp.setAttribute('break', 'false');
bp.classList.remove('breakpoint-selected');
}
window.dispatchEvent(this.manager.statechange);
};
/**
* Handler for operation doubleclick events.
* Removes the operation from the recipe and auto bakes.
*
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.operation_dblclick = function (e) {
e.target.remove();
window.dispatchEvent(this.manager.statechange);
};
/**
* Handler for operation child doubleclick events.
* Removes the operation from the recipe.
*
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.operation_child_dblclick = function (e) {
e.target.parentNode.remove();
window.dispatchEvent(this.manager.statechange);
};
/**
* Generates a configuration object to represent the current recipe.
*
* @returns {recipe_config}
*/
RecipeWaiter.prototype.get_config = function () {
let config = [],
ingredients,
ing_list,
disabled,
bp,
item,
operations = document.querySelectorAll('#rec_list li.operation');
for (let i = 0; i < operations.length; i++) {
ingredients = [];
disabled = operations[i].querySelector('.disable-icon');
bp = operations[i].querySelector('.breakpoint');
ing_list = operations[i].querySelectorAll('.arg');
for (let j = 0; j < ing_list.length; j++) {
if (ing_list[j].getAttribute('type') == 'checkbox') {
// checkbox
ingredients[j] = ing_list[j].checked;
} else if (ing_list[j].classList.contains('toggle-string')) {
// toggle_string
ingredients[j] = {
option: ing_list[j].previousSibling.children[0].textContent.slice(0, -1),
string: ing_list[j].value,
};
} else {
// all others
ingredients[j] = ing_list[j].value;
}
}
item = {
op: operations[i].querySelector('.arg-title').textContent,
args: ingredients,
};
if (disabled && disabled.getAttribute('disabled') == 'true') {
item.disabled = true;
}
if (bp && bp.getAttribute('break') == 'true') {
item.breakpoint = true;
}
config.push(item);
}
return config;
};
/**
* Moves or removes the breakpoint indicator in the recipe based on the position.
*
* @param {number} position
*/
RecipeWaiter.prototype.update_breakpoint_indicator = function (position) {
const operations = document.querySelectorAll('#rec_list li.operation');
for (let i = 0; i < operations.length; i++) {
if (i == position) {
operations[i].classList.add('break');
} else {
operations[i].classList.remove('break');
}
}
};
/**
* Given an operation stub element, this function converts it into a full recipe element with
* arguments.
*
* @param {element} el - The operation stub element from the operations pane
*/
RecipeWaiter.prototype.build_recipe_operation = function (el) {
const op_name = el.textContent;
const op = new HTMLOperation(op_name, this.app.operations[op_name], this.app, this.manager);
el.innerHTML = op.to_full_html();
if (this.app.operations[op_name].flow_control) {
el.classList.add('flow-control-op');
}
// Disable auto-bake if this is a manual op - this should be moved to the 'operationadd'
// handler after event restructuring
if (op.manual_bake && this.app.auto_bake_) {
this.manager.controls.set_auto_bake(false);
this.app.alert('Auto-Bake is disabled by default when using this operation.', 'info', 5000);
}
};
/**
* Adds the specified operation to the recipe.
*
* @fires Manager#operationadd
* @param {string} name - The name of the operation to add
* @returns {element}
*/
RecipeWaiter.prototype.add_operation = function (name) {
const item = document.createElement('li');
item.classList.add('operation');
item.innerHTML = name;
this.build_recipe_operation(item);
document.getElementById('rec_list').appendChild(item);
item.dispatchEvent(this.manager.operationadd);
return item;
};
/**
* Removes all operations from the recipe.
*
* @fires Manager#operationremove
*/
RecipeWaiter.prototype.clear_recipe = function () {
const rec_list = document.getElementById('rec_list');
while (rec_list.firstChild) {
rec_list.removeChild(rec_list.firstChild);
}
rec_list.dispatchEvent(this.manager.operationremove);
};
/**
* Handler for operation dropdown events from toggle_string arguments.
* Sets the selected option as the name of the button.
*
* @param {event} e
*/
RecipeWaiter.prototype.dropdown_toggle_click = function (e) {
let el = e.target,
button = el.parentNode.parentNode.previousSibling;
button.innerHTML = `${el.textContent} <span class='caret'></span>`;
this.ing_change();
};
/**
* Handler for operationadd events.
*
* @listens Manager#operationadd
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.op_add = function () {
window.dispatchEvent(this.manager.statechange);
};
/**
* Handler for operationremove events.
*
* @listens Manager#operationremove
* @fires Manager#statechange
* @param {event} e
*/
RecipeWaiter.prototype.op_remove = function () {
window.dispatchEvent(this.manager.statechange);
};