]> source.dussan.org Git - jquery-ui.git/commitdiff
Copied autocomplete from dev branch.
authorScott González <scott.gonzalez@gmail.com>
Thu, 14 Jan 2010 17:23:11 +0000 (17:23 +0000)
committerScott González <scott.gonzalez@gmail.com>
Thu, 14 Jan 2010 17:23:11 +0000 (17:23 +0000)
27 files changed:
demos/autocomplete/combobox.html [new file with mode: 0644]
demos/autocomplete/custom-data.html [new file with mode: 0644]
demos/autocomplete/default.html [new file with mode: 0644]
demos/autocomplete/index.html [new file with mode: 0644]
demos/autocomplete/remote-jsonp.html [new file with mode: 0644]
demos/autocomplete/remote-with-cache.html [new file with mode: 0644]
demos/autocomplete/remote.html [new file with mode: 0644]
demos/autocomplete/search.php [new file with mode: 0644]
demos/images/jquery_32x32.png [new file with mode: 0644]
demos/images/jqueryui_32x32.png [new file with mode: 0644]
demos/images/sizzlejs_32x32.png [new file with mode: 0644]
demos/images/transparent_1x1.png [new file with mode: 0644]
demos/index.html
tests/unit/autocomplete/autocomplete.html [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_core.js [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_defaults.js [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_events.js [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_methods.js [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_options.js [new file with mode: 0644]
tests/unit/autocomplete/autocomplete_tickets.js [new file with mode: 0644]
tests/unit/autocomplete/remote_object_array_labels.txt [new file with mode: 0644]
tests/unit/autocomplete/remote_object_array_values.txt [new file with mode: 0644]
tests/unit/autocomplete/remote_string_array.txt [new file with mode: 0644]
tests/visual/autocomplete/default.html [new file with mode: 0644]
themes/base/ui.autocomplete.css [new file with mode: 0644]
themes/base/ui.base.css
ui/jquery.ui.autocomplete.js [new file with mode: 0644]

diff --git a/demos/autocomplete/combobox.html b/demos/autocomplete/combobox.html
new file mode 100644 (file)
index 0000000..88e4105
--- /dev/null
@@ -0,0 +1,126 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Combobox Demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.button.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <script type="text/javascript">\r
+       (function($) {\r
+               $.widget("ui.combobox", {\r
+                       _init: function() {\r
+                               var self = this;\r
+                               var select = this.element.hide();\r
+                               var input = $("<input>")\r
+                                       .insertAfter(select)\r
+                                       .autocomplete({\r
+                                               source: function(request, response) {\r
+                                                       var matcher = new RegExp(request.term, "i");\r
+                                                       response(select.children("option").map(function() {\r
+                                                               var text = $(this).text();\r
+                                                               if (!request.term || matcher.test(text))\r
+                                                                       return {\r
+                                                                               id: $(this).val(),\r
+                                                                               label: text.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + request.term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>"),\r
+                                                                               value: text\r
+                                                                       };\r
+                                                       }));\r
+                                               },\r
+                                               delay: 0,\r
+                                               select: function(e, ui) {\r
+                                                       if (!ui.item) {\r
+                                                               // remove invalid value, as it didn't match anything\r
+                                                               $(this).val("");\r
+                                                               return false;\r
+                                                       }\r
+                                                       $(this).focus();\r
+                                                       select.val(ui.item.id);\r
+                                                       self._trigger("selected", null, {\r
+                                                               item: select.find("[value='" + ui.item.id + "']")\r
+                                                       });\r
+                                                       \r
+                                               },\r
+                                               minLength: 0\r
+                                       })\r
+                                       .removeClass("ui-corner-all")\r
+                                       .addClass("ui-corner-left");\r
+                               $("<button>&nbsp;</button>")\r
+                               .insertAfter(input)\r
+                               .button({\r
+                                       icons: {\r
+                                               primary: "ui-icon-triangle-1-s"\r
+                                       },\r
+                                       text: false\r
+                               }).removeClass("ui-corner-all")\r
+                               .addClass("ui-corner-right ui-button-icon")\r
+                               .position({\r
+                                       my: "left center",\r
+                                       at: "right center",\r
+                                       of: input,\r
+                                       offset: "-1 0"\r
+                               }).css("top", "")\r
+                               .click(function() {\r
+                                       // close if already visible\r
+                                       if (input.autocomplete("widget").is(":visible")) {\r
+                                               input.autocomplete("close");\r
+                                               return;\r
+                                       }\r
+                                       // pass empty string as value to search for, displaying all results\r
+                                       input.autocomplete("search", "");\r
+                                       input.focus();\r
+                               });\r
+                       }\r
+               });\r
+\r
+       })(jQuery);\r
+               \r
+       $(function() {\r
+               $("select").combobox();\r
+       });\r
+       </script>\r
+       <style>\r
+               /* TODO shouldn't be necessary */\r
+               .ui-button-icon-only .ui-button-text { padding: 0; } \r
+       </style>\r
+</head>\r
+<body>\r
+       \r
+<div class="demo">\r
+\r
+<div class="ui-widget">\r
+       <label>Your preferred programming language: </label>\r
+       <select>\r
+               <option value="a">asp</option>\r
+        <option value="c">c</option>\r
+        <option value="cpp">c++</option>\r
+        <option value="cf">coldfusion</option>\r
+        <option value="g">groovy</option>\r
+        <option value="h">haskell</option>\r
+        <option value="j">java</option>\r
+        <option value="js">javascript</option>\r
+        <option value="p1">pearl</option>\r
+        <option value="p2">php</option>\r
+        <option value="p3">python</option>\r
+        <option value="r">ruby</option>\r
+        <option value="s">scala</option>\r
+       </select>\r
+</div>\r
+\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+A custom widget built by composition of Autocomplete and Button. You can either type something into the field to get filtered suggestions based on your input, or use the button to get the full list of selections.\r
+</p>\r
+<p>\r
+The input is read from an existing select-element for progressive enhancement, passed to Autocomplete with a customized source-option.\r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/custom-data.html b/demos/autocomplete/custom-data.html
new file mode 100644 (file)
index 0000000..726ace4
--- /dev/null
@@ -0,0 +1,91 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Custom Data Demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <style type="text/css">\r
+       #project-label {\r
+               display: block;\r
+               font-weight: bold;\r
+               margin-bottom: 1em;\r
+       }\r
+       #project-icon {\r
+               float: left;\r
+               height: 32px;\r
+               width: 32px;\r
+       }\r
+       #project-description {\r
+               margin: 0;\r
+               padding: 0;\r
+       }\r
+       </style>\r
+       <script type="text/javascript">\r
+       $(function() {\r
+               var projects = [\r
+                       {\r
+                               value: 'jquery',\r
+                               label: 'jQuery',\r
+                               desc: 'the write less, do more, JavaScript library',\r
+                               icon: 'jquery_32x32.png'\r
+                       },\r
+                       {\r
+                               value: 'jquery-ui',\r
+                               label: 'jQuery UI',\r
+                               desc: 'the official user interface library for jQuery',\r
+                               icon: 'jqueryui_32x32.png'\r
+                       },\r
+                       {\r
+                               value: 'sizzlejs',\r
+                               label: 'Sizzle',\r
+                               desc: 'a pure-JavaScript CSS selector engine',\r
+                               icon: 'sizzlejs_32x32.png'\r
+                       }\r
+               ];\r
+               \r
+               $('#project').autocomplete({\r
+                       minLength: 0,\r
+                       source: projects,\r
+                       focus: function(event, ui) {\r
+                               $('#project').val(ui.item.label);\r
+                               \r
+                               return false;\r
+                       },\r
+                       select: function(event, ui) {\r
+                               $('#project').val(ui.item.label);\r
+                               $('#project-id').val(ui.item.value);\r
+                               $('#project-description').html(ui.item.desc);\r
+                               $('#project-icon').attr('src', '../images/' + ui.item.icon);\r
+                               \r
+                               return false;\r
+                       }\r
+               });\r
+       });\r
+       </script>\r
+</head>\r
+<body>\r
+\r
+<div class="demo">\r
+       <label for="tags" id="project-label">Select a project:</label>\r
+       <img id="project-icon" src="../images/transparent_1x1.png" class="ui-state-default">\r
+       <input id="project">\r
+       <input type="hidden" id="project-id">\r
+       <p id="project-description"></p>\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+You can use your own custom data formats and displays by simply overriding the default focus and select actions.\r
+</p>\r
+<p>\r
+Try typing "j" to get a list of projects or just press the down arrow.\r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/default.html b/demos/autocomplete/default.html
new file mode 100644 (file)
index 0000000..996af87
--- /dev/null
@@ -0,0 +1,42 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Default Demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <script type="text/javascript">\r
+       $(function() {\r
+               var availableTags = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "perl"];\r
+               $("#tags").autocomplete({\r
+                       source: availableTags\r
+               });\r
+       });\r
+       </script>\r
+</head>\r
+<body>\r
+       \r
+<div class="demo">\r
+\r
+<div class="ui-widget">\r
+       <label for="tags">Tags: </label>\r
+       <input id="tags" />\r
+</div>\r
+\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are tags for programming languages, give "ja" (for Java or JavaScript) a try.\r
+</p>\r
+<p>\r
+The datasource is a simple JavaScript array, provided to the widget using the source-option.\r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/index.html b/demos/autocomplete/index.html
new file mode 100644 (file)
index 0000000..d4cc750
--- /dev/null
@@ -0,0 +1,20 @@
+<!doctype html>\r
+<html lang="en">\r
+<head>\r
+       <title>jQuery UI Autocomplete Demos</title>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+</head>\r
+<body>\r
+       <div class="demos-nav">\r
+               <h4>Examples</h4>\r
+               <ul>\r
+                       <li class="demo-config-on"><a href="default.html">Default functionality</a></li>\r
+                       <li><a href="remote.html">Remote datasource</a></li>\r
+                       <li><a href="remote-with-cache.html">Remote with caching</a></li>\r
+                       <li><a href="remote-jsonp.html">Remote JSONP datasource</a></li>\r
+                       <li><a href="combobox.html">Combobox</a></li>\r
+                       <li><a href="custom-data.html">Custom data and display</a></li>\r
+               </ul>\r
+       </div>\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/remote-jsonp.html b/demos/autocomplete/remote-jsonp.html
new file mode 100644 (file)
index 0000000..459cac2
--- /dev/null
@@ -0,0 +1,85 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Remote JSONP datasource demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <script type="text/javascript">\r
+       $(function() {\r
+               function log(message) {\r
+                       $("<div/>").text(message).prependTo("#log");\r
+                       $("#log").attr("scrollTop", 0);\r
+               }\r
+               \r
+               $("#city").autocomplete({\r
+                       source: function(request, response) {\r
+                               $.ajax({\r
+                                       url: "http://ws.geonames.org/searchJSON",\r
+                                       dataType: "jsonp",\r
+                                       data: {\r
+                                               featureClass: "P",\r
+                                               style: "full",\r
+                                               maxRows: 15,\r
+                                               name_startsWith: request.term\r
+                                       },\r
+                                       success: function(data) {\r
+                                               response($.map(data.geonames, function(item) {\r
+                                                       return {\r
+                                                               label: item.name + (item.adminName1 ? ", " + item.adminName1 : "") + ", " + item.countryName,\r
+                                                               value: item.name\r
+                                                       }\r
+                                               }))\r
+                                       }\r
+                               })\r
+                       },\r
+                       minLength: 2,\r
+                       select: function(event, ui) {\r
+                               log(ui.item ? ("Selected: " + ui.item.label) : "Nothing selected, input was " + this.value);\r
+                       },\r
+                       open: function() {\r
+                               $(this).removeClass("ui-corner-all").addClass("ui-corner-top");\r
+                       },\r
+                       close: function() {\r
+                               $(this).removeClass("ui-corner-top").addClass("ui-corner-all");\r
+                       }\r
+               });\r
+       });\r
+       </script>\r
+       <style>\r
+               .ui-autocomplete-loading { background: url(indicator.gif) no-repeat right; }\r
+               #city { width: 25em; }\r
+       </style>\r
+</head>\r
+<body>\r
+\r
+<div class="demo">\r
+\r
+<div class="ui-widget">\r
+       <label for="city">Your city: </label>\r
+       <input id="city" />\r
+       Powered by <a href="http://geonames.org">geonames.org</a>\r
+</div>\r
+\r
+<div class="ui-widget" style="margin-top:2em; font-family:Arial">\r
+       Result:\r
+       <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>\r
+</div>\r
+\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are cities, displayed when at least two characters are entered into the field.\r
+</p>\r
+<p>\r
+In this case, the datasource is the <a href="http://geonames.org">geonames.org webservice</a>. While only the city name itself ends up in the input after selecting an element, more info is displayed in the suggestions to help find the right entry. That data is also available in callbacks, as illustrated by the Result area below the input. \r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/remote-with-cache.html b/demos/autocomplete/remote-with-cache.html
new file mode 100644 (file)
index 0000000..8f1a578
--- /dev/null
@@ -0,0 +1,76 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Remote with caching demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <script type="text/javascript">\r
+       $(function() {\r
+               function log(message) {\r
+                       $("<div/>").text(message).prependTo("#log");\r
+                       $("#log").attr("scrollTop", 0);\r
+               }\r
+               \r
+               var cache = {};\r
+               $("#birds").autocomplete({\r
+                       source: function(request, response) {\r
+                               if (cache.term == request.term && cache.content) {\r
+                                       response(cache.content);\r
+                               }\r
+                               if (new RegExp(cache.term).test(request.term) && cache.content && cache.content.length < 13) {\r
+                                       var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");\r
+                                       response($.grep(cache.content, function(value) {\r
+                                       return matcher.test(value.value)\r
+                                       }));\r
+                               }\r
+                               $.ajax({\r
+                                       url: "search.php",\r
+                                       dataType: "json",\r
+                                       data: request,\r
+                                       success: function(data) {\r
+                                               cache.term = request.term;\r
+                                               cache.content = data;\r
+                                               response(data);\r
+                                       }\r
+                               });\r
+                       },\r
+                       minLength: 2,\r
+                       select: function(event, ui) {\r
+                               log(ui.item ? ("Selected: " + ui.item.value + " aka " + ui.item.id) : "Nothing selected, input was " + this.value);\r
+                       }\r
+               });\r
+       });\r
+       </script>\r
+</head>\r
+<body>\r
+\r
+<div class="demo">\r
+\r
+<div class="ui-widget">\r
+       <label for="birds">Birds: </label>\r
+       <input id="birds" />\r
+</div>\r
+\r
+<div class="ui-widget" style="margin-top:2em; font-family:Arial">\r
+       Result:\r
+       <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>\r
+</div>\r
+\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are bird names, displayed when at least two characters are entered into the field.\r
+</p>\r
+<p>\r
+Similar to the remote datasource demo, though this adds some local caching to improve performance. The cache here saves just one query, and could be extended to cache multiple values, one for each term.\r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/remote.html b/demos/autocomplete/remote.html
new file mode 100644 (file)
index 0000000..9ef27bf
--- /dev/null
@@ -0,0 +1,56 @@
+<!doctype html>\r
+<html>\r
+<head>\r
+       <title>jQuery UI Autocomplete Remote datasource demo</title>\r
+       <link type="text/css" href="../../themes/base/ui.all.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../ui/jquery.ui.autocomplete.js"></script>\r
+       <link type="text/css" href="../demos.css" rel="stylesheet" />\r
+       <script type="text/javascript">\r
+       $(function() {\r
+               function log(message) {\r
+                       $("<div/>").text(message).prependTo("#log");\r
+                       $("#log").attr("scrollTop", 0);\r
+               }\r
+               \r
+               $("#birds").autocomplete({\r
+                       // TODO doesn't work when loaded from /demos/#autocomplete|remote\r
+                       source: "search.php",\r
+                       minLength: 2,\r
+                       select: function(event, ui) {\r
+                               log(ui.item ? ("Selected: " + ui.item.value + " aka " + ui.item.id) : "Nothing selected, input was " + this.value);\r
+                       }\r
+               });\r
+       });\r
+       </script>\r
+</head>\r
+<body>\r
+\r
+<div class="demo">\r
+\r
+<div class="ui-widget">\r
+       <label for="birds">Birds: </label>\r
+       <input id="birds" />\r
+</div>\r
+\r
+<div class="ui-widget" style="margin-top:2em; font-family:Arial">\r
+       Result:\r
+       <div id="log" style="height: 200px; width: 300px; overflow: auto;" class="ui-widget-content"></div>\r
+</div>\r
+\r
+</div><!-- End demo -->\r
+\r
+<div class="demo-description">\r
+<p>\r
+The Autocomplete widgets provides suggestions while you type into the field. Here the suggestions are bird names, displayed when at least two characters are entered into the field.\r
+</p>\r
+<p>\r
+The datasource is a server-side script which returns JSON data, specified via a simple URL for the source-option. In addition, the minLength-option is set to 2 to avoid queries that would return too many results and the select-event is used to display some feedback.\r
+</p>\r
+</div><!-- End demo-description -->\r
+\r
+</body>\r
+</html>\r
diff --git a/demos/autocomplete/search.php b/demos/autocomplete/search.php
new file mode 100644 (file)
index 0000000..565c790
--- /dev/null
@@ -0,0 +1,640 @@
+<?php
+
+$q = strtolower($_GET["term"]);
+if (!$q) return;
+$items = array(
+"Great <em>Bittern</em>"=>"Botaurus stellaris",
+"Little <em>Grebe</em>"=>"Tachybaptus ruficollis",
+"Black-necked Grebe"=>"Podiceps nigricollis",
+"Little Bittern"=>"Ixobrychus minutus",
+"Black-crowned Night Heron"=>"Nycticorax nycticorax",
+"Purple Heron"=>"Ardea purpurea",
+"White Stork"=>"Ciconia ciconia",
+"Spoonbill"=>"Platalea leucorodia",
+"Red-crested Pochard"=>"Netta rufina",
+"Common Eider"=>"Somateria mollissima",
+"Red Kite"=>"Milvus milvus",
+"Hen Harrier"=>"Circus cyaneus",
+"Montagu`s Harrier"=>"Circus pygargus",
+"Black Grouse"=>"Tetrao tetrix",
+"Grey Partridge"=>"Perdix perdix",
+"Spotted Crake"=>"Porzana porzana",
+"Corncrake"=>"Crex crex",
+"Common Crane"=>"Grus grus",
+"Avocet"=>"Recurvirostra avosetta",
+"Stone Curlew"=>"Burhinus oedicnemus",
+"Common Ringed Plover"=>"Charadrius hiaticula",
+"Kentish Plover"=>"Charadrius alexandrinus",
+"Ruff"=>"Philomachus pugnax",
+"Common Snipe"=>"Gallinago gallinago",
+"Black-tailed Godwit"=>"Limosa limosa",
+"Common Redshank"=>"Tringa totanus",
+"Sandwich Tern"=>"Sterna sandvicensis",
+"Common Tern"=>"Sterna hirundo",
+"Arctic Tern"=>"Sterna paradisaea",
+"Little Tern"=>"Sternula albifrons",
+"Black Tern"=>"Chlidonias niger",
+"Barn Owl"=>"Tyto alba",
+"Little Owl"=>"Athene noctua",
+"Short-eared Owl"=>"Asio flammeus",
+"European Nightjar"=>"Caprimulgus europaeus",
+"Common Kingfisher"=>"Alcedo atthis",
+"Eurasian Hoopoe"=>"Upupa epops",
+"Eurasian Wryneck"=>"Jynx torquilla",
+"European Green Woodpecker"=>"Picus viridis",
+"Crested Lark"=>"Galerida cristata",
+"White-headed Duck"=>"Oxyura leucocephala",
+"Pale-bellied Brent Goose"=>"Branta hrota",
+"Tawny Pipit"=>"Anthus campestris",
+"Whinchat"=>"Saxicola rubetra",
+"European Stonechat"=>"Saxicola rubicola",
+"Northern Wheatear"=>"Oenanthe oenanthe",
+"Savi`s Warbler"=>"Locustella luscinioides",
+"Sedge Warbler"=>"Acrocephalus schoenobaenus",
+"Great Reed Warbler"=>"Acrocephalus arundinaceus",
+"Bearded Reedling"=>"Panurus biarmicus",
+"Red-backed Shrike"=>"Lanius collurio",
+"Great Grey Shrike"=>"Lanius excubitor",
+"Woodchat Shrike"=>"Lanius senator",
+"Common Raven"=>"Corvus corax",
+"Yellowhammer"=>"Emberiza citrinella",
+"Ortolan Bunting"=>"Emberiza hortulana",
+"Corn Bunting"=>"Emberiza calandra",
+"Great Cormorant"=>"Phalacrocorax carbo",
+"Hawfinch"=>"Coccothraustes coccothraustes",
+"Common Shelduck"=>"Tadorna tadorna",
+"Bluethroat"=>"Luscinia svecica",
+"Grey Heron"=>"Ardea cinerea",
+"Barn Swallow"=>"Hirundo rustica",
+"Hooded Crow"=>"Corvus cornix",
+"Dunlin"=>"Calidris alpina",
+"Eurasian Pied Flycatcher"=>"Ficedula hypoleuca",
+"Eurasian Nuthatch"=>"Sitta europaea",
+"Short-toed Tree Creeper"=>"Certhia brachydactyla",
+"Wood Lark"=>"Lullula arborea",
+"Tree Pipit"=>"Anthus trivialis",
+"Eurasian Hobby"=>"Falco subbuteo",
+"Marsh Warbler"=>"Acrocephalus palustris",
+"Wood Sandpiper"=>"Tringa glareola",
+"Tawny Owl"=>"Strix aluco",
+"Lesser Whitethroat"=>"Sylvia curruca",
+"Barnacle Goose"=>"Branta leucopsis",
+"Common Goldeneye"=>"Bucephala clangula",
+"Western Marsh Harrier"=>"Circus aeruginosus",
+"Common Buzzard"=>"Buteo buteo",
+"Sanderling"=>"Calidris alba",
+"Little Gull"=>"Larus minutus",
+"Eurasian Magpie"=>"Pica pica",
+"Willow Warbler"=>"Phylloscopus trochilus",
+"Wood Warbler"=>"Phylloscopus sibilatrix",
+"Great Crested Grebe"=>"Podiceps cristatus",
+"Eurasian Jay"=>"Garrulus glandarius",
+"Common Redstart"=>"Phoenicurus phoenicurus",
+"Blue-headed Wagtail"=>"Motacilla flava",
+"Common Swift"=>"Apus apus",
+"Marsh Tit"=>"Poecile palustris",
+"Goldcrest"=>"Regulus regulus",
+"European Golden Plover"=>"Pluvialis apricaria",
+"Eurasian Bullfinch"=>"Pyrrhula pyrrhula",
+"Common Whitethroat"=>"Sylvia communis",
+"Meadow Pipit"=>"Anthus pratensis",
+"Greylag Goose"=>"Anser anser",
+"Spotted Flycatcher"=>"Muscicapa striata",
+"European Greenfinch"=>"Carduelis chloris",
+"Common Greenshank"=>"Tringa nebularia",
+"Great Spotted Woodpecker"=>"Dendrocopos major",
+"Greater Canada Goose"=>"Branta canadensis",
+"Mistle Thrush"=>"Turdus viscivorus",
+"Great Black-backed Gull"=>"Larus marinus",
+"Goosander"=>"Mergus merganser",
+"Great Egret"=>"Casmerodius albus",
+"Northern Goshawk"=>"Accipiter gentilis",
+"Dunnock"=>"Prunella modularis",
+"Stock Dove"=>"Columba oenas",
+"Common Wood Pigeon"=>"Columba palumbus",
+"Eurasian Woodcock"=>"Scolopax rusticola",
+"House Sparrow"=>"Passer domesticus",
+"Common House Martin"=>"Delichon urbicum",
+"Red Knot"=>"Calidris canutus",
+"Western Jackdaw"=>"Corvus monedula",
+"Brambling"=>"Fringilla montifringilla",
+"Northern Lapwing"=>"Vanellus vanellus",
+"European Reed Warbler"=>"Acrocephalus scirpaceus",
+"Lesser Black-backed Gull"=>"Larus fuscus",
+"Little Egret"=>"Egretta garzetta",
+"Little Stint"=>"Calidris minuta",
+"Common Linnet"=>"Carduelis cannabina",
+"Mute Swan"=>"Cygnus olor",
+"Common Cuckoo"=>"Cuculus canorus",
+"Black-headed Gull"=>"Larus ridibundus",
+"Greater White-fronted Goose"=>"Anser albifrons",
+"Great Tit"=>"Parus major",
+"Redwing"=>"Turdus iliacus",
+"Gadwall"=>"Anas strepera",
+"Fieldfare"=>"Turdus pilaris",
+"Tufted Duck"=>"Aythya fuligula",
+"Crested Tit"=>"Lophophanes cristatus",
+"Willow Tit"=>"Poecile montanus",
+"Eurasian Coot"=>"Fulica atra",
+"Common Blackbird"=>"Turdus merula",
+"Smew"=>"Mergus albellus",
+"Common Sandpiper"=>"Actitis hypoleucos",
+"Sand Martin"=>"Riparia riparia",
+"Purple Sandpiper"=>"Calidris maritima",
+"Northern Pintail"=>"Anas acuta",
+"Blue Tit"=>"Cyanistes caeruleus",
+"European Goldfinch"=>"Carduelis carduelis",
+"Eurasian Whimbrel"=>"Numenius phaeopus",
+"Common Reed Bunting"=>"Emberiza schoeniclus",
+"Eurasian Tree Sparrow"=>"Passer montanus",
+"Rook"=>"Corvus frugilegus",
+"European Robin"=>"Erithacus rubecula",
+"Bar-tailed Godwit"=>"Limosa lapponica",
+"Dark-bellied Brent Goose"=>"Branta bernicla",
+"Eurasian Oystercatcher"=>"Haematopus ostralegus",
+"Eurasian Siskin"=>"Carduelis spinus",
+"Northern Shoveler"=>"Anas clypeata",
+"Eurasian Wigeon"=>"Anas penelope",
+"Eurasian Sparrow Hawk"=>"Accipiter nisus",
+"Icterine Warbler"=>"Hippolais icterina",
+"Common Starling"=>"Sturnus vulgaris",
+"Long-tailed Tit"=>"Aegithalos caudatus",
+"Ruddy Turnstone"=>"Arenaria interpres",
+"Mew Gull"=>"Larus canus",
+"Common Pochard"=>"Aythya ferina",
+"Common Chiffchaff"=>"Phylloscopus collybita",
+"Greater Scaup"=>"Aythya marila",
+"Common Kestrel"=>"Falco tinnunculus",
+"Garden Warbler"=>"Sylvia borin",
+"Eurasian Collared Dove"=>"Streptopelia decaocto",
+"Eurasian Skylark"=>"Alauda arvensis",
+"Common Chaffinch"=>"Fringilla coelebs",
+"Common Moorhen"=>"Gallinula chloropus",
+"Water Pipit"=>"Anthus spinoletta",
+"Mallard"=>"Anas platyrhynchos",
+"Winter Wren"=>"Troglodytes troglodytes",
+"Common Teal"=>"Anas crecca",
+"Green Sandpiper"=>"Tringa ochropus",
+"White Wagtail"=>"Motacilla alba",
+"Eurasian Curlew"=>"Numenius arquata",
+"Song Thrush"=>"Turdus philomelos",
+"European Herring Gull"=>"Larus argentatus",
+"Grey Plover"=>"Pluvialis squatarola",
+"Carrion Crow"=>"Corvus corone",
+"Coal Tit"=>"Periparus ater",
+"Spotted Redshank"=>"Tringa erythropus",
+"Blackcap"=>"Sylvia atricapilla",
+"Egyptian Vulture"=>"Neophron percnopterus",
+"Razorbill"=>"Alca torda",
+"Alpine Swift"=>"Apus melba",
+"Long-legged Buzzard"=>"Buteo rufinus",
+"Audouin`s Gull"=>"Larus audouinii",
+"Balearic Shearwater"=>"Puffinus mauretanicus",
+"Upland Sandpiper"=>"Bartramia longicauda",
+"Greater Spotted Eagle"=>"Aquila clanga",
+"Ring Ouzel"=>"Turdus torquatus",
+"Yellow-browed Warbler"=>"Phylloscopus inornatus",
+"Blue Rock Thrush"=>"Monticola solitarius",
+"Buff-breasted Sandpiper"=>"Tryngites subruficollis",
+"Jack Snipe"=>"Lymnocryptes minimus",
+"White-rumped Sandpiper"=>"Calidris fuscicollis",
+"Ruddy Shelduck"=>"Tadorna ferruginea",
+"Cetti's Warbler"=>"Cettia cetti",
+"Citrine Wagtail"=>"Motacilla citreola",
+"Roseate Tern"=>"Sterna dougallii",
+"Black-legged Kittiwake"=>"Rissa tridactyla",
+"Pygmy Cormorant"=>"Phalacrocorax pygmeus",
+"Booted Eagle"=>"Aquila pennata",
+"Lesser White-fronted Goose"=>"Anser erythropus",
+"Little Bunting"=>"Emberiza pusilla",
+"Eleonora's Falcon"=>"Falco eleonorae",
+"European Serin"=>"Serinus serinus",
+"Twite"=>"Carduelis flavirostris",
+"Yellow-legged Gull"=>"Larus michahellis",
+"Gyr Falcon"=>"Falco rusticolus",
+"Greenish Warbler"=>"Phylloscopus trochiloides",
+"Red-necked Phalarope"=>"Phalaropus lobatus",
+"Mealy Redpoll"=>"Carduelis flammea",
+"Glaucous Gull"=>"Larus hyperboreus",
+"Great Skua"=>"Stercorarius skua",
+"Great Bustard"=>"Otis tarda",
+"Velvet Scoter"=>"Melanitta fusca",
+"Pine Grosbeak"=>"Pinicola enucleator",
+"House Crow"=>"Corvus splendens",
+"Hume`s Leaf Warbler"=>"Phylloscopus humei",
+"Great Northern Loon"=>"Gavia immer",
+"Long-tailed Duck"=>"Clangula hyemalis",
+"Lapland Longspur"=>"Calcarius lapponicus",
+"Northern Gannet"=>"Morus bassanus",
+"Eastern Imperial Eagle"=>"Aquila heliaca",
+"Little Auk"=>"Alle alle",
+"Lesser Spotted Woodpecker"=>"Dendrocopos minor",
+"Iceland Gull"=>"Larus glaucoides",
+"Parasitic Jaeger"=>"Stercorarius parasiticus",
+"Bewick`s Swan"=>"Cygnus bewickii",
+"Little Bustard"=>"Tetrax tetrax",
+"Little Crake"=>"Porzana parva",
+"Baillon`s Crake"=>"Porzana pusilla",
+"Long-tailed Jaeger"=>"Stercorarius longicaudus",
+"King Eider"=>"Somateria spectabilis",
+"Greater Short-toed Lark"=>"Calandrella brachydactyla",
+"Houbara Bustard"=>"Chlamydotis undulata",
+"Curlew Sandpiper"=>"Calidris ferruginea",
+"Common Crossbill"=>"Loxia curvirostra",
+"European Shag"=>"Phalacrocorax aristotelis",
+"Horned Grebe"=>"Podiceps auritus",
+"Common Quail"=>"Coturnix coturnix",
+"Bearded Vulture"=>"Gypaetus barbatus",
+"Lanner Falcon"=>"Falco biarmicus",
+"Middle Spotted Woodpecker"=>"Dendrocopos medius",
+"Pomarine Jaeger"=>"Stercorarius pomarinus",
+"Red-breasted Merganser"=>"Mergus serrator",
+"Eurasian Black Vulture"=>"Aegypius monachus",
+"Eurasian Dotterel"=>"Charadrius morinellus",
+"Common Nightingale"=>"Luscinia megarhynchos",
+"Northern willow warbler"=>"Phylloscopus trochilus acredula",
+"Manx Shearwater"=>"Puffinus puffinus",
+"Northern Fulmar"=>"Fulmarus glacialis",
+"Eurasian Eagle Owl"=>"Bubo bubo",
+"Orphean Warbler"=>"Sylvia hortensis",
+"Melodious Warbler"=>"Hippolais polyglotta",
+"Pallas's Leaf Warbler"=>"Phylloscopus proregulus",
+"Atlantic Puffin"=>"Fratercula arctica",
+"Black-throated Loon"=>"Gavia arctica",
+"Bohemian Waxwing"=>"Bombycilla garrulus",
+"Marsh Sandpiper"=>"Tringa stagnatilis",
+"Great Snipe"=>"Gallinago media",
+"Squacco Heron"=>"Ardeola ralloides",
+"Long-eared Owl"=>"Asio otus",
+"Caspian Tern"=>"Hydroprogne caspia",
+"Red-breasted Goose"=>"Branta ruficollis",
+"Red-throated Loon"=>"Gavia stellata",
+"Common Rosefinch"=>"Carpodacus erythrinus",
+"Red-footed Falcon"=>"Falco vespertinus",
+"Ross's Goose"=>"Anser rossii",
+"Red Phalarope"=>"Phalaropus fulicarius",
+"Pied Wagtail"=>"Motacilla yarrellii",
+"Rose-coloured Starling"=>"Sturnus roseus",
+"Rough-legged Buzzard"=>"Buteo lagopus",
+"Saker Falcon"=>"Falco cherrug",
+"European Roller"=>"Coracias garrulus",
+"Short-toed Eagle"=>"Circaetus gallicus",
+"Peregrine Falcon"=>"Falco peregrinus",
+"Merlin"=>"Falco columbarius",
+"Snow Goose"=>"Anser caerulescens",
+"Snowy Owl"=>"Bubo scandiacus",
+"Snow Bunting"=>"Plectrophenax nivalis",
+"Common Grasshopper Warbler"=>"Locustella naevia",
+"Golden Eagle"=>"Aquila chrysaetos",
+"Black-winged Stilt"=>"Himantopus himantopus",
+"Steppe Eagle"=>"Aquila nipalensis",
+"Pallid Harrier"=>"Circus macrourus",
+"European Storm-petrel"=>"Hydrobates pelagicus",
+"Horned Lark"=>"Eremophila alpestris",
+"Eurasian Treecreeper"=>"Certhia familiaris",
+"Taiga Bean Goose"=>"Anser fabalis",
+"Temminck`s Stint"=>"Calidris temminckii",
+"Terek Sandpiper"=>"Xenus cinereus",
+"Tundra Bean Goose"=>"Anser serrirostris",
+"European Turtle Dove"=>"Streptopelia turtur",
+"Leach`s Storm-petrel"=>"Oceanodroma leucorhoa",
+"Eurasian Griffon Vulture"=>"Gyps fulvus",
+"Paddyfield Warbler"=>"Acrocephalus agricola",
+"Osprey"=>"Pandion haliaetus",
+"Firecrest"=>"Regulus ignicapilla",
+"Water Rail"=>"Rallus aquaticus",
+"European Honey Buzzard"=>"Pernis apivorus",
+"Eurasian Golden Oriole"=>"Oriolus oriolus",
+"Whooper Swan"=>"Cygnus cygnus",
+"Two-barred Crossbill"=>"Loxia leucoptera",
+"White-tailed Eagle"=>"Haliaeetus albicilla",
+"Atlantic Murre"=>"Uria aalge",
+"Garganey"=>"Anas querquedula",
+"Black Redstart"=>"Phoenicurus ochruros",
+"Common Scoter"=>"Melanitta nigra",
+"Rock Pipit"=>"Anthus petrosus",
+"Lesser Spotted Eagle"=>"Aquila pomarina",
+"Cattle Egret"=>"Bubulcus ibis",
+"White-winged Black Tern"=>"Chlidonias leucopterus",
+"Black Stork"=>"Ciconia nigra",
+"Mediterranean Gull"=>"Larus melanocephalus",
+"Black Kite"=>"Milvus migrans",
+"Yellow Wagtail"=>"Motacilla flavissima",
+"Red-necked Grebe"=>"Podiceps grisegena",
+"Gull-billed Tern"=>"Gelochelidon nilotica",
+"Pectoral Sandpiper"=>"Calidris melanotos",
+"Barred Warbler"=>"Sylvia nisoria",
+"Red-throated Pipit"=>"Anthus cervinus",
+"Grey Wagtail"=>"Motacilla cinerea",
+"Richard`s Pipit"=>"Anthus richardi",
+"Black Woodpecker"=>"Dryocopus martius",
+"Little Ringed Plover"=>"Charadrius dubius",
+"Whiskered Tern"=>"Chlidonias hybrida",
+"Lesser Redpoll"=>"Carduelis cabaret",
+"Pallas' Bunting"=>"Emberiza pallasi",
+"Ferruginous Duck"=>"Aythya nyroca",
+"Whistling Swan"=>"Cygnus columbianus",
+"Black Brant"=>"Branta nigricans",
+"Marbled Teal"=>"Marmaronetta angustirostris",
+"Canvasback"=>"Aythya valisineria",
+"Redhead"=>"Aythya americana",
+"Lesser Scaup"=>"Aythya affinis",
+"Steller`s Eider"=>"Polysticta stelleri",
+"Spectacled Eider"=>"Somateria fischeri",
+"Harlequin Duck"=>"Histronicus histrionicus",
+"Black Scoter"=>"Melanitta americana",
+"Surf Scoter"=>"Melanitta perspicillata",
+"Barrow`s Goldeneye"=>"Bucephala islandica",
+"Falcated Duck"=>"Anas falcata",
+"American Wigeon"=>"Anas americana",
+"Blue-winged Teal"=>"Anas discors",
+"American Black Duck"=>"Anas rubripes",
+"Baikal Teal"=>"Anas formosa",
+"Green-Winged Teal"=>"Anas carolinensis",
+"Hazel Grouse"=>"Bonasa bonasia",
+"Rock Partridge"=>"Alectoris graeca",
+"Red-legged Partridge"=>"Alectoris rufa",
+"Yellow-billed Loon"=>"Gavia adamsii",
+"Cory`s Shearwater"=>"Calonectris borealis",
+"Madeiran Storm-Petrel"=>"Oceanodroma castro",
+"Great White Pelican"=>"Pelecanus onocrotalus",
+"Dalmatian Pelican"=>"Pelecanus crispus",
+"American Bittern"=>"Botaurus lentiginosus",
+"Glossy Ibis"=>"Plegadis falcinellus",
+"Spanish Imperial Eagle"=>"Aquila adalberti",
+"Lesser Kestrel"=>"Falco naumanni",
+"Houbara Bustard"=>"Chlamydotis undulata",
+"Crab-Plover"=>"Dromas ardeola",
+"Cream-coloured Courser"=>"Cursorius cursor",
+"Collared Pratincole"=>"Glareola pratincola",
+"Black-winged Pratincole"=>"Glareola nordmanni",
+"Killdeer"=>"Charadrius vociferus",
+"Lesser Sand Plover"=>"Charadrius mongolus",
+"Greater Sand Plover"=>"Charadrius leschenaultii",
+"Caspian Plover"=>"Charadrius asiaticus",
+"American Golden Plover"=>"Pluvialis dominica",
+"Pacific Golden Plover"=>"Pluvialis fulva",
+"Sharp-tailed Sandpiper"=>"Calidris acuminata",
+"Broad-billed Sandpiper"=>"Limicola falcinellus",
+"Spoon-Billed Sandpiper"=>"Eurynorhynchus pygmaeus",
+"Short-Billed Dowitcher"=>"Limnodromus griseus",
+"Long-billed Dowitcher"=>"Limnodromus scolopaceus",
+"Hudsonian Godwit"=>"Limosa haemastica",
+"Little Curlew"=>"Numenius minutus",
+"Lesser Yellowlegs"=>"Tringa flavipes",
+"Wilson`s Phalarope"=>"Phalaropus tricolor",
+"Pallas`s Gull"=>"Larus ichthyaetus",
+"Laughing Gull"=>"Larus atricilla",
+"Franklin`s Gull"=>"Larus pipixcan",
+"Bonaparte`s Gull"=>"Larus philadelphia",
+"Ring-billed Gull"=>"Larus delawarensis",
+"American Herring Gull"=>"Larus smithsonianus",
+"Caspian Gull"=>"Larus cachinnans",
+"Ivory Gull"=>"Pagophila eburnea",
+"Royal Tern"=>"Sterna maxima",
+"Brünnich`s Murre"=>"Uria lomvia",
+"Crested Auklet"=>"Aethia cristatella",
+"Parakeet Auklet"=>"Cyclorrhynchus psittacula",
+"Tufted Puffin"=>"Lunda cirrhata",
+"Laughing Dove"=>"Streptopelia senegalensis",
+"Great Spotted Cuckoo"=>"Clamator glandarius",
+"Great Grey Owl"=>"Strix nebulosa",
+"Tengmalm`s Owl"=>"Aegolius funereus",
+"Red-Necked Nightjar"=>"Caprimulgus ruficollis",
+"Chimney Swift"=>"Chaetura pelagica",
+"Green Bea-Eater"=>"Merops orientalis",
+"Grey-headed Woodpecker"=>"Picus canus",
+"Lesser Short-Toed Lark"=>"Calandrella rufescens",
+"Eurasian Crag Martin"=>"Hirundo rupestris",
+"Red-rumped Swallow"=>"Cecropis daurica",
+"Blyth`s Pipit"=>"Anthus godlewskii",
+"Pechora Pipit"=>"Anthus gustavi",
+"Grey-headed Wagtail"=>"Motacilla thunbergi",
+"Yellow-Headed Wagtail"=>"Motacilla lutea",
+"White-throated Dipper"=>"Cinclus cinclus",
+"Rufous-Tailed Scrub Robin"=>"Cercotrichas galactotes",
+"Thrush Nightingale"=>"Luscinia luscinia",
+"White-throated Robin"=>"Irania gutturalis",
+"Caspian Stonechat"=>"Saxicola maura variegata",
+"Western Black-eared Wheatear"=>"Oenanthe hispanica",
+"Rufous-tailed Rock Thrush"=>"Monticola saxatilis",
+"Red-throated Thrush/Black-throated"=>"Turdus ruficollis",
+"American Robin"=>"Turdus migratorius",
+"Zitting Cisticola"=>"Cisticola juncidis",
+"Lanceolated Warbler"=>"Locustella lanceolata",
+"River Warbler"=>"Locustella fluviatilis",
+"Blyth`s Reed Warbler"=>"Acrocephalus dumetorum",
+"Caspian Reed Warbler"=>"Acrocephalus fuscus",
+"Aquatic Warbler"=>"Acrocephalus paludicola",
+"Booted Warbler"=>"Acrocephalus caligatus",
+"Marmora's Warbler"=>"Sylvia sarda",
+"Dartford Warbler"=>"Sylvia undata",
+"Subalpine Warbler"=>"Sylvia cantillans",
+"Ménétries's Warbler"=>"Sylvia mystacea",
+"Rüppel's Warbler"=>"Sylvia rueppelli",
+"Asian Desert Warbler"=>"Sylvia nana",
+"Western Orphean Warbler"=>"Sylvia hortensis hortensis",
+"Arctic Warbler"=>"Phylloscopus borealis",
+"Radde`s Warbler"=>"Phylloscopus schwarzi",
+"Western Bonelli`s Warbler"=>"Phylloscopus bonelli",
+"Red-breasted Flycatcher"=>"Ficedula parva",
+"Eurasian Penduline Tit"=>"Remiz pendulinus",
+"Daurian Shrike"=>"Lanius isabellinus",
+"Long-Tailed Shrike"=>"Lanius schach",
+"Lesser Grey Shrike"=>"Lanius minor",
+"Southern Grey Shrike"=>"Lanius meridionalis",
+"Masked Shrike"=>"Lanius nubicus",
+"Spotted Nutcracker"=>"Nucifraga caryocatactes",
+"Daurian Jackdaw"=>"Corvus dauuricus",
+"Purple-Backed Starling"=>"Sturnus sturninus",
+"Red-Fronted Serin"=>"Serinus pusillus",
+"Arctic Redpoll"=>"Carduelis hornemanni",
+"Scottish Crossbill"=>"Loxia scotica",
+"Parrot Crossbill"=>"Loxia pytyopsittacus",
+"Black-faced Bunting"=>"Emberiza spodocephala",
+"Pink-footed Goose"=>"Anser brachyrhynchus",
+"Black-winged Kite"=>"Elanus caeruleus",
+"European Bee-eater"=>"Merops apiaster",
+"Sabine`s Gull"=>"Larus sabini",
+"Sooty Shearwater"=>"Puffinus griseus",
+"Lesser Canada Goose"=>"Branta hutchinsii",
+"Ring-necked Duck"=>"Aythya collaris",
+"Greater Flamingo"=>"Phoenicopterus roseus",
+"Iberian Chiffchaff"=>"Phylloscopus ibericus",
+"Ashy-headed Wagtail"=>"Motacilla cinereocapilla",
+"Stilt Sandpiper"=>"Calidris himantopus",
+"Siberian Stonechat"=>"Saxicola maurus",
+"Greater Yellowlegs"=>"Tringa melanoleuca",
+"Forster`s Tern"=>"Sterna forsteri",
+"Dusky Warbler"=>"Phylloscopus fuscatus",
+"Cirl Bunting"=>"Emberiza cirlus",
+"Olive-backed Pipit"=>"Anthus hodgsoni",
+"Sociable Lapwing"=>"Vanellus gregarius",
+"Spotted Sandpiper"=>"Actitis macularius",
+"Baird`s Sandpiper"=>"Calidris bairdii",
+"Rustic Bunting"=>"Emberiza rustica",
+"Yellow-browed Bunting"=>"Emberiza chrysophrys",
+"Great Shearwater"=>"Puffinus gravis",
+"Bonelli`s Eagle"=>"Aquila fasciata",
+"Calandra Lark"=>"Melanocorypha calandra",
+"Sardinian Warbler"=>"Sylvia melanocephala",
+"Ross's Gull"=>"Larus roseus",
+"Yellow-Breasted Bunting"=>"Emberiza aureola",
+"Pine Bunting"=>"Emberiza leucocephalos",
+"Black Guillemot"=>"Cepphus grylle",
+"Pied-billed Grebe"=>"Podilymbus podiceps",
+"Soft-plumaged Petrel"=>"Pterodroma mollis",
+"Bulwer's Petrel"=>"Bulweria bulwerii",
+"White-Faced Storm-Petrel"=>"Pelagodroma marina",
+"Pallas’s Fish Eagle"=>"Haliaeetus leucoryphus",
+"Sandhill Crane"=>"Grus canadensis",
+"Macqueen’s Bustard"=>"Chlamydotis macqueenii",
+"White-tailed Lapwing"=>"Vanellus leucurus",
+"Great Knot"=>"Calidris tenuirostris",
+"Semipalmated Sandpiper"=>"Calidris pusilla",
+"Red-necked Stint"=>"Calidris ruficollis",
+"Slender-billed Curlew"=>"Numenius tenuirostris",
+"Bridled Tern"=>"Onychoprion anaethetus",
+"Pallas’s Sandgrouse"=>"Syrrhaptes paradoxus",
+"European Scops Owl"=>"Otus scops",
+"Northern Hawk Owl"=>"Surnia ulula",
+"White-Throated Needletail"=>"Hirundapus caudacutus",
+"Belted Kingfisher"=>"Ceryle alcyon",
+"Blue-cheeked Bee-eater"=>"Merops persicus",
+"Black-headed Wagtail"=>"Motacilla feldegg",
+"Northern Mockingbird"=>"Mimus polyglottos",
+"Alpine Accentor"=>"Prunella collaris",
+"Red-flanked Bluetail"=>"Tarsiger cyanurus",
+"Isabelline Wheatear"=>"Oenanthe isabellina",
+"Pied Wheatear"=>"Oenanthe pleschanka",
+"Eastern Black-eared Wheatear"=>"Oenanthe melanoleuca",
+"Desert Wheatear"=>"Oenanthe deserti",
+"White`s Thrush"=>"Zoothera aurea",
+"Siberian Thrush"=>"Zoothera sibirica",
+"Eyebrowed Thrush"=>"Turdus obscurus",
+"Dusky Thrush"=>"Turdus eunomus",
+"Black-throated Thrush"=>"Turdus atrogularis",
+"Pallas`s Grasshopper Warbler"=>"Locustella certhiola",
+"Spectacled Warbler"=>"Sylvia conspicillata",
+"Two-barred Warbler"=>"Phylloscopus plumbeitarsus",
+"Eastern Bonelli’s Warbler"=>"Phylloscopus orientalis",
+"Collared Flycatcher"=>"Ficedula albicollis",
+"Wallcreeper"=>"Tichodroma muraria",
+"Turkestan Shrike"=>"Lanius phoenicuroides",
+"Steppe Grey Shrike"=>"Lanius pallidirostris",
+"Spanish Sparrow"=>"Passer hispaniolensis",
+"Red-eyed Vireo"=>"Vireo olivaceus",
+"Myrtle Warbler"=>"Dendroica coronata",
+"White-crowned Sparrow"=>"Zonotrichia leucophrys",
+"White-throated Sparrow"=>"Zonotrichia albicollis",
+"Cretzschmar`s Bunting"=>"Emberiza caesia",
+"Chestnut Bunting"=>"Emberiza rutila",
+"Red-headed Bunting"=>"Emberiza bruniceps",
+"Black-headed Bunting"=>"Emberiza melanocephala",
+"Indigo Bunting"=>"Passerina cyanea",
+"Balearic Woodchat Shrike"=>"Lanius senator badius",
+"Demoiselle Crane"=>"Grus virgo",
+"Chough"=>"Pyrrhocorax pyrrhocorax",
+"Red-Billed Chough"=>"Pyrrhocorax graculus",
+"Elegant Tern"=>"Sterna elegans",
+"Chukar"=>"Alectoris chukar",
+"Yellow-Billed Cuckoo"=>"Coccyzus americanus",
+"American Sandwich Tern"=>"Sterna sandvicensis acuflavida",
+"Olive-Tree Warbler"=>"Hippolais olivetorum",
+"Eastern Olivaceous Warbler"=>"Acrocephalus pallidus",
+"Indian Cormorant"=>"Phalacrocorax fuscicollis",
+"Spur-Winged Lapwing"=>"Vanellus spinosus",
+"Yelkouan Shearwater"=>"Puffinus yelkouan",
+"Trumpeter Finch"=>"Bucanetes githagineus",
+"Red Grouse"=>"Lagopus scoticus",
+"Rock Ptarmigan"=>"Lagopus mutus",
+"Long-Tailed Cormorant"=>"Phalacrocorax africanus",
+"Double-crested Cormorant"=>"Phalacrocorax auritus",
+"Magnificent Frigatebird"=>"Fregata magnificens",
+"Naumann's Thrush"=>"Turdus naumanni",
+"Oriental Pratincole"=>"Glareola maldivarum",
+"Bufflehead"=>"Bucephala albeola",
+"Snowfinch"=>"Montifrigilla nivalis",
+"Ural owl"=>"Strix uralensis",
+"Spanish Wagtail"=>"Motacilla iberiae",
+"Song Sparrow"=>"Melospiza melodia",
+"Rock Bunting"=>"Emberiza cia",
+"Siberian Rubythroat"=>"Luscinia calliope",
+"Pallid Swift"=>"Apus pallidus",
+"Eurasian Pygmy Owl"=>"Glaucidium passerinum",
+"Madeira Little Shearwater"=>"Puffinus baroli",
+"House Finch"=>"Carpodacus mexicanus",
+"Green Heron"=>"Butorides virescens",
+"Solitary Sandpiper"=>"Tringa solitaria",
+"Heuglin's Gull"=>"Larus heuglini"
+);
+
+function array_to_json( $array ){
+
+    if( !is_array( $array ) ){
+        return false;
+    }
+
+    $associative = count( array_diff( array_keys($array), array_keys( array_keys( $array )) ));
+    if( $associative ){
+
+        $construct = array();
+        foreach( $array as $key => $value ){
+
+            // We first copy each key/value pair into a staging array,
+            // formatting each key and value properly as we go.
+
+            // Format the key:
+            if( is_numeric($key) ){
+                $key = "key_$key";
+            }
+            $key = "'".addslashes($key)."'";
+
+            // Format the value:
+            if( is_array( $value )){
+                $value = array_to_json( $value );
+            } else if( !is_numeric( $value ) || is_string( $value ) ){
+                $value = "'".addslashes($value)."'";
+            }
+
+            // Add to staging array:
+            $construct[] = "$key: $value";
+        }
+
+        // Then we collapse the staging array into the JSON form:
+        $result = "{ " . implode( ", ", $construct ) . " }";
+
+    } else { // If the array is a vector (not associative):
+
+        $construct = array();
+        foreach( $array as $value ){
+
+            // Format the value:
+            if( is_array( $value )){
+                $value = array_to_json( $value );
+            } else if( !is_numeric( $value ) || is_string( $value ) ){
+                $value = "'".addslashes($value)."'";
+            }
+
+            // Add to staging array:
+            $construct[] = $value;
+        }
+
+        // Then we collapse the staging array into the JSON form:
+        $result = "[ " . implode( ", ", $construct ) . " ]";
+    }
+
+    return $result;
+}
+
+$result = array();
+foreach ($items as $key=>$value) {
+       if (strpos(strtolower($key), $q) !== false) {
+               array_push($result, array("id"=>$value, "label"=>$key, "value" => strip_tags($key)));
+       }
+       if (count($result) > 12)
+               break;
+}
+echo array_to_json($result);
+
+?>
\ No newline at end of file
diff --git a/demos/images/jquery_32x32.png b/demos/images/jquery_32x32.png
new file mode 100644 (file)
index 0000000..1cd42c9
Binary files /dev/null and b/demos/images/jquery_32x32.png differ
diff --git a/demos/images/jqueryui_32x32.png b/demos/images/jqueryui_32x32.png
new file mode 100644 (file)
index 0000000..23ca0f8
Binary files /dev/null and b/demos/images/jqueryui_32x32.png differ
diff --git a/demos/images/sizzlejs_32x32.png b/demos/images/sizzlejs_32x32.png
new file mode 100644 (file)
index 0000000..8d7ae1e
Binary files /dev/null and b/demos/images/sizzlejs_32x32.png differ
diff --git a/demos/images/transparent_1x1.png b/demos/images/transparent_1x1.png
new file mode 100644 (file)
index 0000000..209a438
Binary files /dev/null and b/demos/images/transparent_1x1.png differ
index d62ed9c7dc377e967765f656df0d2ee4ee288829..f3505f993cb23769093d38ccfd0bbfd8aee2b2a4 100644 (file)
@@ -11,6 +11,7 @@
        <script type="text/javascript" src="../ui/jquery.ui.mouse.js"></script>
        <script type="text/javascript" src="../ui/jquery.ui.stackfix.js"></script>
        <script type="text/javascript" src="../ui/jquery.ui.accordion.js"></script>
+       <script type="text/javascript" src="../ui/jquery.ui.autocomplete.js"></script>
        <script type="text/javascript" src="../ui/jquery.ui.button.js"></script>
        <script type="text/javascript" src="../ui/jquery.ui.datepicker.js"></script>
        <script type="text/javascript" src="../ui/jquery.ui.dialog.js"></script>
                                        <dd><a href="sortable/index.html">Sortable</a></dd>
                                <dt>Widgets</dt>
                                        <dd><a href="accordion/index.html">Accordion</a></dd>
+                                       <dd><a href="autocomplete/index.html">Autocomplete</a></dd>
                                        <dd><a href="button/index.html">Button</a></dd>
                                        <dd><a href="datepicker/index.html">Datepicker</a></dd>
                                        <dd><a href="dialog/index.html">Dialog</a></dd>
diff --git a/tests/unit/autocomplete/autocomplete.html b/tests/unit/autocomplete/autocomplete.html
new file mode 100644 (file)
index 0000000..cf03d87
--- /dev/null
@@ -0,0 +1,35 @@
+<!doctype html>\r
+<html lang="en">\r
+<head>\r
+       <title>jQuery UI Autocomplete Test Suite</title>\r
+\r
+       <script type="text/javascript" src="../../../jquery-1.3.2.js"></script>\r
+       <script type="text/javascript" src="../../../ui/jquery.ui.core.js"></script>\r
+       <script type="text/javascript" src="../../../ui/jquery.ui.widget.js"></script>\r
+       <script type="text/javascript" src="../../../ui/jquery.ui.position.js"></script>\r
+       <script type="text/javascript" src="../../../ui/jquery.ui.menu.js"></script>\r
+       <script type="text/javascript" src="../../../ui/jquery.ui.autocomplete.js"></script>\r
+\r
+       <link   type="text/css"       href="../testsuite.css" rel="stylesheet" />\r
+       <script type="text/javascript" src="../../../external/testrunner-r6588.js"></script>\r
+       <script type="text/javascript" src="../../jquery.simulate.js"></script>\r
+       <script type="text/javascript" src="../testsuite.js"></script>\r
+\r
+       <script type="text/javascript" src="autocomplete_core.js"></script>\r
+       <script type="text/javascript" src="autocomplete_defaults.js"></script>\r
+       <script type="text/javascript" src="autocomplete_events.js"></script>\r
+       <script type="text/javascript" src="autocomplete_methods.js"></script>\r
+       <script type="text/javascript" src="autocomplete_options.js"></script>\r
+       <script type="text/javascript" src="autocomplete_tickets.js"></script>\r
+       \r
+</head>\r
+<body>\r
+\r
+<div id="main">\r
+\r
+       <div><input id="autocomplete" class="foo" /></div>\r
+\r
+</div>\r
+\r
+</body>\r
+</html>\r
diff --git a/tests/unit/autocomplete/autocomplete_core.js b/tests/unit/autocomplete/autocomplete_core.js
new file mode 100644 (file)
index 0000000..6c5cccf
--- /dev/null
@@ -0,0 +1,39 @@
+/*\r
+ * autocomplete_core.js\r
+ */\r
+\r
+\r
+(function($) {\r
+\r
+module("autocomplete: core");\r
+\r
+test("close-on-blur is properly delayed", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: ["java", "javascript"]\r
+       }).val("ja").autocomplete("search");\r
+       same( $(".ui-menu:visible").length, 1 );\r
+       ac.blur();\r
+       same( $(".ui-menu:visible").length, 1 );\r
+       stop();\r
+       setTimeout(function() {\r
+               same( $(".ui-menu:visible").length, 0 );\r
+               start();\r
+       }, 200);\r
+})\r
+\r
+test("close-on-blur is cancelled when starting a search", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: ["java", "javascript"]\r
+       }).val("ja").autocomplete("search");\r
+       same( $(".ui-menu:visible").length, 1 );\r
+       ac.blur();\r
+       same( $(".ui-menu:visible").length, 1 );\r
+       ac.autocomplete("search");\r
+       stop();\r
+       setTimeout(function() {\r
+               same( $(".ui-menu:visible").length, 1 );\r
+               start();\r
+       }, 200);\r
+})\r
+\r
+})(jQuery);\r
diff --git a/tests/unit/autocomplete/autocomplete_defaults.js b/tests/unit/autocomplete/autocomplete_defaults.js
new file mode 100644 (file)
index 0000000..dc941b9
--- /dev/null
@@ -0,0 +1,12 @@
+/*\r
+ * autocomplete_defaults.js\r
+ */\r
+\r
+var autocomplete_defaults = {\r
+       delay: 300,\r
+       disabled: false,\r
+       minLength: 1,\r
+       source: undefined\r
+};\r
+\r
+commonWidgetTests('autocomplete', { defaults: autocomplete_defaults });\r
diff --git a/tests/unit/autocomplete/autocomplete_events.js b/tests/unit/autocomplete/autocomplete_events.js
new file mode 100644 (file)
index 0000000..09dd2ef
--- /dev/null
@@ -0,0 +1,120 @@
+/*\r
+ * autocomplete_events.js\r
+ */\r
+(function($) {\r
+\r
+module("autocomplete: events");\r
+\r
+var data = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "pearl"];\r
+\r
+test("all events", function() {\r
+       expect(11);\r
+       var ac = $("#autocomplete").autocomplete({\r
+               delay: 0,\r
+               source: data,\r
+               search: function(event) {\r
+                       same(event.type, "autocompletesearch");\r
+               },\r
+               open: function(event) {\r
+                       same(event.type, "autocompleteopen");\r
+               },\r
+               focus: function(event, ui) {\r
+                       same(event.type, "autocompletefocus");\r
+                       same(ui.item, {label:"java", value:"java"});\r
+               },\r
+               close: function(event) {\r
+                       same(event.type, "autocompleteclose");\r
+                       same( $(".ui-menu").length, 1 );\r
+               },\r
+               select: function(event, ui) {\r
+                       same(event.type, "autocompleteselect");\r
+                       same(ui.item, {label:"java", value:"java"});\r
+               },\r
+               change: function(event) {\r
+                       same(event.type, "autocompletechange");\r
+                       same( $(".ui-menu").length, 0 );\r
+               }\r
+       });\r
+       stop();\r
+       ac.val("ja").keydown();\r
+       setTimeout(function() {\r
+               same( $(".ui-menu").length, 1 );\r
+               ac.simulate("keydown", { keyCode: $.ui.keyCode.DOWN });\r
+               ac.simulate("keydown", { keyCode: $.ui.keyCode.ENTER });\r
+               start();\r
+       }, 50);\r
+});\r
+\r
+test("cancel search", function() {\r
+       expect(6);\r
+       var first = true;\r
+       var ac = $("#autocomplete").autocomplete({\r
+               delay: 0,\r
+               source: data,\r
+               search: function() {\r
+                       if (first) {\r
+                               same( ac.val(), "ja" );\r
+                               first = false;\r
+                               return false;\r
+                       }\r
+                       same( ac.val(), "java" );\r
+               },\r
+               open: function() {\r
+                       ok(true);\r
+               }\r
+       });\r
+       stop();\r
+       ac.val("ja").keydown();\r
+       setTimeout(function() {\r
+               same( $(".ui-menu").length, 0 );\r
+               ac.val("java").keydown();\r
+               setTimeout(function() {\r
+                       same( $(".ui-menu").length, 1 );\r
+                       same( $(".ui-menu .ui-menu-item").length, 2 );\r
+                       start();\r
+               }, 50);\r
+       }, 50);\r
+});\r
+\r
+test("cancel focus", function() {\r
+       expect(1);\r
+       var customVal = 'custom value';\r
+       var ac = $("#autocomplete").autocomplete({\r
+               delay: 0,\r
+               source: data,\r
+               focus: function(event, ui) {\r
+                       $(this).val(customVal);\r
+                       return false;\r
+               }\r
+       });\r
+       stop();\r
+       ac.val("ja").keydown();\r
+       setTimeout(function() {\r
+               ac.simulate("keydown", { keyCode: $.ui.keyCode.DOWN });\r
+               same( ac.val(), customVal );\r
+               start();\r
+       }, 50);\r
+});\r
+\r
+test("cancel select", function() {\r
+       expect(1);\r
+       var customVal = 'custom value';\r
+       var ac = $("#autocomplete").autocomplete({\r
+               delay: 0,\r
+               source: data,\r
+               select: function(event, ui) {\r
+                       $(this).val(customVal);\r
+                       return false;\r
+               }\r
+       });\r
+       stop();\r
+       ac.val("ja").keydown();\r
+       setTimeout(function() {\r
+               ac.simulate("keydown", { keyCode: $.ui.keyCode.DOWN });\r
+               ac.simulate("keydown", { keyCode: $.ui.keyCode.ENTER });\r
+               same( ac.val(), customVal );\r
+               start();\r
+       }, 50);\r
+});\r
+\r
+})(jQuery);\r
diff --git a/tests/unit/autocomplete/autocomplete_methods.js b/tests/unit/autocomplete/autocomplete_methods.js
new file mode 100644 (file)
index 0000000..61cf2a3
--- /dev/null
@@ -0,0 +1,35 @@
+/*\r
+ * autocomplete_methods.js\r
+ */\r
+(function($) {\r
+\r
+\r
+module("autocomplete: methods");\r
+\r
+test("destroy", function() {\r
+       var beforeHtml = $("#autocomplete").parent().html();\r
+       var afterHtml = $("#autocomplete").autocomplete().autocomplete("destroy").parent().html();\r
+       same( beforeHtml, afterHtml );\r
+})\r
+\r
+var data = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "pearl"];\r
+\r
+test("search", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: data,\r
+               minLength: 0\r
+       });\r
+       ac.autocomplete("search");\r
+       same( $(".ui-menu .ui-menu-item").length, data.length, "all items for a blank search" );\r
+       \r
+       ac.val("has");\r
+       ac.autocomplete("search")\r
+       same( $(".ui-menu .ui-menu-item").text(), "haskell", "only one item for set input value" );\r
+       \r
+       ac.autocomplete("search", "ja");\r
+       same( $(".ui-menu .ui-menu-item").length, 2, "only java and javascript for 'ja'" );\r
+       \r
+       $("#autocomplete").autocomplete("destroy");\r
+})\r
+\r
+})(jQuery);\r
diff --git a/tests/unit/autocomplete/autocomplete_options.js b/tests/unit/autocomplete/autocomplete_options.js
new file mode 100644 (file)
index 0000000..1b6857b
--- /dev/null
@@ -0,0 +1,174 @@
+/*\r
+ * autocomplete_options.js\r
+ */\r
+(function($) {\r
+\r
+module("autocomplete: options");\r
+\r
+\r
+/* disabled until autocomplete actually has built-in support for caching \r
+// returns at most 4 items\r
+function source(request) {\r
+       ok(true, "handling a request");\r
+       switch(request.term) {\r
+       case "cha":\r
+               return ["Common Pochard", "Common Chiffchaff", "Common Chaffinch", "Iberian Chiffchaff"]\r
+       case "chaf":\r
+       case "chaff":\r
+               return ["Common Chiffchaff", "Common Chaffinch", "Iberian Chiffchaff"]\r
+       case "chaffi":\r
+               return ["Common Chaffinch"]\r
+       case "schi":\r
+               return ["schifpre"]\r
+       }\r
+}\r
+\r
+function search(input) {\r
+       var autocomplete = input.data("autocomplete");\r
+       autocomplete.search("cha");\r
+       autocomplete.close();\r
+       autocomplete.search("chaf");\r
+       autocomplete.close();\r
+       autocomplete.search("chaff");\r
+       autocomplete.close();\r
+       autocomplete.search("chaffi");\r
+       autocomplete.close();\r
+       autocomplete.search("schi");\r
+}\r
+       \r
+test("cache: default", function() {\r
+       expect(2);\r
+       search($("#autocomplete").autocomplete({\r
+               source: source\r
+       }));\r
+});\r
+\r
+test("cache: {limit:4}", function() {\r
+       expect(3);\r
+       search($("#autocomplete").autocomplete({\r
+               cache: {\r
+                       limit: 4\r
+               },\r
+               source: source\r
+       }));\r
+});\r
+\r
+test("cache: false", function() {\r
+       expect(5);\r
+       search($("#autocomplete").autocomplete({\r
+               cache: false,\r
+               source: source\r
+       }));\r
+});\r
+*/\r
+\r
+var data = ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "pearl"];\r
+\r
+test("delay", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: data,\r
+               delay: 50\r
+       });\r
+       ac.val("ja").keydown();\r
+       \r
+       same( $(".ui-menu").length, 0 );\r
+       \r
+       // wait half a second for the default delay to open the menu\r
+       stop();\r
+       setTimeout(function() {\r
+               same( $(".ui-menu").length, 1 );\r
+               ac.autocomplete("destroy");\r
+               start();                \r
+       }, 100);\r
+});\r
+\r
+test("minLength", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: data\r
+       });\r
+       ac.autocomplete("search", "");\r
+       same( $(".ui-menu").length, 0, "blank not enough for minLength: 1" );\r
+       \r
+       ac.autocomplete("option", "minLength", 0);\r
+       ac.autocomplete("search", "");\r
+       same( $(".ui-menu").length, 1, "blank enough for minLength: 0" );\r
+       ac.autocomplete("destroy");\r
+});\r
+\r
+test("source, local string array", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: data\r
+       });\r
+       ac.val("ja").autocomplete("search");\r
+       same( $(".ui-menu .ui-menu-item").text(), "javajavascript" );\r
+       ac.autocomplete("destroy");\r
+});\r
+\r
+function source_test(source, async) {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: source\r
+       });\r
+       ac.val("ja").autocomplete("search");\r
+       function result(){\r
+               same( $(".ui-menu .ui-menu-item").text(), "javajavascript" );\r
+               ac.autocomplete("destroy");\r
+               async && start();\r
+       }\r
+       if (async) {\r
+               stop();\r
+               setTimeout(result, 100);\r
+       } else {\r
+               result();\r
+       }\r
+}\r
+\r
+test("source, local object array, only label property", function() {\r
+       source_test([\r
+               {label:"java"},\r
+               {label:"php"},\r
+               {label:"coldfusion"},\r
+               {label:"javascript"}\r
+       ]);\r
+});\r
+\r
+test("source, local object array, only value property", function() {\r
+       source_test([\r
+               {value:"java"},\r
+               {value:"php"},\r
+               {value:"coldfusion"},\r
+               {value:"javascript"}\r
+       ]);\r
+});\r
+\r
+test("source, url string with remote json string array", function() {\r
+       source_test("remote_string_array.txt", true);\r
+});\r
+\r
+test("source, url string with remote json object array, only value properties", function() {\r
+       source_test("remote_object_array_values.txt", true);\r
+});\r
+\r
+test("source, url string with remote json object array, only label properties", function() {\r
+       source_test("remote_object_array_labels.txt", true);\r
+});\r
+\r
+test("source, custom", function() {\r
+       source_test(function(request, response) {\r
+               same( request.term, "ja" );\r
+               response(["java", "javascript"]);\r
+       });\r
+});\r
+\r
+test("source, update after init", function() {\r
+       var ac = $("#autocomplete").autocomplete({\r
+               source: ["java", "javascript", "haskell"]\r
+       });\r
+       ac.val("ja").autocomplete("search");\r
+       same( $(".ui-menu .ui-menu-item").text(), "javajavascript" );\r
+       ac.autocomplete("option", "source", ["php", "asp"]);\r
+       ac.val("ph").autocomplete("search");\r
+       same( $(".ui-menu .ui-menu-item").text(), "php" );\r
+       ac.autocomplete("destroy");\r
+});\r
+\r
+})(jQuery);\r
diff --git a/tests/unit/autocomplete/autocomplete_tickets.js b/tests/unit/autocomplete/autocomplete_tickets.js
new file mode 100644 (file)
index 0000000..78f5741
--- /dev/null
@@ -0,0 +1,10 @@
+/*\r
+ * autocomplete_tickets.js\r
+ */\r
+(function($) {\r
+\r
+module("autocomplete: tickets");\r
+\r
+\r
+\r
+})(jQuery);\r
diff --git a/tests/unit/autocomplete/remote_object_array_labels.txt b/tests/unit/autocomplete/remote_object_array_labels.txt
new file mode 100644 (file)
index 0000000..502496c
--- /dev/null
@@ -0,0 +1 @@
+[{"label":"java"},{"label":"javascript"}]
\ No newline at end of file
diff --git a/tests/unit/autocomplete/remote_object_array_values.txt b/tests/unit/autocomplete/remote_object_array_values.txt
new file mode 100644 (file)
index 0000000..029cbb9
--- /dev/null
@@ -0,0 +1 @@
+[{"value":"java"},{"value":"javascript"}]
\ No newline at end of file
diff --git a/tests/unit/autocomplete/remote_string_array.txt b/tests/unit/autocomplete/remote_string_array.txt
new file mode 100644 (file)
index 0000000..3b24c8e
--- /dev/null
@@ -0,0 +1 @@
+["java", "javascript"]
\ No newline at end of file
diff --git a/tests/visual/autocomplete/default.html b/tests/visual/autocomplete/default.html
new file mode 100644 (file)
index 0000000..32b8db0
--- /dev/null
@@ -0,0 +1,70 @@
+<!doctype html>
+<html>
+<head>
+       <title>Autocomplete Visual Test: Default</title>
+       <link rel="stylesheet" href="../visual.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.widget.js"></script>
+       <script type="text/javascript" src="../../../ui/jquery.ui.position.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() {
+               $.fn.themeswitcher && $('<div/>').css({
+                       position: "absolute",
+                       right: 10,
+                       top: 10
+               }).appendTo(document.body).themeswitcher();
+               
+               function log(message) {
+                       $("<div/>").text(message).prependTo("#log");
+                       $("#log").attr("scrollTop", 0);
+               }
+               
+               function enable() {
+                       $("#tags").autocomplete({
+                               source: ["c++", "java", "php", "coldfusion", "javascript", "asp", "ruby", "python", "c", "scala", "groovy", "haskell", "pearl"],
+                               delay: 0,
+                               search: function() {
+                                       log("Searching for: " + this.value);
+                               },
+                               open: function() {
+                                       log("Found something");
+                               },
+                               focus: function(event, ui) {
+                                       log("Moving focus to " + ui.item.label);
+                               },
+                               close: function() {
+                                       log("Hiding suggestions");
+                               },
+                               change: function(event, ui) {
+                                       log(ui.item ? ("Selected: " + ui.item.value) : "Nothing selected, input was " + this.value);
+                               }
+                       });
+               }
+               enable();
+               $("#toggle").toggle(function() {
+                       $("#tags").autocomplete("destroy");
+               }, enable);
+       });
+       </script>
+</head>
+<body>
+
+<div class="ui-widget">
+       <label for="tags">Tags: </label>
+       <input class="ui-widget ui-widget-content ui-corner-all" id="tags" />
+</div>
+
+<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>
+
+<button id="toggle">Toggle widget</button>
+
+</body>
+</html>
diff --git a/themes/base/ui.autocomplete.css b/themes/base/ui.autocomplete.css
new file mode 100644 (file)
index 0000000..0f487c9
--- /dev/null
@@ -0,0 +1,33 @@
+/* Autocomplete
+----------------------------------*/
+.ui-autocomplete-menu { position: absolute; cursor: default; } 
+
+.ui-autocomplete-loading { background: white url('images/ui-anim.basic.16x16.gif') right center no-repeat; }
+.ui-autocomplete-over { background-color: #0A246A; color: white; }
+
+/* Menu
+----------------------------------*/
+.ui-menu {
+       list-style:none;
+       padding: 2px;
+       margin: 0;
+       display:block;
+}
+.ui-menu .ui-menu {
+       margin-top: -3px;
+}
+.ui-menu .ui-menu-item {
+       margin:0;
+       padding: 0;
+       width: 100%;
+}
+.ui-menu .ui-menu-item a {
+       text-decoration:none;
+       display:block;
+       padding:.2em .4em;
+       line-height:1.5;
+}
+.ui-menu .ui-menu-item a.ui-state-hover,
+.ui-menu .ui-menu-item a.ui-state-active {
+       margin: -1px;
+}
index 2d49a753c6bd992887bedc0f0f7cd47719ad1118..d8dc173dbb8033562a8d32b6e298c018e93afc33 100644 (file)
@@ -1,6 +1,7 @@
 @import url("ui.core.css");
 
 @import url("ui.accordion.css");
+@import url("ui.autocomplete.css");
 @import url("ui.button.css");
 @import url("ui.datepicker.css");
 @import url("ui.dialog.css");
diff --git a/ui/jquery.ui.autocomplete.js b/ui/jquery.ui.autocomplete.js
new file mode 100644 (file)
index 0000000..3a6c560
--- /dev/null
@@ -0,0 +1,445 @@
+/*
+ * jQuery UI Autocomplete @VERSION
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Autocomplete
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *     jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.autocomplete", {
+       options: {
+               minLength: 1,
+               delay: 300
+       },
+       _init: function() {
+               var self = this;
+               this.element
+                       .addClass("ui-autocomplete ui-widget ui-widget-content ui-corner-all")
+                       .attr("autocomplete", "off")
+                       // TODO verify these actually work as intended
+                       .attr({
+                               role: "textbox",
+                               "aria-autocomplete": "list",
+                               "aria-haspopup": "true"
+                       })
+                       .bind("keydown.autocomplete", function(event) {
+                               var keyCode = $.ui.keyCode;
+                               switch(event.keyCode) {
+                               case keyCode.PAGE_UP:
+                                       self._move("previousPage", event);
+                                       break;
+                               case keyCode.PAGE_DOWN:
+                                       self._move("nextPage", event);
+                                       break;
+                               case keyCode.UP:
+                                       self._move("previous", event);
+                                       // prevent moving cursor to beginning of text field in some browsers
+                                       event.preventDefault();
+                                       break;
+                               case keyCode.DOWN:
+                                       self._move("next", event);
+                                       // prevent moving cursor to end of text field in some browsers
+                                       event.preventDefault();
+                                       break;
+                               case keyCode.ENTER:
+                                       // when menu is open or has focus
+                                       if (self.menu && self.menu.active) {
+                                               event.preventDefault();
+                                       }
+                               case keyCode.TAB:
+                                       if (!self.menu || !self.menu.active) {
+                                               return;
+                                       }
+                                       self.menu.select();
+                                       break;
+                               case keyCode.ESCAPE:
+                                       self.element.val(self.term);
+                                       self.close(event);
+                                       break;
+                               case 16:
+                               case 17:
+                               case 18:
+                                       // ignore metakeys (shift, ctrl, alt)
+                                       break;
+                               default:
+                                       // keypress is triggered before the input value is changed
+                                       clearTimeout(self.searching);
+                                       self.searching = setTimeout(function() {
+                                               self.search(null, event);
+                                       }, self.options.delay);
+                                       break;
+                               }
+                       })
+                       .bind("focus.autocomplete", function() {
+                               self.previous = self.element.val();
+                       })
+                       .bind("blur.autocomplete", function(event) {
+                               clearTimeout(self.searching);
+                               // 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() {
+                                       self.close(event);
+                               }, 150);
+                       });
+               this._initSource();
+               this.response = function() {
+                       return self._response.apply(self, arguments);
+               };
+       },
+
+       destroy: function() {
+               this.element
+                       .removeClass("ui-autocomplete ui-widget ui-widget-content ui-corner-all")
+                       .removeAttr("autocomplete")
+                       .removeAttr("role")
+                       .removeAttr("aria-autocomplete")
+                       .removeAttr("aria-haspopup");
+               if (this.menu) {
+                       this.menu.element.remove();
+               }
+               $.Widget.prototype.destroy.call(this);
+       },
+
+       _setOption: function(key) {
+               $.Widget.prototype._setOption.apply(this, arguments);
+               if (key == "source") {
+                       this._initSource();
+               }
+       },
+
+       _initSource: function() {
+               if ($.isArray(this.options.source)) {
+                       var array = this.options.source;
+                       this.source = function(request, response) {
+                               // escape regex characters
+                               var matcher = new RegExp($.ui.autocomplete.escapeRegex(request.term), "i");
+                               response($.grep(array, function(value) {
+                               return matcher.test(value.value || value.label || value);
+                               }));
+                       };
+               } else if (typeof this.options.source == "string") {
+                       var url = this.options.source;
+                       this.source = function(request, response) {
+                               $.getJSON(url, request, response);
+                       };
+               } else {
+                       this.source = this.options.source;
+               }
+       },
+
+       search: function(value, event) {
+               value = value != null ? value : this.element.val();
+               if (value.length < this.options.minLength) {
+                       return this.close(event);
+               }
+
+               clearTimeout(this.closing);
+               if (this._trigger("search") === false) {
+                       return;
+               }
+
+               return this._search(value);
+       },
+
+       _search: function(value) {
+               this.term = this.element
+                       .addClass("ui-autocomplete-loading")
+                       // always save the actual value, not the one passed as an argument
+                       .val();
+
+               this.source({ term: value }, this.response);
+       },
+
+       _response: function(content) {
+               if (content.length) {
+                       content = this._normalize(content);
+                       this._trigger("open");
+                       this._suggest(content);
+               } else {
+                       this.close();
+               }
+               this.element.removeClass("ui-autocomplete-loading");
+       },
+
+       close: function(event) {
+               clearTimeout(this.closing);
+               if (this.menu) {
+                       this._trigger("close", event);
+                       this.menu.element.remove();
+                       this.menu = null;
+               }
+               if (this.previous != this.element.val()) {
+                       this._trigger("change", event);
+               }
+       },
+
+       _normalize: function(items) {
+               // assume all items have the right format when the first item is complete
+               if (items.length && items[0].label && items[0].value) {
+                       return items;
+               }
+               return $.map(items, function(item) {
+                       if (typeof item == "string") {
+                               return {
+                                       label: item,
+                                       value: item
+                               };
+                       }
+                       return $.extend({
+                               label: item.label || item.value,
+                               value: item.value || item.label
+                       }, item);
+               });
+       },
+
+       _suggest: function(items) {
+               (this.menu && this.menu.element.remove());
+               var self = this,
+                       ul = $("<ul></ul>"),
+                       parent = this.element.parent();
+
+               $.each(items, function(index, item) {
+                       $("<li></li>")
+                               .data("item.autocomplete", item)
+                               .append("<a>" + item.label + "</a>")
+                               .appendTo(ul);
+               });
+               this.menu = ul
+                       .addClass("ui-autocomplete-menu")
+                       .appendTo(parent)
+                       .menu({
+                               focus: function(event, ui) {
+                                       var item = ui.item.data("item.autocomplete");
+                                       if (false !== self._trigger("focus", null, { item: item })) {
+                                               // use value to match what will end up in the input
+                                               self.element.val(item.value);
+                                       }
+                               },
+                               selected: function(event, ui) {
+                                       var item = ui.item.data("item.autocomplete");
+                                       if (false !== self._trigger('select', event, { item: item })) {
+                                               self.element.val( item.value );
+                                       }
+                                       self.close(event);
+                                       self.previous = self.element.val();
+                                       // only trigger when focus was lost (click on menu)
+                                       if (self.element[0] != document.activeElement) {
+                                               self.element.focus();
+                                       }
+                               }
+                       })
+                       // workaround for jQuery bug #5781 http://dev.jquery.com/ticket/5781
+                       .css({ top: 0, left: 0 })
+                       .position({
+                               my: "left top",
+                               at: "left bottom",
+                               of: this.element
+                       })
+                       .data("menu");
+               if (ul.width() <= this.element.width()) {
+                       ul.width(this.element.width());
+               }
+       },
+
+       _move: function(direction, event) {
+               if (!this.menu) {
+                       this.search(null, event);
+                       return;
+               }
+               if (this.menu.first() && /^previous/.test(direction)
+                               || this.menu.last() && /^next/.test(direction)) {
+                       this.element.val(this.term);
+                       this.menu.deactivate();
+                       return;
+               }
+               this.menu[direction]();
+       },
+
+       widget: function() {
+               // return empty jQuery object when menu isn't initialized yet
+               return this.menu && this.menu.element || $([]);
+       }
+});
+
+$.extend($.ui.autocomplete, {
+       escapeRegex: function(value) {
+               return value.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1");
+       }
+});
+
+})(jQuery);
+
+/*
+ * jQuery UI Menu (not officially released)
+ * 
+ * This widget isn't yet finished and the API is subject to change. We plan to finish
+ * it for the next release. You're welcome to give it a try anyway and give us feedback,
+ * as long as you're okay with migrating your code later on. We can help with that, too.
+ *
+ * Copyright (c) 2009 AUTHORS.txt (http://jqueryui.com/about)
+ * Dual licensed under the MIT (MIT-LICENSE.txt)
+ * and GPL (GPL-LICENSE.txt) licenses.
+ *
+ * http://docs.jquery.com/UI/Menu
+ *
+ * Depends:
+ *     jquery.ui.core.js
+ *  jquery.ui.widget.js
+ */
+(function($) {
+
+$.widget("ui.menu", {
+       _init: function() {
+               var self = this;
+               this.element
+                       .addClass("ui-menu ui-widget ui-widget-content ui-corner-all")
+                       .attr({
+                               role: "menu",
+                               "aria-activedescendant": "ui-active-menuitem"
+                       })
+                       .click(function(e) {
+                               // temporary
+                               e.preventDefault();
+                               self.select();
+                       });
+               var items = this.element.children("li")
+                       .addClass("ui-menu-item")
+                       .attr("role", "menuitem");
+               
+               items.children("a")
+                       .addClass("ui-corner-all")
+                       .attr("tabindex", -1)
+                       // mouseenter doesn't work with event delegation
+                       .mouseenter(function() {
+                               self.activate($(this).parent());
+                       });
+       },
+
+       activate: function(item) {
+               this.deactivate();
+               this.active = item.eq(0)
+                       .children("a")
+                               .addClass("ui-state-hover")
+                               .attr("id", "ui-active-menuitem")
+                       .end();
+               this._trigger("focus", null, { item: item });
+               if (this.hasScroll()) {
+                       var offset = item.offset().top - this.element.offset().top,
+                               scroll = this.element.attr("scrollTop"),
+                               elementHeight = this.element.height();
+                       if (offset < 0) {
+                               this.element.attr("scrollTop", scroll + offset);
+                       } else if (offset > elementHeight) {
+                               this.element.attr("scrollTop", scroll + offset - elementHeight + item.height());
+                       }
+               }
+       },
+
+       deactivate: function() {
+               if (!this.active) { return; }
+
+               this.active.children("a")
+                       .removeClass("ui-state-hover")
+                       .removeAttr("id");
+               this.active = null;
+       },
+
+       next: function() {
+               this.move("next", "li:first");
+       },
+
+       previous: function() {
+               this.move("prev", "li:last");
+       },
+
+       first: function() {
+               return this.active && !this.active.prev().length;
+       },
+
+       last: function() {
+               return this.active && !this.active.next().length;
+       },
+
+       move: function(direction, edge) {
+               if (!this.active) {
+                       this.activate(this.element.children(edge));
+                       return;
+               }
+               var next = this.active[direction]();
+               if (next.length) {
+                       this.activate(next);
+               } else {
+                       this.activate(this.element.children(edge));
+               }
+       },
+
+       // TODO merge with previousPage
+       nextPage: function() {
+               if (this.hasScroll()) {
+                       // TODO merge with no-scroll-else
+                       if (!this.active || this.last()) {
+                               this.activate(this.element.children(":first"));
+                               return;
+                       }
+                       var base = this.active.offset().top,
+                               height = this.element.height(),
+                               result = this.element.children("li").filter(function() {
+                                       var close = $(this).offset().top - base - height + $(this).height();
+                                       // TODO improve approximation
+                                       return close < 10 && close > -10;
+                               });
+
+                       // TODO try to catch this earlier when scrollTop indicates the last page anyway
+                       if (!result.length) {
+                               result = this.element.children(":last");
+                       }
+                       this.activate(result);
+               } else {
+                       this.activate(this.element.children(!this.active || this.last() ? ":first" : ":last"));
+               }
+       },
+
+       // TODO merge with nextPage
+       previousPage: function() {
+               if (this.hasScroll()) {
+                       // TODO merge with no-scroll-else
+                       if (!this.active || this.first()) {
+                               this.activate(this.element.children(":last"));
+                               return;
+                       }
+
+                       var base = this.active.offset().top,
+                               height = this.element.height();
+                               result = this.element.children("li").filter(function() {
+                                       var close = $(this).offset().top - base + height - $(this).height();
+                                       // TODO improve approximation
+                                       return close < 10 && close > -10;
+                               });
+
+                       // TODO try to catch this earlier when scrollTop indicates the last page anyway
+                       if (!result.length) {
+                               result = this.element.children(":first");
+                       }
+                       this.activate(result);
+               } else {
+                       this.activate(this.element.children(!this.active || this.first() ? ":last" : ":first"));
+               }
+       },
+
+       hasScroll: function() {
+               return this.element.height() < this.element.attr("scrollHeight");
+       },
+
+       select: function() {
+               this._trigger("selected", null, { item: this.active });
+       }
+});
+
+})(jQuery);
\ No newline at end of file