2018-05-15 17:36:45 +00:00
/ * *
* @ author n1474335 [ n1474335 @ gmail . com ]
* @ copyright Crown Copyright 2016
* @ license Apache - 2.0
* /
2019-07-09 11:23:59 +00:00
import Utils from "../core/Utils.mjs" ;
2019-02-08 17:28:10 +00:00
2018-05-15 17:36:45 +00:00
/ * *
* Object to handle the creation of operation ingredients .
* /
class HTMLIngredient {
/ * *
* HTMLIngredient constructor .
*
* @ param { Object } config - The configuration object for this ingredient .
* @ param { App } app - The main view object for CyberChef .
* @ param { Manager } manager - The CyberChef event manager .
* /
constructor ( config , app , manager ) {
this . app = app ;
this . manager = manager ;
this . name = config . name ;
this . type = config . type ;
this . value = config . value ;
this . disabled = config . disabled || false ;
2018-06-09 09:43:36 +00:00
this . hint = config . hint || false ;
2019-01-09 15:28:50 +00:00
this . rows = config . rows || false ;
2018-05-15 17:36:45 +00:00
this . target = config . target ;
2018-12-25 19:02:05 +00:00
this . defaultIndex = config . defaultIndex || 0 ;
2022-11-25 16:11:14 +00:00
this . maxLength = config . maxLength || null ;
2018-05-15 17:36:45 +00:00
this . toggleValues = config . toggleValues ;
2019-10-27 14:38:55 +00:00
this . ingId = this . app . nextIngId ( ) ;
this . id = "ing-" + this . ingId ;
this . tabIndex = this . ingId + 2 ; // Input = 1, Search = 2
2019-03-04 11:46:27 +00:00
this . min = ( typeof config . min === "number" ) ? config . min : "" ;
this . max = ( typeof config . max === "number" ) ? config . max : "" ;
this . step = config . step || 1 ;
2018-05-15 17:36:45 +00:00
}
/ * *
* Renders the ingredient in HTML .
*
* @ returns { string }
* /
toHtml ( ) {
2018-06-10 11:03:55 +00:00
let html = "" ,
2019-02-08 00:59:56 +00:00
i , m , eventFn ;
2018-05-15 17:36:45 +00:00
switch ( this . type ) {
case "string" :
case "binaryString" :
case "byteArray" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group ing-wide">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating" > $ { this . name } < / l a b e l >
2018-06-09 09:43:36 +00:00
< input type = "text"
class = "form-control arg"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-09 09:43:36 +00:00
arg - name = "${this.name}"
value = "${this.value}"
2022-11-25 16:11:14 +00:00
$ { this . disabled ? "disabled" : "" }
$ { this . maxLength ? ` maxlength=" ${ this . maxLength } " ` : "" } >
2018-06-09 09:43:36 +00:00
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
case "shortString" :
case "binaryShortString" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group ing-short">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating inline" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< input type = "text"
class = "form-control arg inline"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
value = "${this.value}"
2022-11-25 16:11:14 +00:00
$ { this . disabled ? "disabled" : "" }
$ { this . maxLength ? ` maxlength=" ${ this . maxLength } " ` : "" } >
2018-06-10 11:03:55 +00:00
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
case "toggleString" :
2023-03-17 17:46:13 +00:00
html += ` <div class="form-group input-group ing-wide" data-help-title="Multi-type ingredients" data-help="Selecting a data type from the dropdown will change how the ingredient is interpreted by the operation.">
2018-06-10 11:03:55 +00:00
< div class = "toggle-string" >
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating toggle-string" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< input type = "text"
class = "form-control arg toggle-string"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
value = "${this.value}"
2022-11-25 16:11:14 +00:00
$ { this . disabled ? "disabled" : "" }
$ { this . maxLength ? ` maxlength=" ${ this . maxLength } " ` : "" } >
2018-06-10 11:03:55 +00:00
< / d i v >
2018-06-17 22:52:00 +00:00
< div class = "input-group-append" >
< button class = "btn btn-secondary dropdown-toggle" type = "button" data - toggle = "dropdown" aria - haspopup = "true" aria - expanded = "false" > $ { this . toggleValues [ 0 ] } < / b u t t o n >
< div class = "dropdown-menu toggle-dropdown" > ` ;
for ( i = 0 ; i < this . toggleValues . length ; i ++ ) {
html += ` <a class="dropdown-item" href="#"> ${ this . toggleValues [ i ] } </a> ` ;
}
html += ` </div>
< / d i v >
2018-06-10 11:03:55 +00:00
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
case "number" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group inline ing-medium">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating inline" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< input type = "number"
class = "form-control arg inline"
2018-06-09 09:43:36 +00:00
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-09 09:43:36 +00:00
arg - name = "${this.name}"
2018-06-10 11:03:55 +00:00
value = "${this.value}"
2019-03-04 11:46:27 +00:00
min = "${this.min}"
max = "${this.max}"
step = "${this.step}"
2018-06-10 11:03:55 +00:00
$ { this . disabled ? "disabled" : "" } >
< / d i v > ` ;
break ;
case "boolean" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group inline boolean-arg ing-flexible">
2018-06-10 11:03:55 +00:00
< div class = "checkbox" >
2019-10-31 13:39:06 +00:00
< label $ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" } >
2018-06-17 12:49:32 +00:00
< input type = "checkbox"
class = "arg"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-17 12:49:32 +00:00
arg - name = "${this.name}"
$ { this . value ? " checked" : "" }
$ { this . disabled ? " disabled" : "" }
value = "${this.name}" > $ { this . name }
< / l a b e l >
2018-06-10 11:03:55 +00:00
< / d i v >
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
case "option" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group ing-medium">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating inline" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< select
class = "form-control arg inline"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
$ { this . disabled ? "disabled" : "" } > ` ;
2018-05-15 17:36:45 +00:00
for ( i = 0 ; i < this . value . length ; i ++ ) {
if ( ( m = this . value [ i ] . match ( /\[([a-z0-9 -()^]+)\]/i ) ) ) {
2018-06-10 11:03:55 +00:00
html += ` <optgroup label=" ${ m [ 1 ] } "> ` ;
2021-02-10 13:13:19 +00:00
} else if ( this . value [ i ] . match ( /\[\/([a-z0-9 -()^]+)\]/i ) ) {
2018-05-15 17:36:45 +00:00
html += "</optgroup>" ;
} else {
2018-12-25 19:02:05 +00:00
html += ` <option ${ this . defaultIndex === i ? "selected" : "" } > ${ this . value [ i ] } </option> ` ;
2018-05-15 17:36:45 +00:00
}
}
2018-06-10 11:03:55 +00:00
html += ` </select>
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
case "populateOption" :
2019-02-08 00:59:56 +00:00
case "populateMultiOption" :
2023-03-17 17:46:13 +00:00
html += ` <div class="form-group ing-medium" data-help-title="Population dropdowns" data-help="Selecting a value from this dropdown will populate some of the other ingredients for this operation with pre-canned values.">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< select
2019-02-08 00:59:56 +00:00
class = "form-control arg no-state-change populate-option"
2018-06-10 11:03:55 +00:00
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
$ { this . disabled ? "disabled" : "" } > ` ;
2018-05-15 17:36:45 +00:00
for ( i = 0 ; i < this . value . length ; i ++ ) {
if ( ( m = this . value [ i ] . name . match ( /\[([a-z0-9 -()^]+)\]/i ) ) ) {
2018-06-10 11:03:55 +00:00
html += ` <optgroup label=" ${ m [ 1 ] } "> ` ;
2021-02-10 13:13:19 +00:00
} else if ( this . value [ i ] . name . match ( /\[\/([a-z0-9 -()^]+)\]/i ) ) {
2018-05-15 17:36:45 +00:00
html += "</optgroup>" ;
} else {
2019-02-08 00:59:56 +00:00
const val = this . type === "populateMultiOption" ?
JSON . stringify ( this . value [ i ] . value ) :
this . value [ i ] . value ;
2019-02-28 16:29:21 +00:00
html += ` <option populate-value=' ${ Utils . escapeHtml ( val ) } '> ${ this . value [ i ] . name } </option> ` ;
2018-05-15 17:36:45 +00:00
}
}
2018-06-10 11:03:55 +00:00
html += ` </select>
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
2019-02-08 00:59:56 +00:00
eventFn = this . type === "populateMultiOption" ?
this . populateMultiOptionChange :
this . populateOptionChange ;
this . manager . addDynamicListener ( "#" + this . id , "change" , eventFn , this ) ;
2018-05-15 17:36:45 +00:00
break ;
case "editableOption" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group input-group ing-wide">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating" > $ { this . name } < / l a b e l >
2018-12-26 16:50:32 +00:00
< input type = "text"
class = "form-control arg"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-12-26 16:50:32 +00:00
arg - name = "${this.name}"
value = "${this.value[this.defaultIndex].value}"
$ { this . disabled ? "disabled" : "" } >
< div class = "input-group-append" >
< button type = "button"
class = "btn btn-secondary dropdown-toggle dropdown-toggle-split"
data - toggle = "dropdown"
data - boundary = "scrollParent"
aria - haspopup = "true"
aria - expanded = "false" >
< span class = "sr-only" > Toggle Dropdown < / s p a n >
< / b u t t o n >
< div class = "dropdown-menu editable-option-menu" > ` ;
for ( i = 0 ; i < this . value . length ; i ++ ) {
html += ` <a class="dropdown-item" href="#" value=" ${ this . value [ i ] . value } "> ${ this . value [ i ] . name } </a> ` ;
}
html += ` </div>
< / d i v >
< / d i v > ` ;
this . manager . addDynamicListener ( ".editable-option-menu a" , "click" , this . editableOptionClick , this ) ;
break ;
case "editableOptionShort" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group input-group ing-short">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating inline" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< input type = "text"
class = "form-control arg inline"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
2018-12-25 19:02:05 +00:00
value = "${this.value[this.defaultIndex].value}"
2018-06-10 11:03:55 +00:00
$ { this . disabled ? "disabled" : "" } >
< div class = "input-group-append inline" >
< button type = "button"
class = "btn btn-secondary dropdown-toggle dropdown-toggle-split"
data - toggle = "dropdown"
data - boundary = "scrollParent"
aria - haspopup = "true"
aria - expanded = "false" >
< span class = "sr-only" > Toggle Dropdown < / s p a n >
< / b u t t o n >
< div class = "dropdown-menu editable-option-menu" > ` ;
2018-05-15 17:36:45 +00:00
for ( i = 0 ; i < this . value . length ; i ++ ) {
2018-06-10 11:03:55 +00:00
html += ` <a class="dropdown-item" href="#" value=" ${ this . value [ i ] . value } "> ${ this . value [ i ] . name } </a> ` ;
2018-05-15 17:36:45 +00:00
}
2018-06-10 11:03:55 +00:00
html += ` </div>
< / d i v >
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
2018-06-10 11:03:55 +00:00
this . manager . addDynamicListener ( ".editable-option-menu a" , "click" , this . editableOptionClick , this ) ;
2018-05-15 17:36:45 +00:00
break ;
case "text" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group ing-very-wide">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating" > $ { this . name } < / l a b e l >
2018-06-10 11:03:55 +00:00
< textarea
class = "form-control arg"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2018-06-10 11:03:55 +00:00
arg - name = "${this.name}"
2019-01-09 15:28:50 +00:00
rows = "${this.rows ? this.rows : 3}"
2018-06-10 11:03:55 +00:00
$ { this . disabled ? "disabled" : "" } > $ { this . value } < / t e x t a r e a >
< / d i v > ` ;
2018-05-15 17:36:45 +00:00
break ;
2019-02-28 15:27:35 +00:00
case "argSelector" :
2023-03-17 17:46:13 +00:00
html += ` <div class="form-group inline ing-medium" data-help-title="Ingredient selector" data-help="Selecting options in this dropdown will configure which operation ingredients are visible.">
2019-10-31 13:39:06 +00:00
< label for = "${this.id}"
$ { this . hint ? ` data-toggle="tooltip" title=" ${ this . hint } " ` : "" }
class = "bmd-label-floating inline" > $ { this . name } < / l a b e l >
2019-02-28 15:27:35 +00:00
< select
class = "form-control arg inline arg-selector"
id = "${this.id}"
2019-10-27 14:38:55 +00:00
tabindex = "${this.tabIndex}"
2019-02-28 15:27:35 +00:00
arg - name = "${this.name}"
$ { this . disabled ? "disabled" : "" } > ` ;
for ( i = 0 ; i < this . value . length ; i ++ ) {
html += ` <option ${ this . defaultIndex === i ? "selected" : "" }
turnon = "${JSON.stringify(this.value[i].on || [])}"
turnoff = "${JSON.stringify(this.value[i].off || [])}" >
$ { this . value [ i ] . name }
< / o p t i o n > ` ;
}
html += ` </select>
< / d i v > ` ;
this . manager . addDynamicListener ( ".arg-selector" , "change" , this . argSelectorChange , this ) ;
break ;
2019-11-27 12:49:35 +00:00
case "label" :
2023-03-09 17:31:46 +00:00
html += ` <div class="form-group ing-flexible">
2019-11-27 12:49:35 +00:00
< label > $ { this . name } < / l a b e l >
< input type = "hidden"
class = "form-control arg"
id = "${this.id}"
arg - name = "${this.name}"
value = "" >
< / d i v > ` ;
break ;
2018-05-15 17:36:45 +00:00
default :
break ;
}
return html ;
}
/ * *
* Handler for populate option changes .
* Populates the relevant argument with the specified value .
*
* @ param { event } e
* /
populateOptionChange ( e ) {
2019-02-08 00:59:56 +00:00
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
2018-05-15 17:36:45 +00:00
const el = e . target ;
const op = el . parentNode . parentNode ;
2018-06-10 11:03:55 +00:00
const target = op . querySelectorAll ( ".arg" ) [ this . target ] ;
2018-05-15 17:36:45 +00:00
2019-05-16 10:59:25 +00:00
const popVal = el . childNodes [ el . selectedIndex ] . getAttribute ( "populate-value" ) ;
if ( popVal !== "" ) target . value = popVal ;
2018-06-10 11:03:55 +00:00
const evt = new Event ( "change" ) ;
target . dispatchEvent ( evt ) ;
2018-05-15 17:36:45 +00:00
this . manager . recipe . ingChange ( ) ;
}
2019-02-08 00:59:56 +00:00
/ * *
* Handler for populate multi option changes .
* Populates the relevant arguments with the specified values .
*
* @ param { event } e
* /
populateMultiOptionChange ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const el = e . target ;
const op = el . parentNode . parentNode ;
const args = op . querySelectorAll ( ".arg" ) ;
const targets = this . target . map ( i => args [ i ] ) ;
const vals = JSON . parse ( el . childNodes [ el . selectedIndex ] . getAttribute ( "populate-value" ) ) ;
const evt = new Event ( "change" ) ;
for ( let i = 0 ; i < targets . length ; i ++ ) {
targets [ i ] . value = vals [ i ] ;
}
// Fire change event after all targets have been assigned
this . manager . recipe . ingChange ( ) ;
// Send change event for each target once all have been assigned, to update the label placement.
for ( const target of targets ) {
target . dispatchEvent ( evt ) ;
}
}
2018-05-15 17:36:45 +00:00
/ * *
2018-06-10 11:03:55 +00:00
* Handler for editable option clicks .
2018-05-15 17:36:45 +00:00
* Populates the input box with the selected value .
*
* @ param { event } e
* /
2018-06-10 11:03:55 +00:00
editableOptionClick ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const link = e . target ,
input = link . parentNode . parentNode . parentNode . querySelector ( "input" ) ;
2018-05-15 17:36:45 +00:00
2018-06-10 11:03:55 +00:00
input . value = link . getAttribute ( "value" ) ;
2018-06-17 12:49:32 +00:00
const evt = new Event ( "change" ) ;
input . dispatchEvent ( evt ) ;
2018-05-15 17:36:45 +00:00
this . manager . recipe . ingChange ( ) ;
}
2019-02-28 15:27:35 +00:00
/ * *
* Handler for argument selector changes .
* Shows or hides the relevant arguments for this operation .
*
* @ param { event } e
* /
argSelectorChange ( e ) {
e . preventDefault ( ) ;
e . stopPropagation ( ) ;
const option = e . target . options [ e . target . selectedIndex ] ;
const op = e . target . closest ( ".operation" ) ;
const args = op . querySelectorAll ( ".ingredients .form-group" ) ;
const turnon = JSON . parse ( option . getAttribute ( "turnon" ) ) ;
const turnoff = JSON . parse ( option . getAttribute ( "turnoff" ) ) ;
args . forEach ( ( arg , i ) => {
if ( turnon . includes ( i ) ) {
arg . classList . remove ( "d-none" ) ;
}
if ( turnoff . includes ( i ) ) {
arg . classList . add ( "d-none" ) ;
}
} ) ;
}
2018-05-15 17:36:45 +00:00
}
export default HTMLIngredient ;