31 Commits

Author SHA1 Message Date
d01f9ee30a Merge pull request #1 from emilis/master
Fixed incorrect parameters in activate_selected() call when pressing ENTER
2012-05-07 16:08:11 -07:00
Emilis Dambauskas
600c3c2486 Fixed incorrect parameters in activate_selected() call when pressing ENTER. 2012-04-16 20:56:55 +03:00
2ca2566ae0 Merge branch 'dev' 2011-04-01 00:00:50 +01:00
13454ae718 fixed link to demo page in readme 2011-04-01 00:00:39 +01:00
10d4620917 Merge branch 'dev' 2011-03-31 23:49:55 +01:00
b3d34e6b99 fixed a typo in readme 2011-03-31 23:49:47 +01:00
41c701d7f2 Merge branch 'release/v0.2.1' into dev 2011-03-31 23:44:05 +01:00
92a1daf48f Merge branch 'release/v0.2.1' 2011-03-31 23:44:00 +01:00
0ddd423e11 fixed copyright year :P 2011-03-31 23:43:48 +01:00
c73182341f started release v0.2.1 2011-03-31 23:43:34 +01:00
807c3b5b36 auto-highlight when there is only one result so it can be selected by pressing enter/return 2011-03-31 23:42:27 +01:00
33aefc7bbe updated readme and license files 2011-03-31 23:22:14 +01:00
ab085dbeaf updated Rakefile to reflect project name change 2011-03-31 23:22:02 +01:00
37d1d562fb I have become an indentation nazi 2011-03-31 23:21:26 +01:00
2a615049cb updated jquery to 1.5.2 2011-03-31 23:20:47 +01:00
42d79e670b Renamed project to Fancy Input.
* Major restructuring of the plugin objects for
easier extending.
* Bumped version to v0.2.0.
2010-05-18 02:13:21 +03:00
65d4f6daef bumped version to v0.1.3 2010-03-15 14:19:08 +02:00
fb543ac128 added rake file and updated .gitignore 2010-03-15 14:18:52 +02:00
1f7899e21e updated demo page 2010-03-15 13:49:00 +02:00
01e7282f2f tweaked css a bit for easier customization 2010-03-15 13:48:38 +02:00
89a146d85a improved mustache string matching 2010-03-15 13:48:21 +02:00
ae27ad890c improved redirect_to method and attached it to the
window object
2010-03-15 13:48:07 +02:00
096e625385 added position options 2010-03-15 13:46:48 +02:00
699877ec8a improved exact_match filtering 2010-03-15 13:46:24 +02:00
d1a6f8b003 cleaned up the code a bit 2010-03-15 13:46:08 +02:00
d3cf0ee007 updated todo list in readme 2010-03-10 23:11:04 +02:00
c1e25e30c1 bumped version to v0.1.2 2010-03-10 22:57:47 +02:00
d9e5bf3f22 fixed a serious bug with form submission and
return/enter key handling
2010-03-10 22:57:23 +02:00
05a0ccfe06 bumped version to v0.1.1 2010-03-10 19:45:01 +02:00
7b7ad0a285 better handling when both keyboard and mouse is
used to navigate suggested results
2010-03-10 19:44:34 +02:00
50e0711be0 fixed some typos in readme 2010-03-10 19:37:49 +02:00
12 changed files with 625 additions and 598 deletions

2
.gitignore vendored
View File

@@ -1,2 +1,2 @@
.DS_Store .DS_Store
_releases

View File

@@ -1,6 +1,6 @@
(The MIT License) (The MIT License)
Copyright (c) 2009 Jim Myhrberg. Copyright (c) 2011 Jim Myhrberg.
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the

View File

