1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 |
|
22 |
|
23 |
|
24 |
|
25 |
|
26 |
|
27 | "use strict" ;
|
28 |
|
29 |
|
30 |
|
31 | const Element = require( './Element.js' ) ;
|
32 | const LabeledInput = require( './LabeledInput.js' ) ;
|
33 | const Button = require( './Button.js' ) ;
|
34 |
|
35 |
|
36 |
|
37 | function Form( options ) {
|
38 |
|
39 | options = ! options ? {} : options.internal ? options : Object.create( options ) ;
|
40 | options.internal = true ;
|
41 |
|
42 | if ( ! options.outputWidth && ! options.width ) { options.outputWidth = 78 ; }
|
43 |
|
44 | Element.call( this , options ) ;
|
45 |
|
46 | this.submitValue = null ;
|
47 |
|
48 | this.inputsDef = options.inputs || [] ;
|
49 | this.labeledInputs = [] ;
|
50 | this.buttonsDef = options.buttons || [] ;
|
51 | this.buttons = [] ;
|
52 | this.focusChild = null ;
|
53 | this.onButtonSubmit = this.onButtonSubmit.bind( this ) ;
|
54 | this.onKey = this.onKey.bind( this ) ;
|
55 | this.onFocus = this.onFocus.bind( this ) ;
|
56 |
|
57 |
|
58 | this.textAttr = options.textAttr || null ;
|
59 | this.voidAttr = options.voidAttr || options.emptyAttr || null ;
|
60 | this.labelFocusAttr = options.labelFocusAttr || null ;
|
61 | this.labelBlurAttr = options.labelBlurAttr || null ;
|
62 | this.buttonFocusAttr = options.buttonFocusAttr || null ;
|
63 | this.buttonBlurAttr = options.buttonBlurAttr || null ;
|
64 | this.turnedOnBlurAttr = options.turnedOnBlurAttr || null ;
|
65 | this.turnedOnFocusAttr = options.turnedOnFocusAttr || null ;
|
66 | this.turnedOffBlurAttr = options.turnedOffBlurAttr || null ;
|
67 | this.turnedOffFocusAttr = options.turnedOffFocusAttr || null ;
|
68 |
|
69 | if ( options.keyBindings ) { this.keyBindings = options.keyBindings ; }
|
70 | if ( options.textInputKeyBindings ) { this.textInputKeyBindings = options.textInputKeyBindings ; }
|
71 |
|
72 | this.initChildren() ;
|
73 |
|
74 | this.on( 'key' , this.onKey ) ;
|
75 | this.on( 'focus' , this.onFocus ) ;
|
76 |
|
77 |
|
78 | if ( this.elementType === 'Form' && ! options.noDraw ) { this.draw() ; }
|
79 | }
|
80 |
|
81 | module.exports = Form ;
|
82 |
|
83 | Form.prototype = Object.create( Element.prototype ) ;
|
84 | Form.prototype.constructor = Form ;
|
85 | Form.prototype.elementType = 'Form' ;
|
86 |
|
87 |
|
88 |
|
89 | Form.prototype.destroy = function( isSubDestroy ) {
|
90 | this.off( 'key' , this.onKey ) ;
|
91 | this.off( 'focus' , this.onFocus ) ;
|
92 |
|
93 | Element.prototype.destroy.call( this , isSubDestroy ) ;
|
94 | } ;
|
95 |
|
96 |
|
97 |
|
98 | Form.prototype.keyBindings = {
|
99 | LEFT: 'previous' ,
|
100 | RIGHT: 'next' ,
|
101 | UP: 'previous' ,
|
102 | DOWN: 'next' ,
|
103 | ENTER: 'next' ,
|
104 | KP_ENTER: 'next' ,
|
105 | ALT_ENTER: 'next'
|
106 | } ;
|
107 |
|
108 |
|
109 |
|
110 | Form.prototype.textInputKeyBindings = {} ;
|
111 | Form.prototype.selectInputKeyBindings = {} ;
|
112 | Form.prototype.selectMultiInputKeyBindings = {} ;
|
113 |
|
114 |
|
115 |
|
116 |
|
117 | Form.prototype.initChildren = function() {
|
118 | var labelMaxWidth = 0 ,
|
119 | offsetX = 0 , offsetY = 0 ,
|
120 | buttonsTextWidth = 0 , buttonSpacing = 0 ;
|
121 |
|
122 | this.inputsDef.forEach( def => {
|
123 | def.labelWidth = Element.computeContentWidth( def.label , def.labelHasMarkup ) ;
|
124 | if ( def.labelWidth > labelMaxWidth ) { labelMaxWidth = def.labelWidth ; }
|
125 | } ) ;
|
126 |
|
127 | this.inputsDef.forEach( ( def , index ) => {
|
128 | var height = 1 ,
|
129 | label = def.label + ' '.repeat( labelMaxWidth - def.labelWidth ) ;
|
130 |
|
131 | switch ( def.type ) {
|
132 | case 'select' :
|
133 |
|
134 |
|
135 |
|
136 | this.labeledInputs[ index ] = new LabeledInput( {
|
137 | internal: true ,
|
138 | parent: this ,
|
139 | type: def.type ,
|
140 | key: def.key ,
|
141 | label: label ,
|
142 | content: def.content ,
|
143 | value: def.value ,
|
144 | items: def.items ,
|
145 | outputX: this.outputX ,
|
146 | outputY: this.outputY + offsetY ,
|
147 | outputWidth: def.outputWidth || def.width || this.outputWidth ,
|
148 | outputHeight: height ,
|
149 | labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
|
150 | labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
|
151 | buttonBlurAttr: def.buttonBlurAttr || this.buttonBlurAttr ,
|
152 | buttonFocusAttr: def.buttonFocusAttr || this.buttonFocusAttr ,
|
153 | buttonDisabledAttr: def.buttonDisabledAttr || this.buttonDisabledAttr ,
|
154 | buttonSubmittedAttr: def.buttonSubmittedAttr || this.buttonSubmittedAttr ,
|
155 | keyBindings: this.selectInputKeyBindings ,
|
156 | noDraw: true
|
157 | } ) ;
|
158 |
|
159 | break ;
|
160 |
|
161 | case 'select-multi' :
|
162 | case 'selectMulti' :
|
163 |
|
164 |
|
165 |
|
166 | this.labeledInputs[ index ] = new LabeledInput( {
|
167 | internal: true ,
|
168 | parent: this ,
|
169 | type: def.type ,
|
170 | key: def.key ,
|
171 | label: label ,
|
172 | content: def.content ,
|
173 | value: def.value ,
|
174 | items: def.items ,
|
175 | outputX: this.outputX ,
|
176 | outputY: this.outputY + offsetY ,
|
177 | outputWidth: def.outputWidth || def.width || this.outputWidth ,
|
178 | outputHeight: height ,
|
179 | labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
|
180 | labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
|
181 | buttonBlurAttr: def.buttonBlurAttr || this.buttonBlurAttr ,
|
182 | buttonFocusAttr: def.buttonFocusAttr || this.buttonFocusAttr ,
|
183 | buttonDisabledAttr: def.buttonDisabledAttr || this.buttonDisabledAttr ,
|
184 | buttonSubmittedAttr: def.buttonSubmittedAttr || this.buttonSubmittedAttr ,
|
185 | turnedOnBlurAttr: def.turnedOnBlurAttr || this.turnedOnBlurAttr ,
|
186 | turnedOnFocusAttr: def.turnedOnFocusAttr || this.turnedOnFocusAttr ,
|
187 | turnedOffBlurAttr: def.turnedOffBlurAttr || this.turnedOffBlurAttr ,
|
188 | turnedOffFocusAttr: def.turnedOffFocusAttr || this.turnedOffFocusAttr ,
|
189 | keyBindings: this.selectInputKeyBindings ,
|
190 | noDraw: true
|
191 | } ) ;
|
192 |
|
193 | break ;
|
194 |
|
195 | case 'text' :
|
196 | default :
|
197 | def.type = 'text' ;
|
198 | if ( def.height ) { height = def.height ; }
|
199 |
|
200 | this.labeledInputs[ index ] = new LabeledInput( {
|
201 | internal: true ,
|
202 | parent: this ,
|
203 | type: def.type ,
|
204 | key: def.key ,
|
205 | label: label ,
|
206 | content: def.content ,
|
207 | outputX: this.outputX ,
|
208 | outputY: this.outputY + offsetY ,
|
209 | outputWidth: def.outputWidth || def.width || this.outputWidth ,
|
210 | outputHeight: height ,
|
211 | lineWrap: !! def.lineWrap ,
|
212 | wordWrap: !! def.wordWrap ,
|
213 | scrollable: !! def.scrollable ,
|
214 | vScrollBar: !! def.vScrollBar ,
|
215 | hScrollBar: !! def.hScrollBar ,
|
216 | hiddenContent: def.hiddenContent ,
|
217 | labelFocusAttr: def.labelFocusAttr || this.labelFocusAttr ,
|
218 | labelBlurAttr: def.labelBlurAttr || this.labelBlurAttr ,
|
219 | textAttr: def.textAttr || this.textAttr ,
|
220 | voidAttr: def.voidAttr || def.emptyAttr || this.voidAttr ,
|
221 | keyBindings: this.textInputKeyBindings ,
|
222 | allowNewLine: height > 1 ,
|
223 | noDraw: true
|
224 | } ) ;
|
225 |
|
226 | break ;
|
227 | }
|
228 |
|
229 | offsetY += height ;
|
230 | } ) ;
|
231 |
|
232 |
|
233 |
|
234 | if ( ! this.buttonsDef.length ) {
|
235 | this.buttonsDef.push( {
|
236 | content: 'Submit' ,
|
237 | value: 'submit'
|
238 | } ) ;
|
239 | }
|
240 |
|
241 | this.buttonsDef.forEach( def => {
|
242 | def.contentWidth = Element.computeContentWidth( def.content , def.contentHasMarkup ) ;
|
243 | buttonsTextWidth += def.contentWidth ;
|
244 | } ) ;
|
245 |
|
246 | buttonSpacing = Math.floor( ( this.outputWidth - buttonsTextWidth ) / ( this.buttonsDef.length + 1 ) ) ;
|
247 |
|
248 | offsetX = buttonSpacing ;
|
249 | offsetY ++ ;
|
250 |
|
251 | this.buttonsDef.forEach( ( def , index ) => {
|
252 | this.buttons[ index ] = new Button( {
|
253 | internal: true ,
|
254 | parent: this ,
|
255 | content: def.content ,
|
256 | value: def.value ,
|
257 | outputX: this.outputX + offsetX ,
|
258 | outputY: this.outputY + offsetY ,
|
259 | focusAttr: def.focusAttr || this.buttonFocusAttr ,
|
260 | blurAttr: def.blurAttr || this.buttonBlurAttr ,
|
261 | noDraw: true
|
262 | } ) ;
|
263 |
|
264 | this.buttons[ index ].on( 'submit' , this.onButtonSubmit ) ;
|
265 |
|
266 | offsetX += def.contentWidth + buttonSpacing ;
|
267 | } ) ;
|
268 | } ;
|
269 |
|
270 |
|
271 |
|
272 | Form.prototype.getValue = function() {
|
273 | var fields = {} ;
|
274 |
|
275 | this.labeledInputs.forEach( labeledInput => {
|
276 | fields[ labeledInput.key ] = labeledInput.getValue() ;
|
277 | } ) ;
|
278 |
|
279 | return { submit: this.submitValue , fields } ;
|
280 | } ;
|
281 |
|
282 |
|
283 |
|
284 | Form.prototype.onKey = function( key , altKeys , data ) {
|
285 | switch( this.keyBindings[ key ] ) {
|
286 | case 'previous' :
|
287 | this.focusChild = this.focusPreviousChild() ;
|
288 | break ;
|
289 | case 'next' :
|
290 | this.focusChild = this.focusNextChild() ;
|
291 | break ;
|
292 | default :
|
293 | return ;
|
294 | }
|
295 |
|
296 | return true ;
|
297 | } ;
|
298 |
|
299 |
|
300 |
|
301 | Form.prototype.onFocus = function( focus , type ) {
|
302 | if ( type === 'cycle' ) { return ; }
|
303 |
|
304 | if ( focus ) {
|
305 | if ( this.focusChild ) { this.document.giveFocusTo( this.focusChild , 'delegate' ) ; }
|
306 | else { this.focusChild = this.focusNextChild() ; }
|
307 | }
|
308 | } ;
|
309 |
|
310 |
|
311 |
|
312 | Form.prototype.onButtonSubmit = function( buttonValue , action ) {
|
313 | this.submitValue = buttonValue ;
|
314 | this.emit( 'submit' , this.getValue() , action , this ) ;
|
315 | } ;
|
316 |
|