autocomplete 1.0.1
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/MIT-LICENSE +20 -0
- data/Manifest +16 -0
- data/README.rdoc +10 -0
- data/Rakefile +14 -0
- data/assets/autocomplete-loading.gif +0 -0
- data/assets/autocomplete.css +74 -0
- data/assets/autocomplete.js +320 -0
- data/autocomplete.gemspec +31 -0
- data/id_rsa.pub +1 -0
- data/init.rb +5 -0
- data/install.rb +10 -0
- data/lib/autocomplete.rb +9 -0
- data/lib/autocomplete_controller.rb +78 -0
- data/lib/autocomplete_helper.rb +43 -0
- data/tasks/autocomplete_tasks.rake +15 -0
- data/test/autocomplete_test.rb +8 -0
- metadata +92 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2010 [name of plugin creator]
|
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/Manifest
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
MIT-LICENSE
|
2
|
+
README.rdoc
|
3
|
+
Rakefile
|
4
|
+
assets/autocomplete-loading.gif
|
5
|
+
assets/autocomplete.css
|
6
|
+
assets/autocomplete.js
|
7
|
+
autocomplete.gemspec
|
8
|
+
id_rsa.pub
|
9
|
+
init.rb
|
10
|
+
install.rb
|
11
|
+
lib/autocomplete.rb
|
12
|
+
lib/autocomplete_controller.rb
|
13
|
+
lib/autocomplete_helper.rb
|
14
|
+
tasks/autocomplete_tasks.rake
|
15
|
+
test/autocomplete_test.rb
|
16
|
+
Manifest
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require "echoe"
|
4
|
+
|
5
|
+
Echoe.new('autocomplete', '1.0.1') do |p|
|
6
|
+
p.description = "Insert an autocomplete-search-box inside your view"
|
7
|
+
p.url = "http://www.voja-jovanovic.info"
|
8
|
+
p.author = "Voislav Jovanovic"
|
9
|
+
p.email = "voislavj@gmail.com"
|
10
|
+
p.ignore_pattern = ["tmp/*", "script/*"]
|
11
|
+
p.development_dependencies = []
|
12
|
+
end
|
13
|
+
|
14
|
+
Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each {|ext| load ext}
|
Binary file
|
@@ -0,0 +1,74 @@
|
|
1
|
+
.autocomplete-loading {
|
2
|
+
background-image:url(/images/autocomplete-loading.gif);
|
3
|
+
background-repeat: no-repeat;
|
4
|
+
background-position: 98% center;
|
5
|
+
}
|
6
|
+
.autocomplete-multi-controls .autocomplete-remove {
|
7
|
+
float:right;
|
8
|
+
margin-bottom:5px;
|
9
|
+
}
|
10
|
+
.autocomplete-multi-controls a {
|
11
|
+
outline:none;
|
12
|
+
}
|
13
|
+
.autocomplete-multi-container {
|
14
|
+
width:200px;
|
15
|
+
clear:both;
|
16
|
+
border:1px solid #ccc;
|
17
|
+
overflow:auto;
|
18
|
+
}
|
19
|
+
.autocomplete-multi-rowset {
|
20
|
+
padding:2px 4px;
|
21
|
+
border-bottom:1px dotted #ddd;
|
22
|
+
cursor:default;
|
23
|
+
}
|
24
|
+
.autocomplete-multi-rowset:hover {
|
25
|
+
background-color:#E8730C;
|
26
|
+
color:#fff;
|
27
|
+
}
|
28
|
+
.autocomplete-multi-rowset.selected,
|
29
|
+
.autocomplete-multi-rowset.selected:hover {
|
30
|
+
background:#eee;color:#000
|
31
|
+
}
|
32
|
+
.autocomplete-multi-rowset label{margin-left:5px;}
|
33
|
+
.autocomplete-multi-rowset:hover label{color:#fff}
|
34
|
+
.autocomplete-multi-rowset.selected:hover label{color:#000}
|
35
|
+
|
36
|
+
.autocomplete-list {
|
37
|
+
list-style-type:none;
|
38
|
+
position:absolute;
|
39
|
+
background:#fff;
|
40
|
+
border:1px solid #ccc;
|
41
|
+
width:300px;
|
42
|
+
max-height:200px;
|
43
|
+
overflow:auto;
|
44
|
+
}
|
45
|
+
.autocomplete-list li{
|
46
|
+
margin:0;
|
47
|
+
padding:0;
|
48
|
+
}
|
49
|
+
.autocomplete-list li.empty{
|
50
|
+
padding:2px 4px;
|
51
|
+
color:#777;
|
52
|
+
}
|
53
|
+
.autocomplete-list li.odd {
|
54
|
+
background:#eee;
|
55
|
+
}
|
56
|
+
.autocomplete-list a {
|
57
|
+
display:block;
|
58
|
+
text-decoration:none;
|
59
|
+
padding:2px 4px;
|
60
|
+
outline:none;
|
61
|
+
}
|
62
|
+
.autocomplete-list a:focus,
|
63
|
+
.autocomplete-list a:hover {
|
64
|
+
background:Highlight;
|
65
|
+
color:HighlightText;
|
66
|
+
}
|
67
|
+
.autocomplete-multi-status {
|
68
|
+
width:192px;
|
69
|
+
background:#ccc;
|
70
|
+
font-size:10px;
|
71
|
+
color:#555;
|
72
|
+
padding:2px 5px;
|
73
|
+
text-align:right;
|
74
|
+
}
|
@@ -0,0 +1,320 @@
|
|
1
|
+
/**
|
2
|
+
* @author Voislav Voja Jovanovic
|
3
|
+
* @version 1.0.0
|
4
|
+
*/
|
5
|
+
|
6
|
+
|
7
|
+
function Autocomplete(element, options) {
|
8
|
+
|
9
|
+
// if autocomplete is already initialized, just return
|
10
|
+
if(element.hasAttribute("ready")) {
|
11
|
+
return;
|
12
|
+
}
|
13
|
+
element.writeAttribute("ready", "ready");
|
14
|
+
|
15
|
+
// patch element's name if `multi` is on
|
16
|
+
if(options["multi"] && !options["name"].match(/\[\]$/)) {
|
17
|
+
options["name"] += "[]";
|
18
|
+
}
|
19
|
+
|
20
|
+
// Attach Events
|
21
|
+
document.body.observe("click", function(){
|
22
|
+
Autocomplete_RemoveList(element);
|
23
|
+
});
|
24
|
+
|
25
|
+
element.adjacent(".autocomplete-multi-controls").each(function(ctrl){
|
26
|
+
ctrl.select(".autocomplete-select-all")[0].observe("click", function(){
|
27
|
+
Autocomplete_getCheckboxes(element).each(function(cb){
|
28
|
+
cb.checked = true;
|
29
|
+
cb.parentNode.addClassName("selected");
|
30
|
+
});
|
31
|
+
});
|
32
|
+
ctrl.select(".autocomplete-select-none")[0].observe("click", function(){
|
33
|
+
Autocomplete_getCheckboxes(element).each(function(cb){
|
34
|
+
cb.checked = false;
|
35
|
+
cb.parentNode.removeClassName("selected");
|
36
|
+
});
|
37
|
+
});
|
38
|
+
ctrl.select(".autocomplete-remove")[0].observe("click",function(){
|
39
|
+
var rows = Autocomplete_getCheckedRows(element);
|
40
|
+
if(rows.length>0) {
|
41
|
+
if(confirm("You are about to remove " + rows.length + " item(s).\nDo you want to proceed?")) {
|
42
|
+
rows.each(function(r){r.remove();})
|
43
|
+
Autocomplete_UpdateStatus(element);
|
44
|
+
}
|
45
|
+
}
|
46
|
+
});
|
47
|
+
});
|
48
|
+
|
49
|
+
element.observe("keyup", function(e){
|
50
|
+
var el = e.target;
|
51
|
+
|
52
|
+
// special keys
|
53
|
+
switch(e.keyCode) {
|
54
|
+
|
55
|
+
case 27: // Esc
|
56
|
+
Autocomplete_RemoveList(el);
|
57
|
+
|
58
|
+
case 40: // Arrow Down
|
59
|
+
// focus into list
|
60
|
+
el.adjacent("ul.autocomplete-list li:first-child a").each(function(a){
|
61
|
+
a.focus();
|
62
|
+
a.select();
|
63
|
+
});
|
64
|
+
|
65
|
+
case 9: // Tab
|
66
|
+
case 13: // Enter
|
67
|
+
case 17: // Ctrl
|
68
|
+
case 18: // Alt
|
69
|
+
case 19: // Pause/Break
|
70
|
+
case 20: // Caps Lock
|
71
|
+
case 33: // PageUp
|
72
|
+
case 34: // PageDown
|
73
|
+
case 35: // End
|
74
|
+
case 36: // Home
|
75
|
+
case 37: // Arrow Left
|
76
|
+
case 38: // Arrow Up
|
77
|
+
case 39: // Arrow Right
|
78
|
+
case 44: // PrintScreen
|
79
|
+
case 45: // Insert
|
80
|
+
case 92: // Win
|
81
|
+
case 93: // Context
|
82
|
+
// do nothing
|
83
|
+
return;
|
84
|
+
break;
|
85
|
+
}
|
86
|
+
|
87
|
+
Autocomplete_Stop(el);
|
88
|
+
|
89
|
+
el.timeout = setTimeout(function(){
|
90
|
+
// check keyword length
|
91
|
+
el.value = el.value.replace(/^[\s\r\n\t]+|[\s\r\n\t]+$/g, "")
|
92
|
+
if(el.value.length<3) return;
|
93
|
+
|
94
|
+
// build POST params
|
95
|
+
var data = "";
|
96
|
+
if (typeof(options.glue) != "undefined") {
|
97
|
+
data += "&glue="+escape(options.glue);
|
98
|
+
}
|
99
|
+
if(typeof(options.map) != "undefined") {
|
100
|
+
if(typeof(options.map.value) != "object") {
|
101
|
+
options.map.value = [options.map.value];
|
102
|
+
}
|
103
|
+
for(var i=0; i<options.map.value.length; i++) {
|
104
|
+
data += "&map[value][]=" + options.map.value[i];
|
105
|
+
}
|
106
|
+
if(typeof(options.map.label) != "object") {
|
107
|
+
options.map.label = [options.map.label];
|
108
|
+
}
|
109
|
+
for(var i=0; i<options.map.label.length; i++) {
|
110
|
+
data += "&map[label][]=" + options.map.label[i];
|
111
|
+
}
|
112
|
+
for(var i=0; i<options.map.search.length; i++) {
|
113
|
+
data += "&map[search][]=" + options.map.search[i];
|
114
|
+
}
|
115
|
+
for(var i=0; i<options.joins.length; i++) {
|
116
|
+
data += "&joins[]=" + options.joins[i];
|
117
|
+
}
|
118
|
+
}
|
119
|
+
if (options["multi"]) {
|
120
|
+
el.next(".autocomplete-multi-container").select("input[type=checkbox]").each(function(e){
|
121
|
+
data += "&skip[]=" + e.value
|
122
|
+
});
|
123
|
+
}
|
124
|
+
|
125
|
+
// remove previous list if there's one
|
126
|
+
Autocomplete_RemoveList(el);
|
127
|
+
|
128
|
+
// reset hidden input before sending new request
|
129
|
+
if(!options["multi"]) {
|
130
|
+
el.parentNode.select("input[type=hidden]").first().value = "";
|
131
|
+
}
|
132
|
+
|
133
|
+
// make Request
|
134
|
+
new Ajax.Request("/autocomplete/find/" + options.model + "/" + _esc(el.value), {
|
135
|
+
method: "post",
|
136
|
+
parameters: data.substr(1),
|
137
|
+
onCreate: function(){
|
138
|
+
el.addClassName("autocomplete-loading");
|
139
|
+
},
|
140
|
+
onComplete: function(req){
|
141
|
+
el.removeClassName("autocomplete-loading");
|
142
|
+
if(req.status==200) {
|
143
|
+
var response = eval('(' + req.responseText + ')');
|
144
|
+
|
145
|
+
var ul = new Element("ul", {
|
146
|
+
"class": "autocomplete-list"
|
147
|
+
});
|
148
|
+
var position = el.positionedOffset()
|
149
|
+
ul.setStyle({
|
150
|
+
left: position[0] + "px",
|
151
|
+
top: position[1] + el.getHeight() + "px"
|
152
|
+
});
|
153
|
+
el.insert({
|
154
|
+
before: ul
|
155
|
+
});
|
156
|
+
if (response.length > 0) {
|
157
|
+
var li, a, rem;
|
158
|
+
for (var i = 0; i < response.length; i++) {
|
159
|
+
// list-item
|
160
|
+
li = new Element("li");
|
161
|
+
if (i % 2) {
|
162
|
+
li.addClassName("odd");
|
163
|
+
}
|
164
|
+
// item-value
|
165
|
+
a = new Element("a");
|
166
|
+
a.href = "javascript:void(0);";
|
167
|
+
a.r = response[i];
|
168
|
+
|
169
|
+
a.observe("keyup", function(e){
|
170
|
+
switch(e.keyCode) {
|
171
|
+
case 27: // Esc
|
172
|
+
Autocomplete_RemoveList(el);
|
173
|
+
break;
|
174
|
+
case 40: // Arrow Down
|
175
|
+
this.parentNode.next("li").select("a")[0].focus();
|
176
|
+
break;
|
177
|
+
case 38: // Arrow Up
|
178
|
+
this.parentNode.previous("li").select("a")[0].focus();
|
179
|
+
break;
|
180
|
+
}
|
181
|
+
});
|
182
|
+
|
183
|
+
a.observe("click", function(e){
|
184
|
+
var next = this.parentNode.previous("li");
|
185
|
+
if(next) {
|
186
|
+
next = next.select("a")[0];
|
187
|
+
}else{
|
188
|
+
next = this.parentNode.next("li");
|
189
|
+
if(next) {
|
190
|
+
next = next.select("a")[0];
|
191
|
+
}
|
192
|
+
}
|
193
|
+
if (options["multi"]) {
|
194
|
+
var c = el.next(".autocomplete-multi-container");
|
195
|
+
if (c.select("div.ac-" + this.r["value"]).length == 0) {
|
196
|
+
span = new Element("div", {
|
197
|
+
"class": "autocomplete-multi-rowset ac-" + this.r["value"]
|
198
|
+
});
|
199
|
+
span.observe("click", function(e){
|
200
|
+
var chk = this.select("input[type=checkbox]")[0];
|
201
|
+
if(e.target.hasClassName("autocomplete-multi-rowset")) {
|
202
|
+
chk.checked = chk.checked?false:true;
|
203
|
+
}
|
204
|
+
if(chk.checked) {
|
205
|
+
this.addClassName("selected");
|
206
|
+
}else{
|
207
|
+
this.removeClassName("selected");
|
208
|
+
}
|
209
|
+
});
|
210
|
+
var id = "ac_input_" + this.r["value"];
|
211
|
+
inpt = new Element("input", {
|
212
|
+
"type": "checkbox",
|
213
|
+
"name": options["name"],
|
214
|
+
"id": id,
|
215
|
+
"value": this.r["value"]
|
216
|
+
});
|
217
|
+
span.insert(inpt);
|
218
|
+
|
219
|
+
label = new Element("label", {"for":id}).update(this.r["label"]);
|
220
|
+
span.insert(label)
|
221
|
+
|
222
|
+
c.insert(span);
|
223
|
+
|
224
|
+
if (this.parentNode.parentNode.select("li").length == 1) {
|
225
|
+
Autocomplete_RemoveList(el);
|
226
|
+
}
|
227
|
+
else {
|
228
|
+
this.parentNode.remove();
|
229
|
+
}
|
230
|
+
Autocomplete_UpdateStatus(el);
|
231
|
+
}
|
232
|
+
Event.stop(e);
|
233
|
+
}
|
234
|
+
else {
|
235
|
+
el.value = this.r["label"];
|
236
|
+
el.title = "value: " + this.r["value"];
|
237
|
+
el.parentNode.select("input[type=hidden]").first().value = this.r["value"];
|
238
|
+
}
|
239
|
+
|
240
|
+
// focus to previous
|
241
|
+
if(next) next.focus()
|
242
|
+
});
|
243
|
+
a.innerHTML = response[i]["label"];
|
244
|
+
li.insert(a);
|
245
|
+
|
246
|
+
ul.insert(li);
|
247
|
+
}
|
248
|
+
}else{
|
249
|
+
var empty = new Element("li", {"class": "empty"}).update("No results.");
|
250
|
+
ul.insert(empty);
|
251
|
+
setTimeout(function(){
|
252
|
+
Autocomplete_RemoveList(el);
|
253
|
+
}, 2000);
|
254
|
+
}
|
255
|
+
}else{
|
256
|
+
//alert(req.status + ". " + req.statusText);
|
257
|
+
}
|
258
|
+
}
|
259
|
+
});
|
260
|
+
|
261
|
+
}, 800);
|
262
|
+
|
263
|
+
});
|
264
|
+
element.observe("keypress", function(e){
|
265
|
+
Autocomplete_Stop(e.target);
|
266
|
+
});
|
267
|
+
|
268
|
+
|
269
|
+
}
|
270
|
+
function Autocomplete_Stop(element) {
|
271
|
+
var to = element.timeout;
|
272
|
+
if(to) {
|
273
|
+
clearTimeout(to);
|
274
|
+
element.timeout = null;
|
275
|
+
}
|
276
|
+
}
|
277
|
+
function Autocomplete_RemoveList(element) {
|
278
|
+
var list = $$(".autocomplete-list");
|
279
|
+
list.each(function(el){el.remove();});
|
280
|
+
element.focus();
|
281
|
+
}
|
282
|
+
function Autocomplete_UpdateStatus(element) {
|
283
|
+
var container = element.next(".autocomplete-multi-container");
|
284
|
+
if (container) {
|
285
|
+
element.next(".autocomplete-multi-status").select(".length").each(function(s){
|
286
|
+
s.innerHTML = container.select(".autocomplete-multi-rowset").length+"";
|
287
|
+
});
|
288
|
+
}
|
289
|
+
|
290
|
+
}
|
291
|
+
function Autocomplete_getCheckboxes(element) {
|
292
|
+
ret = [];
|
293
|
+
element.adjacent(".autocomplete-multi-container")[0].select("input[type=checkbox]").each(function(cb){
|
294
|
+
ret.push(cb);
|
295
|
+
});
|
296
|
+
return ret;
|
297
|
+
}
|
298
|
+
function Autocomplete_getCheckedRows(element) {
|
299
|
+
ret = [];
|
300
|
+
element.adjacent(".autocomplete-multi-container")[0].select("input[type=checkbox]").each(function(cb){
|
301
|
+
if(cb.checked) {
|
302
|
+
ret.push(cb.parentNode);
|
303
|
+
}
|
304
|
+
});
|
305
|
+
return ret;
|
306
|
+
}
|
307
|
+
|
308
|
+
function _esc(str) {
|
309
|
+
str = str + ""
|
310
|
+
var ret = "", c;
|
311
|
+
for(var i=0; i<str.length; i++) {
|
312
|
+
c = str[i];
|
313
|
+
if(!c.match(/[a-z0-9]/i)) {
|
314
|
+
c = c.charCodeAt(0);
|
315
|
+
c = "%" + c.toString(16);
|
316
|
+
}
|
317
|
+
ret += c;
|
318
|
+
}
|
319
|
+
return ret;
|
320
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{autocomplete}
|
5
|
+
s.version = "1.0.1"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Voislav Jovanovic"]
|
9
|
+
s.date = %q{2010-07-23}
|
10
|
+
s.description = %q{Insert an autocomplete-search-box inside your view}
|
11
|
+
s.email = %q{voislavj@gmail.com}
|
12
|
+
s.extra_rdoc_files = ["README.rdoc", "lib/autocomplete.rb", "lib/autocomplete_controller.rb", "lib/autocomplete_helper.rb", "tasks/autocomplete_tasks.rake"]
|
13
|
+
s.files = ["MIT-LICENSE", "README.rdoc", "Rakefile", "assets/autocomplete-loading.gif", "assets/autocomplete.css", "assets/autocomplete.js", "autocomplete.gemspec", "id_rsa.pub", "init.rb", "install.rb", "lib/autocomplete.rb", "lib/autocomplete_controller.rb", "lib/autocomplete_helper.rb", "tasks/autocomplete_tasks.rake", "test/autocomplete_test.rb", "Manifest"]
|
14
|
+
s.homepage = %q{http://www.voja-jovanovic.info}
|
15
|
+
s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Autocomplete", "--main", "README.rdoc"]
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
s.rubyforge_project = %q{autocomplete}
|
18
|
+
s.rubygems_version = %q{1.3.7}
|
19
|
+
s.summary = %q{Insert an autocomplete-search-box inside your view}
|
20
|
+
s.test_files = ["test/autocomplete_test.rb"]
|
21
|
+
|
22
|
+
if s.respond_to? :specification_version then
|
23
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
24
|
+
s.specification_version = 3
|
25
|
+
|
26
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
27
|
+
else
|
28
|
+
end
|
29
|
+
else
|
30
|
+
end
|
31
|
+
end
|
data/id_rsa.pub
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEAxm7FhP0LM71UVtxZLiZdmx0cyzj2cNCqE4UOD+JF5HvkbAmv2ySIqyO4NMSq9Ww96P+GW8o6UlnhzhziNMRoeon2b59AsjV5d8q8rlOKGT5z7zkN8VpoT8iht+wpQrZX4SLshlmwR90nP4ygDWh9Z9wZ1GVje36GqVUdYAKh0c2zYH5MZrfEuTIIMzaMJm6faRE/MG1Gx/Lqo+0mGw6k1Uz4Y7j8M/rm+SDC6Ws0TotKnnHkvhT2ZDi8ZB0Qvf7Zz0U/oamKYArXeLAeR6U+ytNozXq9lWvB15wKzLFYEHJonpmh3IH2oazfvksE1nzBlMGF37oHTJCDZ/hsQ+yAiQ== User@VOJA
|
data/init.rb
ADDED
data/install.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
# Install hook code here
|
2
|
+
#require 'ftools'
|
3
|
+
#
|
4
|
+
#plugins_dir = File.expand_path(".")
|
5
|
+
#autocomplete_dir = File.join(plugins_dir, 'autocomplete')
|
6
|
+
#root_dir = File.join(autocomplete_dir, '..', '..', '..')
|
7
|
+
#
|
8
|
+
#File.copy File.join(autocomplete_dir, 'assets', 'autocomplete.js'), File.join(root_dir, 'public', 'javascripts', 'autocomplete.js')
|
9
|
+
#File.copy File.join(autocomplete_dir, 'assets', 'autocomplete.css'), File.join(root_dir, 'public', 'stylesheets', 'autocomplete.css')
|
10
|
+
#File.copy File.join(autocomplete_dir, 'assets', 'autocomplete-loading.gif'), File.join(root_dir, 'public', 'images', 'autocomplete-loading.gif')
|
data/lib/autocomplete.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
# Autocomplete
|
2
|
+
%w{ models controllers }.each do |dir|
|
3
|
+
path = File.join(File.dirname(__FILE__), 'app', dir)
|
4
|
+
$LOAD_PATH << path
|
5
|
+
ActiveSupport::Dependencies.load_paths << path
|
6
|
+
ActiveSupport::Dependencies.load_once_paths.delete(path)
|
7
|
+
end
|
8
|
+
|
9
|
+
require "autocomplete/routing"
|
@@ -0,0 +1,78 @@
|
|
1
|
+
class AutocompleteController < ActionController::Base
|
2
|
+
|
3
|
+
def find
|
4
|
+
params[:map] = {:value=>[:id], :label=>[:name], :search=>[:name]} unless params[:map]
|
5
|
+
params[:limit] ||= 50
|
6
|
+
params[:skip] ||= []
|
7
|
+
params[:map][:search] ||= []
|
8
|
+
params[:glue] ||= " "
|
9
|
+
|
10
|
+
params[:joins] ||= []
|
11
|
+
|
12
|
+
@modelname = params[:model]
|
13
|
+
@model = params[:model].constantize
|
14
|
+
|
15
|
+
params[:map][:label] = parse_table_fields(params[:map][:label])
|
16
|
+
params[:map][:value] = parse_table_fields(params[:map][:value])
|
17
|
+
params[:map][:search] = parse_table_fields(params[:map][:search])
|
18
|
+
|
19
|
+
params[:joins].each_with_index do |v,k|
|
20
|
+
params[:joins][k] = eval(":#{v}")
|
21
|
+
end
|
22
|
+
|
23
|
+
value = "CONCAT_WS(' ',#{params[:map][:value].join(",")})"
|
24
|
+
label = "CONCAT_WS('#{params[:glue]}',#{params[:map][:label].join(",")})"
|
25
|
+
search = "CONCAT_WS(' ',#{(params[:map][:label]|params[:map][:search]).join(",")})"
|
26
|
+
|
27
|
+
skip = ""
|
28
|
+
toskip = ""
|
29
|
+
|
30
|
+
params[:skip].each do |s|
|
31
|
+
toskip += ","
|
32
|
+
unless s.to_s.match(/^\d+$/i)
|
33
|
+
s = "'#{s}'"
|
34
|
+
end
|
35
|
+
toskip += s
|
36
|
+
end
|
37
|
+
|
38
|
+
toskip = toskip.gsub(/^,/, "")
|
39
|
+
if params[:skip].length>0
|
40
|
+
skip = "AND #{value} NOT IN(#{toskip})"
|
41
|
+
end
|
42
|
+
|
43
|
+
debug params[:joins]
|
44
|
+
|
45
|
+
result = @model.find(
|
46
|
+
:all, {
|
47
|
+
:select => "#{value} as `value`, #{label} as `label`",
|
48
|
+
:conditions => ["#{search} LIKE ? #{skip}", "%" + params[:keyword].gsub("%", "\%") + "%"],
|
49
|
+
:joins => params[:joins],
|
50
|
+
:order => "label",
|
51
|
+
:limit => params[:limit]
|
52
|
+
}
|
53
|
+
);
|
54
|
+
render :text => result.to_json
|
55
|
+
end
|
56
|
+
|
57
|
+
def debug(var, mode="w")
|
58
|
+
File.open("D:\\voja.log", mode) {|f| f.write(var.to_yaml); f.close}
|
59
|
+
end
|
60
|
+
|
61
|
+
def parse_table_fields(arr)
|
62
|
+
single = false
|
63
|
+
unless arr.is_a?(Array)
|
64
|
+
arr = [arr]
|
65
|
+
single = true
|
66
|
+
end
|
67
|
+
|
68
|
+
arr.each_with_index do |v,k|
|
69
|
+
puts v.inspect + " => " + v.to_s.inspect
|
70
|
+
if v.to_s.match(/\./)
|
71
|
+
items = v.to_s.split(".",2)
|
72
|
+
arr[k] = "#{items[0].constantize.table_name}.#{items[1]}"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
return single ? arr[0] : arr;
|
76
|
+
end
|
77
|
+
|
78
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AutocompleteHelper
|
2
|
+
|
3
|
+
# generate autocomplete text-field
|
4
|
+
def autocomplete(options={})
|
5
|
+
return nil unless options[:model]
|
6
|
+
|
7
|
+
options[:name] ||= :autocomplete_keyword
|
8
|
+
options[:multi] ||= false;
|
9
|
+
|
10
|
+
options[:multi_height] ||= 150
|
11
|
+
options[:uuid] = _uuid
|
12
|
+
|
13
|
+
options[:map][:value] ||= :id
|
14
|
+
options[:map][:label] ||= [:name]
|
15
|
+
options[:map][:search] ||= []
|
16
|
+
|
17
|
+
#txt_name = options[:multi] ? "autocomplete-keyword-#{options[:uuid]}" : options[:name]
|
18
|
+
txt_name = "autocomplete-keyword-#{options[:uuid]}"
|
19
|
+
|
20
|
+
ret = []
|
21
|
+
ret << javascript_include_tag("autocomplete")
|
22
|
+
ret << stylesheet_link_tag("autocomplete")
|
23
|
+
ret << label_tag(txt_name, options[:model].camelize)
|
24
|
+
ret << text_field_tag(txt_name, nil, {
|
25
|
+
:onfocus => "Autocomplete(this, " + options.to_json.to_s + ")"
|
26
|
+
})
|
27
|
+
|
28
|
+
if options[:multi]
|
29
|
+
ret << content_tag(:div, '<a class="autocomplete-remove" href="javascript:void(0);">remove selected</a> select <a href="javascript:void(0)" class="autocomplete-select-all">all</a> / <a href="javascript:void(0)" class="autocomplete-select-none">none</a>', :class=>"autocomplete-multi-controls")
|
30
|
+
ret << content_tag(:div, "", :class=>"autocomplete-multi-container", :style=>"height: #{options[:multi_height]}px")
|
31
|
+
ret << content_tag(:div, "<span class=\"length\">0</span> items", :class=>"autocomplete-multi-status")
|
32
|
+
else
|
33
|
+
ret << hidden_field_tag(options[:name], "")
|
34
|
+
end
|
35
|
+
|
36
|
+
return ret.join("\r\n")
|
37
|
+
end
|
38
|
+
|
39
|
+
def _uuid
|
40
|
+
o = [('a'..'z'),('A'..'Z'),(0..9)].map{|i| i.to_a}.flatten;
|
41
|
+
string = (0..10).map{ o[rand(o.length)] }.join;
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# desc "Explaining what the task does"
|
2
|
+
# task :autocomplete do
|
3
|
+
# # Task goes here
|
4
|
+
# end
|
5
|
+
desc 'Update autocomplete JavaScripts and CSSs'
|
6
|
+
task :update_scripts => [] do |t|
|
7
|
+
|
8
|
+
autocomplete_dir = File.expand_path(".")
|
9
|
+
[["javascripts","autocomplete.js"], ["stylesheets","autocomplete.css"]].each do |file|
|
10
|
+
unless File.exists?(File.join(RAILS_ROOT, "public", file[0], file[1]))
|
11
|
+
File.copy(File.join("assets", file[1]), File.join(RAILS_ROOT, "public", file[0], file[1]))
|
12
|
+
end
|
13
|
+
end
|
14
|
+
puts "JavaScripts and CSSs updated."
|
15
|
+
end
|
metadata
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: autocomplete
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
5
|
+
prerelease: false
|
6
|
+
segments:
|
7
|
+
- 1
|
8
|
+
- 0
|
9
|
+
- 1
|
10
|
+
version: 1.0.1
|
11
|
+
platform: ruby
|
12
|
+
authors:
|
13
|
+
- Voislav Jovanovic
|
14
|
+
autorequire:
|
15
|
+
bindir: bin
|
16
|
+
cert_chain: []
|
17
|
+
|
18
|
+
date: 2010-07-23 00:00:00 +02:00
|
19
|
+
default_executable:
|
20
|
+
dependencies: []
|
21
|
+
|
22
|
+
description: Insert an autocomplete-search-box inside your view
|
23
|
+
email: voislavj@gmail.com
|
24
|
+
executables: []
|
25
|
+
|
26
|
+
extensions: []
|
27
|
+
|
28
|
+
extra_rdoc_files:
|
29
|
+
- README.rdoc
|
30
|
+
- lib/autocomplete.rb
|
31
|
+
- lib/autocomplete_controller.rb
|
32
|
+
- lib/autocomplete_helper.rb
|
33
|
+
- tasks/autocomplete_tasks.rake
|
34
|
+
files:
|
35
|
+
- MIT-LICENSE
|
36
|
+
- README.rdoc
|
37
|
+
- Rakefile
|
38
|
+
- assets/autocomplete-loading.gif
|
39
|
+
- assets/autocomplete.css
|
40
|
+
- assets/autocomplete.js
|
41
|
+
- autocomplete.gemspec
|
42
|
+
- id_rsa.pub
|
43
|
+
- init.rb
|
44
|
+
- install.rb
|
45
|
+
- lib/autocomplete.rb
|
46
|
+
- lib/autocomplete_controller.rb
|
47
|
+
- lib/autocomplete_helper.rb
|
48
|
+
- tasks/autocomplete_tasks.rake
|
49
|
+
- test/autocomplete_test.rb
|
50
|
+
- Manifest
|
51
|
+
has_rdoc: true
|
52
|
+
homepage: http://www.voja-jovanovic.info
|
53
|
+
licenses: []
|
54
|
+
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --line-numbers
|
58
|
+
- --inline-source
|
59
|
+
- --title
|
60
|
+
- Autocomplete
|
61
|
+
- --main
|
62
|
+
- README.rdoc
|
63
|
+
require_paths:
|
64
|
+
- lib
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
66
|
+
none: false
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
hash: 3
|
71
|
+
segments:
|
72
|
+
- 0
|
73
|
+
version: "0"
|
74
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
75
|
+
none: false
|
76
|
+
requirements:
|
77
|
+
- - ">="
|
78
|
+
- !ruby/object:Gem::Version
|
79
|
+
hash: 11
|
80
|
+
segments:
|
81
|
+
- 1
|
82
|
+
- 2
|
83
|
+
version: "1.2"
|
84
|
+
requirements: []
|
85
|
+
|
86
|
+
rubyforge_project: autocomplete
|
87
|
+
rubygems_version: 1.3.7
|
88
|
+
signing_key:
|
89
|
+
specification_version: 3
|
90
|
+
summary: Insert an autocomplete-search-box inside your view
|
91
|
+
test_files:
|
92
|
+
- test/autocomplete_test.rb
|