sdsykes-ferret 0.11.6.19

Sign up to get free protection for your applications and to get access to all the features.
Files changed (195) hide show
  1. data/CHANGELOG +24 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README +102 -0
  4. data/Rakefile +338 -0
  5. data/TODO +17 -0
  6. data/TUTORIAL +231 -0
  7. data/bin/ferret-browser +79 -0
  8. data/ext/analysis.c +1555 -0
  9. data/ext/analysis.h +219 -0
  10. data/ext/api.c +69 -0
  11. data/ext/api.h +27 -0
  12. data/ext/array.c +123 -0
  13. data/ext/array.h +53 -0
  14. data/ext/bitvector.c +540 -0
  15. data/ext/bitvector.h +272 -0
  16. data/ext/compound_io.c +383 -0
  17. data/ext/config.h +42 -0
  18. data/ext/document.c +156 -0
  19. data/ext/document.h +53 -0
  20. data/ext/except.c +120 -0
  21. data/ext/except.h +168 -0
  22. data/ext/extconf.rb +14 -0
  23. data/ext/ferret.c +402 -0
  24. data/ext/ferret.h +91 -0
  25. data/ext/filter.c +156 -0
  26. data/ext/fs_store.c +483 -0
  27. data/ext/global.c +418 -0
  28. data/ext/global.h +117 -0
  29. data/ext/hash.c +567 -0
  30. data/ext/hash.h +473 -0
  31. data/ext/hashset.c +170 -0
  32. data/ext/hashset.h +187 -0
  33. data/ext/header.h +58 -0
  34. data/ext/helper.c +62 -0
  35. data/ext/helper.h +13 -0
  36. data/ext/inc/lang.h +48 -0
  37. data/ext/inc/threading.h +31 -0
  38. data/ext/index.c +6425 -0
  39. data/ext/index.h +961 -0
  40. data/ext/lang.h +66 -0
  41. data/ext/libstemmer.c +92 -0
  42. data/ext/libstemmer.h +79 -0
  43. data/ext/mempool.c +87 -0
  44. data/ext/mempool.h +35 -0
  45. data/ext/modules.h +162 -0
  46. data/ext/multimapper.c +310 -0
  47. data/ext/multimapper.h +51 -0
  48. data/ext/posh.c +1006 -0
  49. data/ext/posh.h +1007 -0
  50. data/ext/priorityqueue.c +151 -0
  51. data/ext/priorityqueue.h +143 -0
  52. data/ext/q_boolean.c +1608 -0
  53. data/ext/q_const_score.c +161 -0
  54. data/ext/q_filtered_query.c +209 -0
  55. data/ext/q_fuzzy.c +268 -0
  56. data/ext/q_match_all.c +148 -0
  57. data/ext/q_multi_term.c +677 -0
  58. data/ext/q_parser.c +2825 -0
  59. data/ext/q_phrase.c +1126 -0
  60. data/ext/q_prefix.c +100 -0
  61. data/ext/q_range.c +350 -0
  62. data/ext/q_span.c +2402 -0
  63. data/ext/q_term.c +337 -0
  64. data/ext/q_wildcard.c +171 -0
  65. data/ext/r_analysis.c +2575 -0
  66. data/ext/r_index.c +3472 -0
  67. data/ext/r_qparser.c +585 -0
  68. data/ext/r_search.c +4105 -0
  69. data/ext/r_store.c +513 -0
  70. data/ext/r_utils.c +963 -0
  71. data/ext/ram_store.c +471 -0
  72. data/ext/search.c +1741 -0
  73. data/ext/search.h +885 -0
  74. data/ext/similarity.c +150 -0
  75. data/ext/similarity.h +82 -0
  76. data/ext/sort.c +983 -0
  77. data/ext/stem_ISO_8859_1_danish.c +338 -0
  78. data/ext/stem_ISO_8859_1_danish.h +16 -0
  79. data/ext/stem_ISO_8859_1_dutch.c +635 -0
  80. data/ext/stem_ISO_8859_1_dutch.h +16 -0
  81. data/ext/stem_ISO_8859_1_english.c +1156 -0
  82. data/ext/stem_ISO_8859_1_english.h +16 -0
  83. data/ext/stem_ISO_8859_1_finnish.c +792 -0
  84. data/ext/stem_ISO_8859_1_finnish.h +16 -0
  85. data/ext/stem_ISO_8859_1_french.c +1276 -0
  86. data/ext/stem_ISO_8859_1_french.h +16 -0
  87. data/ext/stem_ISO_8859_1_german.c +512 -0
  88. data/ext/stem_ISO_8859_1_german.h +16 -0
  89. data/ext/stem_ISO_8859_1_italian.c +1091 -0
  90. data/ext/stem_ISO_8859_1_italian.h +16 -0
  91. data/ext/stem_ISO_8859_1_norwegian.c +296 -0
  92. data/ext/stem_ISO_8859_1_norwegian.h +16 -0
  93. data/ext/stem_ISO_8859_1_porter.c +776 -0
  94. data/ext/stem_ISO_8859_1_porter.h +16 -0
  95. data/ext/stem_ISO_8859_1_portuguese.c +1035 -0
  96. data/ext/stem_ISO_8859_1_portuguese.h +16 -0
  97. data/ext/stem_ISO_8859_1_spanish.c +1119 -0
  98. data/ext/stem_ISO_8859_1_spanish.h +16 -0
  99. data/ext/stem_ISO_8859_1_swedish.c +307 -0
  100. data/ext/stem_ISO_8859_1_swedish.h +16 -0
  101. data/ext/stem_KOI8_R_russian.c +701 -0
  102. data/ext/stem_KOI8_R_russian.h +16 -0
  103. data/ext/stem_UTF_8_danish.c +344 -0
  104. data/ext/stem_UTF_8_danish.h +16 -0
  105. data/ext/stem_UTF_8_dutch.c +653 -0
  106. data/ext/stem_UTF_8_dutch.h +16 -0
  107. data/ext/stem_UTF_8_english.c +1176 -0
  108. data/ext/stem_UTF_8_english.h +16 -0
  109. data/ext/stem_UTF_8_finnish.c +808 -0
  110. data/ext/stem_UTF_8_finnish.h +16 -0
  111. data/ext/stem_UTF_8_french.c +1296 -0
  112. data/ext/stem_UTF_8_french.h +16 -0
  113. data/ext/stem_UTF_8_german.c +526 -0
  114. data/ext/stem_UTF_8_german.h +16 -0
  115. data/ext/stem_UTF_8_italian.c +1113 -0
  116. data/ext/stem_UTF_8_italian.h +16 -0
  117. data/ext/stem_UTF_8_norwegian.c +302 -0
  118. data/ext/stem_UTF_8_norwegian.h +16 -0
  119. data/ext/stem_UTF_8_porter.c +794 -0
  120. data/ext/stem_UTF_8_porter.h +16 -0
  121. data/ext/stem_UTF_8_portuguese.c +1055 -0
  122. data/ext/stem_UTF_8_portuguese.h +16 -0
  123. data/ext/stem_UTF_8_russian.c +709 -0
  124. data/ext/stem_UTF_8_russian.h +16 -0
  125. data/ext/stem_UTF_8_spanish.c +1137 -0
  126. data/ext/stem_UTF_8_spanish.h +16 -0
  127. data/ext/stem_UTF_8_swedish.c +313 -0
  128. data/ext/stem_UTF_8_swedish.h +16 -0
  129. data/ext/stopwords.c +401 -0
  130. data/ext/store.c +692 -0
  131. data/ext/store.h +777 -0
  132. data/ext/term_vectors.c +352 -0
  133. data/ext/threading.h +31 -0
  134. data/ext/utilities.c +446 -0
  135. data/ext/win32.h +54 -0
  136. data/lib/ferret.rb +29 -0
  137. data/lib/ferret/browser.rb +246 -0
  138. data/lib/ferret/browser/s/global.js +192 -0
  139. data/lib/ferret/browser/s/style.css +148 -0
  140. data/lib/ferret/browser/views/document/list.rhtml +49 -0
  141. data/lib/ferret/browser/views/document/show.rhtml +27 -0
  142. data/lib/ferret/browser/views/error/index.rhtml +7 -0
  143. data/lib/ferret/browser/views/help/index.rhtml +8 -0
  144. data/lib/ferret/browser/views/home/index.rhtml +29 -0
  145. data/lib/ferret/browser/views/layout.rhtml +22 -0
  146. data/lib/ferret/browser/views/term-vector/index.rhtml +4 -0
  147. data/lib/ferret/browser/views/term/index.rhtml +199 -0
  148. data/lib/ferret/browser/views/term/termdocs.rhtml +1 -0
  149. data/lib/ferret/browser/webrick.rb +14 -0
  150. data/lib/ferret/document.rb +130 -0
  151. data/lib/ferret/field_infos.rb +44 -0
  152. data/lib/ferret/index.rb +786 -0
  153. data/lib/ferret/number_tools.rb +157 -0
  154. data/lib/ferret_version.rb +3 -0
  155. data/setup.rb +1555 -0
  156. data/test/test_all.rb +5 -0
  157. data/test/test_helper.rb +24 -0
  158. data/test/threading/number_to_spoken.rb +132 -0
  159. data/test/threading/thread_safety_index_test.rb +79 -0
  160. data/test/threading/thread_safety_read_write_test.rb +76 -0
  161. data/test/threading/thread_safety_test.rb +133 -0
  162. data/test/unit/analysis/tc_analyzer.rb +548 -0
  163. data/test/unit/analysis/tc_token_stream.rb +646 -0
  164. data/test/unit/index/tc_index.rb +762 -0
  165. data/test/unit/index/tc_index_reader.rb +699 -0
  166. data/test/unit/index/tc_index_writer.rb +437 -0
  167. data/test/unit/index/th_doc.rb +315 -0
  168. data/test/unit/largefile/tc_largefile.rb +46 -0
  169. data/test/unit/query_parser/tc_query_parser.rb +238 -0
  170. data/test/unit/search/tc_filter.rb +135 -0
  171. data/test/unit/search/tc_fuzzy_query.rb +147 -0
  172. data/test/unit/search/tc_index_searcher.rb +61 -0
  173. data/test/unit/search/tc_multi_searcher.rb +128 -0
  174. data/test/unit/search/tc_multiple_search_requests.rb +58 -0
  175. data/test/unit/search/tc_search_and_sort.rb +179 -0
  176. data/test/unit/search/tc_sort.rb +49 -0
  177. data/test/unit/search/tc_sort_field.rb +27 -0
  178. data/test/unit/search/tc_spans.rb +190 -0
  179. data/test/unit/search/tm_searcher.rb +384 -0
  180. data/test/unit/store/tc_fs_store.rb +77 -0
  181. data/test/unit/store/tc_ram_store.rb +35 -0
  182. data/test/unit/store/tm_store.rb +34 -0
  183. data/test/unit/store/tm_store_lock.rb +68 -0
  184. data/test/unit/tc_document.rb +81 -0
  185. data/test/unit/ts_analysis.rb +2 -0
  186. data/test/unit/ts_index.rb +2 -0
  187. data/test/unit/ts_largefile.rb +4 -0
  188. data/test/unit/ts_query_parser.rb +2 -0
  189. data/test/unit/ts_search.rb +2 -0
  190. data/test/unit/ts_store.rb +2 -0
  191. data/test/unit/ts_utils.rb +2 -0
  192. data/test/unit/utils/tc_bit_vector.rb +295 -0
  193. data/test/unit/utils/tc_number_tools.rb +117 -0
  194. data/test/unit/utils/tc_priority_queue.rb +106 -0
  195. metadata +285 -0
@@ -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
@@ -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] + '&#8230;'
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", "&#171; Previous")
140
+ else
141
+ res << "<a href=\"/#{url}/0\" onclick=\"return false;\" " +
142
+ "class=\"disabled\" title=\"Disabled\">&#171; 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 << '&nbsp;&#8230;&nbsp;'
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 << '&nbsp;&#8230;&nbsp;'
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 &#187;")
161
+ else
162
+ res << "<a href=\"/#{url}/#{max-1}\" onclick=\"return false;\" " +
163
+ "class=\"disabled\" title=\"Disabled\"}\">Next &#187;</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
+ }