@@ -1,4 +1,4 @@
# Suggest Results # Fancy Input / Fancy Suggest
Easily customizable search suggestion plugin for jQuery, which suggests results directly, rather than search terms. Easily customizable search suggestion plugin for jQuery, which suggests results directly, rather than search terms.
@@ -10,18 +10,19 @@ Check out the [Demo][].
First of all you will need [jQuery][], visit their [site][jquery] for more info. First of all you will need [jQuery][], visit their [site][jquery] for more info.
Then include both `jquery.suggest_results.js` and `jquery.suggest_results.css` on your page. Then include the required files:
<script src="/suggest_results/jquery.suggest_results.js" type="text/javascript"></script> <script src="/fancy_input/jquery.fancy_input.js" type="text/javascript"></script>
<link href="/suggest_results/jquery.suggest_results.css" rel="stylesheet" type="text/css" /> <script src="/fancy_input/jquery.fancy_suggest.js" type="text/javascript"></script>
<link href="/fancy_input/jquery.fancy_suggest.css" rel="stylesheet" type="text/css" />
## Usage ## Usage
### Local Javascript Array ### Local Javascript Array
To use Suggest Results, simply call it on a targeted text input element. For example if you have a search box for a users friends: To use Fancy Results, simply call it on a targeted text input element. For example if you have a search box for a users friends:
$("#search_friends").suggest_results({ $("#search_friends").fancy_suggest({
data: userFriends data: userFriends
}); });
@@ -32,43 +33,43 @@ In the above example, `userFriends` is a javascript array that would look someth
{title: "Mike Smith", href: "/user/msmith", match: "msmith mikesmith@gmail.com"} {title: "Mike Smith", href: "/user/msmith", match: "msmith mikesmith@gmail.com"}
]; ];
This array needs to have two vital attributes, `title` and `href`. When filtering results based on what's been typed in the input field, the `title` and `match` attributes are used. `match` is optional, but `title` is required. `href` is the url to redirect the browser too when the result is selected. This array needs to have two vital attributes, `title` and `href`. When filtering results based on what's been typed in the input field, the `title` and `match` attributes are used. `match` is optional, but `title` is required. `href` is the url to redirect the browser to when the result is selected.
The `match` attribute is useful when you need to include extra information like a username or email and get results based on this data without having to display it. The `match` attribute is useful when you need to include extra information like a username or email and get results based on this data without having to display it.
### Ajax Call ### Ajax Call
To fetch results via an Ajax call, attach Suggest Results like this to your input element: To fetch results via an Ajax call, attach Fancy Results like this to your input element:
$("#search_friends").suggest_results({ $("#search_friends").fancy_suggest({
url: "/search_friends_json.php" url: "/search_friends_json.php"
}); });
As a user starts typing, this will trigger a GET request to `/search_friends/json?limit=6&search=j` if the user has started typing `j`. We'll get to the `limit` option a bit later. As a user starts typing, this will trigger a GET request to `/search_friends/json?limit=6&search=j` if the user has started typing `j`. We'll get to the `limit` option a bit later.
Output from `search_friends_json.php` must be in JSON, and look like this: Output from `search_friends_json.php` must be in JSON, and look something like this:
{"results": [ {"results": [
{"title": "John Doe", "href": "/user/johndoe"}, {"title": "John Doe", "href": "/user/johndoe"},
{"title": "Mike Smith", "href": "/user/msmith"} {"title": "Mike Smith", "href": "/user/msmith"}
]} ]}
Notice how the `match` attribute is not included, as it's not supported for server-side result fetching. No filtering is done in javascript of the results returned, hence `match` is useless. You should do all filtering and order server-side if going ajax-style. Notice how the `match` attribute is not included, as it's not supported for server-side result fetching. No filtering is done client-side of the results returned, hence `match` is useless. You should do all filtering and ordering server-side if going ajax-style.
Also, a query cache is used so a specific search term is only requested once per page load. Otherwise new ajax calls would be triggered every for each time a user hits the backspace key to remove a letter. Also, a query cache is used so a specific search term is only requested once per page load. Otherwise new ajax calls would be triggered each time a user hits the backspace key to remove a letter for example.
## Options ## Options
There's a number of options you can pass `$.suggest_results`. There's a number of options you can pass `$.fancy_suggest()`.
* **data:** Javascript array with available results. * **data:** Javascript array with available results.
* **url:** URL to send ajax request to for results. Either `url` or `data` are required for Suggest Results to work at all. * **url:** URL to send ajax request to for results. Either `url` or `data` are required for Fancy Results to work at all.
* **name:** When used, the value is used as the class for the suggest box. Useful for having a custom styled suggest box one of two or more input fields with suggestion turned on. * **name:** When used, the value is used as the class for the suggest box. Useful for having a custom styled suggest box one of two or more input fields with suggestion turned on.
* **exact_match:** Results much be an exact match to typed input. If disabled any one word typed is a value match. Enabled by default, and has no effect when fetching results via Ajax. * **exact_match:** Results much be an exact match to typed input. If disabled any one word typed is a value match. Enabled by default, and has no effect when fetching results via Ajax.
* **limit:** Limit the number of suggestions shown. When using the Ajax method, an extra `limit` GET/POST var is supplied. * **limit:** Limit the number of suggestions shown. When using the Ajax method, an extra `limit` GET/POST var is supplied.
* **no_results:** When set to true, a "No Results" label is shown if entered text doesn't yield any results. Enabled by default. * **no\_results:** When set to true, a "No Results" label is shown if entered text doesn't yield any results. Enabled by default.
* **no\_results\_label:** Text shown when there are no results if `no_results` is enabled. Default is "No Results". * **no\_results\_label:** Text shown when there are no results if `no_results` is enabled. Default is "No Results".
* **url_method:** URL method used for Ajax call. Set to "get" or "post". Default is "get". * **url\_method:** URL method used for Ajax call. Set to "get" or "post". Default is "get".
## Customization ## Customization
@@ -83,7 +84,7 @@ For example, if we want to display some extra info underneath our friend's names
When we attach the suggestions to our input element, we specify the `tpl_result_body` option: When we attach the suggestions to our input element, we specify the `tpl_result_body` option:
$("#search_friends").suggest_results({ $("#search_friends").fancy_suggest({
url: "/search_friends_json.php", url: "/search_friends_json.php",
name: "search_friends", name: "search_friends",
tpl_result_body: '<span class="title">{{title}}</span><span class="info">{{info}}</span>' tpl_result_body: '<span class="title">{{title}}</span><span class="info">{{info}}</span>'
@@ -94,11 +95,11 @@ In the `tpl_result_body` option, `{{title}}` is replaced with the `title` attrib
Then to prettify it, we add some CSS: Then to prettify it, we add some CSS:
/* Effects all suggestion boxes */ /* Effects all suggestion boxes */
#suggest_results li span.info { #fancy_suggest li span.info {
color: #888; color: #888;
} }
/* Effects only the #search_friends suggestion box */ /* Effects only the #search_friends suggestion box */
#suggest_results.search_friends li { #fancy_suggest.search_friends li {
border-bottom-style: dashed; border-bottom-style: dashed;
} }
@@ -109,6 +110,8 @@ There are some more options available too for customization, so I recommend you
* Better documentation and readme. * Better documentation and readme.
* Handle mouse hovering and keyboard navigation a bit better when used at the same time on a suggest box. * Handle mouse hovering and keyboard navigation a bit better when used at the same time on a suggest box.
* Support suggesting search terms in addition to currently only supporting results. * Support suggesting search terms in addition to currently only supporting results.
* Add callbacks for most things.
* Build other "fancy" input methods on top of `$.fancy_text()`.
## Notice ## Notice
@@ -118,7 +121,7 @@ I wrote this plugin in about 6-7 hours, so things could be a bit stupid. But at
(The MIT License) (The MIT License)
Copyright (c) 2009 Jim Myhrberg. Copyright (c) 2011 Jim Myhrberg.
Permission is hereby granted, free of charge, to any person obtaining Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the a copy of this software and associated documentation files (the
@@ -141,5 +144,5 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
[jquery]: http://jquery.com/ [jquery]: http://jquery.com/
[demo]: http://files.jimeh.me/projects/suggest_results/demo/ [demo]: http://files.jimeh.me/projects/fancy_input/demo/fancy_suggest.html

42
Rakefile Normal file
View File

@@ -0,0 +1,42 @@
require 'fileutils'
RLS_PATH = "_releases"
RLS_IGNORE = ["#{RLS_PATH}/*", ".git*", "*.DS_Store", "Rakefile", "tmp/*"]
desc "Build a release package"
task :release do
FileUtils.mkdir_p(RLS_PATH)
file = File.read("fancy_input/jquery.fancy_input.js")
if file =~ /\* Fancy Input v([0-9\.]+)\n/
version = $1
target = "#{RLS_PATH}/jquery.fancy_input-#{version}.zip"
if File.exist?(target)
puts "ERROR: #{target} already exists."
else
ignore = RLS_IGNORE.map { |i| "-x \"#{i}\"" }.join(" ")
system("zip #{ignore} -r #{target} .")
puts "packaged #{target}"
end
end
end
desc "Update demo page."
task :demo do
rsync(".", "jimeh@jimeh.me:jimeh.me/files/projects/fancy_input", ["--exclude='#{RLS_PATH}'",
"--exclude='tmp'",
"--delete"])
end
def rsync(source, dest, options = [])
if source.is_a?(Array)
source.map! { |dir, i| "\"#{dir}\"" }
source = source.join(" ")
end
options << "--exclude='.DS_Store'"
options << "--exclude='.git*'"
system "rsync -vr #{options.join(" ")} #{source} #{dest}"
end

View File

@@ -1,4 +1,4 @@
var data = [ var exampleData = [
{"title": "Ädams, Egbert", "info": "Bedfordshire", "href": "/demo/user/1", "match": "zzzz"}, {"title": "Ädams, Egbert", "info": "Bedfordshire", "href": "/demo/user/1", "match": "zzzz"},
{"title": "Altman, Alisha", "info": "Buckinghamshire", "href": "/demo/user/2", "match": "zzzz"}, {"title": "Altman, Alisha", "info": "Buckinghamshire", "href": "/demo/user/2", "match": "zzzz"},
{"title": "Archibald, Janna", "info": "Cambridgeshire", "href": "/demo/user/3", "match": "zzzz"}, {"title": "Archibald, Janna", "info": "Cambridgeshire", "href": "/demo/user/3", "match": "zzzz"},

View File

@@ -5,33 +5,41 @@
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Suggest Results Demo</title> <title>Fancy Suggest Demo</title>
<link rel="stylesheet" href="demo.css" type="text/css" media="screen" title="no title" charset="utf-8"> <link rel="stylesheet" href="demo.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script src="../javascripts/jquery.js" type="text/javascript" charset="utf-8"></script> <script src="../javascripts/jquery.js" type="text/javascript" charset="utf-8"></script>
<script src="../suggest_results/jquery.suggest_results.js" type="text/javascript" charset="utf-8"></script> <script src="../fancy_input/jquery.fancy_input.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="../suggest_results/jquery.suggest_results.css" type="text/css" media="screen" title="no title" charset="utf-8"> <script src="../fancy_input/jquery.fancy_suggest.js" type="text/javascript" charset="utf-8"></script>
<link rel="stylesheet" href="../fancy_input/jquery.fancy_suggest.css" type="text/css" media="screen" title="no title" charset="utf-8">
<script src="demo.js" type="text/javascript" charset="utf-8"></script> <script src="demo.js" type="text/javascript" charset="utf-8"></script>
<script type="text/javascript" charset="utf-8"> <script type="text/javascript" charset="utf-8">
$(document).ready(function(){ $(document).ready(function(){
$("#example1").suggest_results({ $("#example1").fancy_suggest({
data: data data: exampleData
}); });
$("#example2").suggest_results({ $("#example2").fancy_suggest({
url: "server-side.php" url: "server-side.php",
}); });
$("#example3").suggest_results({ $("#example3").fancy_suggest({
url: "server-side.php", url: "server-side.php",
name: "example3", name: "example3",
empty: false, no_results: false,
limit: 4, limit: 4,
tpl_result_body: '<span class="title">{{title}}</span><span class="info">{{info}}</span>' tpl_result_body: '<span class="title">{{title}}</span><span class="info">{{ info }}</span>'
});
$("#example4").fancy_suggest({
data: exampleData,
pos_top: 0,
pos_left: 20,
width: 168
}); });
}); });
@@ -40,10 +48,10 @@
<style type="text/css" media="screen"> <style type="text/css" media="screen">
/* Example 3 */ /* Example 3 */
#suggest_results li span.info { #fancy_suggest li span.info {
color: #888; color: #888;
} }
#suggest_results.example3 li { #fancy_suggest.example3 li {
border-bottom-style: dashed; border-bottom-style: dashed;
} }
@@ -55,7 +63,7 @@
<div id="container"> <div id="container">
<p></p> <p></p>
<h1>Suggest Results Demo</h1> <h1>Fancy Suggest Demo</h1>
<p> <p>
<strong>Results from local array:</strong><br /> <strong>Results from local array:</strong><br />
<input type="text" name="example1" value="" id="example1" /> <input type="text" name="example1" value="" id="example1" />
@@ -69,7 +77,8 @@
<input type="text" name="example3" value="" id="example3" /> <input type="text" name="example3" value="" id="example3" />
</p> </p>
<p> <p>
<a href="http://github.com/jimeh/suggest_results">GitHub Project Page</a> <strong>Customized position with results from local array:</strong><br />
<input type="text" name="example4" value="" id="example4" />
</p> </p>
</div> </div>

