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 field 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 initial field value, defaults
28 * to an empty string
29 * @param {string} [attrs.tag] the HTML tag to use, defaults to "span"
30 * @param {string} [attrs.format] the field format string, defaults
31 * to "{:s}"
32 * @param {function} [attrs.formatter] the value formatter function
33 * @param {number} [attrs.maxLength] the maximum data length,
34 * overflow will be displayed as a tooltip, defaults to
35 * unlimited
36 * @param {boolean} [attrs.mask] the masked display flag, when set
37 * the field value is only displayed after the user has
38 * clicked the field, defaults to false
39 * @param {boolean} [attrs.hidden] the hidden widget flag, defaults to false
40 *
41 * @return {Widget} the widget DOM node
42 *
43 * @class The field widget class. This widget is useful for providing
44 * visible display of form data, using a `<span>` HTML element.
45 * @extends RapidContext.Widget
46 *
47 * @example <caption>JavaScript</caption>
48 * let attrs = { name: "ratio", value: 0.23, format: "Ratio: {:%}" };
49 * let field = RapidContext.Widget.Field(attrs);
50 *
51 * @example <caption>User Interface XML</caption>
52 * <Field name="ratio" value="0.23" format="Ratio: {:%}" />
53 */
54RapidContext.Widget.Field = function (attrs) {
55 let o = document.createElement(attrs.tag || "span");
56 RapidContext.Widget._widgetMixin(o, RapidContext.Widget.Field);
57 o.addClass("widgetField");
58 o.setAttrs(Object.assign({ name: "", value: "" }, attrs));
59 o.defaultValue = o.value;
60 o.defaultMask = !!o.mask;
61 o.on("click", o._handleClick);
62 return o;
63};
64
65// Register widget class
66RapidContext.Widget.Classes.Field = RapidContext.Widget.Field;
67
68/**
69 * Returns the widget container DOM node.
70 *
71 * @return {Node} returns null, since child nodes are not supported
72 */
73RapidContext.Widget.Field.prototype._containerNode = function () {
74 return null;
75};
76
77/**
78 * Updates the widget or HTML DOM node attributes.
79 *
80 * @param {Object} attrs the widget and node attributes to set
81 * @param {string} [attrs.name] the form field name
82 * @param {string} [attrs.value] the field value
83 * @param {string} [attrs.format] the field format string
84 * @param {function} [attrs.formatter] the value formatter function
85 * @param {number} [attrs.maxLength] the maximum data length,
86 * overflow will be displayed as a tooltip
87 * @param {boolean} [attrs.mask] the masked display flag, when set
88 * the field value is only displayed after the user has
89 * clicked the field
90 * @param {boolean} [attrs.hidden] the hidden widget flag
91 *
92 * @example
93 * field.setAttrs({ value: 0.23 });
94 */
95RapidContext.Widget.Field.prototype.setAttrs = function (attrs) {
96 attrs = Object.assign({}, attrs);
97 if ("formatter" in attrs) {
98 let valid = typeof(attrs.formatter) == "function";
99 attrs.formatter = valid ? attrs.formatter : null;
100 }
101 if ("maxLength" in attrs) {
102 let val = parseInt(attrs.maxLength, 10);
103 attrs.maxLength = isNaN(val) ? null : val;
104 }
105 if ("mask" in attrs) {
106 attrs.mask = RapidContext.Data.bool(attrs.mask);
107 }
108 this.__setAttrs(attrs);
109 this.redraw();
110};
111
112/**
113 * Redraws the field from updated values or status. Note that this
114 * method is called automatically whenever the `setAttrs()` method is
115 * called.
116 */
117RapidContext.Widget.Field.prototype.redraw = function () {
118 let str = this.value;
119 if (this.formatter) {
120 try {
121 str = this.formatter(str);
122 } catch (e) {
123 str = e.message;
124 }
125 } else if (str == null || str === "") {
126 str = "";
127 } else if (this.format) {
128 str = MochiKit.Text.format(this.format, str);
129 } else if (typeof(str) != "string") {
130 str = str.toString();
131 }
132 let longStr = str;
133 if (this.maxLength > 0) {
134 str = MochiKit.Text.truncate(str, this.maxLength, "...");
135 }
136 if (this.mask) {
137 this.addClass("widgetFieldMask");
138 this.title = "Click to show";
139 this.innerText = "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022";
140 this.append(RapidContext.Widget.Icon({ ref: "LOCK", tooltip: "Click to show" }));
141 } else {
142 if (str == longStr) {
143 delete this.title;
144 } else {
145 this.title = longStr;
146 }
147 this.innerText = str;
148 }
149};
150
151/**
152 * Resets the field value to the initial value.
153 */
154RapidContext.Widget.Field.prototype.reset = function () {
155 this.setAttrs({ value: this.defaultValue, mask: this.defaultMask });
156};
157
158/**
159 * Handles click events on the field (if masked).
160 */
161RapidContext.Widget.Field.prototype._handleClick = function () {
162 if (this.mask) {
163 this.mask = false;
164 this.removeClass("widgetFieldMask");
165 this.redraw();
166 }
167};
168
RapidContext
Access · Discovery · Insight
www.rapidcontext.com