flexselect 1.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +21 -0
- data/LICENSE +20 -0
- data/README.rdoc +28 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/generators/flexselect/USAGE +19 -0
- data/generators/flexselect/flexselect_generator.rb +9 -0
- data/generators/flexselect/templates/flexselect.css +40 -0
- data/generators/flexselect/templates/jquery.flexselect.js +284 -0
- data/generators/flexselect/templates/liquidmetal.js +88 -0
- data/install.rb +3 -0
- data/lib/flexselect.rb +55 -0
- data/rails/init.rb +1 -0
- data/spec/active_record_helper.rb +9 -0
- data/spec/flexselect_spec.rb +60 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +15 -0
- data/tasks/flexselect_tasks.rake +4 -0
- data/uninstall.rb +2 -0
- metadata +116 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 Emmanuel Oga for Broadspire, Inc
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
= flexselect
|
2
|
+
|
3
|
+
This gem is just a convenient packaging of the excellent
|
4
|
+
|
5
|
+
jQuery plugin flexselect.
|
6
|
+
|
7
|
+
http://github.com/rmm5t/jquery-flexselect/
|
8
|
+
|
9
|
+
A very little modification was made to the flexselect()
|
10
|
+
function arguments for making it more convenient to use with Rails.
|
11
|
+
|
12
|
+
In addition it provides an ActiveRecord class method to configure
|
13
|
+
certain attributes for autocompletion.
|
14
|
+
|
15
|
+
|
16
|
+
== Note on Patches/Pull Requests
|
17
|
+
|
18
|
+
* Fork the project.
|
19
|
+
* Make your feature addition or bug fix.
|
20
|
+
* Add tests for it. This is important so I don't break it in a
|
21
|
+
future version unintentionally.
|
22
|
+
* Commit, do not mess with rakefile, version, or history.
|
23
|
+
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
|
24
|
+
* Send me a pull request. Bonus points for topic branches.
|
25
|
+
|
26
|
+
== Copyright
|
27
|
+
|
28
|
+
Copyright (c) 2010 Emmanuel Oga for Broadspire, Inc. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "flexselect"
|
8
|
+
gem.summary = %Q{A jQuery plugin and an activerecord extension to provide auto-complete on html select tags}
|
9
|
+
gem.description = %Q{For rails applications, requires jQuery}
|
10
|
+
gem.email = "EmmanuelOga@gmail.com"
|
11
|
+
gem.homepage = "http://github.com/EmmanuelOga/flexselect"
|
12
|
+
gem.authors = ["Emmanuel Oga"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_development_dependency "yard", ">= 0"
|
15
|
+
gem.add_development_dependency "activerecord", ">= 0"
|
16
|
+
gem.add_development_dependency "sqlite3-ruby", ">= 0"
|
17
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
18
|
+
end
|
19
|
+
Jeweler::GemcutterTasks.new
|
20
|
+
rescue LoadError
|
21
|
+
puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
|
22
|
+
end
|
23
|
+
|
24
|
+
require 'spec/rake/spectask'
|
25
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
26
|
+
spec.libs << 'lib' << 'spec'
|
27
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
28
|
+
end
|
29
|
+
|
30
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
31
|
+
spec.libs << 'lib' << 'spec'
|
32
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
33
|
+
spec.rcov = true
|
34
|
+
end
|
35
|
+
|
36
|
+
task :spec => :check_dependencies
|
37
|
+
|
38
|
+
task :default => :spec
|
39
|
+
|
40
|
+
begin
|
41
|
+
require 'yard'
|
42
|
+
YARD::Rake::YardocTask.new
|
43
|
+
rescue LoadError
|
44
|
+
task :yardoc do
|
45
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
46
|
+
end
|
47
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
1.1.0
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Description:
|
2
|
+
Just copies the javascript and css stylesheets you need to the public directory.
|
3
|
+
NOTE: you need to include jQuery on your html page for the plugin to work.
|
4
|
+
|
5
|
+
Example:
|
6
|
+
./script/generate flexselect
|
7
|
+
|
8
|
+
This will create:
|
9
|
+
|
10
|
+
public/stylesheets/flexselect.css
|
11
|
+
public/javascripts/jquery.flexselect.js
|
12
|
+
public/javascripts/liquidmetal.js
|
13
|
+
|
14
|
+
Later, on your template:
|
15
|
+
|
16
|
+
<%= stylesheet_link_tag "flexselect.css" %>
|
17
|
+
<%= javascript_include_tag "jquery.js", "liquidmetal.js", "jquery.flexselect.js" %>
|
18
|
+
|
19
|
+
You get the idea.
|
@@ -0,0 +1,9 @@
|
|
1
|
+
class FlexselectGenerator < Rails::Generator::NamedBase
|
2
|
+
def manifest
|
3
|
+
record do |m|
|
4
|
+
m.template "flexselect.css" , "public/stylesheets/flexselect.css"
|
5
|
+
m.template "jquery.flexselect.js" , "public/javascripts/jquery.flexselect.js"
|
6
|
+
m.template "liquidmetal.js" , "public/javascripts/liquidmetal.js"
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
.flexselect_dropdown {
|
2
|
+
display: none;
|
3
|
+
position: absolute;
|
4
|
+
z-index: 999;
|
5
|
+
margin: 0;
|
6
|
+
padding: 0;
|
7
|
+
border: 1px solid WindowFrame;
|
8
|
+
max-height: 200px;
|
9
|
+
overflow-x: hidden;
|
10
|
+
overflow-y: auto;
|
11
|
+
background-color: Window;
|
12
|
+
color: WindowText;
|
13
|
+
color: #777;
|
14
|
+
text-align: left;
|
15
|
+
box-shadow: 0 6px 12px #ccc;
|
16
|
+
-webkit-box-shadow: 0 6px 12px #ccc;
|
17
|
+
}
|
18
|
+
|
19
|
+
.flexselect_dropdown ul {
|
20
|
+
width: 100%;
|
21
|
+
list-style-position: outside;
|
22
|
+
list-style: none;
|
23
|
+
padding: 0;
|
24
|
+
margin: 0;
|
25
|
+
}
|
26
|
+
|
27
|
+
.flexselect_dropdown li {
|
28
|
+
margin: 0px;
|
29
|
+
padding: 2px 5px;
|
30
|
+
cursor: pointer;
|
31
|
+
display: block;
|
32
|
+
width: 100%;
|
33
|
+
font: Menu;
|
34
|
+
overflow: hidden;
|
35
|
+
}
|
36
|
+
|
37
|
+
.flexselect_selected {
|
38
|
+
background-color: Highlight;
|
39
|
+
color: HighlightText;
|
40
|
+
}
|
@@ -0,0 +1,284 @@
|
|
1
|
+
/*
|
2
|
+
* flexselect: a jQuery plugin, version: %RELEASE_VERSION% (%RELEASE_DATE%)
|
3
|
+
* @requires jQuery v1.3 or later
|
4
|
+
*
|
5
|
+
* FlexSelect is a jQuery plugin that makes it easy to convert a select box into
|
6
|
+
* a Quicksilver-style, autocompleting, flex matching selection tool.
|
7
|
+
*
|
8
|
+
* For usage and examples, visit:
|
9
|
+
* http://rmm5t.github.com/jquery-flexselect/
|
10
|
+
*
|
11
|
+
* Licensed under the MIT:
|
12
|
+
* http://www.opensource.org/licenses/mit-license.php
|
13
|
+
*
|
14
|
+
* Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
|
15
|
+
*
|
16
|
+
* Modified by Emmanuel Oga, see for details on
|
17
|
+
*
|
18
|
+
* http://github.com/EmmanuelOga/flexselect
|
19
|
+
*
|
20
|
+
* Copyright (c) 2010, Emmanuel Oga for Broadspire Inc.
|
21
|
+
*/
|
22
|
+
(function($) {
|
23
|
+
$.flexselect = function(select, options) { this.init(select, options); };
|
24
|
+
|
25
|
+
$.extend($.flexselect.prototype, {
|
26
|
+
settings: {
|
27
|
+
allowMismatch: false,
|
28
|
+
selectedClass: "flexselect_selected",
|
29
|
+
dropdownClass: "flexselect_dropdown",
|
30
|
+
inputIdTransform: function(id) { return id + "_flexselect"; },
|
31
|
+
inputNameTransform: function(name) { return; },
|
32
|
+
dropdownIdTransform: function(id) { return id + "_flexselect_dropdown"; }
|
33
|
+
},
|
34
|
+
select: null,
|
35
|
+
input: null,
|
36
|
+
hidden: null,
|
37
|
+
dropdown: null,
|
38
|
+
dropdownList: null,
|
39
|
+
cache: [],
|
40
|
+
results: [],
|
41
|
+
lastAbbreviation: null,
|
42
|
+
abbreviationBeforeFocus: null,
|
43
|
+
selectedIndex: 0,
|
44
|
+
picked: false,
|
45
|
+
dropdownMouseover: false, // Workaround for poor IE behaviors
|
46
|
+
|
47
|
+
init: function(select, options) {
|
48
|
+
$.extend(this.settings, options);
|
49
|
+
this.select = $(select);
|
50
|
+
this.preloadCache();
|
51
|
+
this.renderControls();
|
52
|
+
this.wire();
|
53
|
+
},
|
54
|
+
|
55
|
+
preloadCache: function() {
|
56
|
+
this.cache = this.select.children("option").map(function() {
|
57
|
+
return { name: $.trim($(this).text()), value: $(this).val(), score: 0.0 };
|
58
|
+
});
|
59
|
+
},
|
60
|
+
|
61
|
+
renderControls: function() {
|
62
|
+
var selected = this.select.children("option:selected");
|
63
|
+
|
64
|
+
this.hidden = $("<input type='hidden'/>").attr({
|
65
|
+
id: this.select.attr("id"),
|
66
|
+
name: this.select.attr("name")
|
67
|
+
}).val(selected.val());
|
68
|
+
|
69
|
+
this.input = $("<input type='text' autocomplete='off' />").attr({
|
70
|
+
id: this.settings.inputIdTransform(this.select.attr("id")),
|
71
|
+
name: this.settings.inputNameTransform(this.select.attr("name")),
|
72
|
+
accesskey: this.select.attr("accesskey"),
|
73
|
+
tabindex: this.select.attr("tabindex"),
|
74
|
+
style: this.select.attr("style")
|
75
|
+
}).addClass(this.select.attr("class")).val($.trim(selected.text()));
|
76
|
+
|
77
|
+
this.dropdown = $("<div></div>").attr({
|
78
|
+
id: this.settings.dropdownIdTransform(this.select.attr("id"))
|
79
|
+
}).addClass(this.settings.dropdownClass);
|
80
|
+
this.dropdownList = $("<ul></ul>");
|
81
|
+
this.dropdown.append(this.dropdownList);
|
82
|
+
|
83
|
+
this.select.after(this.input).after(this.hidden).remove();
|
84
|
+
$("body").append(this.dropdown);
|
85
|
+
},
|
86
|
+
|
87
|
+
wire: function() {
|
88
|
+
var self = this;
|
89
|
+
|
90
|
+
this.input.click(function() {
|
91
|
+
self.lastAbbreviation = null;
|
92
|
+
self.focus();
|
93
|
+
});
|
94
|
+
|
95
|
+
this.input.mouseup(function(event) {
|
96
|
+
// This is so Safari selection actually occurs.
|
97
|
+
event.preventDefault();
|
98
|
+
});
|
99
|
+
|
100
|
+
this.input.focus(function() {
|
101
|
+
self.abbreviationBeforeFocus = self.input.val();
|
102
|
+
self.input.select();
|
103
|
+
if (!self.picked) self.filterResults();
|
104
|
+
});
|
105
|
+
|
106
|
+
this.input.blur(function() {
|
107
|
+
if (!self.dropdownMouseover) {
|
108
|
+
self.hide();
|
109
|
+
if (!self.picked) self.reset();
|
110
|
+
}
|
111
|
+
});
|
112
|
+
|
113
|
+
this.dropdownList.mouseover(function (event) {
|
114
|
+
if (event.target.tagName == "LI") {
|
115
|
+
var rows = self.dropdown.find("li");
|
116
|
+
self.markSelected(rows.index($(event.target)));
|
117
|
+
}
|
118
|
+
});
|
119
|
+
this.dropdownList.mouseleave(function () {
|
120
|
+
self.markSelected(-1);
|
121
|
+
});
|
122
|
+
this.dropdownList.mouseup(function (event) {
|
123
|
+
self.pickSelected();
|
124
|
+
self.focusAndHide();
|
125
|
+
});
|
126
|
+
this.dropdown.mouseover(function (event) {
|
127
|
+
self.dropdownMouseover = true;
|
128
|
+
});
|
129
|
+
this.dropdown.mouseleave(function (event) {
|
130
|
+
self.dropdownMouseover = false;
|
131
|
+
});
|
132
|
+
this.dropdown.mousedown(function (event) {
|
133
|
+
event.preventDefault();
|
134
|
+
});
|
135
|
+
|
136
|
+
this.input.keyup(function(event) {
|
137
|
+
switch (event.keyCode) {
|
138
|
+
case 13: // return
|
139
|
+
event.preventDefault();
|
140
|
+
self.pickSelected();
|
141
|
+
self.focusAndHide();
|
142
|
+
break;
|
143
|
+
case 27: // esc
|
144
|
+
event.preventDefault();
|
145
|
+
self.reset();
|
146
|
+
self.focusAndHide();
|
147
|
+
break;
|
148
|
+
default:
|
149
|
+
self.filterResults();
|
150
|
+
break;
|
151
|
+
}
|
152
|
+
});
|
153
|
+
|
154
|
+
this.input.keydown(function(event) {
|
155
|
+
switch (event.keyCode) {
|
156
|
+
case 9: // tab
|
157
|
+
self.pickSelected();
|
158
|
+
self.hide();
|
159
|
+
break;
|
160
|
+
case 33: // pgup
|
161
|
+
event.preventDefault();
|
162
|
+
self.markFirst();
|
163
|
+
break;
|
164
|
+
case 34: // pgedown
|
165
|
+
event.preventDefault();
|
166
|
+
self.markLast();
|
167
|
+
break;
|
168
|
+
case 38: // up
|
169
|
+
event.preventDefault();
|
170
|
+
self.moveSelected(-1);
|
171
|
+
break;
|
172
|
+
case 40: // down
|
173
|
+
event.preventDefault();
|
174
|
+
self.moveSelected(1);
|
175
|
+
break;
|
176
|
+
case 13: // return
|
177
|
+
case 27: // esc
|
178
|
+
event.preventDefault();
|
179
|
+
event.stopPropagation();
|
180
|
+
break;
|
181
|
+
}
|
182
|
+
});
|
183
|
+
},
|
184
|
+
|
185
|
+
filterResults: function() {
|
186
|
+
var abbreviation = this.input.val();
|
187
|
+
if (abbreviation == this.lastAbbreviation) return;
|
188
|
+
|
189
|
+
var results = [];
|
190
|
+
$.each(this.cache, function() {
|
191
|
+
this.score = LiquidMetal.score(this.name, abbreviation);
|
192
|
+
if (this.score > 0.0) results.push(this);
|
193
|
+
});
|
194
|
+
this.results = results;
|
195
|
+
|
196
|
+
this.sortResults();
|
197
|
+
this.renderDropdown();
|
198
|
+
this.markFirst();
|
199
|
+
this.lastAbbreviation = abbreviation;
|
200
|
+
this.picked = false;
|
201
|
+
},
|
202
|
+
|
203
|
+
sortResults: function() {
|
204
|
+
this.results.sort(function(a, b) { return b.score - a.score; });
|
205
|
+
},
|
206
|
+
|
207
|
+
renderDropdown: function() {
|
208
|
+
var dropdownBorderWidth = this.dropdown.outerWidth() - this.dropdown.innerWidth();
|
209
|
+
var inputOffset = this.input.offset();
|
210
|
+
this.dropdown.css({
|
211
|
+
width: (this.input.outerWidth() - dropdownBorderWidth) + "px",
|
212
|
+
top: (inputOffset.top + this.input.outerHeight()) + "px",
|
213
|
+
left: inputOffset.left + "px"
|
214
|
+
});
|
215
|
+
|
216
|
+
var list = this.dropdownList.html("");
|
217
|
+
$.each(this.results, function() {
|
218
|
+
// list.append($("<li/>").html(this.name + " <small>[" + Math.round(this.score*100)/100 + "]</small>"));
|
219
|
+
list.append($("<li/>").html(this.name));
|
220
|
+
});
|
221
|
+
this.dropdown.show();
|
222
|
+
},
|
223
|
+
|
224
|
+
markSelected: function(n) {
|
225
|
+
if (n > this.results.length) return;
|
226
|
+
|
227
|
+
var rows = this.dropdown.find("li");
|
228
|
+
rows.removeClass(this.settings.selectedClass);
|
229
|
+
this.selectedIndex = n;
|
230
|
+
|
231
|
+
if (n >= 0) $(rows[n]).addClass(this.settings.selectedClass);
|
232
|
+
},
|
233
|
+
|
234
|
+
pickSelected: function() {
|
235
|
+
var selected = this.results[this.selectedIndex];
|
236
|
+
if (selected) {
|
237
|
+
this.input.val(selected.name);
|
238
|
+
this.hidden.val(selected.value);
|
239
|
+
this.picked = true;
|
240
|
+
} else if (this.settings.allowMismatch) {
|
241
|
+
this.hidden.val("");
|
242
|
+
} else {
|
243
|
+
this.reset();
|
244
|
+
}
|
245
|
+
},
|
246
|
+
|
247
|
+
hide: function() {
|
248
|
+
this.dropdown.hide();
|
249
|
+
this.lastAbbreviation = null;
|
250
|
+
},
|
251
|
+
|
252
|
+
moveSelected: function(n) { this.markSelected(this.selectedIndex+n); },
|
253
|
+
markFirst: function() { this.markSelected(0); },
|
254
|
+
markLast: function() { this.markSelected(this.results.length - 1); },
|
255
|
+
reset: function() { this.input.val(this.abbreviationBeforeFocus); },
|
256
|
+
focus: function() { this.input.focus(); },
|
257
|
+
focusAndHide: function() { this.focus(); this.hide(); }
|
258
|
+
});
|
259
|
+
|
260
|
+
$.fn.flexselect = function() {
|
261
|
+
// EOGA: Little modification for the flexselect gem.
|
262
|
+
// Allow the user to provide just two options,
|
263
|
+
// name of the activerecord class and name of the attribute to autocomplete.
|
264
|
+
// The third parameter can contain the options object as before.
|
265
|
+
var options;
|
266
|
+
|
267
|
+
if (arguments.length == 1) { var object_opt = arguments[0]; } else { var array_opt = arguments; }
|
268
|
+
|
269
|
+
this.each(function() {
|
270
|
+
if (array_opt) {
|
271
|
+
var transf = function(id) { return array_opt[0] + "[" + array_opt[1] + "]"; }
|
272
|
+
options = { allowMismatch: true, inputIdTransform: transf };
|
273
|
+
|
274
|
+
// Allow additional options to follow the string params
|
275
|
+
if (array_opt[2]) $.extend(options, array_opt[2]);
|
276
|
+
} else {
|
277
|
+
options = object_opt;
|
278
|
+
}
|
279
|
+
|
280
|
+
if (this.tagName == "SELECT") new $.flexselect(this, options);
|
281
|
+
});
|
282
|
+
return this;
|
283
|
+
};
|
284
|
+
})(jQuery);
|
@@ -0,0 +1,88 @@
|
|
1
|
+
/*
|
2
|
+
* LiquidMetal, version: 0.1 (2009-02-05)
|
3
|
+
*
|
4
|
+
* A mimetic poly-alloy of Quicksilver's scoring algorithm, essentially
|
5
|
+
* LiquidMetal.
|
6
|
+
*
|
7
|
+
* For usage and examples, visit:
|
8
|
+
* http://github.com/rmm5t/liquidmetal
|
9
|
+
*
|
10
|
+
* Licensed under the MIT:
|
11
|
+
* http://www.opensource.org/licenses/mit-license.php
|
12
|
+
*
|
13
|
+
* Copyright (c) 2009, Ryan McGeary (ryanonjavascript -[at]- mcgeary [*dot*] org)
|
14
|
+
*/
|
15
|
+
var LiquidMetal = function() {
|
16
|
+
var SCORE_NO_MATCH = 0.0;
|
17
|
+
var SCORE_MATCH = 1.0;
|
18
|
+
var SCORE_TRAILING = 0.8;
|
19
|
+
var SCORE_TRAILING_BUT_STARTED = 0.9;
|
20
|
+
var SCORE_BUFFER = 0.85;
|
21
|
+
|
22
|
+
return {
|
23
|
+
score: function(string, abbreviation) {
|
24
|
+
// Short circuits
|
25
|
+
if (abbreviation.length == 0) return SCORE_TRAILING;
|
26
|
+
if (abbreviation.length > string.length) return SCORE_NO_MATCH;
|
27
|
+
|
28
|
+
var scores = this.buildScoreArray(string, abbreviation);
|
29
|
+
|
30
|
+
var sum = 0.0;
|
31
|
+
for (var i in scores) {
|
32
|
+
sum += scores[i];
|
33
|
+
}
|
34
|
+
|
35
|
+
return (sum / scores.length);
|
36
|
+
},
|
37
|
+
|
38
|
+
buildScoreArray: function(string, abbreviation) {
|
39
|
+
var scores = new Array(string.length);
|
40
|
+
var lower = string.toLowerCase();
|
41
|
+
var chars = abbreviation.toLowerCase().split("");
|
42
|
+
|
43
|
+
var lastIndex = -1;
|
44
|
+
var started = false;
|
45
|
+
for (var i in chars) {
|
46
|
+
var c = chars[i];
|
47
|
+
var index = lower.indexOf(c, lastIndex+1);
|
48
|
+
if (index < 0) return fillArray(scores, SCORE_NO_MATCH);
|
49
|
+
if (index == 0) started = true;
|
50
|
+
|
51
|
+
if (isNewWord(string, index)) {
|
52
|
+
scores[index-1] = 1;
|
53
|
+
fillArray(scores, SCORE_BUFFER, lastIndex+1, index-1);
|
54
|
+
}
|
55
|
+
else if (isUpperCase(string, index)) {
|
56
|
+
fillArray(scores, SCORE_BUFFER, lastIndex+1, index);
|
57
|
+
}
|
58
|
+
else {
|
59
|
+
fillArray(scores, SCORE_NO_MATCH, lastIndex+1, index);
|
60
|
+
}
|
61
|
+
|
62
|
+
scores[index] = SCORE_MATCH;
|
63
|
+
lastIndex = index;
|
64
|
+
}
|
65
|
+
|
66
|
+
var trailingScore = started ? SCORE_TRAILING_BUT_STARTED : SCORE_TRAILING;
|
67
|
+
fillArray(scores, trailingScore, lastIndex+1);
|
68
|
+
return scores;
|
69
|
+
}
|
70
|
+
};
|
71
|
+
|
72
|
+
function isUpperCase(string, index) {
|
73
|
+
var c = string.charAt(index);
|
74
|
+
return ("A" <= c && c <= "Z");
|
75
|
+
}
|
76
|
+
|
77
|
+
function isNewWord(string, index) {
|
78
|
+
var c = string.charAt(index-1);
|
79
|
+
return (c == " " || c == "\t");
|
80
|
+
}
|
81
|
+
|
82
|
+
function fillArray(array, value, from, to) {
|
83
|
+
from = Math.max(from || 0, 0);
|
84
|
+
to = Math.min(to || array.length, array.length);
|
85
|
+
for (var i = from; i < to; i++) { array[i] = value; }
|
86
|
+
return array;
|
87
|
+
}
|
88
|
+
}();
|
data/install.rb
ADDED
data/lib/flexselect.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Flexselect
|
2
|
+
|
3
|
+
# Creates an virtual attribute to handle new values for
|
4
|
+
# an autocomplete select tag handled by flexquery and a callback
|
5
|
+
# to update the attribute using the virtual one.
|
6
|
+
#
|
7
|
+
# The name of the virtual attribute and the callback is derived
|
8
|
+
# from the name of the attribute:
|
9
|
+
#
|
10
|
+
# attr_flex_selector :thing
|
11
|
+
#
|
12
|
+
# produces a virtual attribute named "flex_thing_input" and
|
13
|
+
# the private method "callback_for_flex_thing_input" for
|
14
|
+
# callback.
|
15
|
+
#
|
16
|
+
# Options:
|
17
|
+
#
|
18
|
+
# attr :: The attribute to autocomple
|
19
|
+
#
|
20
|
+
# :on :: ActiveRecord callback to use for updating the attribute.
|
21
|
+
# Defaults to :before_validation_on_create
|
22
|
+
#
|
23
|
+
# :if :: A Proc that is called before updating the attribute.
|
24
|
+
# defaults to lambda { |virtual_attribute_value| virtual_attribute_value.present? }
|
25
|
+
#
|
26
|
+
# &block :: If provided, is called to get the new value for the attribute.
|
27
|
+
# Defaults to lambda{ |virtual_attribute_value| virtual_attribute_value }
|
28
|
+
#
|
29
|
+
def attr_flex_selector(attr, options = {}, &block)
|
30
|
+
virtual_attribute = "flex_#{ attr }_input"
|
31
|
+
callback_method = "callback_for_#{ virtual_attribute }"
|
32
|
+
|
33
|
+
attr_accessor virtual_attribute
|
34
|
+
|
35
|
+
if_lambda = options[:if] || lambda { |new| new.present? }
|
36
|
+
action_lambda = block || lambda { |new| new }
|
37
|
+
|
38
|
+
# A new Module allows to redefine method retaining the old one for "super" usage.
|
39
|
+
mod = Module.new do
|
40
|
+
define_method(callback_method) do
|
41
|
+
value = send(virtual_attribute)
|
42
|
+
|
43
|
+
if if_lambda.bind(self).call(value)
|
44
|
+
send "#{attr}=", action_lambda.bind(self).call(value)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
private(callback_method)
|
48
|
+
end
|
49
|
+
|
50
|
+
include mod
|
51
|
+
|
52
|
+
send(options[:on] || :before_validation_on_create, callback_method)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
data/rails/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ActiveRecord::Base.extend Flexselect
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
class Thing < ActiveRecord::Base
|
4
|
+
|
5
|
+
attr_flex_selector :a
|
6
|
+
|
7
|
+
attr_flex_selector :b, :on => :after_save
|
8
|
+
|
9
|
+
attr_flex_selector :c, :if => lambda { |val| val && (val.to_i % 2 == 0) }
|
10
|
+
|
11
|
+
attr_flex_selector :d do |new|
|
12
|
+
[a, b, c, flex_d_input, new].join("-")
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe "Flexselect" do
|
17
|
+
it "should create an virtual attribute derived from the actual one" do
|
18
|
+
Thing.new.should respond_to("flex_a_input")
|
19
|
+
Thing.new.should respond_to("flex_a_input=")
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "with defaults" do
|
23
|
+
it "should update the attribute with the value on the virtual attribute if it is present" do
|
24
|
+
thing = Thing.create! :a => "a", :flex_a_input => "flex a", :b => "unrelated b"
|
25
|
+
thing.a.should == "flex a"
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should not update the attribute if the virtual one is not present" do
|
29
|
+
thing = Thing.create! :a => "a"
|
30
|
+
thing.a.should == "a"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "using an :on option" do
|
35
|
+
it "should determine the calling time of the callback using the provided option" do
|
36
|
+
thing = Thing.new :b => "b", :flex_b_input => "flex b"
|
37
|
+
|
38
|
+
lambda do
|
39
|
+
thing.save
|
40
|
+
end.should change(thing, :b).from("b").to("flex b")
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "with an :if option" do
|
45
|
+
it "should only update the attribute if the provided lambda returns true" do
|
46
|
+
thing = Thing.create! :c => "c", :flex_c_input => "1"
|
47
|
+
thing.c.should == "c"
|
48
|
+
|
49
|
+
thing = Thing.create! :c => "c", :flex_c_input => "2"
|
50
|
+
thing.c.should == "2"
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "with a custom block" do
|
55
|
+
it "should use the custom block to update the attribute value" do
|
56
|
+
thing = Thing.create! :a => "a", :b => "b", :c => "c", :d => "d", :flex_d_input => "flex d"
|
57
|
+
thing.d.should == "a-b-c-flex d-flex d"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
2
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
|
+
|
4
|
+
require 'rubygems'
|
5
|
+
require 'active_record'
|
6
|
+
require 'flexselect'
|
7
|
+
require 'spec'
|
8
|
+
require 'spec/autorun'
|
9
|
+
require 'active_record_helper'
|
10
|
+
|
11
|
+
# Rails hook.
|
12
|
+
require(File.join(File.dirname(__FILE__), '..', 'init.rb'))
|
13
|
+
|
14
|
+
Spec::Runner.configure do |config|
|
15
|
+
end
|
data/uninstall.rb
ADDED
metadata
ADDED
@@ -0,0 +1,116 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: flexselect
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Emmanuel Oga
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-07 00:00:00 -03:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: activerecord
|
37
|
+
type: :development
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
- !ruby/object:Gem::Dependency
|
46
|
+
name: sqlite3-ruby
|
47
|
+
type: :development
|
48
|
+
version_requirement:
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
description: For rails applications, requires jQuery
|
56
|
+
email: EmmanuelOga@gmail.com
|
57
|
+
executables: []
|
58
|
+
|
59
|
+
extensions: []
|
60
|
+
|
61
|
+
extra_rdoc_files:
|
62
|
+
- LICENSE
|
63
|
+
- README.rdoc
|
64
|
+
files:
|
65
|
+
- .document
|
66
|
+
- .gitignore
|
67
|
+
- LICENSE
|
68
|
+
- README.rdoc
|
69
|
+
- Rakefile
|
70
|
+
- VERSION
|
71
|
+
- generators/flexselect/USAGE
|
72
|
+
- generators/flexselect/flexselect_generator.rb
|
73
|
+
- generators/flexselect/templates/flexselect.css
|
74
|
+
- generators/flexselect/templates/jquery.flexselect.js
|
75
|
+
- generators/flexselect/templates/liquidmetal.js
|
76
|
+
- install.rb
|
77
|
+
- lib/flexselect.rb
|
78
|
+
- rails/init.rb
|
79
|
+
- spec/active_record_helper.rb
|
80
|
+
- spec/flexselect_spec.rb
|
81
|
+
- spec/spec.opts
|
82
|
+
- spec/spec_helper.rb
|
83
|
+
- tasks/flexselect_tasks.rake
|
84
|
+
- uninstall.rb
|
85
|
+
has_rdoc: true
|
86
|
+
homepage: http://github.com/EmmanuelOga/flexselect
|
87
|
+
licenses: []
|
88
|
+
|
89
|
+
post_install_message:
|
90
|
+
rdoc_options:
|
91
|
+
- --charset=UTF-8
|
92
|
+
require_paths:
|
93
|
+
- lib
|
94
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
95
|
+
requirements:
|
96
|
+
- - ">="
|
97
|
+
- !ruby/object:Gem::Version
|
98
|
+
version: "0"
|
99
|
+
version:
|
100
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
101
|
+
requirements:
|
102
|
+
- - ">="
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: "0"
|
105
|
+
version:
|
106
|
+
requirements: []
|
107
|
+
|
108
|
+
rubyforge_project:
|
109
|
+
rubygems_version: 1.3.5
|
110
|
+
signing_key:
|
111
|
+
specification_version: 3
|
112
|
+
summary: A jQuery plugin and an activerecord extension to provide auto-complete on html select tags
|
113
|
+
test_files:
|
114
|
+
- spec/spec_helper.rb
|
115
|
+
- spec/flexselect_spec.rb
|
116
|
+
- spec/active_record_helper.rb
|