View File

@@ -0,0 +1,126 @@
/*!
* Fancy Input v0.2.1
*
* Copyright (c) 2011 Jim Myhrberg.
* Released under the MIT license.
*/
(function($){
$.fn.fancy_input = {
timeout: null,
default_options: {},
keys: {
BACKSPACE: 8,
TAB: 9,
RETURN: 13,
ENTER: 13,
SHIFT: 16,
CTRL: 17,
ALT: 18,
PAUSE: 19,
BREAK: 19,
CAPSLOCK: 20,
ESC: 27,
PAGEUP: 33,
PAGEDOWN: 34,
END: 35,
HOME: 36,
ARROW_LEFT: 37,
ARROW_UP: 38,
ARROW_RIGHT: 39,
ARROW_DOWN: 40,
INSERT: 45,
DELETE: 46,
SPECIALS_END: 47
},
elm_uid: function(elm){
if (elm.attr("id") !== "") {
return "ID_" + elm.attr("id");
} else if (elm.attr("class") !== "") {
return "CL_" + elm.attr("class");
} else if (elm.attr("name") !== "") {
return "NA_" + elm.attr("name");
};
return "";
},
setTimeout: function(callback, delay){
this.clearTimeout();
this.timeout = setTimeout(callback, delay);
},
clearTimeout: function(){
if (this.timeout !== null) {
clearTimeout(this.timeout);
this.timeout = null;
};
},
mustache: function(string, data){
if (typeof(string) === "string" && typeof(data) === "object") {
for (var key in data) {
string = string.replace(new RegExp("{{\\s*" + key + "\\s*}}", "g"), data[key]);
}
};
return string;
},
replace_elm: function(target, replacement){
if (typeof(target.attr("id")) !== "undefined" && target.attr("id") !== "") {
replacement = replacement.attr("id", target.attr("id"));
};
if (typeof(target.attr("class")) !== "undefined" && target.attr("class") !== "") {
replacement = replacement.attr("class", target.attr("class"));
};
target.replaceWith(replacement);
return replacement;
},
/*
redirect_to method from: http://gist.github.com/327227
*/
redirect_to: function(url, location){
var redirect_to = "";
if (typeof(location) == "undefined") location = window.location;
if (url.match(/^[a-zA-Z]+\:\/\/.+/) === null) {
redirect_to += location.protocol + "//" + location.hostname;
if (location.port != "") redirect_to += ":" + location.port;
if (url.charAt(0) !== "/") redirect_to += location.pathname.substr(0, location.pathname.lastIndexOf("/")+1);
window.location.href = redirect_to + url;
} else {
window.location.href = url;
};
},
// "borrowed" from PutCursorAtEnd plugin: http://plugins.jquery.com/project/PutCursorAtEnd
putCursorAtEnd: function(){
return this.each(function(){
$(this).focus();
if (this.setSelectionRange) {
var len = $(this).val().length * 2;
this.setSelectionRange(len, len);
} else {
$(this).val($(this).val());
}
this.scrollTop = 999999;
});
}
};
})(jQuery);
/*
Crossbrowser hasOwnProperty solution, based on answers from:
http://stackoverflow.com/questions/135448/how-do-i-check-to-see-if-an-object-has-an-attribute-in-javascript
*/
if ( !Object.prototype.hasOwnProperty ) {
Object.prototype.hasOwnProperty = function(prop){
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
};
}

