Merge pull request #1 from shackbarth/keith1
[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       {controlClasses: 'enyo-inline', components: [
41         {name: "label", classes: "xv-label"},
42         {kind: "onyx.InputDecorator", components: [
43           {name: "input", kind: "onyx.Input", onchange: "inputChanged", onkeydown: "keyDown"}
44         ]}
45       ]}
46     ],
47     create: function () {
48       this.inherited(arguments);
49       this.placeholderChanged();
50       this.disabledChanged();
51       this.typeChanged();
52       this.maxlengthChanged();
53       this.labelChanged();
54       this.showLabelChanged();
55     },
56     /**
57       Sets the value of the input to an empty string
58     */
59     clear: function (options) {
60       this.setValue("", options);
61     },
62     /**
63      The disabledChanged method, and many below it, are here to deal with
64      the fact that XV.Input does not inherit from onyx.input or enyo.input,
65      and so insofar as it needs to support their APIs, we
66      have to redeclare the methods and pass through the data.
67     */
68     disabledChanged: function () {
69       this.$.input.setDisabled(this.getDisabled());
70       this.$.label.addRemoveClass("disabled", this.getDisabled());
71     },
72     focus: function () {
73       this.$.input.focus();
74     },
75     /**
76       Called when the user changes the input. Validates field.
77       If valid, calls setValue, which bubbles the event. Otherwise
78       we bubble ourselves the empty string.
79     */
80     inputChanged: function (inSender, inEvent) {
81       var input = this.$.input.getValue(),
82         value = this.validate(input);
83       if (value !== false) {
84         // is valid and not empty
85         this.setValue(value);
86       } else {
87         // is invalid or empty
88         this.setValue(null);
89       }
90     },
91     /**
92       "text-shadow-none" hack here to fix issue #18397
93       @todo Revisit/remove after fix to ENYO-1104. See also issue 18397.
94
95       Added some handling here to force the input to save the value when
96       the user uses the arrows or tab key.
97     */
98     keyDown: function (inSender, inEvent) {
99       var shadowNone = inEvent.originator.hasClass("text-shadow-none"),
100         keyCode = inEvent.keyCode;
101
102       inEvent.originator.addRemoveClass("text-shadow-none", !shadowNone);
103       inEvent.originator.addRemoveClass("text-shadow-0", shadowNone);
104
105       // this adds support for tabbing and arrowing through grids
106       if (keyCode === XV.KEY_TAB || keyCode === XV.KEY_DOWN || keyCode === XV.KEY_UP) {
107         this.receiveBlur();
108       }
109     },
110     /**
111       Sets the placeholder on the input field.
112     */
113     placeholderChanged: function () {
114       if (_.isFunction(this.$.input.setPlaceholder)) {
115         this.$.input.setPlaceholder(this.placeholder);
116       }
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       Sets the type attribute for input field.
166     */
167     typeChanged: function () {
168       this.$.input.setType(this.getType());
169     },
170
171     /**
172       Set the maxlength attribute on the input field.
173     */
174     maxlengthChanged: function () {
175       this.$.input.setAttribute("maxlength", this.maxlength);
176     },
177
178     /**
179       Sets the label content based on the label value or the attribute text.
180     */
181     labelChanged: function () {
182       var label = (this.getLabel() || ("_" + this.attr || "").loc());
183       this.$.label.setContent(label + ":");
184     },
185
186     /**
187       Sets visibility of the widget label.
188     */
189     showLabelChanged: function () {
190       this.$.label.setShowing(this.getShowLabel());
191     }
192   });
193
194 }());