Skip to content

Commit 9d14034

Browse files
author
Abadi Kurniawan
committed
Add knockout-jqAutocomplete 0.4.3
1 parent a0cffc9 commit 9d14034

File tree

4 files changed

+237
-0
lines changed

4 files changed

+237
-0
lines changed

knockout-jqAutocomplete/CHANGES.htm

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
<ol>
2+
<li>
3+
<b>0.4.3 - <em>May 10, 2015</em></b>
4+
<ul>
5+
<li>use correct path for requiring autocomplete via AMD</li>
6+
</ul>
7+
</li>
8+
</ol>

knockout-jqAutocomplete/LICENSE.htm

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<p>Licensed under the <a href="http://www.opensource.org/licenses/mit-license.php">MIT license</a></p>
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<dotnetnuke type="Package" version="5.0">
2+
<packages>
3+
<package name="knockout-jqAutocomplete" type="JavaScript_Library" version="0.4.3">
4+
<friendlyName>knockout-jqAutocomplete</friendlyName>
5+
<description>knockout-jqAutocomplete is a Knockout.js plugin designed to work with jQuery UI's autocomplete widget.</description>
6+
<owner>
7+
<name>Engage Software</name>
8+
<organization>Engage Software</organization>
9+
<url>http://www.engagesoftware.com</url>
10+
<email>support@engagesoftware.com</email>
11+
</owner>
12+
<license src="LICENSE.htm" />
13+
<releaseNotes src="CHANGES.htm" />
14+
<azureCompatible>true</azureCompatible>
15+
<dependencies>
16+
<dependency type="managedPackage" version="2.2.1">Knockout</dependency>
17+
<dependency type="managedPackage" version="1.10.3">jQuery-UI</dependency>
18+
</dependencies>
19+
<components>
20+
<component type="JavaScript_Library">
21+
<javaScriptLibrary>
22+
<libraryName>knockout-jqAutocomplete</libraryName>
23+
<fileName>knockout-jqAutocomplete.js</fileName>
24+
<objectName>ko.bindingHandlers.jqAuto</objectName>
25+
<preferredScriptLocation>BodyBottom</preferredScriptLocation>
26+
</javaScriptLibrary>
27+
</component>
28+
<component type="JavaScriptFile">
29+
<jsfiles>
30+
<libraryFolderName>knockout-jqAutocomplete</libraryFolderName>
31+
<jsfile>
32+
<name>knockout-jqAutocomplete.js</name>
33+
</jsfile>
34+
</jsfiles>
35+
</component>
36+
</components>
37+
</package>
38+
</packages>
39+
</dotnetnuke>
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
// knockout-jqAutocomplete 0.4.3 | (c) 2015 Ryan Niemeyer | http://www.opensource.org/licenses/mit-license
2+
;(function(factory) {
3+
if (typeof define === "function" && define.amd) {
4+
// AMD anonymous module
5+
define(["knockout", "jquery", "jquery-ui/autocomplete"], factory);
6+
} else {
7+
// No module loader - put directly in global namespace
8+
factory(window.ko, jQuery);
9+
}
10+
})(function(ko, $) {
11+
var JqAuto = function() {
12+
var self = this,
13+
unwrap = ko.utils.unwrapObservable; //support older KO versions that did not have ko.unwrap
14+
15+
//binding's init function
16+
this.init = function(element, valueAccessor, allBindings, data, context) {
17+
var existingSelect, existingChange,
18+
options = unwrap(valueAccessor()),
19+
config = {},
20+
filter = typeof options.filter === "function" ? options.filter : self.defaultFilter;
21+
22+
//extend with global options
23+
ko.utils.extend(config, self.options);
24+
//override with options passed in binding
25+
ko.utils.extend(config, options.options);
26+
27+
//get source from a function (can be remote call)
28+
if (typeof options.source === "function" && !ko.isObservable(options.source)) {
29+
config.source = function(request, response) {
30+
//provide a wrapper to the normal response callback
31+
var callback = function(data) {
32+
self.processOptions(valueAccessor, null, data, request, response);
33+
};
34+
35+
//call the provided function for retrieving data
36+
options.source.call(context.$data, request.term, callback);
37+
};
38+
}
39+
else {
40+
//process local data
41+
config.source = self.processOptions.bind(self, valueAccessor, filter, options.source);
42+
}
43+
44+
//save any passed in select/change calls
45+
existingSelect = typeof config.select === "function" && config.select;
46+
existingChange = typeof config.change === "function" && config.change;
47+
48+
//handle updating the actual value
49+
config.select = function(event, ui) {
50+
if (ui.item && ui.item.actual) {
51+
options.value(ui.item.actual);
52+
53+
if (ko.isWriteableObservable(options.dataValue)) {
54+
options.dataValue(ui.item.data);
55+
}
56+
}
57+
58+
if (existingSelect) {
59+
existingSelect.apply(this, arguments);
60+
}
61+
};
62+
63+
//user made a change without selecting a value from the list
64+
config.change = function(event, ui) {
65+
if (!ui.item || !ui.item.actual) {
66+
options.value(event.target && event.target.value);
67+
68+
if (ko.isWriteableObservable(options.dataValue)) {
69+
options.dataValue(null);
70+
}
71+
}
72+
73+
if (existingChange) {
74+
existingChange.apply(this, arguments);
75+
}
76+
};
77+
78+
//initialize the widget
79+
var widget = $(element).autocomplete(config).data("ui-autocomplete");
80+
81+
//render a template for the items
82+
if (options.template) {
83+
widget._renderItem = self.renderItem.bind(self, options.template, context);
84+
}
85+
86+
//destroy the widget if KO removes the element
87+
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
88+
if (widget && typeof widget.destroy === "function") {
89+
widget.destroy();
90+
widget = null;
91+
}
92+
});
93+
};
94+
95+
//the binding's update function. keep value in sync with model
96+
this.update = function(element, valueAccessor) {
97+
var propNames, sources,
98+
options = unwrap(valueAccessor()),
99+
value = unwrap(options && options.value);
100+
101+
if (!value && value !== 0) {
102+
value = "";
103+
}
104+
105+
// find the appropriate value for the input
106+
sources = unwrap(options.source);
107+
propNames = self.getPropertyNames(valueAccessor);
108+
109+
// if there is local data, then try to determine the appropriate value for the input
110+
if ($.isArray(sources) && propNames.value) {
111+
value = ko.utils.arrayFirst(sources, function (opt) {
112+
return opt[propNames.value] == value;
113+
}
114+
) || value;
115+
}
116+
117+
if (propNames.input && value && typeof value === "object") {
118+
element.value = value[propNames.input];
119+
}
120+
else {
121+
element.value = value;
122+
}
123+
};
124+
125+
//if dealing with local data, the default filtering function
126+
this.defaultFilter = function(item, term) {
127+
term = term && term.toLowerCase();
128+
return (item || item === 0) && ko.toJSON(item).toLowerCase().indexOf(term) > -1;
129+
};
130+
131+
//filter/map options to be in a format that autocomplete requires
132+
this.processOptions = function(valueAccessor, filter, data, request, response) {
133+
var item, index, length,
134+
items = unwrap(data) || [],
135+
results = [],
136+
props = this.getPropertyNames(valueAccessor);
137+
138+
//filter/map items
139+
for (index = 0, length = items.length; index < length; index++) {
140+
item = items[index];
141+
142+
if (!filter || filter(item, request.term)) {
143+
results.push({
144+
label: props.label ? item[props.label] : item.toString(),
145+
value: props.input ? item[props.input] : item.toString(),
146+
actual: props.value ? item[props.value] : item,
147+
data: item
148+
});
149+
}
150+
}
151+
152+
//call autocomplete callback to display list
153+
response(results);
154+
};
155+
156+
//if specified, use a template to render an item
157+
this.renderItem = function(templateName, context, ul, item) {
158+
var $li = $("<li></li>").appendTo(ul),
159+
itemContext = context.createChildContext(item.data);
160+
161+
//apply the template binding
162+
ko.applyBindingsToNode($li[0], { template: templateName }, itemContext);
163+
164+
//clean up
165+
$li.one("remove", ko.cleanNode.bind(ko, $li[0]));
166+
167+
return $li;
168+
};
169+
170+
//retrieve the property names to use for the label, input, and value
171+
this.getPropertyNames = function(valueAccessor) {
172+
var options = ko.toJS(valueAccessor());
173+
174+
return {
175+
label: options.labelProp || options.valueProp,
176+
input: options.inputProp || options.labelProp || options.valueProp,
177+
value: options.valueProp
178+
};
179+
};
180+
181+
//default global options passed into autocomplete widget
182+
this.options = {
183+
autoFocus: true,
184+
delay: 50
185+
};
186+
};
187+
188+
ko.bindingHandlers.jqAuto = new JqAuto();
189+
});

0 commit comments

Comments
 (0)