View File

@@ -0,0 +1,61 @@
.fancy_suggest {
border-bottom: 1px solid rgba(187,187,187,0.4);
display: none;
position: absolute;
z-index: 1000;
}
.fancy_suggest ol {
background: #fbfbfb;
border: 1px solid #bbb;
border-top-style: none;
margin: 0px;
padding: 0px;
list-style: none;
}
.fancy_suggest li {
margin: 0px;
border-bottom: 1px solid #ddd;
}
.fancy_suggest li.result {
}
.fancy_suggest li.label {
padding: 3px 5px;
color: #888;
}
.fancy_suggest li.first {
}
.fancy_suggest li.last {
border-bottom: none !important;
}
.fancy_suggest li.result a {
color: #000;
display: block;
padding: 3px 5px;
text-decoration: none;
}
.fancy_suggest li.result a span {
display: block;
}
.fancy_suggest li.result.selected a {
background: #698DE5;
color: #fff;
}
.fancy_suggest li.result .highlight {
background: #ddd;
font-weight: bold;
}
.fancy_suggest li.result.selected .highlight {
background: #7893E5;
color: #fff;
}

View File

@@ -0,0 +1,330 @@
/*!
* Fancy Suggest
*
* Copyright (c) 2011 Jim Myhrberg.
* Released under the MIT license.
*/
(function($){
var Super = $.fn.fancy_input;
var Self = $.fn.fancy_suggest = function(options){
var $options = $.extend({}, Self.default_options, options);
return this.each(function(){
var $e = $(this);
Self.init_suggest($e, $options);
$e.focus(function(){
Self.attach_suggest_box($e, $options);
if ($e.val().length > 0) {
Self.setTimeout(function(){
Self.suggest($e, $options);
}, $options.delay);
};
}).blur(function(){
Self.hide();
}).keydown(function(e){
switch(e.keyCode) {
case Self.keys.ARROW_UP:
Self.select_prev($e, $options);
return false;
case Self.keys.ARROW_DOWN:
Self.select_next($e, $options);
return false;
case Self.keys.ESC:
Self.clear($e, $options);
break;
case Self.keys.RETURN:
if (Self.selected_result !== null) {
Self.activate_selected($e, $options);
return false;
}
break;
}
}).keyup(function(e){
var key = e.keyCode;
Self.align_suggest_box($e, $options);
if (key > Self.keys.SPECIALS_END || key == Self.keys.BACKSPACE || key == Self.keys.DELETE) {
Self.clearTimeout();
Self.suggest($e, $options);
};
});
});
};
$.extend(Self, Super, {
box: null,
list: null,
attached_to: null,
current_results: [],
selected_result: null,
query_cache: [],
default_options: $.extend({}, Super.default_options, {
name: "",
exact_match: true,
limit: 6,
no_results: true,
no_results_label: "No Results",
url: null,
url_method: "get",
url_query_var: "search",
pos_top: 0,
pos_left: 0,
hide_delay: 0,
data: null,
tpl_container_id: "fancy_suggest",
tpl_container_class: "fancy_suggest",
tpl_container: '<div id="{{id}}"><ol></ol></div>',
tpl_result_begin: '<li class="result {{class}}" id="{{id}}" result_id="{{result_id}}"><a href="{{href}}">',
tpl_result_body: '<span class="title">{{title}}</span>',
tpl_result_end: '</a></li>',
tpl_label: '<li class="label {{class}}">{{label}}</li>',
tpl_highlight: '<b class="highlight">$1</b>'
}),
init_suggest: function(elm, options){
this.box = $("#" + options.tpl_container_id);
if (this.box.length == 0) {
$("body").append($(options.tpl_container).attr("id", options.tpl_container_id).attr("class", options.tpl_container_class));
this.box = $("#" + options.tpl_container_id);
};
this.list = this.box.children("ol");
},
attach_suggest_box: function(elm, options){
var elm_uid = this.elm_uid(elm);
this.align_suggest_box(elm, options);
if (elm_uid !== this.attached_to) {
if (typeof(options.name) == "string" && options.name != "") {
this.box.hide().addClass(options.name);
};
this.attached_to = elm_uid;
};
},
align_suggest_box: function(elm, options){
var offset = elm.offset();
// left
this.box.css("left", (offset.left + options.pos_left));
// top
var top = offset.top + elm.innerHeight();
top += parseInt(elm.css("border-top-width"), 10) + parseInt(elm.css("border-bottom-width"), 10);
top += options.pos_top;
this.box.css("top", top);
// width
if (typeof(options.width) === "number" || (typeof(options.width) === "string" && options.width != "")) {
this.box.css("width", options.width);
} else {
var width = elm.innerWidth();
width += parseInt(elm.css("border-left-width"), 10) + parseInt(elm.css("border-right-width"), 10);
width -= parseInt(this.box.css("border-left-width"), 10) + parseInt(this.box.css("border-right-width"), 10);
this.box.css("width", width);
};
},
suggest: function(elm, options){
var terms = $.trim(this.get_value(elm, options));
if (options.exact_match) terms = terms.split(/\s/);
if ((typeof(terms) == "string" && terms != "") || (typeof(terms) == "object" && terms.length > 0)) {
this.selected_result = null;
if (typeof(options.url) === "string" && options.url !== "") {
this.query_for_data(elm, options);
} else {
this.current_results = this.filter_data(terms, options.data, options);
this.prerender(elm, this.current_results, options);
};
};
},
filter_data: function(terms, data, options){
if (typeof(terms) === "string") { terms = [terms]; };
var matched = "";
var results = [];
var terms_length = terms.length;
for (var i=0; i < terms_length; i++) {
var term = terms[i];
term = term.toLowerCase();
if (data !== null && typeof(term) !== "undefined" && term !== "") {
var data_length = data.length;
for (var n=0; n < data_length; n++) {
var title = data[n].title.toLowerCase();
var match = (typeof(data[n].match) !== "undefined") ? data[n].match.toLowerCase() : "" ;
if (title.indexOf(term) !== -1 || match.indexOf(term) !== -1) {
if (matched.indexOf(":" + n + ":") == -1) {
results.push(data[n]);
matched += ":" + n + ":";
if (results.length >= options.limit) {
return results;
};
};
};
};
};
};
return results;
},
query_for_data: function(elm, options){
var term = this.get_value(elm, options);
var uid = options.url + "?" + term + ":" + options.limit;
if (term !== "") {
if (this.query_cache.hasOwnProperty(uid)) {
this.current_results = this.query_cache[uid];
this.prerender(elm, this.current_results, options);
} else {
var data = { limit: options.limit };
data[options.url_query_var] = term;
var Self = this;
$.ajax({
type: options.url_method,
url: options.url,
data: data,
dataType: "json",
success: function(response){
Self.current_results = response.results;
Self.query_cache[uid] = Self.current_results;
Self.prerender(elm, Self.current_results, options);
}
});
};
} else {
this.no_results(elm, options);
};
},
clear: function(elm, options){
this.set_value(elm, "");
this.hide();
this.selected_result = null;
},
no_results: function(elm, options){
if (options.no_results && this.get_value(elm, options) !== "") {
var meta = {label: options.no_results_label, "class": "last"};
this.list.html(this.mustache(options.tpl_label, meta));
this.show();
} else {
this.hide(0);
};
},
prerender: function(elm, results, options){
if (results.length > 0) {
this.render(elm, results, options);
this.show();
if (results.length == 1) {
this.select_next(elm, options);
};
} else {
this.no_results(elm, options);
};
},
render: function(elm, results, options){
var results_length = results.length;
var html = "";
for (var i=0; i < results_length; i++) {
var elm_id = "fancy_suggest_result_" + i;
var meta = $.extend({}, results[i], {id: elm_id, "class": "", result_id: i});
if (i == 0) { $.extend(meta, {"class": "first"}); };
if (i == results_length - 1) { $.extend(meta, {"class": "last"}); };
meta["title"] = meta["title"].replace(new RegExp("(" + this.get_value(elm, options) + ")", "ig"), options.tpl_highlight);
html += this.mustache(options.tpl_result_begin, meta);
html += this.mustache(options.tpl_result_body, meta);
html += this.mustache(options.tpl_result_end, meta);
};
this.list.html(html);
var Self = this;
$(".result a", this.list).mousedown(function(){
Self.selected_result = parseInt($(this).parent().attr("result_id"), 10);
Self.activate_selected(elm, options);
}).click(function(){
return false;
});
$(".result", this.list).hover(function(){
$(".selected", Self.list).removeClass("selected");
Self.selected_result = $(this).addClass("selected").attr("result_id");
},function(){
$(this).removeClass("selected");
Self.selected_result = null;
});
},
show: function(){
this.box.show();
},
hide: function(delay){
if (typeof(delay) !== "number") { delay = this.default_options.hide_delay; };
this.selected_result = null;
var Self = this;
this.setTimeout(function(){
Self.selected_result = null;
$(".selected", this.list).removeClass("selected");
Self.box.hide();
}, delay);
},
select_next: function(elm, options){
var limit = this.current_results.length;
var result_id = "fancy_suggest_result_";
if (limit > 0) {
if (this.selected_result === null) {
$(".selected", this.list).removeClass("selected");
this.selected_result = 0;
$("#" + result_id + this.selected_result).addClass("selected");
} else if (this.selected_result + 1 < limit) {
$(".selected", this.list).removeClass("selected");
this.selected_result++;
$("#" + result_id + this.selected_result).addClass("selected");
} else {
$(".selected", this.list).removeClass("selected");
this.selected_result = null;
elm.putCursorAtEnd();
};
};
return false;
},
select_prev: function(elm, options){
var limit = this.current_results.length;
var result_id = "fancy_suggest_result_";
if (limit > 0) {
if (this.selected_result === null) {
$(".selected", this.list).removeClass("selected");
this.selected_result = limit - 1;
$("#" + result_id + this.selected_result).addClass("selected");
} else if (this.selected_result > 0) {
$(".selected", this.list).removeClass("selected");
this.selected_result--;
$("#" + result_id + this.selected_result).addClass("selected");
} else {
$(".selected", this.list).removeClass("selected");
this.selected_result = null;
elm.putCursorAtEnd();
};
};
return false;
},
activate_selected: function(elm, options){
if (this.selected_result !== null) {
this.redirect_to(this.current_results[this.selected_result].href);
};
},
get_value: function(elm, options) {
return elm.val();
},
set_value: function(elm, value, options) {
elm.val(value);
}
});
})(jQuery);

