sdsykes-ferret 0.11.6.19
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +24 -0
- data/MIT-LICENSE +20 -0
- data/README +102 -0
- data/Rakefile +338 -0
- data/TODO +17 -0
- data/TUTORIAL +231 -0
- data/bin/ferret-browser +79 -0
- data/ext/analysis.c +1555 -0
- data/ext/analysis.h +219 -0
- data/ext/api.c +69 -0
- data/ext/api.h +27 -0
- data/ext/array.c +123 -0
- data/ext/array.h +53 -0
- data/ext/bitvector.c +540 -0
- data/ext/bitvector.h +272 -0
- data/ext/compound_io.c +383 -0
- data/ext/config.h +42 -0
- data/ext/document.c +156 -0
- data/ext/document.h +53 -0
- data/ext/except.c +120 -0
- data/ext/except.h +168 -0
- data/ext/extconf.rb +14 -0
- data/ext/ferret.c +402 -0
- data/ext/ferret.h +91 -0
- data/ext/filter.c +156 -0
- data/ext/fs_store.c +483 -0
- data/ext/global.c +418 -0
- data/ext/global.h +117 -0
- data/ext/hash.c +567 -0
- data/ext/hash.h +473 -0
- data/ext/hashset.c +170 -0
- data/ext/hashset.h +187 -0
- data/ext/header.h +58 -0
- data/ext/helper.c +62 -0
- data/ext/helper.h +13 -0
- data/ext/inc/lang.h +48 -0
- data/ext/inc/threading.h +31 -0
- data/ext/index.c +6425 -0
- data/ext/index.h +961 -0
- data/ext/lang.h +66 -0
- data/ext/libstemmer.c +92 -0
- data/ext/libstemmer.h +79 -0
- data/ext/mempool.c +87 -0
- data/ext/mempool.h +35 -0
- data/ext/modules.h +162 -0
- data/ext/multimapper.c +310 -0
- data/ext/multimapper.h +51 -0
- data/ext/posh.c +1006 -0
- data/ext/posh.h +1007 -0
- data/ext/priorityqueue.c +151 -0
- data/ext/priorityqueue.h +143 -0
- data/ext/q_boolean.c +1608 -0
- data/ext/q_const_score.c +161 -0
- data/ext/q_filtered_query.c +209 -0
- data/ext/q_fuzzy.c +268 -0
- data/ext/q_match_all.c +148 -0
- data/ext/q_multi_term.c +677 -0
- data/ext/q_parser.c +2825 -0
- data/ext/q_phrase.c +1126 -0
- data/ext/q_prefix.c +100 -0
- data/ext/q_range.c +350 -0
- data/ext/q_span.c +2402 -0
- data/ext/q_term.c +337 -0
- data/ext/q_wildcard.c +171 -0
- data/ext/r_analysis.c +2575 -0
- data/ext/r_index.c +3472 -0
- data/ext/r_qparser.c +585 -0
- data/ext/r_search.c +4105 -0
- data/ext/r_store.c +513 -0
- data/ext/r_utils.c +963 -0
- data/ext/ram_store.c +471 -0
- data/ext/search.c +1741 -0
- data/ext/search.h +885 -0
- data/ext/similarity.c +150 -0
- data/ext/similarity.h +82 -0
- data/ext/sort.c +983 -0
- data/ext/stem_ISO_8859_1_danish.c +338 -0
- data/ext/stem_ISO_8859_1_danish.h +16 -0
- data/ext/stem_ISO_8859_1_dutch.c +635 -0
- data/ext/stem_ISO_8859_1_dutch.h +16 -0
- data/ext/stem_ISO_8859_1_english.c +1156 -0
- data/ext/stem_ISO_8859_1_english.h +16 -0
- data/ext/stem_ISO_8859_1_finnish.c +792 -0
- data/ext/stem_ISO_8859_1_finnish.h +16 -0
- data/ext/stem_ISO_8859_1_french.c +1276 -0
- data/ext/stem_ISO_8859_1_french.h +16 -0
- data/ext/stem_ISO_8859_1_german.c +512 -0
- data/ext/stem_ISO_8859_1_german.h +16 -0
- data/ext/stem_ISO_8859_1_italian.c +1091 -0
- data/ext/stem_ISO_8859_1_italian.h +16 -0
- data/ext/stem_ISO_8859_1_norwegian.c +296 -0
- data/ext/stem_ISO_8859_1_norwegian.h +16 -0
- data/ext/stem_ISO_8859_1_porter.c +776 -0
- data/ext/stem_ISO_8859_1_porter.h +16 -0
- data/ext/stem_ISO_8859_1_portuguese.c +1035 -0
- data/ext/stem_ISO_8859_1_portuguese.h +16 -0
- data/ext/stem_ISO_8859_1_spanish.c +1119 -0
- data/ext/stem_ISO_8859_1_spanish.h +16 -0
- data/ext/stem_ISO_8859_1_swedish.c +307 -0
- data/ext/stem_ISO_8859_1_swedish.h +16 -0
- data/ext/stem_KOI8_R_russian.c +701 -0
- data/ext/stem_KOI8_R_russian.h +16 -0
- data/ext/stem_UTF_8_danish.c +344 -0
- data/ext/stem_UTF_8_danish.h +16 -0
- data/ext/stem_UTF_8_dutch.c +653 -0
- data/ext/stem_UTF_8_dutch.h +16 -0
- data/ext/stem_UTF_8_english.c +1176 -0
- data/ext/stem_UTF_8_english.h +16 -0
- data/ext/stem_UTF_8_finnish.c +808 -0
- data/ext/stem_UTF_8_finnish.h +16 -0
- data/ext/stem_UTF_8_french.c +1296 -0
- data/ext/stem_UTF_8_french.h +16 -0
- data/ext/stem_UTF_8_german.c +526 -0
- data/ext/stem_UTF_8_german.h +16 -0
- data/ext/stem_UTF_8_italian.c +1113 -0
- data/ext/stem_UTF_8_italian.h +16 -0
- data/ext/stem_UTF_8_norwegian.c +302 -0
- data/ext/stem_UTF_8_norwegian.h +16 -0
- data/ext/stem_UTF_8_porter.c +794 -0
- data/ext/stem_UTF_8_porter.h +16 -0
- data/ext/stem_UTF_8_portuguese.c +1055 -0
- data/ext/stem_UTF_8_portuguese.h +16 -0
- data/ext/stem_UTF_8_russian.c +709 -0
- data/ext/stem_UTF_8_russian.h +16 -0
- data/ext/stem_UTF_8_spanish.c +1137 -0
- data/ext/stem_UTF_8_spanish.h +16 -0
- data/ext/stem_UTF_8_swedish.c +313 -0
- data/ext/stem_UTF_8_swedish.h +16 -0
- data/ext/stopwords.c +401 -0
- data/ext/store.c +692 -0
- data/ext/store.h +777 -0
- data/ext/term_vectors.c +352 -0
- data/ext/threading.h +31 -0
- data/ext/utilities.c +446 -0
- data/ext/win32.h +54 -0
- data/lib/ferret.rb +29 -0
- data/lib/ferret/browser.rb +246 -0
- data/lib/ferret/browser/s/global.js +192 -0
- data/lib/ferret/browser/s/style.css +148 -0
- data/lib/ferret/browser/views/document/list.rhtml +49 -0
- data/lib/ferret/browser/views/document/show.rhtml +27 -0
- data/lib/ferret/browser/views/error/index.rhtml +7 -0
- data/lib/ferret/browser/views/help/index.rhtml +8 -0
- data/lib/ferret/browser/views/home/index.rhtml +29 -0
- data/lib/ferret/browser/views/layout.rhtml +22 -0
- data/lib/ferret/browser/views/term-vector/index.rhtml +4 -0
- data/lib/ferret/browser/views/term/index.rhtml +199 -0
- data/lib/ferret/browser/views/term/termdocs.rhtml +1 -0
- data/lib/ferret/browser/webrick.rb +14 -0
- data/lib/ferret/document.rb +130 -0
- data/lib/ferret/field_infos.rb +44 -0
- data/lib/ferret/index.rb +786 -0
- data/lib/ferret/number_tools.rb +157 -0
- data/lib/ferret_version.rb +3 -0
- data/setup.rb +1555 -0
- data/test/test_all.rb +5 -0
- data/test/test_helper.rb +24 -0
- data/test/threading/number_to_spoken.rb +132 -0
- data/test/threading/thread_safety_index_test.rb +79 -0
- data/test/threading/thread_safety_read_write_test.rb +76 -0
- data/test/threading/thread_safety_test.rb +133 -0
- data/test/unit/analysis/tc_analyzer.rb +548 -0
- data/test/unit/analysis/tc_token_stream.rb +646 -0
- data/test/unit/index/tc_index.rb +762 -0
- data/test/unit/index/tc_index_reader.rb +699 -0
- data/test/unit/index/tc_index_writer.rb +437 -0
- data/test/unit/index/th_doc.rb +315 -0
- data/test/unit/largefile/tc_largefile.rb +46 -0
- data/test/unit/query_parser/tc_query_parser.rb +238 -0
- data/test/unit/search/tc_filter.rb +135 -0
- data/test/unit/search/tc_fuzzy_query.rb +147 -0
- data/test/unit/search/tc_index_searcher.rb +61 -0
- data/test/unit/search/tc_multi_searcher.rb +128 -0
- data/test/unit/search/tc_multiple_search_requests.rb +58 -0
- data/test/unit/search/tc_search_and_sort.rb +179 -0
- data/test/unit/search/tc_sort.rb +49 -0
- data/test/unit/search/tc_sort_field.rb +27 -0
- data/test/unit/search/tc_spans.rb +190 -0
- data/test/unit/search/tm_searcher.rb +384 -0
- data/test/unit/store/tc_fs_store.rb +77 -0
- data/test/unit/store/tc_ram_store.rb +35 -0
- data/test/unit/store/tm_store.rb +34 -0
- data/test/unit/store/tm_store_lock.rb +68 -0
- data/test/unit/tc_document.rb +81 -0
- data/test/unit/ts_analysis.rb +2 -0
- data/test/unit/ts_index.rb +2 -0
- data/test/unit/ts_largefile.rb +4 -0
- data/test/unit/ts_query_parser.rb +2 -0
- data/test/unit/ts_search.rb +2 -0
- data/test/unit/ts_store.rb +2 -0
- data/test/unit/ts_utils.rb +2 -0
- data/test/unit/utils/tc_bit_vector.rb +295 -0
- data/test/unit/utils/tc_number_tools.rb +117 -0
- data/test/unit/utils/tc_priority_queue.rb +106 -0
- metadata +285 -0
data/ext/win32.h
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
#include "global.h"
|
2
|
+
|
3
|
+
#ifndef FRT_WIN32_H
|
4
|
+
#define FRT_WIN32_H
|
5
|
+
|
6
|
+
#include <io.h>
|
7
|
+
|
8
|
+
struct dirent
|
9
|
+
{
|
10
|
+
char *d_name;
|
11
|
+
};
|
12
|
+
|
13
|
+
typedef struct DIR
|
14
|
+
{
|
15
|
+
struct _finddata_t find_data;
|
16
|
+
struct dirent de;
|
17
|
+
long handle;
|
18
|
+
} DIR;
|
19
|
+
|
20
|
+
DIR *opendir(const char *dirname)
|
21
|
+
{
|
22
|
+
DIR *d = ALLOC_AND_ZERO(DIR);
|
23
|
+
char dirname_buf[MAX_FILE_PATH];
|
24
|
+
long ff_res;
|
25
|
+
sprintf(dirname_buf, "%s\\*", dirname);
|
26
|
+
ff_res = _findfirst(dirname_buf, &d->find_data);
|
27
|
+
if (ff_res < 0) {
|
28
|
+
free(d);
|
29
|
+
d = NULL;
|
30
|
+
} else {
|
31
|
+
d->de.d_name = NULL;
|
32
|
+
d->handle = ff_res;
|
33
|
+
}
|
34
|
+
return d;
|
35
|
+
}
|
36
|
+
|
37
|
+
struct dirent *readdir(DIR *d)
|
38
|
+
{
|
39
|
+
/* _findfirst already returned so do _findnext */
|
40
|
+
if (d->de.d_name != NULL) {
|
41
|
+
if (_findnext(d->handle, &d->find_data) < 0) {
|
42
|
+
return NULL;
|
43
|
+
}
|
44
|
+
}
|
45
|
+
d->de.d_name = d->find_data.name;
|
46
|
+
return &d->de;
|
47
|
+
}
|
48
|
+
|
49
|
+
void closedir(DIR *d)
|
50
|
+
{
|
51
|
+
_findclose(d->handle);
|
52
|
+
free(d);
|
53
|
+
}
|
54
|
+
#endif
|
data/lib/ferret.rb
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2005 David Balmain
|
3
|
+
#
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
# a copy of this software and associated documentation files (the
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
# the following conditions:
|
11
|
+
#
|
12
|
+
# The above copyright notice and this permission notice shall be
|
13
|
+
# included in all copies or substantial portions of the Software.
|
14
|
+
#
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
22
|
+
#++
|
23
|
+
# :include: ../TUTORIAL
|
24
|
+
$: << File.expand_path(File.join(File.dirname(__FILE__), "../ext"))
|
25
|
+
require 'ferret_ext'
|
26
|
+
require 'ferret_version'
|
27
|
+
require 'ferret/document'
|
28
|
+
require 'ferret/index'
|
29
|
+
require 'ferret/field_infos'
|
@@ -0,0 +1,246 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
|
4
|
+
module Ferret::Browser
|
5
|
+
class Delegator
|
6
|
+
def initialize(reader, path)
|
7
|
+
@reader, @path = reader, path
|
8
|
+
end
|
9
|
+
|
10
|
+
def run(env)
|
11
|
+
controller, action, args = :home, :index, nil
|
12
|
+
query_string = env['QUERY_STRING']||''
|
13
|
+
params = parse_query_string(query_string)
|
14
|
+
req_path = env['PATH_INFO'].gsub(/\/+/, '/')
|
15
|
+
case req_path
|
16
|
+
when '/'
|
17
|
+
# nothing to do
|
18
|
+
when /^\/?([-a-zA-Z]+)\/?$/
|
19
|
+
controller = $1
|
20
|
+
when /^\/?([-a-zA-Z]+)\/([-a-zA-Z]+)\/?(.*)?$/
|
21
|
+
controller = $1
|
22
|
+
action = $2
|
23
|
+
args = $3
|
24
|
+
else
|
25
|
+
controller = :error
|
26
|
+
args = req_path
|
27
|
+
end
|
28
|
+
controller_vars = {
|
29
|
+
:params => params,
|
30
|
+
:req_path => req_path,
|
31
|
+
:query_string => query_string,
|
32
|
+
}
|
33
|
+
delegate(controller, action, args, controller_vars)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
def delegate(controller, action, args, controller_vars)
|
39
|
+
begin
|
40
|
+
controller = to_const(controller, 'Controller').
|
41
|
+
new(@reader, @path, controller_vars)
|
42
|
+
controller.send(action, args)
|
43
|
+
rescue Exception => e
|
44
|
+
puts e.to_s
|
45
|
+
controller_vars[:params][:error] = e
|
46
|
+
ErrorController.new(@reader, @path, controller_vars).index
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def to_const(str, suffix='')
|
51
|
+
Ferret::Browser.const_get(str.to_s.split('-').
|
52
|
+
map {|w| w.capitalize}.join('') + suffix)
|
53
|
+
end
|
54
|
+
|
55
|
+
# from _why's camping
|
56
|
+
def unescape_uri(s)
|
57
|
+
s.tr('+', ' ').gsub(/%([\da-f]{2})/in){[$1].pack('H*')}
|
58
|
+
end
|
59
|
+
|
60
|
+
def parse_query_string(query_string, delim = '&;')
|
61
|
+
m = proc {|_,o,n| o.update(n, &m) rescue ([*o] << n)}
|
62
|
+
(query_string||'').split(/[#{delim}] */n).
|
63
|
+
inject({}) { |hash, param| key, val = unescape_uri(param).split('=',2)
|
64
|
+
hash.update(key.split(/[\]\[]+/).reverse.
|
65
|
+
inject(val) { |x,i| Hash[i,x] }, &m)
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
module ViewHelper
|
71
|
+
# truncates the string at the first space after +len+ characters
|
72
|
+
def truncate(str, len = 80)
|
73
|
+
if str and str.length > len and (add = str[len..-1].index(' '))
|
74
|
+
str = str[0, len + add] + '…'
|
75
|
+
end
|
76
|
+
str
|
77
|
+
end
|
78
|
+
|
79
|
+
def tick_or_cross(t)
|
80
|
+
"<img src=\"/s/i/#{t ?'tick':'cross'}.png\" alt=\"#{t ?'yes':'no'}\"/>"
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
class Controller
|
85
|
+
include ViewHelper
|
86
|
+
APP_DIR = File.expand_path(File.join(File.dirname(__FILE__), "browser/"))
|
87
|
+
STATIC_DIR = File.expand_path(File.join(APP_DIR, "s/"))
|
88
|
+
|
89
|
+
def initialize(reader, path, vars)
|
90
|
+
@reader = reader
|
91
|
+
@path = path
|
92
|
+
vars.each_pair {|key, val| instance_eval("@#{key} = val")}
|
93
|
+
@controller_path = pathify(self.class.to_s.gsub(/.*:/, ''))
|
94
|
+
end
|
95
|
+
|
96
|
+
def method_missing(meth_id, *args)
|
97
|
+
render(:action => meth_id)
|
98
|
+
end
|
99
|
+
|
100
|
+
protected
|
101
|
+
|
102
|
+
def load_page(page)
|
103
|
+
File.read(File.join(APP_DIR, page))
|
104
|
+
end
|
105
|
+
|
106
|
+
def render(options = {})
|
107
|
+
options = {
|
108
|
+
:controller => @controller_path,
|
109
|
+
:action => :index,
|
110
|
+
:status => 200,
|
111
|
+
:content_type => 'text/html',
|
112
|
+
:env => nil,
|
113
|
+
:layout => 'views/layout.rhtml',
|
114
|
+
}.update(options)
|
115
|
+
|
116
|
+
path = "views/#{options[:controller]}/#{options[:action]}.rhtml"
|
117
|
+
content = ERB.new(load_page(path)).result(lambda{})
|
118
|
+
if options[:layout]
|
119
|
+
content = ERB.new(load_page(options[:layout])).result(lambda{})
|
120
|
+
end
|
121
|
+
|
122
|
+
return options[:status], options[:content_type], content
|
123
|
+
end
|
124
|
+
|
125
|
+
# takes an optional block to set optional attributes in the links
|
126
|
+
def paginate(idx, max, url, &b)
|
127
|
+
return '' if max == 0
|
128
|
+
url = url.gsub(%r{^/?(.*?)/?$}, '\1')
|
129
|
+
b ||= lambda{}
|
130
|
+
link = lambda {|*args|
|
131
|
+
i, title, text = args
|
132
|
+
"<a href=\"/#{url}/#{i}#{'?' + @query_string if @query_string}\" " +
|
133
|
+
"#{'onclick="return false;"' if (i == idx)} " +
|
134
|
+
"class=\"#{'disabled ' if (i == idx)}#{b.call(i)}\" " +
|
135
|
+
"title=\"#{title||"Go to page #{i}"}\">#{text||i}</a>"
|
136
|
+
}
|
137
|
+
res = '<div class="nav">'
|
138
|
+
if (idx > 0)
|
139
|
+
res << link.call(idx - 1, "Go to previous page", "« Previous")
|
140
|
+
else
|
141
|
+
res << "<a href=\"/#{url}/0\" onclick=\"return false;\" " +
|
142
|
+
"class=\"disabled\" title=\"Disabled\">« Previous</a>"
|
143
|
+
end
|
144
|
+
if idx < 10
|
145
|
+
idx.times {|i| res << link.call(i)}
|
146
|
+
else
|
147
|
+
(0..2).each {|i| res << link.call(i)}
|
148
|
+
res << ' … '
|
149
|
+
((idx-4)...idx).each {|i| res << link.call(i)}
|
150
|
+
end
|
151
|
+
res << link.call(idx, 'Current Page')
|
152
|
+
if idx > (max - 10)
|
153
|
+
((idx+1)...max).each {|i| res << link.call(i)}
|
154
|
+
else
|
155
|
+
((idx+1)..(idx+4)).each {|i| res << link.call(i)}
|
156
|
+
res << ' … '
|
157
|
+
((max-3)...max).each {|i| res << link.call(i)}
|
158
|
+
end
|
159
|
+
if (idx < (max - 1))
|
160
|
+
res << link.call(idx + 1, "Go to next page", "Next »")
|
161
|
+
else
|
162
|
+
res << "<a href=\"/#{url}/#{max-1}\" onclick=\"return false;\" " +
|
163
|
+
"class=\"disabled\" title=\"Disabled\"}\">Next »</a>"
|
164
|
+
end
|
165
|
+
res << '</div>'
|
166
|
+
end
|
167
|
+
private
|
168
|
+
|
169
|
+
def pathify(str)
|
170
|
+
str.gsub(/Controller$/, '').gsub(/([a-z])([A-Z])/) {"#{$1}-#{$2}"}.downcase
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class ErrorController < Controller
|
175
|
+
def index
|
176
|
+
render(:status => 404)
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
class HomeController < Controller
|
181
|
+
end
|
182
|
+
|
183
|
+
class DocumentController < Controller
|
184
|
+
def list(page = 0)
|
185
|
+
@page = (page||0).to_i
|
186
|
+
@page_size = @params[:page_size]||10
|
187
|
+
@first = @page * @page_size
|
188
|
+
@last = [@reader.max_doc, (@page + 1) * @page_size].min
|
189
|
+
render(:action => :list)
|
190
|
+
end
|
191
|
+
alias :index :list
|
192
|
+
|
193
|
+
def show(doc_id)
|
194
|
+
doc_id = @params['doc_id']||doc_id||'0'
|
195
|
+
if doc_id !~ /^\d+$/
|
196
|
+
raise ArgumentError.new("invalid document number '#{doc_id}'")
|
197
|
+
end
|
198
|
+
@doc_id = doc_id.to_i
|
199
|
+
@doc = @reader[@doc_id].load unless @reader.deleted?(@doc_id)
|
200
|
+
render(:action => :show)
|
201
|
+
end
|
202
|
+
|
203
|
+
private
|
204
|
+
|
205
|
+
def choose_document(doc_id='')
|
206
|
+
<<-EOF
|
207
|
+
<form action="" method="get" onsubmit="location.href='/document/show/' + document.getElementById('doc_id').value;return false;">
|
208
|
+
<label for="doc_id">Go to document:
|
209
|
+
<input type="text" id="doc_id" name="doc_id" size="4" value="#{@doc_id}"/>
|
210
|
+
</label>
|
211
|
+
</form>
|
212
|
+
EOF
|
213
|
+
end
|
214
|
+
|
215
|
+
def paginate_docs
|
216
|
+
paginate(@doc_id, @reader.max_doc, '/document/show/') {|i|
|
217
|
+
'deleted' if @reader.deleted?(i)
|
218
|
+
}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class TermController < Controller
|
223
|
+
def show(field)
|
224
|
+
if field and field.length > 0
|
225
|
+
@field = field.to_sym
|
226
|
+
@terms = @reader.terms(@field).to_json(:fast)
|
227
|
+
end
|
228
|
+
render(:action => :index)
|
229
|
+
end
|
230
|
+
|
231
|
+
def termdocs(args)
|
232
|
+
args = args.split('/')
|
233
|
+
@field = args.shift.intern
|
234
|
+
@term = args.join('/')
|
235
|
+
render(:action => :termdocs,
|
236
|
+
:content_type => 'text/plain',
|
237
|
+
:layout => false)
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
class TermVectorController < Controller
|
242
|
+
end
|
243
|
+
|
244
|
+
class HelpController < Controller
|
245
|
+
end
|
246
|
+
end
|
@@ -0,0 +1,192 @@
|
|
1
|
+
function AutoSuggestControl(oTextbox, oProvider) {
|
2
|
+
this.cur = -1;
|
3
|
+
this.layer = null;
|
4
|
+
this.provider = oProvider;
|
5
|
+
this.textbox = oTextbox;
|
6
|
+
this.init();
|
7
|
+
}
|
8
|
+
AutoSuggestControl.prototype.init = function() {
|
9
|
+
var oThis = this;
|
10
|
+
this.textbox.setAttribute("autocomplete", "off");
|
11
|
+
this.textbox.onkeyup = function(oEvent) {
|
12
|
+
if (!oEvent) {
|
13
|
+
oEvent = window.event;
|
14
|
+
}
|
15
|
+
oThis.handleKeyUp(oEvent);
|
16
|
+
};
|
17
|
+
this.textbox.onkeydown = function (oEvent) {
|
18
|
+
if (!oEvent) {
|
19
|
+
oEvent = window.event;
|
20
|
+
}
|
21
|
+
oThis.handleKeyDown(oEvent);
|
22
|
+
};
|
23
|
+
this.textbox.onblur = function () {
|
24
|
+
oThis.hideSuggestions();
|
25
|
+
};
|
26
|
+
|
27
|
+
this.createDropDown();
|
28
|
+
};
|
29
|
+
AutoSuggestControl.prototype.selectRange = function(iStart, iLength) {
|
30
|
+
if (this.textbox.createTextRange) {
|
31
|
+
var oRange = this.textbox.createTextRange();
|
32
|
+
oRange.moveStart("character", iStart);
|
33
|
+
oRange.moveEnd("character", iLength - this.textbox.value.length);
|
34
|
+
oRange.select();
|
35
|
+
} else if (this.textbox.setSelectionRange) {
|
36
|
+
this.textbox.setSelectionRange(iStart, iLength);
|
37
|
+
}
|
38
|
+
this.textbox.focus();
|
39
|
+
};
|
40
|
+
AutoSuggestControl.prototype.typeAhead = function(sSuggestion) {
|
41
|
+
if (this.textbox.createTextRange || this.textbox.setSelectionRange) {
|
42
|
+
var iLen = this.textbox.value.length;
|
43
|
+
this.textbox.value = sSuggestion;
|
44
|
+
this.selectRange(iLen, sSuggestion.length);
|
45
|
+
}
|
46
|
+
};
|
47
|
+
AutoSuggestControl.prototype.autosuggest = function(aSuggestions, bTypeAhead) {
|
48
|
+
if (aSuggestions.length > 0) {
|
49
|
+
if (bTypeAhead) {
|
50
|
+
this.typeAhead(aSuggestions[0]);
|
51
|
+
}
|
52
|
+
this.showSuggestions(aSuggestions);
|
53
|
+
} else {
|
54
|
+
this.hideSuggestions();
|
55
|
+
}
|
56
|
+
};
|
57
|
+
AutoSuggestControl.prototype.handleKeyUp = function(oEvent) {
|
58
|
+
var iKeyCode = oEvent.keyCode;
|
59
|
+
|
60
|
+
if (iKeyCode == 8 || iKeyCode == 46) {
|
61
|
+
this.provider.requestSuggestions(this, false);
|
62
|
+
|
63
|
+
} else if (iKeyCode < 32 || (iKeyCode >= 33 && iKeyCode <= 46) || (iKeyCode >= 112 && iKeyCode <= 123)) {
|
64
|
+
//ignore
|
65
|
+
} else {
|
66
|
+
this.provider.requestSuggestions(this, true);
|
67
|
+
}
|
68
|
+
};
|
69
|
+
AutoSuggestControl.prototype.handleKeyDown = function (oEvent) {
|
70
|
+
switch(oEvent.keyCode) {
|
71
|
+
case 38: //up arrow
|
72
|
+
this.previousSuggestion();
|
73
|
+
break;
|
74
|
+
case 40: //down arrow
|
75
|
+
this.nextSuggestion();
|
76
|
+
break;
|
77
|
+
case 13: //enter
|
78
|
+
this.hideSuggestions();
|
79
|
+
break;
|
80
|
+
}
|
81
|
+
};
|
82
|
+
AutoSuggestControl.prototype.hideSuggestions = function() {
|
83
|
+
this.layer.style.visibility = "hidden";
|
84
|
+
};
|
85
|
+
AutoSuggestControl.prototype.highlightSuggestion = function(oSuggestionNode) {
|
86
|
+
for (var i=0; i < this.layer.childNodes.length; i++) {
|
87
|
+
var oNode = this.layer.childNodes[i];
|
88
|
+
if (oNode == oSuggestionNode) {
|
89
|
+
oNode.className = "current"
|
90
|
+
} else if (oNode.className == "current") {
|
91
|
+
oNode.className = "";
|
92
|
+
}
|
93
|
+
}
|
94
|
+
};
|
95
|
+
AutoSuggestControl.prototype.createDropDown = function() {
|
96
|
+
|
97
|
+
this.layer = document.createElement("div");
|
98
|
+
this.layer.className = "suggestions";
|
99
|
+
this.layer.style.visibility = "hidden";
|
100
|
+
this.layer.style.width = this.textbox.offsetWidth;
|
101
|
+
document.body.appendChild(this.layer);
|
102
|
+
|
103
|
+
var oThis = this;
|
104
|
+
|
105
|
+
this.layer.onmousedown = this.layer.onmouseup =
|
106
|
+
this.layer.onmouseover = function(oEvent) {
|
107
|
+
oEvent = oEvent || window.event;
|
108
|
+
oTarget = oEvent.target || oEvent.srcElement;
|
109
|
+
|
110
|
+
if (oEvent.type == "mousedown") {
|
111
|
+
oThis.textbox.value = oTarget.firstChild.nodeValue;
|
112
|
+
oThis.hideSuggestions();
|
113
|
+
} else if (oEvent.type == "mouseover") {
|
114
|
+
oThis.highlightSuggestion(oTarget);
|
115
|
+
} else {
|
116
|
+
oThis.textbox.focus();
|
117
|
+
}
|
118
|
+
};
|
119
|
+
};
|
120
|
+
AutoSuggestControl.prototype.getLeft = function() {
|
121
|
+
|
122
|
+
var oNode = this.textbox;
|
123
|
+
var iLeft = 0;
|
124
|
+
|
125
|
+
while (oNode.tagName != "BODY") {
|
126
|
+
iLeft += oNode.offsetLeft;
|
127
|
+
oNode = oNode.offsetParent;
|
128
|
+
}
|
129
|
+
|
130
|
+
return iLeft;
|
131
|
+
};
|
132
|
+
AutoSuggestControl.prototype.getTop = function() {
|
133
|
+
|
134
|
+
var oNode = this.textbox;
|
135
|
+
var iTop = 0;
|
136
|
+
|
137
|
+
while (oNode.tagName != "BODY") {
|
138
|
+
iTop += oNode.offsetTop;
|
139
|
+
oNode = oNode.offsetParent;
|
140
|
+
}
|
141
|
+
|
142
|
+
return iTop;
|
143
|
+
};
|
144
|
+
AutoSuggestControl.prototype.showSuggestions = function(aSuggestions) {
|
145
|
+
|
146
|
+
var oDiv = null;
|
147
|
+
this.layer.innerHTML = "";
|
148
|
+
|
149
|
+
for (var i = 0; i < aSuggestions.length; i++) {
|
150
|
+
oDiv = document.createElement("div");
|
151
|
+
oDiv.appendChild(document.createTextNode(aSuggestions[i]));
|
152
|
+
this.layer.appendChild(oDiv);
|
153
|
+
}
|
154
|
+
|
155
|
+
this.layer.style.left = this.getLeft() + "px";
|
156
|
+
this.layer.style.top = (this.getTop()+this.textbox.offsetHeight) + "px";
|
157
|
+
this.layer.style.width = this.textbox.offsetWidth + "px";
|
158
|
+
this.layer.style.visibility = "visible";
|
159
|
+
};
|
160
|
+
AutoSuggestControl.prototype.nextSuggestion = function() {
|
161
|
+
var cSuggestionNodes = this.layer.childNodes;
|
162
|
+
|
163
|
+
if (cSuggestionNodes.length > 0 && this.cur < cSuggestionNodes.length-1) {
|
164
|
+
var oNode = cSuggestionNodes[++this.cur];
|
165
|
+
this.highlightSuggestion(oNode);
|
166
|
+
this.textbox.value = oNode.firstChild.nodeValue;
|
167
|
+
}
|
168
|
+
};
|
169
|
+
AutoSuggestControl.prototype.previousSuggestion = function() {
|
170
|
+
var cSuggestionNodes = this.layer.childNodes;
|
171
|
+
|
172
|
+
if (cSuggestionNodes.length > 0 && this.cur > 0) {
|
173
|
+
var oNode = cSuggestionNodes[--this.cur];
|
174
|
+
this.highlightSuggestion(oNode);
|
175
|
+
this.textbox.value = oNode.firstChild.nodeValue;
|
176
|
+
}
|
177
|
+
};
|
178
|
+
|
179
|
+
function bsearch(aArray, item, less_than) {
|
180
|
+
var left = -1,
|
181
|
+
right = aArray.length,
|
182
|
+
mid;
|
183
|
+
while (right > left + 1) {
|
184
|
+
mid = (left + right) >>> 1;
|
185
|
+
if (less_than(aArray[mid], item)) {
|
186
|
+
left = mid;
|
187
|
+
} else {
|
188
|
+
right = mid;
|
189
|
+
}
|
190
|
+
}
|
191
|
+
return right;
|
192
|
+
}
|