Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F83577908
prompt.js
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Sep 17, 22:13
Size
12 KB
Mime Type
text/x-c++
Expires
Thu, Sep 19, 22:13 (2 d)
Engine
blob
Format
Raw Data
Handle
20853948
Attached To
rOACCT Open Access Compliance Check Tool (OACCT)
prompt.js
View Options
'use strict'
;
const
Events
=
require
(
'events'
);
const
colors
=
require
(
'ansi-colors'
);
const
keypress
=
require
(
'./keypress'
);
const
timer
=
require
(
'./timer'
);
const
State
=
require
(
'./state'
);
const
theme
=
require
(
'./theme'
);
const
utils
=
require
(
'./utils'
);
const
ansi
=
require
(
'./ansi'
);
/**
* Base class for creating a new Prompt.
* @param {Object} `options` Question object.
*/
class
Prompt
extends
Events
{
constructor
(
options
=
{})
{
super
();
this
.
name
=
options
.
name
;
this
.
type
=
options
.
type
;
this
.
options
=
options
;
theme
(
this
);
timer
(
this
);
this
.
state
=
new
State
(
this
);
this
.
initial
=
[
options
.
initial
,
options
.
default
].
find
(
v
=>
v
!=
null
);
this
.
stdout
=
options
.
stdout
||
process
.
stdout
;
this
.
stdin
=
options
.
stdin
||
process
.
stdin
;
this
.
scale
=
options
.
scale
||
1
;
this
.
term
=
this
.
options
.
term
||
process
.
env
.
TERM_PROGRAM
;
this
.
margin
=
margin
(
this
.
options
.
margin
);
this
.
setMaxListeners
(
0
);
setOptions
(
this
);
}
async
keypress
(
input
,
event
=
{})
{
this
.
keypressed
=
true
;
let
key
=
keypress
.
action
(
input
,
keypress
(
input
,
event
),
this
.
options
.
actions
);
this
.
state
.
keypress
=
key
;
this
.
emit
(
'keypress'
,
input
,
key
);
this
.
emit
(
'state'
,
this
.
state
.
clone
());
let
fn
=
this
.
options
[
key
.
action
]
||
this
[
key
.
action
]
||
this
.
dispatch
;
if
(
typeof
fn
===
'function'
)
{
return
await
fn
.
call
(
this
,
input
,
key
);
}
this
.
alert
();
}
alert
()
{
delete
this
.
state
.
alert
;
if
(
this
.
options
.
show
===
false
)
{
this
.
emit
(
'alert'
);
}
else
{
this
.
stdout
.
write
(
ansi
.
code
.
beep
);
}
}
cursorHide
()
{
this
.
stdout
.
write
(
ansi
.
cursor
.
hide
());
utils
.
onExit
(()
=>
this
.
cursorShow
());
}
cursorShow
()
{
this
.
stdout
.
write
(
ansi
.
cursor
.
show
());
}
write
(
str
)
{
if
(
!
str
)
return
;
if
(
this
.
stdout
&&
this
.
state
.
show
!==
false
)
{
this
.
stdout
.
write
(
str
);
}
this
.
state
.
buffer
+=
str
;
}
clear
(
lines
=
0
)
{
let
buffer
=
this
.
state
.
buffer
;
this
.
state
.
buffer
=
''
;
if
((
!
buffer
&&
!
lines
)
||
this
.
options
.
show
===
false
)
return
;
this
.
stdout
.
write
(
ansi
.
cursor
.
down
(
lines
)
+
ansi
.
clear
(
buffer
,
this
.
width
));
}
restore
()
{
if
(
this
.
state
.
closed
||
this
.
options
.
show
===
false
)
return
;
let
{
prompt
,
after
,
rest
}
=
this
.
sections
();
let
{
cursor
,
initial
=
''
,
input
=
''
,
value
=
''
}
=
this
;
let
size
=
this
.
state
.
size
=
rest
.
length
;
let
state
=
{
after
,
cursor
,
initial
,
input
,
prompt
,
size
,
value
};
let
codes
=
ansi
.
cursor
.
restore
(
state
);
if
(
codes
)
{
this
.
stdout
.
write
(
codes
);
}
}
sections
()
{
let
{
buffer
,
input
,
prompt
}
=
this
.
state
;
prompt
=
colors
.
unstyle
(
prompt
);
let
buf
=
colors
.
unstyle
(
buffer
);
let
idx
=
buf
.
indexOf
(
prompt
);
let
header
=
buf
.
slice
(
0
,
idx
);
let
rest
=
buf
.
slice
(
idx
);
let
lines
=
rest
.
split
(
'\n'
);
let
first
=
lines
[
0
];
let
last
=
lines
[
lines
.
length
-
1
];
let
promptLine
=
prompt
+
(
input
?
' '
+
input
:
''
);
let
len
=
promptLine
.
length
;
let
after
=
len
<
first
.
length
?
first
.
slice
(
len
+
1
)
:
''
;
return
{
header
,
prompt
:
first
,
after
,
rest
:
lines
.
slice
(
1
),
last
};
}
async
submit
()
{
this
.
state
.
submitted
=
true
;
this
.
state
.
validating
=
true
;
// this will only be called when the prompt is directly submitted
// without initializing, i.e. when the prompt is skipped, etc. Otherwize,
// "options.onSubmit" is will be handled by the "initialize()" method.
if
(
this
.
options
.
onSubmit
)
{
await
this
.
options
.
onSubmit
.
call
(
this
,
this
.
name
,
this
.
value
,
this
);
}
let
result
=
this
.
state
.
error
||
await
this
.
validate
(
this
.
value
,
this
.
state
);
if
(
result
!==
true
)
{
let
error
=
'\n'
+
this
.
symbols
.
pointer
+
' '
;
if
(
typeof
result
===
'string'
)
{
error
+=
result
.
trim
();
}
else
{
error
+=
'Invalid input'
;
}
this
.
state
.
error
=
'\n'
+
this
.
styles
.
danger
(
error
);
this
.
state
.
submitted
=
false
;
await
this
.
render
();
await
this
.
alert
();
this
.
state
.
validating
=
false
;
this
.
state
.
error
=
void
0
;
return
;
}
this
.
state
.
validating
=
false
;
await
this
.
render
();
await
this
.
close
();
this
.
value
=
await
this
.
result
(
this
.
value
);
this
.
emit
(
'submit'
,
this
.
value
);
}
async
cancel
(
err
)
{
this
.
state
.
cancelled
=
this
.
state
.
submitted
=
true
;
await
this
.
render
();
await
this
.
close
();
if
(
typeof
this
.
options
.
onCancel
===
'function'
)
{
await
this
.
options
.
onCancel
.
call
(
this
,
this
.
name
,
this
.
value
,
this
);
}
this
.
emit
(
'cancel'
,
await
this
.
error
(
err
));
}
async
close
()
{
this
.
state
.
closed
=
true
;
try
{
let
sections
=
this
.
sections
();
let
lines
=
Math
.
ceil
(
sections
.
prompt
.
length
/
this
.
width
);
if
(
sections
.
rest
)
{
this
.
write
(
ansi
.
cursor
.
down
(
sections
.
rest
.
length
));
}
this
.
write
(
'\n'
.
repeat
(
lines
));
}
catch
(
err
)
{
/* do nothing */
}
this
.
emit
(
'close'
);
}
start
()
{
if
(
!
this
.
stop
&&
this
.
options
.
show
!==
false
)
{
this
.
stop
=
keypress
.
listen
(
this
,
this
.
keypress
.
bind
(
this
));
this
.
once
(
'close'
,
this
.
stop
);
}
}
async
skip
()
{
this
.
skipped
=
this
.
options
.
skip
===
true
;
if
(
typeof
this
.
options
.
skip
===
'function'
)
{
this
.
skipped
=
await
this
.
options
.
skip
.
call
(
this
,
this
.
name
,
this
.
value
);
}
return
this
.
skipped
;
}
async
initialize
()
{
let
{
format
,
options
,
result
}
=
this
;
this
.
format
=
()
=>
format
.
call
(
this
,
this
.
value
);
this
.
result
=
()
=>
result
.
call
(
this
,
this
.
value
);
if
(
typeof
options
.
initial
===
'function'
)
{
this
.
initial
=
await
options
.
initial
.
call
(
this
,
this
);
}
if
(
typeof
options
.
onRun
===
'function'
)
{
await
options
.
onRun
.
call
(
this
,
this
);
}
// if "options.onSubmit" is defined, we wrap the "submit" method to guarantee
// that "onSubmit" will always called first thing inside the submit
// method, regardless of how it's handled in inheriting prompts.
if
(
typeof
options
.
onSubmit
===
'function'
)
{
let
onSubmit
=
options
.
onSubmit
.
bind
(
this
);
let
submit
=
this
.
submit
.
bind
(
this
);
delete
this
.
options
.
onSubmit
;
this
.
submit
=
async
()
=>
{
await
onSubmit
(
this
.
name
,
this
.
value
,
this
);
return
submit
();
};
}
await
this
.
start
();
await
this
.
render
();
}
render
()
{
throw
new
Error
(
'expected prompt to have a custom render method'
);
}
run
()
{
return
new
Promise
(
async
(
resolve
,
reject
)
=>
{
this
.
once
(
'submit'
,
resolve
);
this
.
once
(
'cancel'
,
reject
);
if
(
await
this
.
skip
())
{
this
.
render
=
()
=>
{};
return
this
.
submit
();
}
await
this
.
initialize
();
this
.
emit
(
'run'
);
});
}
async
element
(
name
,
choice
,
i
)
{
let
{
options
,
state
,
symbols
,
timers
}
=
this
;
let
timer
=
timers
&&
timers
[
name
];
state
.
timer
=
timer
;
let
value
=
options
[
name
]
||
state
[
name
]
||
symbols
[
name
];
let
val
=
choice
&&
choice
[
name
]
!=
null
?
choice
[
name
]
:
await
value
;
if
(
val
===
''
)
return
val
;
let
res
=
await
this
.
resolve
(
val
,
state
,
choice
,
i
);
if
(
!
res
&&
choice
&&
choice
[
name
])
{
return
this
.
resolve
(
value
,
state
,
choice
,
i
);
}
return
res
;
}
async
prefix
()
{
let
element
=
await
this
.
element
(
'prefix'
)
||
this
.
symbols
;
let
timer
=
this
.
timers
&&
this
.
timers
.
prefix
;
let
state
=
this
.
state
;
state
.
timer
=
timer
;
if
(
utils
.
isObject
(
element
))
element
=
element
[
state
.
status
]
||
element
.
pending
;
if
(
!
utils
.
hasColor
(
element
))
{
let
style
=
this
.
styles
[
state
.
status
]
||
this
.
styles
.
pending
;
return
style
(
element
);
}
return
element
;
}
async
message
()
{
let
message
=
await
this
.
element
(
'message'
);
if
(
!
utils
.
hasColor
(
message
))
{
return
this
.
styles
.
strong
(
message
);
}
return
message
;
}
async
separator
()
{
let
element
=
await
this
.
element
(
'separator'
)
||
this
.
symbols
;
let
timer
=
this
.
timers
&&
this
.
timers
.
separator
;
let
state
=
this
.
state
;
state
.
timer
=
timer
;
let
value
=
element
[
state
.
status
]
||
element
.
pending
||
state
.
separator
;
let
ele
=
await
this
.
resolve
(
value
,
state
);
if
(
utils
.
isObject
(
ele
))
ele
=
ele
[
state
.
status
]
||
ele
.
pending
;
if
(
!
utils
.
hasColor
(
ele
))
{
return
this
.
styles
.
muted
(
ele
);
}
return
ele
;
}
async
pointer
(
choice
,
i
)
{
let
val
=
await
this
.
element
(
'pointer'
,
choice
,
i
);
if
(
typeof
val
===
'string'
&&
utils
.
hasColor
(
val
))
{
return
val
;
}
if
(
val
)
{
let
styles
=
this
.
styles
;
let
focused
=
this
.
index
===
i
;
let
style
=
focused
?
styles
.
primary
:
val
=>
val
;
let
ele
=
await
this
.
resolve
(
val
[
focused
?
'on'
:
'off'
]
||
val
,
this
.
state
);
let
styled
=
!
utils
.
hasColor
(
ele
)
?
style
(
ele
)
:
ele
;
return
focused
?
styled
:
' '
.
repeat
(
ele
.
length
);
}
}
async
indicator
(
choice
,
i
)
{
let
val
=
await
this
.
element
(
'indicator'
,
choice
,
i
);
if
(
typeof
val
===
'string'
&&
utils
.
hasColor
(
val
))
{
return
val
;
}
if
(
val
)
{
let
styles
=
this
.
styles
;
let
enabled
=
choice
.
enabled
===
true
;
let
style
=
enabled
?
styles
.
success
:
styles
.
dark
;
let
ele
=
val
[
enabled
?
'on'
:
'off'
]
||
val
;
return
!
utils
.
hasColor
(
ele
)
?
style
(
ele
)
:
ele
;
}
return
''
;
}
body
()
{
return
null
;
}
footer
()
{
if
(
this
.
state
.
status
===
'pending'
)
{
return
this
.
element
(
'footer'
);
}
}
header
()
{
if
(
this
.
state
.
status
===
'pending'
)
{
return
this
.
element
(
'header'
);
}
}
async
hint
()
{
if
(
this
.
state
.
status
===
'pending'
&&
!
this
.
isValue
(
this
.
state
.
input
))
{
let
hint
=
await
this
.
element
(
'hint'
);
if
(
!
utils
.
hasColor
(
hint
))
{
return
this
.
styles
.
muted
(
hint
);
}
return
hint
;
}
}
error
(
err
)
{
return
!
this
.
state
.
submitted
?
(
err
||
this
.
state
.
error
)
:
''
;
}
format
(
value
)
{
return
value
;
}
result
(
value
)
{
return
value
;
}
validate
(
value
)
{
if
(
this
.
options
.
required
===
true
)
{
return
this
.
isValue
(
value
);
}
return
true
;
}
isValue
(
value
)
{
return
value
!=
null
&&
value
!==
''
;
}
resolve
(
value
,
...
args
)
{
return
utils
.
resolve
(
this
,
value
,
...
args
);
}
get
base
()
{
return
Prompt
.
prototype
;
}
get
style
()
{
return
this
.
styles
[
this
.
state
.
status
];
}
get
height
()
{
return
this
.
options
.
rows
||
utils
.
height
(
this
.
stdout
,
25
);
}
get
width
()
{
return
this
.
options
.
columns
||
utils
.
width
(
this
.
stdout
,
80
);
}
get
size
()
{
return
{
width
:
this
.
width
,
height
:
this
.
height
};
}
set
cursor
(
value
)
{
this
.
state
.
cursor
=
value
;
}
get
cursor
()
{
return
this
.
state
.
cursor
;
}
set
input
(
value
)
{
this
.
state
.
input
=
value
;
}
get
input
()
{
return
this
.
state
.
input
;
}
set
value
(
value
)
{
this
.
state
.
value
=
value
;
}
get
value
()
{
let
{
input
,
value
}
=
this
.
state
;
let
result
=
[
value
,
input
].
find
(
this
.
isValue
.
bind
(
this
));
return
this
.
isValue
(
result
)
?
result
:
this
.
initial
;
}
static
get
prompt
()
{
return
options
=>
new
this
(
options
).
run
();
}
}
function
setOptions
(
prompt
)
{
let
isValidKey
=
key
=>
{
return
prompt
[
key
]
===
void
0
||
typeof
prompt
[
key
]
===
'function'
;
};
let
ignore
=
[
'actions'
,
'choices'
,
'initial'
,
'margin'
,
'roles'
,
'styles'
,
'symbols'
,
'theme'
,
'timers'
,
'value'
];
let
ignoreFn
=
[
'body'
,
'footer'
,
'error'
,
'header'
,
'hint'
,
'indicator'
,
'message'
,
'prefix'
,
'separator'
,
'skip'
];
for
(
let
key
of
Object
.
keys
(
prompt
.
options
))
{
if
(
ignore
.
includes
(
key
))
continue
;
if
(
/^on[A-Z]/
.
test
(
key
))
continue
;
let
option
=
prompt
.
options
[
key
];
if
(
typeof
option
===
'function'
&&
isValidKey
(
key
))
{
if
(
!
ignoreFn
.
includes
(
key
))
{
prompt
[
key
]
=
option
.
bind
(
prompt
);
}
}
else
if
(
typeof
prompt
[
key
]
!==
'function'
)
{
prompt
[
key
]
=
option
;
}
}
}
function
margin
(
value
)
{
if
(
typeof
value
===
'number'
)
{
value
=
[
value
,
value
,
value
,
value
];
}
let
arr
=
[].
concat
(
value
||
[]);
let
pad
=
i
=>
i
%
2
===
0
?
'\n'
:
' '
;
let
res
=
[];
for
(
let
i
=
0
;
i
<
4
;
i
++
)
{
let
char
=
pad
(
i
);
if
(
arr
[
i
])
{
res
.
push
(
char
.
repeat
(
arr
[
i
]));
}
else
{
res
.
push
(
''
);
}
}
return
res
;
}
module
.
exports
=
Prompt
;
Event Timeline
Log In to Comment