155
javascripts/jquery.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,54 +0,0 @@
#suggest_results {
border-bottom: 1px solid rgba(187,187,187,0.4);
display: none;
position: absolute;
z-index: 1000;
}
#suggest_results ol {
background: #fbfbfb;
border: 1px solid #bbb;
border-top: none;
margin: 0px;
padding: 0px;
list-style: none;
}
#suggest_results li {
margin: 0px;
border-bottom: 1px solid #ddd;
}
#suggest_results li.result {
}
#suggest_results li.label {
padding: 3px 5px;
color: #888;
}
#suggest_results li.first {
}
#suggest_results li.last {
border-bottom: none !important;
}
#suggest_results li.result a {
color: #000;
display: block;
padding: 3px 5px;
text-decoration: none;
}
#suggest_results li.result a span {
display: block;
}
#suggest_results li.result a:hover,
#suggest_results li.result.selected a {
background: #698DE5;
color: #fff;
}

View File

@@ -1,367 +0,0 @@
/*!
* Suggest Results v0.1.0
* http://github.com/jimeh/suggest_results
*
* Copyright (c) 2010 Jim Myhrberg.
* Released under the MIT license.
*/
(function($){
$.fn.suggest_results = function(options){
var self = $.fn.suggest_results;
var $options = $.extend({}, self.defaults, options);
self.init($options);
var BACKSPACE = 8;
var RETURN = 13;
var ESC = 27;
var ARRUP = 38;
var ARRDN = 40;
var SPECIALS_END = 45;
return this.each(function(){
var $e = $(this);
$e.focus(function(){
self.attach($e, $options);
if ($e.val().length > 0) {
self.search_timeout = self.setTimeout(function(){
self.search($e, $options);
}, $options.delay);
};
}).blur(function(){
self.hide();
}).keydown(function(e){
switch(e.keyCode) {
case ARRUP: self.select_prev($e, $options); return false;
case ARRDN: self.select_next($e, $options); return false;
case ESC: self.clear($e, $options); break;
case RETURN: self.activate_selected($options); return false;
default: self.clearTimeout(); self.search($e, $options);
}
}).keyup(function(e){
if (e.keyCode > SPECIALS_END || e.keyCode == BACKSPACE) {
self.clearTimeout();
self.search($e, $options);
};
});
});
};
$.fn.suggest_results.box = null;
$.fn.suggest_results.list = null;
$.fn.suggest_results.attached_to = null;
$.fn.suggest_results.current_results = [];
$.fn.suggest_results.selected_result = null;
$.fn.suggest_results.timeout = null;
$.fn.suggest_results.query_cache = [];
$.fn.suggest_results.init = function(options){
var self = $.fn.suggest_results;
self.box = $("#" + options.tpl_container_id);
if (self.box.length == 0) {
$("body").append(self.mustache(options.tpl_container, {id: options.tpl_container_id}));
self.box = $("#" + options.tpl_container_id);
self.list = $("ol", self.box);
};
};
$.fn.suggest_results.search = function(elm, options){
var self = $.fn.suggest_results;
var terms = (options.exact_match) ? $.trim(elm.val()) : elm.val().split(/\s/);
if (typeof(options.url) === "string" && options.url !== "") {
self.query_for_data(elm, options);
} else {
self.current_results = self.filter_data(terms, options.data, options);
self.prerender(elm, self.current_results, options);
};
};
$.fn.suggest_results.clear = function(elm, options){
var self = $.fn.suggest_results;
elm.val("");
self.hide(0);
self.selected_result = null;
};
$.fn.suggest_results.no_results = function(elm, options){
var self = $.fn.suggest_results;
if (options.no_results && elm.val() !== "") {
var meta = {label: options.no_results_label, "class": "last"};
self.list.html(self.mustache(options.tpl_label, meta));
self.show();
} else {
self.hide(0);
};
};
$.fn.suggest_results.prerender = function(elm, results, options){
var self = $.fn.suggest_results;
if (results.length > 0) {
self.render(results, options);
self.show();
} else {
self.no_results(elm, options);
};
};
$.fn.suggest_results.render = function(results, options){
var self = $.fn.suggest_results;
var results_length = results.length;
var html = "";
for (var i=0; i < results_length; i++) {
var meta = $.extend({}, results[i], {id: "suggested_result_" + i, "class": ""});
if (i == 0) { $.extend(meta, {"class": "first"}); };
if (i == results_length - 1) { $.extend(meta, {"class": "last"}); };
html += self.mustache(options.tpl_result_begin, meta);
html += self.mustache(options.tpl_result_body, meta);
html += self.mustache(options.tpl_result_end, meta);
};
self.list.html(html);
$(".result", self.list).click(function(){
self.redirect_to($("a", $(this)).attr("href"));
});
};
$.fn.suggest_results.attach = function(elm, options){
var self = $.fn.suggest_results;
var elm_uid = self.elm_uid(elm);
if (elm_uid !== self.attached_to) {
self.box.hide().attr("class", options.name);
var offset = elm.offset();
// left offset
self.box.css("left", offset.left + "px");
// top offset
var top = offset.top + elm.innerHeight();
top += parseInt(elm.css("border-top-width"), 10) + parseInt(elm.css("border-bottom-width"), 10);
self.box.css("top", top + "px");
// width
if (typeof(options.width) === "number" || (typeof(options.width) === "string" && options.width != "")) {
self.box.css("width", options.width);
} else {
var width = elm.innerWidth();
width += parseInt(elm.css("border-left-width"), 10) + parseInt(elm.css("border-right-width"), 10);
width -= parseInt(self.box.css("border-left-width"), 10) + parseInt(self.box.css("border-right-width"), 10);
self.box.css("width", width + "px");
};
self.attached_to = elm_uid;
};
};
$.fn.suggest_results.show = function(){
$.fn.suggest_results.box.show();
};
$.fn.suggest_results.hide = function(delay){
var self = $.fn.suggest_results;
if (typeof(delay) !== "number") { delay = 250; };
self.selected_result = null;
self.setTimeout(function(){
self.selected_result = null;
self.box.hide();
}, delay);
};
$.fn.suggest_results.select_next = function(elm, options){
var self = $.fn.suggest_results;
var limit = self.current_results.length;
if (limit > 0) {
if (self.selected_result === null) {
self.selected_result = 0;
$("#suggested_result_" + self.selected_result, self.box).addClass("selected");
} else if (self.selected_result + 1 < limit) {
$(".selected", self.box).removeClass("selected");
self.selected_result++;
$("#suggested_result_" + self.selected_result, self.box).addClass("selected");
} else {
$(".selected", self.box).removeClass("selected");
self.selected_result = null;
elm.putCursorAtEnd();
};
};
return false;
};
$.fn.suggest_results.select_prev = function(elm, options){
var self = $.fn.suggest_results;
var limit = self.current_results.length;
if (limit > 0) {
if (self.selected_result === null) {
self.selected_result = limit - 1;
$("#suggested_result_" + self.selected_result, self.box).addClass("selected");
} else if (self.selected_result > 0) {
$(".selected", self.box).removeClass("selected");
self.selected_result--;
$("#suggested_result_" + self.selected_result, self.box).addClass("selected");
} else {
$(".selected", self.box).removeClass("selected");
self.selected_result = null;
elm.putCursorAtEnd();
};
};
return false;
};
$.fn.suggest_results.activate_selected = function(options){
var self = $.fn.suggest_results;
if (self.selected_result !== null) {
self.redirect_to(self.current_results[self.selected_result].href);
};
};
$.fn.suggest_results.filter_data = function(terms, data, options){
if (typeof(terms) === "string") { terms = [terms]; };
var matched = "";
var results = [];
for (var i = terms.length - 1; i >= 0; i--){
var term = terms[i];
term = term.toLowerCase();
if (data !== null && typeof(term) !== "undefined" && term !== "") {
var data_length = data.length;
for (var n=0; n < data_length; n++) {
var title = data[n].title.toLowerCase();
var match = (typeof(data[n].match) !== "undefined") ? data[n].match.toLowerCase() : "" ;
if (title.indexOf(term) !== -1 || match.indexOf(term) !== -1) {
if (matched.indexOf(":" + n + ":") == -1) {
results.push(data[n]);
matched += ":" + n + ":";
if (results.length >= options.limit) {
return results;
};
};
};
};
};
};
return results;
};
$.fn.suggest_results.query_for_data = function(elm, options){
var self = $.fn.suggest_results;
var term = elm.val();
var uid = options.url + "?" + term + ":" + options.limit;
if (term !== "") {
if (self.query_cache.hasOwnProperty(uid)) {
self.current_results = self.query_cache[uid];
self.prerender(elm, self.current_results, options);
} else {
var data = { limit: options.limit };
data[options.url_query_var] = term;
$.ajax({
type: options.url_method,
url: options.url,
data: data,
dataType: "json",
success: function(response){
self.current_results = response.results;
self.query_cache[uid] = self.current_results;
self.prerender(elm, self.current_results, options);
}
});
};
} else {
self.no_results(elm, options);
};
return [];
};
$.fn.suggest_results.elm_uid = function(elm){
if (elm.attr("id") !== "") {
return "#" + elm.attr("id");
} else if (elm.attr("class") !== "") {
return "." + elm.attr("class");
} else if (elm.attr("name") !== "") {
return "!" + elm.attr("name");
};
return "";
};
$.fn.suggest_results.mustache = function(string, data){
if (typeof(string) === "string" && typeof(data) === "object") {
for (var key in data) {
string = string.replace(new RegExp("{{" + key + "}}", "g"), data[key]);
}
};
return string;
};
$.fn.suggest_results.redirect_to = function(url, location){
if (typeof(location) == "undefined") {
location = window.location;
};
var redirect_to = "";
if (url.match(/.+\:\/\/.+/) === null) {
redirect_to += location.protocol + "//";
redirect_to += location.hostname;
if (location.port != "") { redirect_to += ":" + location.port; };
if (url.charAt(0) !== "/") {
redirect_to += location.pathname.substr(0, location.pathname.lastIndexOf("/")+1);
};
window.location.href = redirect_to + url;
} else {
window.location.href = url;
};
};
$.fn.suggest_results.setTimeout = function(callback, delay){
var self = $.fn.suggest_results;
self.clearTimeout();
self.timeout = setTimeout(callback, delay);
};
$.fn.suggest_results.clearTimeout = function(){
var self = $.fn.suggest_results;
if (self.timeout !== null) {
clearTimeout(self.timeout);
self.timeout = null;
};
};
// "borrowed" from PutCursorAtEnd plugin: http://plugins.jquery.com/project/PutCursorAtEnd
$.fn.putCursorAtEnd = function(){
return this.each(function(){
$(this).focus();
if (this.setSelectionRange) {
var len = $(this).val().length * 2;
this.setSelectionRange(len, len);
} else {
$(this).val($(this).val());
}
this.scrollTop = 999999;
});
};
$.fn.suggest_results.defaults = {
name: "",
exact_match: true,
limit: 6,
no_results: true,
no_results_label: "No Results",
url: null,
url_method: "get",
url_query_var: "search",
delay: 100,
data: null,
tpl_container_id: "suggest_results",
tpl_container: '<div id="{{id}}"><ol></ol></div>',
tpl_result_begin: '<li class="result {{class}}" id="{{id}}"><a href="{{href}}">',
tpl_result_body: '<span class="title">{{title}}</span>',
tpl_result_end: '</a></li>',
tpl_label: '<li class="label {{class}}">{{label}}</li>'
};
})(jQuery);
/*
Crossbrowser hasOwnProperty solution, based on answers from:
http://stackoverflow.com/questions/135448/how-do-i-check-to-see-if-an-object-has-an-attribute-in-javascript
*/
if ( !Object.prototype.hasOwnProperty ) {
Object.prototype.hasOwnProperty = function(prop) {
var proto = obj.__proto__ || obj.constructor.prototype;
return (prop in this) && (!(prop in proto) || proto[prop] !== this[prop]);
};
}