From ab66af4ad4b8d2e0ede346385349d0c27f546e21 Mon Sep 17 00:00:00 2001 From: Herre Groen Date: Mon, 26 Oct 2015 13:26:02 +0100 Subject: [PATCH 1/5] add key value support --- awesomplete.js | 51 +++++++++++++++++++++++++++++++++++--------- index.html | 58 +++++++++++++++++++++++++++++++++----------------- 2 files changed, 79 insertions(+), 30 deletions(-) diff --git a/awesomplete.js b/awesomplete.js index 660b8958..3ef4f023 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -9,6 +9,7 @@ var _ = function (input, o) { var me = this; + var complex = (o.list && o.list.length > 0 && Array.isArray(o.list[0])); // Setup @@ -24,15 +25,8 @@ var _ = function (input, o) { autoFirst: false, filter: _.FILTER_CONTAINS, sort: _.SORT_BYLENGTH, - item: function (text, input) { - return $.create("li", { - innerHTML: text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"), - "aria-selected": "false" - }); - }, - replace: function (text) { - this.input.value = text; - } + item: complex? _.RENDER_OBJ : _.RENDER_STRING, + replace: complex? _.REPLACE_OBJ : _.REPLACE_STRING }, o); this.index = -1; @@ -57,6 +51,16 @@ var _ = function (input, o) { inside: this.container }); + if (complex) { + this.hidden = $.create("input", { + type: "hidden", + name: input.name, + value: input.value, + before: input + }); + input.removeAttribute('name'); + } + // Bind events $.bind(this.input, { @@ -203,7 +207,7 @@ _.prototype = { }); if (!prevented) { - this.replace(selected.textContent); + this.replace(selected); this.close(); $.fire(this.input, "awesomplete-selectcomplete"); } @@ -262,6 +266,30 @@ _.SORT_BYLENGTH = function (a, b) { return a < b? -1 : 1; }; +_.RENDER_STRING = function (text, input) { + return $.create("li", { + innerHTML: text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"), + "aria-selected": "false" + }); +}; + +_.RENDER_OBJ = function (obj, input) { + return $.create("li", { + innerHTML: obj[1].replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"), + "aria-selected": "false", + "data-id": obj[0] + }); +}; + +_.REPLACE_STRING = function (li) { + this.input.value = li.textContent; +}; + +_.REPLACE_OBJ = function (li) { + this.input.value = li.textContent; + this.hidden.value = li.getAttribute("data-id"); +}; + // Private functions function configure(properties, o) { @@ -314,6 +342,9 @@ $.create = function(tag, o) { ref.parentNode.insertBefore(element, ref); element.appendChild(ref); } + else if (i === "before") { + $(val).parentElement.insertBefore(element, $(val)); + } else if (i in element) { element[i] = val; } diff --git a/index.html b/index.html index 07108454..fba4602c 100644 --- a/index.html +++ b/index.html @@ -115,6 +115,16 @@

Basic usage

list: ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"] }); +

We can also use an array of arrays to map strings to ids.

+ +

This will create a hidden input with the same name as the original input, removing it from the original:

+ +
<input id="myinput" name="lang" />
+
var input = document.getElementById("myinput");
+new Awesomplete(input, {
+	list: [[1, "Ada"], [2, "Java"], [3, "JavaScript"], [4, "Brainfuck"], [5, "LOLCODE"], [6, "Node.js"], [7, "Ruby on Rails"]]
+});
+

We can even set it (or override it) later and it will just work:

<input id="myinput" />
@@ -124,21 +134,21 @@

Basic usage

/* ...more code... */ awesomplete.list = ["Ada", "Java", "JavaScript", "Brainfuck", "LOLCODE", "Node.js", "Ruby on Rails"]; - +

Customize

- +

All settings discussed in this section are settable via either a data- attribute on the <input> element or a JS property on the second argument of the Awesomplete constructor, like so:

- +
new Awesomplete(inputReference, {
 	minChars: 3,
 	maxItems: 15,
 	...
 });

You can of course combine both HTML attributes and JS properties. In case of conflict (e.g. you’ve specified both a data-minchars on the text field and a minChars JS property, the HTML attribute wins. You can also use the JS properties to change a parameter after the object has been created, in which case the change will apply even if there is a conflicting HTML attribute.

- + @@ -191,7 +201,7 @@

Customize

Extend

The following JS properties do not have equivalent HTML attributes, because their values are functions. They allow you to completely change the way Awesomplete works:

- +
@@ -227,11 +237,19 @@

Extend

- + -
replace Controls how the user’s selection replaces the user’s input. For example, this is useful if you want the selection to only partially replace the user’s input.Function that takes one parameter, the text of the selected option, and is responsible for replacing the current input value with it. + Function that takes one parameter, the li element of the selected option, and is responsible for replacing the current input value with it. + + For simple input: +
function (li) {
+						this.input.value = li.textContent;
+					}
+ For complex input (strings mapped to ids): +
function (li) {
+						this.input.value = li.textContent;
+						this.hidden.value = li.getAttribute('data-id');
+					}
function (text) {
-	this.input.value = value;
-}
@@ -239,9 +257,9 @@

Extend

Events

- +

Custom events are thrown in several places and are often cancellable. To avoid conflicts, all custom events are prefixed with awesomplete-.

- + @@ -282,9 +300,9 @@

Events

API

- +

There are several methods on every Awesomplete instance that you can call to customize behavior:

- +
@@ -328,7 +346,7 @@

API

Advanced Examples

These examples show how powerful Awesomplete’s minimal API can be.

- +

E-mail autocomplete

@@ -337,7 +355,7 @@

E-mail autocomplete

list: ["@aol.com", "@att.net", "@comcast.net", "@facebook.com", "@gmail.com", "@gmx.com", "@googlemail.com", "@google.com", "@hotmail.com", "@hotmail.co.uk", "@mac.com", "@me.com", "@mail.com", "@msn.com", "@live.com", "@sbcglobal.net", "@verizon.net", "@yahoo.com", "@yahoo.co.uk"], item: function(text, input){ var newText = input.slice(0, input.indexOf("@")) + text; - + return Awesomplete.$.create("li", { innerHTML: newText.replace(RegExp(input.trim(), "gi"), "$&"), "aria-selected": "false" @@ -348,7 +366,7 @@

E-mail autocomplete

} });
- +

Multiple values

@@ -357,25 +375,25 @@

Multiple values

filter: function(text, input) { return Awesomplete.FILTER_CONTAINS(text, input.match(/[^,]*$/)[0]); }, - + replace: function(text) { var before = this.input.value.match(/^.+,\s*|/)[0]; this.input.value = before + text + ", "; - } + } });

