Daniel@0
|
1 "use strict";
|
Daniel@0
|
2
|
Daniel@0
|
3 (function($) {
|
Daniel@0
|
4 $.widget("cgpma.textfield", {
|
Daniel@0
|
5
|
Daniel@0
|
6 options: {
|
Daniel@0
|
7 value: "",
|
Daniel@0
|
8 baseValue: "",
|
Daniel@0
|
9 autocompleteMaxItemCount: 15,
|
Daniel@0
|
10 autocompleteSort: false,
|
Daniel@0
|
11 autocompleteSuggestions: null,
|
Daniel@0
|
12 autocompleteIsAdvisory: false,
|
Daniel@0
|
13 autocompleteCSSClasses: ""
|
Daniel@0
|
14 },
|
Daniel@0
|
15
|
Daniel@0
|
16 isFocused: function() {
|
Daniel@0
|
17 var widget = this;
|
Daniel@0
|
18 return widget.$input.is(":focus");
|
Daniel@0
|
19 },
|
Daniel@0
|
20
|
Daniel@0
|
21 focus: function() {
|
Daniel@0
|
22 var widget = this;
|
Daniel@0
|
23 return widget.$input.focus();
|
Daniel@0
|
24 },
|
Daniel@0
|
25
|
Daniel@0
|
26 getTextRange: function() {
|
Daniel@0
|
27 var widget = this;
|
Daniel@0
|
28 return widget.$input.textrange();
|
Daniel@0
|
29 },
|
Daniel@0
|
30
|
Daniel@0
|
31 setTextRange: function(textRange) {
|
Daniel@0
|
32 var widget = this;
|
Daniel@0
|
33 if (textRange.start != textRange.end) {
|
Daniel@0
|
34 return widget.$input.textrange("set", textRange.start, textRange.end);
|
Daniel@0
|
35 }
|
Daniel@0
|
36 return widget.$input.textrange("setcursor", textRange.position);
|
Daniel@0
|
37 },
|
Daniel@0
|
38
|
Daniel@0
|
39 _create : function() {
|
Daniel@0
|
40 var widget = this;
|
Daniel@0
|
41 widget.$element = this.element;
|
Daniel@0
|
42 widget.$input = $.bem.generateElement("input", "cgpma", "textfield-input");
|
Daniel@0
|
43 widget.$input.appendTo(widget.$element);
|
Daniel@0
|
44
|
Daniel@0
|
45 widget.$input.data("widget", widget);
|
Daniel@0
|
46
|
Daniel@0
|
47 widget._applyAutocompleteSuggestions();
|
Daniel@0
|
48
|
Daniel@0
|
49 widget.$input.bind("input", widget.__handleInputChange);
|
Daniel@0
|
50 widget.$input.bind("keydown", widget.__handleInputKeyDown);
|
Daniel@0
|
51 widget.$input.bind("keyup", widget.__handleInputKeyUp);
|
Daniel@0
|
52 widget.$input.bind("click", widget.__handleInputClick);
|
Daniel@0
|
53
|
Daniel@0
|
54 },
|
Daniel@0
|
55
|
Daniel@0
|
56 _applyAutocompleteSuggestions: function() {
|
Daniel@0
|
57 var widget = this;
|
Daniel@0
|
58
|
Daniel@0
|
59 if (widget.$input.data("ui-autocomplete")) {
|
Daniel@0
|
60 widget.$input.autocomplete("destroy");
|
Daniel@0
|
61 }
|
Daniel@0
|
62
|
Daniel@0
|
63 var autocompleteSuggestions = widget.options.autocompleteSuggestions;
|
Daniel@0
|
64 if (!autocompleteSuggestions) {
|
Daniel@0
|
65 return;
|
Daniel@0
|
66 delete widget._invertedAutocompleteSuggestions;
|
Daniel@0
|
67 }
|
Daniel@0
|
68 widget._invertedAutocompleteSuggestions = _.invert(autocompleteSuggestions);
|
Daniel@0
|
69
|
Daniel@0
|
70 widget.$input.autocomplete({
|
Daniel@0
|
71 delay : 0,
|
Daniel@0
|
72 minLength : 0,
|
Daniel@0
|
73 source : $.proxy(widget, "_autocompleteSource")
|
Daniel@0
|
74 });
|
Daniel@0
|
75
|
Daniel@0
|
76 widget.$input.autocomplete(
|
Daniel@0
|
77 "widget").addClass(widget.options.autocompleteCSSClasses);
|
Daniel@0
|
78
|
Daniel@0
|
79 widget._on(widget.$input, {
|
Daniel@0
|
80 autocompleteselect : widget.__handleInputChange,
|
Daniel@0
|
81 autocompleteopen : widget.__handleAutocompleteOpen,
|
Daniel@0
|
82 autocompleteclose : widget.__handleAutocompleteClose,
|
Daniel@0
|
83 //autocompletechange : widget.__handleInputChange
|
Daniel@0
|
84 });
|
Daniel@0
|
85 },
|
Daniel@0
|
86
|
Daniel@0
|
87 _autocompleteSource: function(request, response) {
|
Daniel@0
|
88 var widget = this;
|
Daniel@0
|
89
|
Daniel@0
|
90 var matcher = new RegExp($.ui.autocomplete
|
Daniel@0
|
91 .escapeRegex(_.str.trim(request.term)), "i");
|
Daniel@0
|
92
|
Daniel@0
|
93 var responseItems = [];
|
Daniel@0
|
94 _.each(widget.options.autocompleteSuggestions, function(kindName, kind) {
|
Daniel@0
|
95 if (widget.options.autocompleteAlwaysFull || !request.term || matcher.test(kindName)) {
|
Daniel@0
|
96 responseItems.push({
|
Daniel@0
|
97 label : kindName,
|
Daniel@0
|
98 value : kindName,
|
Daniel@0
|
99 });
|
Daniel@0
|
100 };
|
Daniel@0
|
101 });
|
Daniel@0
|
102
|
Daniel@0
|
103 if (responseItems.length == 1 && responseItems[0].label === request.term) {
|
Daniel@0
|
104 responseItems = [];
|
Daniel@0
|
105 }
|
Daniel@0
|
106 if (responseItems.length > widget.options.autocompleteMaxItemCount) {
|
Daniel@0
|
107 responseItems = responseItems.slice(0, widget.options.autocompleteMaxItemCount);
|
Daniel@0
|
108 }
|
Daniel@0
|
109 if (widget.options.autocompleteSort) {
|
Daniel@0
|
110 responseItems = _.sortBy(responseItems, "label");
|
Daniel@0
|
111 }
|
Daniel@0
|
112
|
Daniel@0
|
113 response(responseItems);
|
Daniel@0
|
114 },
|
Daniel@0
|
115
|
Daniel@0
|
116 _createShowAllButton : function() {
|
Daniel@0
|
117 return;
|
Daniel@0
|
118 var input = this.input, wasOpen = false;
|
Daniel@0
|
119 $("<a>").attr("tabIndex", -1).attr("title",
|
Daniel@0
|
120 "Show All Items").tooltip().appendTo(
|
Daniel@0
|
121 this.wrapper).button({
|
Daniel@0
|
122 icons : {
|
Daniel@0
|
123 primary : "ui-icon-triangle-1-s"
|
Daniel@0
|
124 },
|
Daniel@0
|
125 text : false
|
Daniel@0
|
126 }).removeClass("ui-corner-all").addClass(
|
Daniel@0
|
127 "custom-combobox-toggle ui-corner-right")
|
Daniel@0
|
128 .mousedown(
|
Daniel@0
|
129 function() {
|
Daniel@0
|
130 wasOpen = input.autocomplete(
|
Daniel@0
|
131 "widget")
|
Daniel@0
|
132 .is(":visible");
|
Daniel@0
|
133 }).click(function() {
|
Daniel@0
|
134 input.focus();
|
Daniel@0
|
135 // Close if already visible
|
Daniel@0
|
136 if (wasOpen) {
|
Daniel@0
|
137 return;
|
Daniel@0
|
138 }
|
Daniel@0
|
139 // Pass empty string as value to search
|
Daniel@0
|
140 // for, displaying all results
|
Daniel@0
|
141 input.autocomplete("search", "");
|
Daniel@0
|
142 });
|
Daniel@0
|
143 },
|
Daniel@0
|
144
|
Daniel@0
|
145 _isAutocompleteVisible : function() {
|
Daniel@0
|
146 var widget = this;
|
Daniel@0
|
147 if (widget.$input.data("ui-autocomplete")) {
|
Daniel@0
|
148 return widget.$input.autocomplete("widget").is(":visible");
|
Daniel@0
|
149 } else {
|
Daniel@0
|
150 return false;
|
Daniel@0
|
151 }
|
Daniel@0
|
152 },
|
Daniel@0
|
153
|
Daniel@0
|
154 _destroy : function() {
|
Daniel@0
|
155 this.$input.removeData();
|
Daniel@0
|
156 },
|
Daniel@0
|
157
|
Daniel@0
|
158 _setOption: function (key, value) {
|
Daniel@0
|
159 var widget = this;
|
Daniel@0
|
160
|
Daniel@0
|
161 // Check if such option exists, throw an error if not
|
Daniel@0
|
162 if (!widget.options.hasOwnProperty(key)) {
|
Daniel@0
|
163 throw "Option " + key + " does not exist";
|
Daniel@0
|
164 }
|
Daniel@0
|
165
|
Daniel@0
|
166 // Check if value matches what it was, do nothing if yes
|
Daniel@0
|
167 if (value === widget.options[key] || (_.isArray(value) && _.isEqual(value, widget.options[key]))) {
|
Daniel@0
|
168 return;
|
Daniel@0
|
169 }
|
Daniel@0
|
170
|
Daniel@0
|
171 // Save old option value
|
Daniel@0
|
172 var prev = widget.options[key];
|
Daniel@0
|
173
|
Daniel@0
|
174 // Apply the option
|
Daniel@0
|
175 this._super(key, value);
|
Daniel@0
|
176
|
Daniel@0
|
177 // Call corresponding update method depending on the option key
|
Daniel@0
|
178 switch (key) {
|
Daniel@0
|
179
|
Daniel@0
|
180 case "value":
|
Daniel@0
|
181 this._applyValue();
|
Daniel@0
|
182 break;
|
Daniel@0
|
183
|
Daniel@0
|
184 case "baseValue":
|
Daniel@0
|
185 this._updateStatus();
|
Daniel@0
|
186 break;
|
Daniel@0
|
187
|
Daniel@0
|
188 case "autocompleteSuggestions":
|
Daniel@0
|
189 this._applyAutocompleteSuggestions();
|
Daniel@0
|
190 break;
|
Daniel@0
|
191 }
|
Daniel@0
|
192 widget._trigger("change" + key.toLowerCase(), null, {newValue: value, prevValue: prev});
|
Daniel@0
|
193 },
|
Daniel@0
|
194
|
Daniel@0
|
195 _realValueToInputValue: function(realValue) {
|
Daniel@0
|
196 var widget = this;
|
Daniel@0
|
197
|
Daniel@0
|
198 var value = realValue;
|
Daniel@0
|
199 var valueInOptionsIsString = _.isString(value);
|
Daniel@0
|
200 var trimmedValueInOptions = valueInOptionsIsString ? _.str.trim(value) : value;
|
Daniel@0
|
201 var trimmedCharsOnLeft = valueInOptionsIsString ? value.indexOf(trimmedValueInOptions) : null;
|
Daniel@0
|
202 var trimmedCharsOnRight = valueInOptionsIsString ? value.length - trimmedValueInOptions.length - trimmedCharsOnLeft : null;
|
Daniel@0
|
203
|
Daniel@0
|
204 if (valueInOptionsIsString && widget.options.autocompleteSuggestions && widget.options.autocompleteSuggestions[trimmedValueInOptions] !== undefined) {
|
Daniel@0
|
205 value = value.substring(0, trimmedCharsOnLeft) + widget.options.autocompleteSuggestions[trimmedValueInOptions] + value.substring(value.length - trimmedCharsOnRight);
|
Daniel@0
|
206 }
|
Daniel@0
|
207 if (valueInOptionsIsString && value.length >= 4 && value.substring(0, 2) == "__" && value.substring(value.length - 2) == "__") {
|
Daniel@0
|
208 value = value.substring(2, value.length - 2);
|
Daniel@0
|
209 }
|
Daniel@0
|
210
|
Daniel@0
|
211 return value;
|
Daniel@0
|
212
|
Daniel@0
|
213 },
|
Daniel@0
|
214
|
Daniel@0
|
215 _inputValueToRealValue: function(inputValue) {
|
Daniel@0
|
216 var widget = this;
|
Daniel@0
|
217
|
Daniel@0
|
218 var value = inputValue;
|
Daniel@0
|
219 var trimmedValueInInput = _.str.trim(value);
|
Daniel@0
|
220 var trimmedCharsOnLeft = value.indexOf(trimmedValueInInput);
|
Daniel@0
|
221 var trimmedCharsOnRight = value.length - trimmedValueInInput.length - trimmedCharsOnLeft;
|
Daniel@0
|
222
|
Daniel@0
|
223 if (widget.options.autocompleteSuggestions && widget.options.autocompleteSuggestions[trimmedValueInInput] && widget.options.autocompleteSuggestions[trimmedValueInInput] !== trimmedValueInInput) {
|
Daniel@0
|
224 value = "__" + value + "__";
|
Daniel@0
|
225 }
|
Daniel@0
|
226 if (widget._invertedAutocompleteSuggestions && widget._invertedAutocompleteSuggestions[trimmedValueInInput] !== undefined) {
|
Daniel@0
|
227 value = value.substring(0, trimmedCharsOnLeft) + widget._invertedAutocompleteSuggestions[trimmedValueInInput] + value.substring(value.length - trimmedCharsOnRight);
|
Daniel@0
|
228 }
|
Daniel@0
|
229
|
Daniel@0
|
230 return value;
|
Daniel@0
|
231 },
|
Daniel@0
|
232
|
Daniel@0
|
233 _applyValue: function() {
|
Daniel@0
|
234 var widget = this;
|
Daniel@0
|
235
|
Daniel@0
|
236 var inputValue = widget._realValueToInputValue(widget.options.value);
|
Daniel@0
|
237
|
Daniel@0
|
238 if (inputValue !== widget.$input.val() && !widget._doNotUpdateInputOnValueChange) {
|
Daniel@0
|
239 widget.$input.val(inputValue);
|
Daniel@0
|
240 }
|
Daniel@0
|
241 widget._updateStatus();
|
Daniel@0
|
242 },
|
Daniel@0
|
243
|
Daniel@0
|
244 _updateStatus: function() {
|
Daniel@0
|
245 var widget = this;
|
Daniel@0
|
246 widget.$element.toggleClass("cgpma__textfield_status_modified", widget.options.baseValue !== widget.options.value);
|
Daniel@0
|
247 },
|
Daniel@0
|
248
|
Daniel@0
|
249 __handleInputChange: function() {
|
Daniel@0
|
250
|
Daniel@0
|
251 // this can be an instance of the widget or the DOM node
|
Daniel@0
|
252 var $input = null;
|
Daniel@0
|
253 if (this.element) {
|
Daniel@0
|
254 $input = this.$input;
|
Daniel@0
|
255 } else {
|
Daniel@0
|
256 $input = $(this);
|
Daniel@0
|
257 }
|
Daniel@0
|
258 var widget = $input.data("widget");
|
Daniel@0
|
259 var valueInInput = $input.val();
|
Daniel@0
|
260
|
Daniel@0
|
261 if (arguments[1] && arguments[1].item) {
|
Daniel@0
|
262 valueInInput = arguments[1].item.value;
|
Daniel@0
|
263 }
|
Daniel@0
|
264
|
Daniel@0
|
265 var realValue = widget._inputValueToRealValue(valueInInput);
|
Daniel@0
|
266
|
Daniel@0
|
267 widget._setOption("value", realValue);
|
Daniel@0
|
268 },
|
Daniel@0
|
269
|
Daniel@0
|
270 __handleInputKeyDown: function(event) {
|
Daniel@0
|
271 var $this = $(this);
|
Daniel@0
|
272 var widget = $this.data("widget");
|
Daniel@0
|
273 if (event.keyCode == 13) {
|
Daniel@0
|
274 //widget._lastInputChangeWasCausedByAutocomplete = widget.$nput.autocomplete("widget").is(":visible");
|
Daniel@0
|
275 }
|
Daniel@0
|
276 if (event.keyCode == 38 || event.keyCode == 40) { // up or down
|
Daniel@0
|
277 if (widget.$input.data("ui-autocomplete")) {
|
Daniel@0
|
278 if (!widget._isAutocompleteVisible()) {
|
Daniel@0
|
279 widget.$input.autocomplete("search", "");
|
Daniel@0
|
280 } else {
|
Daniel@0
|
281 widget._doNotUpdateInputOnValueChange = true;
|
Daniel@0
|
282 widget._setOption("value", widget._inputValueToRealValue(widget.$input.val()));
|
Daniel@0
|
283 delete widget._doNotUpdateInputOnValueChange;
|
Daniel@0
|
284 }
|
Daniel@0
|
285 }
|
Daniel@0
|
286 }
|
Daniel@0
|
287 },
|
Daniel@0
|
288
|
Daniel@0
|
289 __handleInputKeyUp: function(event) {
|
Daniel@0
|
290 var $this = $(this);
|
Daniel@0
|
291 var widget = $this.data("widget");
|
Daniel@0
|
292 if (event.keyCode == 13) {
|
Daniel@0
|
293 if (widget._lastInputChangeWasCausedByAutocomplete) {
|
Daniel@0
|
294 widget._lastInputChangeWasCausedByAutocomplete = false;
|
Daniel@0
|
295 } else {
|
Daniel@0
|
296 widget._trigger("apply");
|
Daniel@0
|
297 if (widget.$input.data("ui-autocomplete")) {
|
Daniel@0
|
298 widget.$input.autocomplete("close");
|
Daniel@0
|
299 }
|
Daniel@0
|
300 }
|
Daniel@0
|
301 }
|
Daniel@0
|
302 if (event.keyCode == 27) {
|
Daniel@0
|
303 if (!widget._isAutocompleteVisible()) {
|
Daniel@0
|
304 widget._trigger("discard");
|
Daniel@0
|
305 }
|
Daniel@0
|
306 event.preventDefault();
|
Daniel@0
|
307 }
|
Daniel@0
|
308 },
|
Daniel@0
|
309
|
Daniel@0
|
310 __handleAutocompleteOpen: function(event) {
|
Daniel@0
|
311 var widget = this;
|
Daniel@0
|
312 widget.$element.addClass("cgpma__textfield_autocomplete-is-open");
|
Daniel@0
|
313 },
|
Daniel@0
|
314
|
Daniel@0
|
315 __handleAutocompleteClose: function(event) {
|
Daniel@0
|
316 var widget = this;
|
Daniel@0
|
317 widget.$element.removeClass("cgpma__textfield_autocomplete-is-open");
|
Daniel@0
|
318 },
|
Daniel@0
|
319
|
Daniel@0
|
320 __handleInputClick: function() {
|
Daniel@0
|
321 var $this = $(this);
|
Daniel@0
|
322 var widget = $this.data("widget");
|
Daniel@0
|
323 if (widget.$input.data("ui-autocomplete")) {
|
Daniel@0
|
324 widget.$input.autocomplete("search", "");
|
Daniel@0
|
325 }
|
Daniel@0
|
326 }
|
Daniel@0
|
327
|
Daniel@0
|
328 });
|
Daniel@0
|
329 })(jQuery); |