-
Notifications
You must be signed in to change notification settings - Fork 2
/
jquery.ui.combobox.js
219 lines (205 loc) · 9.5 KB
/
jquery.ui.combobox.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
/*
* Combobox widget. This widget extends the jQuery UI autocomplete widget.
*
* usage: $("#select_element").combox();
* $("#input_element").combox( {minLength: 2, delay: 200, source: anArray} );
*
* For select elements, the source list is automatically generated from the option elements.
*
* To configure a label decorator (i.e. "[admin]"), set the decoratorField when you
* initialize the combobox. The field corresponds to the object field, if the source is
* an array of objects. Or it's the attribute in <option> if the source is a select drop-down menu.
*/
(function ($) {
$.widget("ui.combobox", $.ui.autocomplete,
{
options: {
/* override default values here */
minLength: 2,
/* the argument to pass to ajax to get the complete list */
ajaxGetAll: { get: "all" },
/* you can specify the field to use as a label decorator,
* it's appended to the end of the label and is excluded
* from pattern matching.
*/
decoratorField: null
},
_create: function () {
$.ui.autocomplete.prototype._create.call(this);
var input = this.element;
input.addClass("ui-widget ui-widget-content ui-corner-left")
.click(function () { this.select(); });
this.button = $("<button type='button'>выбор...</button>")
.attr("tabIndex", -1)
.attr("title", "Выбрать элемент")
.insertAfter(input)
.button({
disabled: true, // to be enabled when the menu is ready.
icons: { primary: "ui-icon-triangle-1-s" },
text: true
})
.removeClass("ui-corner-all")
.addClass("ui-corner-right ui-button-icon")
.css("float", "left").css("font-size", "0.8em")
.click(function (event) {
// when user clicks the show all button, we display the cached full menu
var data = input.data("ui-combobox");
clearTimeout(data.closing);
if (!input.isFullMenu) {
data._swapMenu();
}
/* input/select that are initially hidden (display=none, i.e. second level menus),
will not have position cordinates until they are visible. */
input.combobox("widget").css("display", "inline")
.position($.extend({ of: input },
data.options.position
));
input.focus();
data._trigger("open");
// containers such as jquery-ui dialog box will adjust it's zIndex to overlay above other elements.
// this becomes a problem if the combobox is inside of a dialog box, the full drop down will show
// under the dialog box.
if (input.combobox("widget").zIndex() <= input.parent().zIndex()) {
input.combobox("widget").zIndex(input.parent().zIndex() + 1);
}
});
/* to better handle large lists, put in a queue and process sequentially */
$(document).queue(function () {
var data = input.data("ui-combobox");
if ($.isArray(data.options.source)) {
$.ui.combobox.prototype._renderFullMenu.call(data, data.options.source);
} else if (typeof data.options.source === "string") {
$.getJSON(data.options.source, data.options.ajaxGetAll, function (source) {
$.ui.combobox.prototype._renderFullMenu.call(data, source);
});
} else {
$.ui.combobox.prototype._renderFullMenu.call(data, data.source());
}
});
},
/* initialize the full list of items, this menu will be reused whenever the user clicks the show all button */
_renderFullMenu: function (source) {
var self = this,
input = this.element,
ul = input.data("ui-combobox").menu.element,
lis = [];
source = this._normalize(source);
input.data("ui-combobox").menuAll = input.data("ui-combobox").menu.element.clone(true).appendTo("body")[0];
for (var i = 0; i < source.length; i++) {
var item = source[i],
label = item.label;
if (this.options.decoratorField != null) {
var d = item[this.options.decoratorField] || (item.option && $(item.option).attr(this.options.decoratorField));
if (d != undefined)
label = label + " " + d;
}
lis[i] = "<li class=\"ui-menu-item\" role=\"menuitem\"><a class=\"ui-corner-all\" tabindex=\"-1\">" + label + "</a></li>";
}
ul[0].innerHTML = lis.join("");
this._resizeMenu();
var items = $("li", ul).on("mouseover", "mouseout", function (event) {
if (event.type == "mouseover") {
self.menu.focus(event, $(this));
} else {
self.menu.blur();
}
});
for (var i = 0; i < items.length; i++) {
$(items[i]).data("ui-autocomplete-item", source[i]);
}
input.isFullMenu = true;
this._swapMenu();
// full menu has been rendered, now we can enable the show all button.
self.button.button("enable");
setTimeout(function () {
$(document).dequeue();
}, 0);
},
/* overwrite. make the matching string bold and added label decorator */
_renderItem: function (ul, item) {
var label = item.label.replace(new RegExp(
"(?![^&;]+;)(?!<[^<>]*)(" + $.ui.autocomplete.escapeRegex(this.term) +
")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
if (this.options.decoratorField != null) {
var d = item[this.options.decoratorField] || (item.option && $(item.option).attr(this.options.decoratorField));
if (d != undefined) {
label = label + " " + d;
}
}
return $("<li></li>")
.data("ui-autocomplete-item", item)
.append("<a>" + label + "</a>")
.appendTo(ul);
},
close: function () {
if (this.element.isFullMenu) {
this._swapMenu();
}
// super()
$.ui.autocomplete.prototype.close.call(this);
},
/* overwrite. to cleanup additional stuff that was added */
destroy: function () {
if (this.element.is("SELECT")) {
this.input.removeData("ui-combobox", "menuAll");
this.input.remove();
this.element.removeData().show();
return;
}
// super()
$.ui.autocomplete.prototype.destroy.call(this);
// clean up new stuff
this.element.removeClass("ui-widget ui-widget-content ui-corner-left");
this.button.remove();
},
/* overwrite. to swap out and preserve the full menu */
search: function (value, event) {
var input = this.element;
if (input.isFullMenu) {
this._swapMenu();
}
// super()
$.ui.autocomplete.prototype.search.call(this, value, event);
},
_change: function (event) {
if (!this.selectedItem) {
var matcher = new RegExp("^" + $.ui.autocomplete.escapeRegex(this.element.val()) + "$", "i"),
match = $.grep(this.options.source, function (value) {
return matcher.test(value.label);
});
if (match.length) {
if (match[0].option != undefined) match[0].option.selected = true;
} else {
// remove invalid value, as it didn't match anything
if (this.options.selectElement) {
var firstItem = this.options.selectElement.children("option:first");
this.element.val(firstItem.text());
firstItem.prop("selected", true);
} else {
this.element.val("");
}
$(event.target).data("ui-combobox").previous = null; // this will force a change event
}
}
// super()
$.ui.autocomplete.prototype._change.call(this, event);
},
_swapMenu: function () {
var input = this.element,
data = input.data("ui-combobox"),
tmp = data.menuAll;
data.menuAll = data.menu.element.hide()[0];
data.menu.element[0] = tmp;
input.isFullMenu = !input.isFullMenu;
},
inputbox: function () {
if (this.element.is("SELECT")) {
return this.input;
} else {
return this.element;
}
}
}
);
})(jQuery);
var ggg;