Download!

- + - +

Pull requests are very welcome, as long as you maintain the code style and ask before adding tons of LOCs to the codebase!

From bd370e4c5b63a58a593e41b57a566e985b136d3d Mon Sep 17 00:00:00 2001 From: Herre Groen Date: Wed, 28 Oct 2015 16:09:10 +0100 Subject: [PATCH 2/5] fix issue of no options given and minify --- awesomplete.js | 2 +- awesomplete.min.js | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/awesomplete.js b/awesomplete.js index 4635e3a9..86198db9 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -9,7 +9,7 @@ var _ = function (input, o) { var me = this; - var complex = (o.list && o.list.length > 0 && Array.isArray(o.list[0])); + var complex = (o && o.list && o.list.length > 0 && Array.isArray(o.list[0])); // Setup diff --git a/awesomplete.min.js b/awesomplete.min.js index f5a6f19a..99454619 100644 --- a/awesomplete.min.js +++ b/awesomplete.min.js @@ -1,10 +1,11 @@ // Awesomplete - Lea Verou - MIT license -(function(){function m(a,b){for(var c in a){var g=a[c],e=this.input.getAttribute("data-"+c.toLowerCase());this[c]="number"===typeof g?parseInt(e):!1===g?null!==e:g instanceof Function?null:e;this[c]||0===this[c]||(this[c]=c in b?b[c]:g)}}function d(a,b){return"string"===typeof a?(b||document).querySelector(a):a||null}function h(a,b){return k.call((b||document).querySelectorAll(a))}function l(){h("input.awesomplete").forEach(function(a){new f(a)})}var f=function(a,b){var c=this;this.input=d(a);this.input.setAttribute("autocomplete", -"off");this.input.setAttribute("aria-autocomplete","list");b=b||{};m.call(this,{minChars:2,maxItems:10,autoFirst:!1,filter:f.FILTER_CONTAINS,sort:f.SORT_BYLENGTH,item:function(a,b){return d.create("li",{innerHTML:a.replace(RegExp(d.regExpEscape(b.trim()),"gi"),"$&"),"aria-selected":"false"})},replace:function(a){this.input.value=a}},b);this.index=-1;this.container=d.create("div",{className:"awesomplete",around:a});this.ul=d.create("ul",{hidden:"",inside:this.container});this.status=d.create("span", -{className:"visually-hidden",role:"status","aria-live":"assertive","aria-relevant":"additions",inside:this.container});d.bind(this.input,{input:this.evaluate.bind(this),blur:this.close.bind(this),keydown:function(a){var b=a.keyCode;if(c.opened)if(13===b&&c.selected)a.preventDefault(),c.select();else if(27===b)c.close();else if(38===b||40===b)a.preventDefault(),c[38===b?"previous":"next"]()}});d.bind(this.input.form,{submit:this.close.bind(this)});d.bind(this.ul,{mousedown:function(a){a=a.target;if(a!== -this){for(;a&&!/li/i.test(a.nodeName);)a=a.parentNode;a&&c.select(a)}}});this.input.hasAttribute("list")?(this.list="#"+a.getAttribute("list"),a.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||b.list||[];f.all.push(this)};f.prototype={set list(a){Array.isArray(a)?this._list=a:"string"===typeof a&&-1=this.minChars&&0=this.minChars&&0$&"),"aria-selected":"false"})};d.RENDER_OBJ=function(a,b){return c.create("li",{innerHTML:a[1].replace(RegExp(c.regExpEscape(b.trim()),"gi"),"$&"),"aria-selected":"false","data-id":a[0]})}; +d.REPLACE_STRING=function(a){this.input.value=a.textContent};d.REPLACE_OBJ=function(a){this.input.value=a.textContent;this.hidden.value=a.getAttribute("data-id")};var k=Array.prototype.slice;c.create=function(a,b){var e=document.createElement(a),d;for(d in b){var f=b[d];"inside"===d?c(f).appendChild(e):"around"===d?(f=c(f),f.parentNode.insertBefore(e,f),e.appendChild(f)):"before"===d?c(f).parentElement.insertBefore(e,c(f)):d in e?e[d]=f:e.setAttribute(d,f)}return e};c.bind=function(a,b){if(a)for(var c in b){var d= +b[c];c.split(/\s+/).forEach(function(b){a.addEventListener(b,d)})}};c.fire=function(a,b,c){var d=document.createEvent("HTMLEvents");d.initEvent(b,!0,!0);for(var f in c)d[f]=c[f];a.dispatchEvent(d)};c.regExpEscape=function(a){return a.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")};"undefined"!==typeof Document&&("loading"!==document.readyState?l():document.addEventListener("DOMContentLoaded",l));d.$=c;d.$$=h;"undefined"!==typeof self&&(self.Awesomplete=d);"object"===typeof exports&&(module.exports=d);return d})(); From 8c61b10da05cbb1815ea8ced4fbeecd62ff776b5 Mon Sep 17 00:00:00 2001 From: Herre Groen Date: Wed, 28 Oct 2015 16:49:02 +0100 Subject: [PATCH 3/5] add test --- awesomplete.js | 1 + test/awesompleteSpec.js | 25 ++++++++++++++++++------- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/awesomplete.js b/awesomplete.js index 86198db9..dfe20829 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -274,6 +274,7 @@ _.RENDER_STRING = function (text, input) { }; _.RENDER_OBJ = function (obj, input) { + console.log('rendering obj'); return $.create("li", { innerHTML: obj[1].replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"), "aria-selected": "false", diff --git a/test/awesompleteSpec.js b/test/awesompleteSpec.js index 2b31531d..1286b078 100644 --- a/test/awesompleteSpec.js +++ b/test/awesompleteSpec.js @@ -2,16 +2,17 @@ describe("awesomplete",function(){ var awesompleter; var dummyInput; + var dummyBody; beforeEach(function(){ - var dummyBody = document.createElement('body'); + dummyBody = document.createElement('body'); dummyInput = document.createElement('input'); dummyBody.appendChild(dummyInput); awesompleter = new Awesomplete(dummyInput); }) describe("default object",function(){ - + it("should have an empty list",function(){ expect(awesompleter._list.length).toBe(0); }) @@ -22,7 +23,7 @@ describe("awesomplete",function(){ it("should have 10 max items",function(){ expect(awesompleter.maxItems).toBe(10); - }) + }) }) describe("custom object",function(){ @@ -35,8 +36,18 @@ describe("awesomplete",function(){ awesompleter = new Awesomplete(dummyInput,{minChars:3}); expect(awesompleter.minChars).toBe(3); }) + + it("should support key value pairs", function(){ + awesompleter = new Awesomplete(dummyInput,{list:[[0,'Test0'],[1,'Test1']]}); + //Give browser time to add elements to DOM + setTimeout(function(){ + awesompleter.select(dummyBody.querySelector('[data-id="1"]')); + expect(awesompleter.hidden.value).toBe('1'); + expect(awesompleter.input.value).toBe('Test1'); + }, 1); + }) }) - + }); describe("awesomplete helpers",function(){ @@ -46,7 +57,7 @@ describe("awesomplete helpers",function(){ it("should return null when is called without input",function(){ var null_selector = Awesomplete.$(); expect(null_selector).toBe(null); - }) + }) it("should escape regular expression tokens",function(){ @@ -69,7 +80,7 @@ describe("awesomplete helpers",function(){ it("should have been called with null when called with no args",function(){ expect(Awesomplete.$).toHaveBeenCalledWith(null); - }) + }) }) -}); \ No newline at end of file +}); From df77df871602d37745317307b46aae4d9ea281f5 Mon Sep 17 00:00:00 2001 From: Herre Groen Date: Wed, 28 Oct 2015 17:10:42 +0100 Subject: [PATCH 4/5] fix test --- awesomplete.js | 1 - test/awesompleteSpec.js | 11 +++++------ 2 files changed, 5 insertions(+), 7 deletions(-) diff --git a/awesomplete.js b/awesomplete.js index dfe20829..86198db9 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -274,7 +274,6 @@ _.RENDER_STRING = function (text, input) { }; _.RENDER_OBJ = function (obj, input) { - console.log('rendering obj'); return $.create("li", { innerHTML: obj[1].replace(RegExp($.regExpEscape(input.trim()), "gi"), "$&"), "aria-selected": "false", diff --git a/test/awesompleteSpec.js b/test/awesompleteSpec.js index 1286b078..01694832 100644 --- a/test/awesompleteSpec.js +++ b/test/awesompleteSpec.js @@ -39,12 +39,11 @@ describe("awesomplete",function(){ it("should support key value pairs", function(){ awesompleter = new Awesomplete(dummyInput,{list:[[0,'Test0'],[1,'Test1']]}); - //Give browser time to add elements to DOM - setTimeout(function(){ - awesompleter.select(dummyBody.querySelector('[data-id="1"]')); - expect(awesompleter.hidden.value).toBe('1'); - expect(awesompleter.input.value).toBe('Test1'); - }, 1); + awesompleter.input.value = 'Tes'; + awesompleter.evaluate(); + awesompleter.select(dummyBody.querySelector('[data-id="1"]')); + expect(awesompleter.hidden.value).toBe('1'); + expect(awesompleter.input.value).toBe('Test1'); }) }) From 721f603e7f498e8d4885769f7fbecb93dfd37d19 Mon Sep 17 00:00:00 2001 From: Herre Groen Date: Wed, 13 Jan 2016 16:23:47 +0100 Subject: [PATCH 5/5] keep API the same --- awesomplete.js | 10 +++++----- awesomplete.min.js | 10 +++++----- index.html | 8 ++++---- 3 files changed, 14 insertions(+), 14 deletions(-) diff --git a/awesomplete.js b/awesomplete.js index 913c3327..5b6b2b7a 100644 --- a/awesomplete.js +++ b/awesomplete.js @@ -208,7 +208,7 @@ _.prototype = { }); if (!prevented) { - this.replace(selected); + this.replace(selected.textContent, selected); this.close(); $.fire(this.input, "awesomplete-selectcomplete"); } @@ -282,12 +282,12 @@ _.RENDER_OBJ = function (obj, input) { }); }; -_.REPLACE_STRING = function (li) { - this.input.value = li.textContent; +_.REPLACE_STRING = function (text, li) { + this.input.value = text; }; -_.REPLACE_OBJ = function (li) { - this.input.value = li.textContent; +_.REPLACE_OBJ = function (text, li) { + this.input.value = text; this.hidden.value = li.getAttribute("data-id"); }; diff --git a/awesomplete.min.js b/awesomplete.min.js index 79e42cfb..9e0d021c 100644 --- a/awesomplete.min.js +++ b/awesomplete.min.js @@ -5,8 +5,8 @@ role:"status","aria-live":"assertive","aria-relevant":"additions",inside:this.co {submit:this.close.bind(this)});c.bind(this.ul,{mousedown:function(a){var b=a.target;if(b!==this){for(;b&&!/li/i.test(b.nodeName);)b=b.parentNode;b&&0===a.button&&e.select(b,a)}}});this.input.hasAttribute("list")?(this.list="#"+this.input.getAttribute("list"),this.input.removeAttribute("list")):this.list=this.input.getAttribute("data-list")||b.list||[];d.all.push(this)};d.prototype={set list(a){Array.isArray(a)?this._list=a:"string"===typeof a&&-1=this.minChars&&0$&"),"aria-selected":"false"})};d.RENDER_OBJ=function(a,b){return c.create("li",{innerHTML:a[1].replace(RegExp(c.regExpEscape(b.trim()), -"gi"),"$&"),"aria-selected":"false","data-id":a[0]})};d.REPLACE_STRING=function(a){this.input.value=a.textContent};d.REPLACE_OBJ=function(a){this.input.value=a.textContent;this.hidden.value=a.getAttribute("data-id")};var k=Array.prototype.slice;c.create=function(a,b){var e=document.createElement(a),d;for(d in b){var f=b[d];"inside"===d?c(f).appendChild(e):"around"===d?(f=c(f),f.parentNode.insertBefore(e,f),e.appendChild(f)):"before"===d?(f=c(f),f.parentElement.insertBefore(e,f)):d in -e?e[d]=f:e.setAttribute(d,f)}return e};c.bind=function(a,b){if(a)for(var e in b){var c=b[e];e.split(/\s+/).forEach(function(b){a.addEventListener(b,c)})}};c.fire=function(a,b,c){var d=document.createEvent("HTMLEvents");d.initEvent(b,!0,!0);for(var f in c)d[f]=c[f];a.dispatchEvent(d)};c.regExpEscape=function(a){return a.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")};"undefined"!==typeof Document&&("loading"!==document.readyState?l():document.addEventListener("DOMContentLoaded",l));d.$=c;d.$$=h;"undefined"!== -typeof self&&(self.Awesomplete=d);"object"===typeof module&&module.exports&&(module.exports=d);return d})(); \ No newline at end of file +{text:a.textContent,preventDefault:function(){e=!0},originalEvent:b});e||(this.replace(a.textContent,a),this.close(),c.fire(this.input,"awesomplete-selectcomplete"))}},evaluate:function(){var a=this,b=this.input.value;b.length>=this.minChars&&0$&"),"aria-selected":"false"})};d.RENDER_OBJ=function(a,b){return c.create("li",{innerHTML:a[1].replace(RegExp(c.regExpEscape(b.trim()), +"gi"),"$&"),"aria-selected":"false","data-id":a[0]})};d.REPLACE_STRING=function(a,b){this.input.value=a};d.REPLACE_OBJ=function(a,b){this.input.value=a;this.hidden.value=b.getAttribute("data-id")};var k=Array.prototype.slice;c.create=function(a,b){var e=document.createElement(a),d;for(d in b){var f=b[d];"inside"===d?c(f).appendChild(e):"around"===d?(f=c(f),f.parentNode.insertBefore(e,f),e.appendChild(f)):"before"===d?(f=c(f),f.parentElement.insertBefore(e,f)):d in e?e[d]=f:e.setAttribute(d, +f)}return e};c.bind=function(a,b){if(a)for(var e in b){var c=b[e];e.split(/\s+/).forEach(function(b){a.addEventListener(b,c)})}};c.fire=function(a,b,c){var d=document.createEvent("HTMLEvents");d.initEvent(b,!0,!0);for(var f in c)d[f]=c[f];a.dispatchEvent(d)};c.regExpEscape=function(a){return a.replace(/[-\\^$*+?.()|[\]{}]/g,"\\$&")};"undefined"!==typeof Document&&("loading"!==document.readyState?l():document.addEventListener("DOMContentLoaded",l));d.$=c;d.$$=h;"undefined"!==typeof self&&(self.Awesomplete= +d);"object"===typeof module&&module.exports&&(module.exports=d);return d})(); \ No newline at end of file diff --git a/index.html b/index.html index 2c72ca96..94110756 100644 --- a/index.html +++ b/index.html @@ -241,12 +241,12 @@

Extend

For simple input: -
function (li) {
-	this.input.value = li.textContent;
+					
function (text, li) {
+	this.input.value = text;
 }
For complex input (strings mapped to ids): -
function (li) {
-	this.input.value = li.textContent;
+					
function (text, li) {
+	this.input.value = text;
 	this.hidden.value = li.getAttribute('data-id');
 }