1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import java.io.PrintWriter;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.Map;
22
23 import org.apache.commons.lang3.StringUtils;
24 import org.htmlunit.SgmlPage;
25 import org.htmlunit.html.impl.SelectableTextInput;
26 import org.htmlunit.html.impl.SelectableTextSelectionDelegate;
27 import org.htmlunit.javascript.host.event.Event;
28 import org.htmlunit.javascript.host.event.MouseEvent;
29 import org.htmlunit.util.NameValuePair;
30 import org.w3c.dom.Node;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class HtmlTextArea extends HtmlElement implements DisabledElement, SubmittableElement,
49 LabelableElement, SelectableTextInput, FormFieldWithNameHistory, ValidatableElement {
50
51 public static final String TAG_NAME = "textarea";
52
53 private String defaultValue_;
54 private String valueAtFocus_;
55 private final String originalName_;
56 private Collection<String> newNames_ = Collections.emptySet();
57 private String customValidity_;
58
59 private SelectableTextSelectionDelegate selectionDelegate_ = new SelectableTextSelectionDelegate(this);
60 private DoTypeProcessor doTypeProcessor_ = new DoTypeProcessor(this);
61
62
63
64
65
66
67
68
69 HtmlTextArea(final String qualifiedName, final SgmlPage page,
70 final Map<String, DomAttr> attributes) {
71 super(qualifiedName, page, attributes);
72 originalName_ = getNameAttribute();
73 }
74
75
76
77
78
79
80 private void initDefaultValue() {
81 if (defaultValue_ == null) {
82 defaultValue_ = readValue();
83 }
84 }
85
86
87
88
89 @Override
90 public boolean handles(final Event event) {
91 if (event instanceof MouseEvent) {
92 return true;
93 }
94
95 return super.handles(event);
96 }
97
98
99
100
101
102
103 @Override
104 public final String getText() {
105 return readValue();
106 }
107
108 private String readValue() {
109 final StringBuilder builder = new StringBuilder();
110 for (final DomNode node : getChildren()) {
111 if (node instanceof DomText) {
112 builder.append(((DomText) node).getData());
113 }
114 }
115
116 if (builder.length() != 0 && builder.charAt(0) == '\n') {
117 builder.deleteCharAt(0);
118 }
119 return builder.toString();
120 }
121
122
123
124
125
126
127
128
129
130 @Override
131 public final void setText(final String newValue) {
132 setTextInternal(newValue);
133
134 HtmlInput.executeOnChangeHandlerIfAppropriate(this);
135 }
136
137 private void setTextInternal(final String newValue) {
138 initDefaultValue();
139 DomNode child = getFirstChild();
140 if (child == null) {
141 final DomText newChild = new DomText(getPage(), newValue);
142 appendChild(newChild);
143 }
144 else {
145 DomNode next = child.getNextSibling();
146 while (next != null && !(next instanceof DomText)) {
147 child = next;
148 next = child.getNextSibling();
149 }
150
151 if (next == null) {
152 removeChild(child);
153 final DomText newChild = new DomText(getPage(), newValue);
154 appendChild(newChild);
155 }
156 else {
157 ((DomText) next).setData(newValue);
158 }
159 }
160
161 final int pos = newValue.length();
162 setSelectionStart(pos);
163 setSelectionEnd(pos);
164 }
165
166
167
168
169 @Override
170 public NameValuePair[] getSubmitNameValuePairs() {
171 String text = getText();
172 text = text.replace("\r\n", "\n").replace("\n", "\r\n");
173
174 return new NameValuePair[]{new NameValuePair(getNameAttribute(), text)};
175 }
176
177
178
179
180
181 @Override
182 public void reset() {
183 initDefaultValue();
184 setText(defaultValue_);
185 }
186
187
188
189
190
191 @Override
192 public void setDefaultValue(String defaultValue) {
193 initDefaultValue();
194 if (defaultValue == null) {
195 defaultValue = "";
196 }
197
198
199 if (getText().equals(getDefaultValue())) {
200 setTextInternal(defaultValue);
201 }
202 defaultValue_ = defaultValue;
203 }
204
205
206
207
208
209 @Override
210 public String getDefaultValue() {
211 initDefaultValue();
212 return defaultValue_;
213 }
214
215
216
217
218
219
220
221
222 @Override
223 public void setDefaultChecked(final boolean defaultChecked) {
224
225 }
226
227
228
229
230
231
232
233
234 @Override
235 public boolean isDefaultChecked() {
236 return false;
237 }
238
239
240
241
242
243
244
245
246 public final String getNameAttribute() {
247 return getAttributeDirect(DomElement.NAME_ATTRIBUTE);
248 }
249
250
251
252
253
254
255
256
257 public final String getRowsAttribute() {
258 return getAttributeDirect("rows");
259 }
260
261
262
263
264
265
266
267
268 public final String getColumnsAttribute() {
269 return getAttributeDirect("cols");
270 }
271
272
273
274
275 @Override
276 public final boolean isDisabled() {
277 if (hasAttribute(ATTRIBUTE_DISABLED)) {
278 return true;
279 }
280
281 Node node = getParentNode();
282 while (node != null) {
283 if (node instanceof DisabledElement
284 && ((DisabledElement) node).isDisabled()) {
285 return true;
286 }
287 node = node.getParentNode();
288 }
289
290 return false;
291 }
292
293
294
295
296 @Override
297 public final String getDisabledAttribute() {
298 return getAttributeDirect(ATTRIBUTE_DISABLED);
299 }
300
301
302
303
304
305
306
307
308 public final String getReadOnlyAttribute() {
309 return getAttributeDirect("readonly");
310 }
311
312
313
314
315
316
317
318
319 public final String getTabIndexAttribute() {
320 return getAttributeDirect("tabindex");
321 }
322
323
324
325
326
327
328
329
330 public final String getAccessKeyAttribute() {
331 return getAttributeDirect("accesskey");
332 }
333
334
335
336
337
338
339
340
341 public final String getOnFocusAttribute() {
342 return getAttributeDirect("onfocus");
343 }
344
345
346
347
348
349
350
351
352 public final String getOnBlurAttribute() {
353 return getAttributeDirect("onblur");
354 }
355
356
357
358
359
360
361
362
363 public final String getOnSelectAttribute() {
364 return getAttributeDirect("onselect");
365 }
366
367
368
369
370
371
372
373
374 public final String getOnChangeAttribute() {
375 return getAttributeDirect("onchange");
376 }
377
378
379
380
381 @Override
382 public void select() {
383 selectionDelegate_.select();
384 }
385
386
387
388
389 @Override
390 public String getSelectedText() {
391 return selectionDelegate_.getSelectedText();
392 }
393
394
395
396
397 @Override
398 public int getSelectionStart() {
399 return selectionDelegate_.getSelectionStart();
400 }
401
402
403
404
405 @Override
406 public void setSelectionStart(final int selectionStart) {
407 selectionDelegate_.setSelectionStart(selectionStart);
408 }
409
410
411
412
413 @Override
414 public int getSelectionEnd() {
415 return selectionDelegate_.getSelectionEnd();
416 }
417
418
419
420
421 @Override
422 public void setSelectionEnd(final int selectionEnd) {
423 selectionDelegate_.setSelectionEnd(selectionEnd);
424 }
425
426
427
428
429 @Override
430 protected boolean printXml(final String indent, final boolean tagBefore, final PrintWriter printWriter) {
431 printWriter.print(indent + "<");
432 printOpeningTagContentAsXml(printWriter);
433
434 printWriter.print(">");
435 printWriter.print(org.htmlunit.util.StringUtils.escapeXml(getText()));
436 printWriter.print("</textarea>");
437 return true;
438 }
439
440
441
442
443 @Override
444 protected void doType(final char c, final boolean lastType) {
445 doTypeProcessor_.doType(getText(), selectionDelegate_, c, this, lastType);
446 }
447
448
449
450
451 @Override
452 protected void doType(final int keyCode, final boolean lastType) {
453 doTypeProcessor_.doType(getText(), selectionDelegate_, keyCode, this, lastType);
454 }
455
456
457
458
459 @Override
460 protected void typeDone(final String newValue, final boolean notifyAttributeChangeListeners) {
461 setTextInternal(newValue);
462 }
463
464
465
466
467 @Override
468 protected boolean acceptChar(final char c) {
469 return super.acceptChar(c) || c == '\n' || c == '\r';
470 }
471
472
473
474
475 @Override
476 public void focus() {
477 super.focus();
478 valueAtFocus_ = getText();
479 }
480
481
482
483
484 @Override
485 public void removeFocus() {
486 super.removeFocus();
487 if (valueAtFocus_ != null && !valueAtFocus_.equals(getText())) {
488 HtmlInput.executeOnChangeHandlerIfAppropriate(this);
489 }
490 valueAtFocus_ = null;
491 }
492
493
494
495
496
497
498 public void setReadOnly(final boolean isReadOnly) {
499 if (isReadOnly) {
500 setAttribute("readOnly", "readOnly");
501 }
502 else {
503 removeAttribute("readOnly");
504 }
505 }
506
507
508
509
510
511 public boolean isReadOnly() {
512 return hasAttribute("readOnly");
513 }
514
515
516
517
518 @Override
519 protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue,
520 final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {
521 final String qualifiedNameLC = org.htmlunit.util.StringUtils.toRootLowerCase(qualifiedName);
522 if (DomElement.NAME_ATTRIBUTE.equals(qualifiedNameLC)) {
523 if (newNames_.isEmpty()) {
524 newNames_ = new HashSet<>();
525 }
526 newNames_.add(attributeValue);
527 }
528 super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
529 notifyMutationObservers);
530 }
531
532
533
534
535 @Override
536 public String getOriginalName() {
537 return originalName_;
538 }
539
540
541
542
543 @Override
544 public Collection<String> getNewNames() {
545 return newNames_;
546 }
547
548
549
550
551
552 @Override
553 protected boolean isEmptyXmlTagExpanded() {
554 return true;
555 }
556
557
558
559
560 @Override
561 public DisplayStyle getDefaultStyleDisplay() {
562 return DisplayStyle.INLINE_BLOCK;
563 }
564
565
566
567
568
569
570 public String getPlaceholder() {
571 return getAttributeDirect("placeholder");
572 }
573
574
575
576
577
578
579 public void setPlaceholder(final String placeholder) {
580 setAttribute("placeholder", placeholder);
581 }
582
583
584
585
586 @Override
587 protected boolean isRequiredSupported() {
588 return true;
589 }
590
591
592
593
594 @Override
595 public DomNode cloneNode(final boolean deep) {
596 final HtmlTextArea newnode = (HtmlTextArea) super.cloneNode(deep);
597 newnode.selectionDelegate_ = new SelectableTextSelectionDelegate(newnode);
598 newnode.doTypeProcessor_ = new DoTypeProcessor(newnode);
599 newnode.newNames_ = new HashSet<>(newNames_);
600
601 return newnode;
602 }
603
604
605
606
607 @Override
608 public boolean willValidate() {
609 return !isDisabled() && !isReadOnly();
610 }
611
612
613
614
615 @Override
616 public void setCustomValidity(final String message) {
617 customValidity_ = message;
618 }
619
620
621
622
623 @Override
624 public boolean isValid() {
625 return isValidValidityState();
626 }
627
628
629
630
631 @Override
632 public boolean isCustomErrorValidityState() {
633 return !StringUtils.isEmpty(customValidity_);
634 }
635
636 @Override
637 public boolean isValidValidityState() {
638 return !isCustomErrorValidityState()
639 && !isValueMissingValidityState();
640 }
641
642
643
644
645 @Override
646 public boolean isValueMissingValidityState() {
647 return ATTRIBUTE_NOT_DEFINED != getAttributeDirect(ATTRIBUTE_REQUIRED)
648 && getText().isEmpty();
649 }
650 }