r3322 committed - autocomplete: select autocomplete visual test; switched from keyup to ...

r3322 committed - autocomplete: select autocomplete visual test; switched from keyup to ...


Revision: 3322
Author: joern.zaefferer
Date: Tue Sep 29 13:36:21 2009
Log: autocomplete: select autocomplete visual test; switched from keyup to
keypress to handle holding keys down (up/down curors); search event
cancellable; source option can return content instead of using response
callback; improved event triggering for close and change
http://code.google.com/p/jquery-ui/source/detail?r=3322
Added:
/branches/dev/tests/visual/autocomplete/select.html
Modified:
/branches/dev/tests/visual/autocomplete/default.html
/branches/dev/ui/jquery.ui.autocomplete.js
=======================================
--- /dev/null
+++ /branches/dev/tests/visual/autocomplete/select.html    Tue Sep 29 13:36:21
2009
@@ -0,0 +1,98 @@
+<!doctype html>
+<html>
+<head>
+    <title>Autocomplete Visual Test: Default</title>
+    <link rel="stylesheet" href="../static.css" type="text/css" />
+    <link rel="stylesheet" href="../../../themes/base/ui.base.css"
type="text/css" />
+    <link rel="stylesheet" href="../../../themes/base/ui.theme.css"
type="text/css" title="ui-theme" />
+    <script type="text/javascript" src="../../../jquery-1.3.2.js"></script>
+    <script type="text/javascript"
src="../../../ui/jquery.ui.core.js"></script>
+    <script type="text/javascript"
src="../../../ui/jquery.ui.position.js"></script>
+    <script type="text/javascript"
src="../../../ui/jquery.ui.button.js"></script>
+    <script type="text/javascript"
src="../../../ui/jquery.ui.menu.js"></script>
+    <script type="text/javascript"
src="../../../ui/jquery.ui.autocomplete.js"></script>
+    <script type="text/javascript"
src="http://jqueryui.com/themeroller/themeswitchertool/"></script>
+    <script type="text/javascript">
+    (function($) {
+        $.widget("ui.selectAutocomplete", {
+            _init: function() {
+                var self = this;
+                var select = this.element.hide();
+                var input = $("<input/>").addClass("ui-widget ui-widget-content
ui-corner-left").insertAfter(select).autocomplete({
+                    source: function(request) {
+                        return select.children("option").map(function() {
+                            var text = $(this).text();
+                            return {
+                                id: $(this).val(),
+                                label: text,
+                                result: text
+                            };
+                        });
+                    },
+                    change: function(e, ui) {
+                        if (!ui.item) {
+                            // remove invalid value, as it didn't match anything
+                            $(this).val("");
+                            return;
+                        }
+                        $(this).focus();
+                        select.val(ui.item.id);
+                    },
+                    minLength: 0
+                });
+                $("<button/>")
+                .html('<span class="ui-icon ui-icon-triangle-1-s"/>')
+                .insertAfter(input)
+                .button().removeClass("ui-corner-all")
+                // TODO remove/replace once button is finished
+                .addClass("ui-corner-right ui-button-icon").css("margin-left", -1)
+                .click(function() {
+                    input.autocomplete("search");
+                    input.focus();
+                });
+            }
+        });
+
+    })(jQuery);
+
+    $(function() {
+        $('<div/>').css({
+            position: "absolute",
+            right: 10,
+            top: 10
+        }).appendTo(document.body).themeswitcher();
+
+        function log(message) {
+            $("<div/>").text(message).prependTo("#log");
+            $("#log").attr("scrollTop", 0);
+        }
+
+        $("select").selectAutocomplete();
+        $("input").focus();
+    });
+    </script>
+    <style>body { font-size:62.5%; }</style>
+</head>
+<body>
+
+<div class="ui-widget">
+    <label>Tags: </label>
+    <select>
+        <option>asp</option>
+ <option>c</option>
+ <option>c++</option>
+ <option>coldfusion</option>
+ <option>groovy</option>
+ <option>haskell</option>
+ <option>java</option>
+ <option>javascript</option>
+ <option>pearl</option>
+ <option>php</option>
+ <option>python</option>
+ <option>ruby</option>
+ <option>scala</option>
+    </select>
+</div>
+
+</body>
+</html>
=======================================
--- /branches/dev/tests/visual/autocomplete/default.html    Tue Sep 29
12:30:10 2009
+++ /branches/dev/tests/visual/autocomplete/default.html    Tue Sep 29
13:36:21 2009
@@ -44,15 +44,16 @@
        }).focus();
    });
    </script>
