diff --git a/awesomplete.js b/awesomplete.js index 950a5ace..545086ed 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -22,6 +22,7 @@ var _ = function (input, o) { minChars: 2, maxItems: 10, autoFirst: false, + data: _.DATA, filter: _.FILTER_CONTAINS, sort: _.SORT_BYLENGTH, item: _.ITEM, @@ -117,9 +118,18 @@ _.prototype = { list = $(list); if (list && list.children) { - this._list = slice.apply(list.children).map(function (el) { - return el.textContent.trim(); + var items = []; + slice.apply(list.children).forEach(function (el) { + if (!el.disabled) { + var text = el.textContent.trim(); + var value = el.value || text; + var label = el.label || text; + if (value !== "") { + items.push({ label: label, value: value }); + } + } }); + this._list = items; } } @@ -184,17 +194,21 @@ _.prototype = { }, select: function (selected, origin) { - selected = selected || this.ul.children[this.index]; + if (selected) { + this.index = $.siblingIndex(selected); + } else { + selected = this.ul.children[this.index]; + } if (selected) { var allowed = $.fire(this.input, "awesomplete-select", { - text: selected.textContent, - data: this.suggestions[$.siblingIndex(selected)], + text: this.suggestions[this.index], + data: this.suggestions[this.index], origin: origin || selected }); if (allowed) { - this.replace(selected.textContent); + this.replace(this.suggestions[this.index]); this.close(); $.fire(this.input, "awesomplete-selectcomplete"); } @@ -211,6 +225,9 @@ _.prototype = { this.ul.innerHTML = ""; this.suggestions = this._list + .map(function(item) { + return new Suggestion(me.data(item, value)); + }) .filter(function(item) { return me.filter(item, value); }) @@ -262,11 +279,28 @@ _.ITEM = function (text, input) { }; _.REPLACE = function (text) { - this.input.value = text; + this.input.value = text.value; }; +_.DATA = function (item/*, input*/) { return item; }; + // Private functions +function Suggestion(data) { + var o = Array.isArray(data) + ? { label: data[0], value: data[1] } + : typeof data === "object" && "label" in data && "value" in data ? data : { label: data, value: data }; + + this.label = o.label || o.value; + this.value = o.value; +} +Object.defineProperty(Suggestion.prototype = Object.create(String.prototype), "length", { + get: function() { return this.label.length; } +}); +Suggestion.prototype.toString = Suggestion.prototype.valueOf = function () { + return "" + this.label; +}; + function configure(instance, properties, o) { for (var i in properties) { var initial = properties[i], diff --git a/index.html b/index.html index b9ff3412..0dc39172 100644 --- a/index.html +++ b/index.html @@ -125,6 +125,29 @@
Suggestions with different label and value are supported too. The label will be shown in autocompleter and the value will be inserted into the input.
+ +<input id="myinput" />
+ var input = document.getElementById("myinput");
+
+// Show label but insert value into the input:
+new Awesomplete(input, {
+ list: [
+ { label: "Belarus", value: "BY" },
+ { label: "China", value: "CN" },
+ { label: "United States", value: "US" }
+ ]
+});
+
+// Same with arrays:
+new Awesomplete(input, {
+ list: [
+ [ "Belarus", "BY" ],
+ [ "China", "CN" ],
+ [ "United States", "US" ]
+ ]
+});
+
data
label
and value
. This is useful if you have list items in custom format, or want to change list items based on user's input."JavaScript"
{ label: "JavaScript", value: "JS" }
[ "JavaScript", "JS" ]
label
or value
properties, e.g. name
and id
instead, you can do this:
+data: function (item, input) {
+ return { label: item.name, value: item.id };
+}
+You can use any object for label
and value
and it will be converted to String where necessary:
+list: [ new Date("2015-01-01"), ... ]
+Original list items as Date objects will be accessible in filter
, sort
, item
and replace
functions, but by default we'll just see Date objects converted to strings in autocompleter and the same value will be inserted to the input.
+Awesomplete.DATA
: Identity function which just returns the original list item.<input type="email" />
diff --git a/test/api/selectSpec.js b/test/api/selectSpec.js
index 84895b16..9a1214e0 100644
--- a/test/api/selectSpec.js
+++ b/test/api/selectSpec.js
@@ -46,8 +46,8 @@ describe("awesomplete.select", function () {
expect(handler).toHaveBeenCalledWith(
jasmine.objectContaining({
- text: expectedTxt,
- data: expectedTxt,
+ text: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }),
+ data: jasmine.objectContaining({ label: expectedTxt, value: expectedTxt }),
origin: this.selectArgument || this.subject.ul.children[0]
})
);
diff --git a/test/init/listSpec.js b/test/init/listSpec.js
index 6f95ff05..eff78d61 100644
--- a/test/init/listSpec.js
+++ b/test/init/listSpec.js
@@ -23,12 +23,20 @@ describe("Awesomplete list", function () {
it("assigns from element specified by selector", function () {
this.subject.list = "#data-list";
- expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "Data", value: "Data" },
+ { label: "List", value: "List" }
+ ]);
});
it("assigns from element", function () {
this.subject.list = $("#data-list");
- expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "Data", value: "Data" },
+ { label: "List", value: "List" }
+ ]);
});
it("does not assigns from not found list", function () {
@@ -68,12 +76,20 @@ describe("Awesomplete list", function () {
it("assigns from element specified by selector", function () {
this.options = { list: "#data-list" };
- expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "Data", value: "Data" },
+ { label: "List", value: "List" }
+ ]);
});
it("assigns from list specified by element", function () {
this.options = { list: $("#data-list") };
- expect(this.subject._list).toEqual([ "With", "Data", "List" ]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "Data", value: "Data" },
+ { label: "List", value: "List" }
+ ]);
});
});
@@ -85,14 +101,21 @@ describe("Awesomplete list", function () {
it("assigns from element referenced by selector", function () {
this.element = "#with-data-list";
- expect(this.subject._list).toEqual(["With", "Data", "List"]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "Data", value: "Data" },
+ { label: "List", value: "List" }
+ ]);
});
});
describe("list html attribute", function () {
it("assigns from element referenced by id", function () {
this.element = "#with-list";
- expect(this.subject._list).toEqual(["With", "List"]);
+ expect(this.subject._list).toEqual([
+ { label: "With", value: "With" },
+ { label: "List", value: "List" }
+ ]);
});
});
});
diff --git a/test/init/optionsSpec.js b/test/init/optionsSpec.js
index 40153c1d..fc60997c 100644
--- a/test/init/optionsSpec.js
+++ b/test/init/optionsSpec.js
@@ -19,6 +19,10 @@ describe("Constructor options", function () {
expect(this.subject.autoFirst).toBe(false);
});
+ it("modifies list item with DATA", function () {
+ expect(this.subject.data).toBe(Awesomplete.DATA);
+ });
+
it("filters with FILTER_CONTAINS", function () {
expect(this.subject.filter).toBe(Awesomplete.FILTER_CONTAINS);
});
diff --git a/test/static/dataSpec.js b/test/static/dataSpec.js
new file mode 100644
index 00000000..311c6eb0
--- /dev/null
+++ b/test/static/dataSpec.js
@@ -0,0 +1,19 @@
+describe("Awesomplete.DATA", function () {
+
+ subject(function () { return Awesomplete.DATA(this.item) });
+
+ it("returns original String", function () {
+ this.item = "JavaScript";
+ expect(this.subject).toEqual("JavaScript");
+ });
+
+ it("returns original Object", function () {
+ this.item = { label: "JavaScript", value: "JS" };
+ expect(this.subject).toEqual({ label: "JavaScript", value: "JS" });
+ });
+
+ it("returns original Array", function () {
+ this.item = [ "JavaScript", "JS" ];
+ expect(this.subject).toEqual([ "JavaScript", "JS" ]);
+ });
+});
diff --git a/test/static/replaceSpec.js b/test/static/replaceSpec.js
index 4339ff21..e2eddbae 100644
--- a/test/static/replaceSpec.js
+++ b/test/static/replaceSpec.js
@@ -5,7 +5,7 @@ describe("Awesomplete.REPLACE", function () {
def("instance", function() { return { input: { value: "initial" } } });
it("replaces input value", function () {
- this.subject.call(this.instance, "JavaScript");
+ this.subject.call(this.instance, { value: "JavaScript" });
expect(this.instance.input.value).toBe("JavaScript");
});
});