Daniel@0: "use strict"; Daniel@0: Daniel@0: (function($) { Daniel@0: $.widget("cgpma.textfield", { Daniel@0: Daniel@0: options: { Daniel@0: value: "", Daniel@0: baseValue: "", Daniel@0: autocompleteMaxItemCount: 15, Daniel@0: autocompleteSort: false, Daniel@0: autocompleteSuggestions: null, Daniel@0: autocompleteIsAdvisory: false, Daniel@0: autocompleteCSSClasses: "" Daniel@0: }, Daniel@0: Daniel@0: isFocused: function() { Daniel@0: var widget = this; Daniel@0: return widget.$input.is(":focus"); Daniel@0: }, Daniel@0: Daniel@0: focus: function() { Daniel@0: var widget = this; Daniel@0: return widget.$input.focus(); Daniel@0: }, Daniel@0: Daniel@0: getTextRange: function() { Daniel@0: var widget = this; Daniel@0: return widget.$input.textrange(); Daniel@0: }, Daniel@0: Daniel@0: setTextRange: function(textRange) { Daniel@0: var widget = this; Daniel@0: if (textRange.start != textRange.end) { Daniel@0: return widget.$input.textrange("set", textRange.start, textRange.end); Daniel@0: } Daniel@0: return widget.$input.textrange("setcursor", textRange.position); Daniel@0: }, Daniel@0: Daniel@0: _create : function() { Daniel@0: var widget = this; Daniel@0: widget.$element = this.element; Daniel@0: widget.$input = $.bem.generateElement("input", "cgpma", "textfield-input"); Daniel@0: widget.$input.appendTo(widget.$element); Daniel@0: Daniel@0: widget.$input.data("widget", widget); Daniel@0: Daniel@0: widget._applyAutocompleteSuggestions(); Daniel@0: Daniel@0: widget.$input.bind("input", widget.__handleInputChange); Daniel@0: widget.$input.bind("keydown", widget.__handleInputKeyDown); Daniel@0: widget.$input.bind("keyup", widget.__handleInputKeyUp); Daniel@0: widget.$input.bind("click", widget.__handleInputClick); Daniel@0: Daniel@0: }, Daniel@0: Daniel@0: _applyAutocompleteSuggestions: function() { Daniel@0: var widget = this; Daniel@0: Daniel@0: if (widget.$input.data("ui-autocomplete")) { Daniel@0: widget.$input.autocomplete("destroy"); Daniel@0: } Daniel@0: Daniel@0: var autocompleteSuggestions = widget.options.autocompleteSuggestions; Daniel@0: if (!autocompleteSuggestions) { Daniel@0: return; Daniel@0: delete widget._invertedAutocompleteSuggestions; Daniel@0: } Daniel@0: widget._invertedAutocompleteSuggestions = _.invert(autocompleteSuggestions); Daniel@0: Daniel@0: widget.$input.autocomplete({ Daniel@0: delay : 0, Daniel@0: minLength : 0, Daniel@0: source : $.proxy(widget, "_autocompleteSource") Daniel@0: }); Daniel@0: Daniel@0: widget.$input.autocomplete( Daniel@0: "widget").addClass(widget.options.autocompleteCSSClasses); Daniel@0: Daniel@0: widget._on(widget.$input, { Daniel@0: autocompleteselect : widget.__handleInputChange, Daniel@0: autocompleteopen : widget.__handleAutocompleteOpen, Daniel@0: autocompleteclose : widget.__handleAutocompleteClose, Daniel@0: //autocompletechange : widget.__handleInputChange Daniel@0: }); Daniel@0: }, Daniel@0: Daniel@0: _autocompleteSource: function(request, response) { Daniel@0: var widget = this; Daniel@0: Daniel@0: var matcher = new RegExp($.ui.autocomplete Daniel@0: .escapeRegex(_.str.trim(request.term)), "i"); Daniel@0: Daniel@0: var responseItems = []; Daniel@0: _.each(widget.options.autocompleteSuggestions, function(kindName, kind) { Daniel@0: if (widget.options.autocompleteAlwaysFull || !request.term || matcher.test(kindName)) { Daniel@0: responseItems.push({ Daniel@0: label : kindName, Daniel@0: value : kindName, Daniel@0: }); Daniel@0: }; Daniel@0: }); Daniel@0: Daniel@0: if (responseItems.length == 1 && responseItems[0].label === request.term) { Daniel@0: responseItems = []; Daniel@0: } Daniel@0: if (responseItems.length > widget.options.autocompleteMaxItemCount) { Daniel@0: responseItems = responseItems.slice(0, widget.options.autocompleteMaxItemCount); Daniel@0: } Daniel@0: if (widget.options.autocompleteSort) { Daniel@0: responseItems = _.sortBy(responseItems, "label"); Daniel@0: } Daniel@0: Daniel@0: response(responseItems); Daniel@0: }, Daniel@0: Daniel@0: _createShowAllButton : function() { Daniel@0: return; Daniel@0: var input = this.input, wasOpen = false; Daniel@0: $("").attr("tabIndex", -1).attr("title", Daniel@0: "Show All Items").tooltip().appendTo( Daniel@0: this.wrapper).button({ Daniel@0: icons : { Daniel@0: primary : "ui-icon-triangle-1-s" Daniel@0: }, Daniel@0: text : false Daniel@0: }).removeClass("ui-corner-all").addClass( Daniel@0: "custom-combobox-toggle ui-corner-right") Daniel@0: .mousedown( Daniel@0: function() { Daniel@0: wasOpen = input.autocomplete( Daniel@0: "widget") Daniel@0: .is(":visible"); Daniel@0: }).click(function() { Daniel@0: input.focus(); Daniel@0: // Close if already visible Daniel@0: if (wasOpen) { Daniel@0: return; Daniel@0: } Daniel@0: // Pass empty string as value to search Daniel@0: // for, displaying all results Daniel@0: input.autocomplete("search", ""); Daniel@0: }); Daniel@0: }, Daniel@0: Daniel@0: _isAutocompleteVisible : function() { Daniel@0: var widget = this; Daniel@0: if (widget.$input.data("ui-autocomplete")) { Daniel@0: return widget.$input.autocomplete("widget").is(":visible"); Daniel@0: } else { Daniel@0: return false; Daniel@0: } Daniel@0: }, Daniel@0: Daniel@0: _destroy : function() { Daniel@0: this.$input.removeData(); Daniel@0: }, Daniel@0: Daniel@0: _setOption: function (key, value) { Daniel@0: var widget = this; Daniel@0: Daniel@0: // Check if such option exists, throw an error if not Daniel@0: if (!widget.options.hasOwnProperty(key)) { Daniel@0: throw "Option " + key + " does not exist"; Daniel@0: } Daniel@0: Daniel@0: // Check if value matches what it was, do nothing if yes Daniel@0: if (value === widget.options[key] || (_.isArray(value) && _.isEqual(value, widget.options[key]))) { Daniel@0: return; Daniel@0: } Daniel@0: Daniel@0: // Save old option value Daniel@0: var prev = widget.options[key]; Daniel@0: Daniel@0: // Apply the option Daniel@0: this._super(key, value); Daniel@0: Daniel@0: // Call corresponding update method depending on the option key Daniel@0: switch (key) { Daniel@0: Daniel@0: case "value": Daniel@0: this._applyValue(); Daniel@0: break; Daniel@0: Daniel@0: case "baseValue": Daniel@0: this._updateStatus(); Daniel@0: break; Daniel@0: Daniel@0: case "autocompleteSuggestions": Daniel@0: this._applyAutocompleteSuggestions(); Daniel@0: break; Daniel@0: } Daniel@0: widget._trigger("change" + key.toLowerCase(), null, {newValue: value, prevValue: prev}); Daniel@0: }, Daniel@0: Daniel@0: _realValueToInputValue: function(realValue) { Daniel@0: var widget = this; Daniel@0: Daniel@0: var value = realValue; Daniel@0: var valueInOptionsIsString = _.isString(value); Daniel@0: var trimmedValueInOptions = valueInOptionsIsString ? _.str.trim(value) : value; Daniel@0: var trimmedCharsOnLeft = valueInOptionsIsString ? value.indexOf(trimmedValueInOptions) : null; Daniel@0: var trimmedCharsOnRight = valueInOptionsIsString ? value.length - trimmedValueInOptions.length - trimmedCharsOnLeft : null; Daniel@0: Daniel@0: if (valueInOptionsIsString && widget.options.autocompleteSuggestions && widget.options.autocompleteSuggestions[trimmedValueInOptions] !== undefined) { Daniel@0: value = value.substring(0, trimmedCharsOnLeft) + widget.options.autocompleteSuggestions[trimmedValueInOptions] + value.substring(value.length - trimmedCharsOnRight); Daniel@0: } Daniel@0: if (valueInOptionsIsString && value.length >= 4 && value.substring(0, 2) == "__" && value.substring(value.length - 2) == "__") { Daniel@0: value = value.substring(2, value.length - 2); Daniel@0: } Daniel@0: Daniel@0: return value; Daniel@0: Daniel@0: }, Daniel@0: Daniel@0: _inputValueToRealValue: function(inputValue) { Daniel@0: var widget = this; Daniel@0: Daniel@0: var value = inputValue; Daniel@0: var trimmedValueInInput = _.str.trim(value); Daniel@0: var trimmedCharsOnLeft = value.indexOf(trimmedValueInInput); Daniel@0: var trimmedCharsOnRight = value.length - trimmedValueInInput.length - trimmedCharsOnLeft; Daniel@0: Daniel@0: if (widget.options.autocompleteSuggestions && widget.options.autocompleteSuggestions[trimmedValueInInput] && widget.options.autocompleteSuggestions[trimmedValueInInput] !== trimmedValueInInput) { Daniel@0: value = "__" + value + "__"; Daniel@0: } Daniel@0: if (widget._invertedAutocompleteSuggestions && widget._invertedAutocompleteSuggestions[trimmedValueInInput] !== undefined) { Daniel@0: value = value.substring(0, trimmedCharsOnLeft) + widget._invertedAutocompleteSuggestions[trimmedValueInInput] + value.substring(value.length - trimmedCharsOnRight); Daniel@0: } Daniel@0: Daniel@0: return value; Daniel@0: }, Daniel@0: Daniel@0: _applyValue: function() { Daniel@0: var widget = this; Daniel@0: Daniel@0: var inputValue = widget._realValueToInputValue(widget.options.value); Daniel@0: Daniel@0: if (inputValue !== widget.$input.val() && !widget._doNotUpdateInputOnValueChange) { Daniel@0: widget.$input.val(inputValue); Daniel@0: } Daniel@0: widget._updateStatus(); Daniel@0: }, Daniel@0: Daniel@0: _updateStatus: function() { Daniel@0: var widget = this; Daniel@0: widget.$element.toggleClass("cgpma__textfield_status_modified", widget.options.baseValue !== widget.options.value); Daniel@0: }, Daniel@0: Daniel@0: __handleInputChange: function() { Daniel@0: Daniel@0: // this can be an instance of the widget or the DOM node Daniel@0: var $input = null; Daniel@0: if (this.element) { Daniel@0: $input = this.$input; Daniel@0: } else { Daniel@0: $input = $(this); Daniel@0: } Daniel@0: var widget = $input.data("widget"); Daniel@0: var valueInInput = $input.val(); Daniel@0: Daniel@0: if (arguments[1] && arguments[1].item) { Daniel@0: valueInInput = arguments[1].item.value; Daniel@0: } Daniel@0: Daniel@0: var realValue = widget._inputValueToRealValue(valueInInput); Daniel@0: Daniel@0: widget._setOption("value", realValue); Daniel@0: }, Daniel@0: Daniel@0: __handleInputKeyDown: function(event) { Daniel@0: var $this = $(this); Daniel@0: var widget = $this.data("widget"); Daniel@0: if (event.keyCode == 13) { Daniel@0: //widget._lastInputChangeWasCausedByAutocomplete = widget.$nput.autocomplete("widget").is(":visible"); Daniel@0: } Daniel@0: if (event.keyCode == 38 || event.keyCode == 40) { // up or down Daniel@0: if (widget.$input.data("ui-autocomplete")) { Daniel@0: if (!widget._isAutocompleteVisible()) { Daniel@0: widget.$input.autocomplete("search", ""); Daniel@0: } else { Daniel@0: widget._doNotUpdateInputOnValueChange = true; Daniel@0: widget._setOption("value", widget._inputValueToRealValue(widget.$input.val())); Daniel@0: delete widget._doNotUpdateInputOnValueChange; Daniel@0: } Daniel@0: } Daniel@0: } Daniel@0: }, Daniel@0: Daniel@0: __handleInputKeyUp: function(event) { Daniel@0: var $this = $(this); Daniel@0: var widget = $this.data("widget"); Daniel@0: if (event.keyCode == 13) { Daniel@0: if (widget._lastInputChangeWasCausedByAutocomplete) { Daniel@0: widget._lastInputChangeWasCausedByAutocomplete = false; Daniel@0: } else { Daniel@0: widget._trigger("apply"); Daniel@0: if (widget.$input.data("ui-autocomplete")) { Daniel@0: widget.$input.autocomplete("close"); Daniel@0: } Daniel@0: } Daniel@0: } Daniel@0: if (event.keyCode == 27) { Daniel@0: if (!widget._isAutocompleteVisible()) { Daniel@0: widget._trigger("discard"); Daniel@0: } Daniel@0: event.preventDefault(); Daniel@0: } Daniel@0: }, Daniel@0: Daniel@0: __handleAutocompleteOpen: function(event) { Daniel@0: var widget = this; Daniel@0: widget.$element.addClass("cgpma__textfield_autocomplete-is-open"); Daniel@0: }, Daniel@0: Daniel@0: __handleAutocompleteClose: function(event) { Daniel@0: var widget = this; Daniel@0: widget.$element.removeClass("cgpma__textfield_autocomplete-is-open"); Daniel@0: }, Daniel@0: Daniel@0: __handleInputClick: function() { Daniel@0: var $this = $(this); Daniel@0: var widget = $this.data("widget"); Daniel@0: if (widget.$input.data("ui-autocomplete")) { Daniel@0: widget.$input.autocomplete("search", ""); Daniel@0: } Daniel@0: } Daniel@0: Daniel@0: }); Daniel@0: })(jQuery);