1/*
2 * RapidContext <https://www.rapidcontext.com/>
3 * Copyright (c) 2007-2024 Per Cederberg. All rights reserved.
4 *
5 * This program is free software: you can redistribute it and/or
6 * modify it under the terms of the BSD license.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11 *
12 * See the RapidContext LICENSE for more details.
13 */
14
15// Namespace initialization
16if (typeof(RapidContext) == "undefined") {
17 RapidContext = {};
18}
19RapidContext.Widget = RapidContext.Widget || { Classes: {} };
20
21/**
22 * Creates a new text area (or text box) widget.
23 *
24 * @constructor
25 * @param {Object} attrs the widget and node attributes
26 * @param {string} [attrs.name] the form field name
27 * @param {string} [attrs.value] the field value, defaults to ""
28 * @param {string} [attrs.helpText] the help text when empty (deprecated)
29 * @param {boolean} [attrs.autosize] the auto-resize flag, defaults to false
30 * @param {boolean} [attrs.disabled] the disabled widget flag, defaults to
31 * false
32 * @param {boolean} [attrs.hidden] the hidden widget flag, defaults to false
33 * @param {...(string|Node)} [value] the initial text content
34 *
35 * @return {Widget} the widget DOM node
36 *
37 * @class The text area widget class. Used to provide a text input field
38 * spanning multiple rows, using the `<textarea>` HTML element.
39 * @property {boolean} disabled The read-only widget disabled flag.
40 * @property {string} defaultValue The value to use on form reset.
41 * @extends RapidContext.Widget
42 *
43 * @example <caption>JavaScript</caption>
44 * var attrs = { name="description", placeholder: "Description Text" };
45 * var field = RapidContext.Widget.TextArea(attrs);
46 *
47 * @example <caption>User Interface XML</caption>
48 * <TextArea name="description" placeholder="Description Text" />
49 */
50RapidContext.Widget.TextArea = function (attrs/*, ...*/) {
51 function scrape(val) {
52 return String(val && val.textContent || val || "");
53 }
54 var text = (attrs && attrs.value) || "";
55 text += Array.from(arguments).slice(1).map(scrape).join("");
56 var o = RapidContext.UI.TEXTAREA({
57 autocapitalize: "off",
58 autocomplete: "off",
59 autocorrect: "off",
60 spellcheck: "off",
61 value: text,
62 });
63 RapidContext.Widget._widgetMixin(o, RapidContext.Widget.TextArea);
64 o.addClass("widgetTextArea");
65 o.setAttrs(Object.assign({}, attrs, { value: text }));
66 o.on("input", o._handleChange);
67 return o;
68};
69
70// Register widget class
71RapidContext.Widget.Classes.TextArea = RapidContext.Widget.TextArea;
72
73/**
74 * Emitted when the text is modified. This event is triggered by either
75 * user events (keypress, paste, cut, blur) or by setting the value via
76 * setAttrs(). The DOM standard onchange event has no 'event.detail'
77 * data and is triggered on blur. The synthetic onchange events all
78 * contain an 'event.detail' object with 'before', 'after' and 'cause'
79 * properties.
80 *
81 * @name RapidContext.Widget.TextArea#onchange
82 * @event
83 */
84
85/**
86 * Updates the widget or HTML DOM node attributes.
87 *
88 * @param {Object} attrs the widget and node attributes to set
89 * @param {string} [attrs.name] the form field name
90 * @param {string} [attrs.value] the field value
91 * @param {string} [attrs.helpText] the help text when empty (deprecated)
92 * @param {boolean} [attrs.autosize] the auto-resize flag
93 * @param {boolean} [attrs.disabled] the disabled widget flag
94 * @param {boolean} [attrs.hidden] the hidden widget flag
95 */
96RapidContext.Widget.TextArea.prototype.setAttrs = function (attrs) {
97 attrs = Object.assign({}, attrs);
98 if ("helpText" in attrs) {
99 console.warn("deprecated: setting 'helpText' attribute, use 'placeholder' instead");
100 attrs.placeholder = attrs.placeholder || attrs.helpText;
101 delete attrs.helpText;
102 }
103 if ("value" in attrs) {
104 // FIXME: This is wrong, since we're setting an attribute here.
105 // But until Form.update() has some other way to set a field
106 // value and trigger changes, this will remain.
107 this.value = attrs.value || "";
108 this._handleChange(null);
109 delete attrs.value;
110 }
111 this.__setAttrs(attrs);
112 if (this.autosize && this.scrollHeight == 0) {
113 this.rows = this.value.split("\n").length;
114 }
115};
116
117/**
118 * Resets the text area form value to the initial value.
119 */
120RapidContext.Widget.TextArea.prototype.reset = function () {
121 this.setAttrs({ value: this.defaultValue });
122};
123
124/**
125 * Returns the text area value. This function is slightly different from using
126 * the `value` property directly, since it will attempt to normalize newlines
127 * in the value.
128 *
129 * @return {string} the field value
130 *
131 * @example
132 * var str = field.getValue();
133 * var lines = str.split("\n").map((s) => s.trim());
134 * field.setAttrs({ "value": lines.join("\n") });
135 */
136RapidContext.Widget.TextArea.prototype.getValue = function () {
137 var str = this.value;
138 // This is a hack to remove multiple newlines caused by
139 // platforms inserting or failing to normalize newlines
140 // within the HTML textarea control.
141 str = str.replace(/\r\n\n/g, "\n");
142 if (this.value != str) {
143 this.value = str;
144 }
145 return str;
146};
147
148/**
149 * Handles input events for this this widget.
150 *
151 * @param {Event} [evt] the DOM Event object or null for manual
152 */
153RapidContext.Widget.TextArea.prototype._handleChange = function (evt) {
154 var cause = (evt && evt.inputType) || "set";
155 var detail = { before: this.storedValue || "", after: this.value, cause: cause };
156 this.emit("change", { detail: detail, bubbles: true });
157 this.storedValue = this.value;
158 if (this.autosize) {
159 this.style.height = "auto";
160 if (this.scrollHeight > 10) {
161 this.style.height = this.scrollHeight + "px";
162 }
163 }
164};
165
RapidContext
Access · Discovery · Insight
www.rapidcontext.com