+    <style>body { font-size:62.5%; }</style>
</head>
<body>
-<div>
+<div class="ui-widget">
    <label>Tags: </label>
-    <input id="tags" />
+    <input class="ui-widget ui-widget-content ui-corner-all" id="tags" />
</div>
-<div style="margin-top:2em; font-family:Arial">
+<div class="ui-widget" style="margin-top:2em; font-family:Arial">
    Log:
    <div id="log" style="height: 400px; width: 300px; overflow: auto;"
class="ui-widget-content"></div>
</div>
=======================================
--- /branches/dev/ui/jquery.ui.autocomplete.js    Tue Sep 29 12:30:10 2009
+++ /branches/dev/ui/jquery.ui.autocomplete.js    Tue Sep 29 13:36:21 2009
@@ -15,7 +15,8 @@
$.widget("ui.autocomplete", {
    _init: function() {
        var self = this;
-        this.element.attr("autocomplete", "off").keyup(function(event) {
+        // keyup is triggered only once when hold down, keypress multiple times
+        this.element.attr("autocomplete", "off").keypress(function(event) {
            switch(event.keyCode) {
            case $.ui.keyCode.UP:
                self.focusUp();
@@ -27,19 +28,20 @@
                self.select();
                break;
            default:
-                self.search();
+                // keypress is triggered before the input value is changed
+                setTimeout(function() {
+                    self.search(event);
+                }, 13);
                break;
            }
        }).focus(function() {
            self.previous = self.element.val();
        }).blur(function() {
-            // TODO replace with a more reliable variant
-            // without the timeout, the menu is removed before the click is
triggered on it, which also causes the blur
-            setTimeout(function() {
+            // clicks on the menu (or a button to trigger a search) will cause a
blur event
+            // TODO try to implement this without a timeout, see clearTimeout in
search()
+            self.closing = setTimeout(function() {
+                // TODO pass {data: item} when a valid value is selected, even when
the suggestionlist wasn't used
                self.close();
-                if (self.previous != self.element.val()) {
-                    self._trigger("change");
-                }
            }, 150);
        });
        this.initSource();
@@ -68,29 +70,38 @@
    search: function() {
        var self = this;
+        clearTimeout(self.closing);
        var value = this.element.val();
        if (value.length >= this.options.minLength) {
-            // TODO check return value to make search-event cancellable
-            this._trigger("search");
-            this.source({ term: value }, function response(content) {
+            if (this._trigger("search") === false)
+                return;
+            function response(content) {
                if (content.length) {
                    self._trigger("open");
                    self.suggest(content);
                } else {
                    self.close();
                }
-            });
+            }
+            // source can call response or return content directly
+            var result = this.source({ term: value }, response);
+            if (result) {
+                response(result);
+            }
        } else {
            this.close();
        }
    },
-    close: function() {
-        if (!this.menu)
-            return;
-        this._trigger("close");
-        this.menu.remove();
-        this.menu = null;
+    close: function(selected) {
+        if (this.menu) {
+            this._trigger("close");
+            this.menu.remove();
+            this.menu = null;
+        }
+        if (this.previous != this.element.val()) {
+            this._trigger("change", null, selected);
+        }
    },
    suggest: function(items) {
@@ -105,14 +116,12 @@
        }).appendTo(document.body).menu({
            focus: function(event, ui) {
                self._trigger("focus", null, { item: ui.item.data("item.autocomplete")
});
+                // TODO update input value and revert back to the input when
focus "moves back" to the input
            },
            selected: function(event, ui) {
                var data = ui.item.data("item.autocomplete");
                self.element.val( data.result );
-                self._trigger("close");
-                self.menu.remove();
-                self.menu = null;
-                self._trigger("change", null, { item: data });
+                self.close({ item: data })
            }
        }).position({
            my: "left top",