move changes to branch
[xtuple] / lib / enyo-x / source / widgets / input.js
1 /*jshint node:true, indent:2, curly:true, eqeqeq:true, immed:true, latedef:true, newcap:true, noarg:true,
2 regexp:true, undef:true, trailing:true, white:true */
3 /*global XT:true, XV:true, Globalize:true, enyo:true, _:true */
4
5 (function () {
6
7   /**
8     @name XV.InputWidget
9     @class Maintains a consistent API to be used by workspaces.<br />
10     The superkind from which other xTuple input objects are derived.<br />
11     To create an input field for strings, see {@link XV.InputWidget}.<br />
12     To create an input field for dates, see {@link XV.DateWidget}.<br />
13     To create an input field for files, see {@link XV.FileInput}.<br />
14     To create an input field for numbers, see {@link XV.NumberWidget}.<br />
15     To create a multi-line textarea input box, see {@link XV.TextArea}.<br />
16     To create a checkbox, see {@link XV.CheckboxWidget}.<br />
17     To create a togglebutton, see {@link XV.TogglebuttonWidget}.<br />
18    */
19   enyo.kind(
20     /** @lends XV.InputWidget# */{
21     name: "XV.InputWidget",
22     classes: "xv-input",
23     published: {
24       attr: null,
25       value: null,
26       disabled: false,
27       placeholder: null,
28       type: null,
29       maxlength: null,
30       label: "",
31       showLabel: true
32     },
33     events: {
34       "onValueChange": ""
35     },
36     handlers: {
37       onblur: "receiveBlur"
38     },
39     components: [
40       {kind: "FittableColumns", components: [
41         {name: "label", content: "", fit: true, classes: "xv-flexible-label"},
42         {kind: "onyx.InputDecorator", classes: "xv-input-decorator",
43           components: [
44           {name: "input", kind: "onyx.Input", classes: "xv-subinput",
45             onchange: "inputChanged", onkeydown: "keyDown"}
46         ]}
47       ]}
48     ],
49     create: function () {
50       this.inherited(arguments);
51       this.placeholderChanged();
52       this.disabledChanged();
53       this.typeChanged();
54       this.maxlengthChanged();
55       this.labelChanged();
56       this.showLabelChanged();
57     },
58     /**
59       Sets the value of the input to an empty string
60     */
61     clear: function (options) {
62       this.setValue("", options);
63     },
64     /**
65      The disabledChanged method, and many below it, are here to deal with
66      the fact that XV.Input doesvnot inherit from onyx.input or enyo.input,
67      and so insofar as it needs to support their APIs, we
68      have to redeclare the methods and pass through the data.
69     */
70     disabledChanged: function () {
71       this.$.input.setDisabled(this.getDisabled());
72     },
73     focus: function () {
74       this.$.input.focus();
75     },
76     /**
77       Called when the user changes the input. Validates field.
78       If valid, calls setValue, which bubbles the event. Otherwise
79       we bubble ourselves the empty string.
80     */
81     inputChanged: function (inSender, inEvent) {
82       var input = this.$.input.getValue(),
83         value = this.validate(input);
84       if (value !== false) {
85         // is valid and not empty
86         this.setValue(value);
87       } else {
88         // is invalid or empty
89         this.setValue(null);
90       }
91     },
92     /**
93       "text-shadow-none" hack here to fix issue #18397
94       @todo Revisit/remove after fix to ENYO-1104. See also issue 18397.
95
96       Added some handling here to force the input to save the value when
97       the user uses the arrows or tab key.
98     */
99     keyDown: function (inSender, inEvent) {
100       var shadowNone = inEvent.originator.hasClass("text-shadow-none"),
101         keyCode = inEvent.keyCode;
102
103       inEvent.originator.addRemoveClass("text-shadow-none", !shadowNone);
104       inEvent.originator.addRemoveClass("text-shadow-0", shadowNone);
105
106       // this adds support for tabbing and arrowing through grids
107       if (keyCode === XV.KEY_TAB || keyCode === XV.KEY_DOWN || keyCode === XV.KEY_UP) {
108         this.receiveBlur();
109       }
110     },
111     /**
112     @todo Document the placeholderChanged method.
113     */
114     placeholderChanged: function () {
115       var placeholder = this.getPlaceholder();
116       this.$.input.setPlaceholder(placeholder);
117     },
118     /**
119       Webkit browsers do not always emit the proper change event,
120       so this ensures that the changed value is saved.
121     */
122     receiveBlur: function () {
123       if (this.$.input.getValue() !== this.getValue()) {
124         this.inputChanged();
125       }
126     },
127     /**
128       If the value has changed, update the user field using valueChanged,
129       and bubble up the event so that the model may be updated (unless
130       silent is true, which is how the workspace invokes it). This
131       function nicely handles being told what to do both from below, by
132       the user, or from above, by the workspace. The event
133       that it bubbles up is valueChange, whereas the event that probably
134       triggered it is inputChange.
135     */
136     setValue: function (value, options) {
137       options = options || {};
138       var oldValue = this.getValue(),
139         inEvent;
140       if (oldValue !== value) {
141         this.value = value;
142         this.valueChanged(value);
143         if (!options.silent) {
144           inEvent = { value: value, originator: this };
145           // this bubbles up the onValueChange event which then
146           // triggers the controlValueChanged in the workspace
147           this.doValueChange(inEvent);
148         }
149       }
150     },
151     /**
152       Returns the value if it's valid, or false if it's not
153     */
154     validate: function (value) {
155       return value;
156     },
157     /**
158       Updates the field that the user sees based on the published field.
159     */
160     valueChanged: function (value) {
161       this.$.input.setValue(value || "");
162       return value;
163     },
164     /**
165       Pass through attributes intended for onyx input inside.
166     */
167     setInputStyle: function (style) {
168       this.$.input.setStyle(style);
169     },
170     /**
171       Pass through attributes intended for onyx input inside.
172     */
173     typeChanged: function () {
174       this.$.input.setType(this.getType());
175     },
176
177     maxlengthChanged: function () {
178       this.$.input.setAttribute("maxlength", this.maxlength);
179     },
180
181     /**
182       Sets the label content based on the label value or the attribute text.
183     */
184     labelChanged: function () {
185       var label = (this.getLabel() || ("_" + this.attr || "").loc());
186       this.$.label.setContent(label + ":");
187     },
188
189     /**
190     @todo Document the showLabelChanged method.
191     */
192     showLabelChanged: function () {
193       if (this.getShowLabel()) {
194         this.$.label.show();
195       } else {
196         this.$.label.hide();
197       }
198     }
199   });
200
201 }());