/[bfilter]/trunk/bfilter.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Contents of /trunk/bfilter.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 42 - (show annotations)
Wed Dec 15 15:35:24 2004 UTC (19 years, 4 months ago) by dpavlin
File MIME type: application/javascript
File size: 6453 byte(s)
Added show_all to dump all entries (might be useful for comboboxes without
any data entered). On the other hand, it might be very, very, very slow...
You have been warned.

1 /*
2 Fast filtering function using pre-sorted list elements
3 Dobrica Pavlinusic, dpavlin@rot13.org 2004-09-06
4 Matko Andjelinic, matko.andjelinic@gmail.com 2004-09-09 (contributed OO implementation)
5 */
6
7 top.__bfilter_obj = new Array();
8
9
10 function BFilter(arr) {
11 this.id_cache = Array();
12 // total number of hits
13 this.hits = 0;
14
15 // store reference to this object for later (YAK)
16 this.obj_count = top.__bfilter_obj.length;
17 top.__bfilter_obj[this.obj_count] = this;
18
19 // clear results html
20 this.results_html = null;
21
22 // show results after 0.2s
23 this.timeout = 200;
24 this.timeout_handle = null;
25
26 if (! arr) {
27 this.debug("ERROR: can't search empty array");
28 return;
29 }
30
31 this.arr = arr;
32 this.debug("index has "+this.arr.length+" parts");
33
34 if (! arr.min_len) {
35 alert("ERROR: index structure problem");
36 return;
37 } else {
38 this.min_len = arr.min_len;
39 }
40
41 this.debug("index part has "+this.min_len+" parts");
42
43 this.show_status();
44
45 // show alert for non-existent elements?
46 this.show_alert = 1;
47
48 }
49
50 BFilter.prototype.element_id = function (id,no_alert) {
51 if (this.id_cache[id]) {
52 return this.id_cache[id];
53 } else {
54 var el = self.document.getElementById(id);
55 if (el) {
56 this.id_cache[id] = el;
57 return el;
58 } else {
59 if (! no_alert && this.show_alert) {
60 this.show_alert = confirm("can't find element id: "+id);
61 } else {
62 // don't look it up again
63 this.id_cache[id] = null;
64 }
65 }
66 }
67 return null;
68 }
69
70
71 BFilter.prototype.show_status = function (query,no_hits) {
72 var html = "";
73 if (! no_hits) {
74 if (this.hits > 0) {
75 html = "shown "+this.hits+" entries";
76 } else {
77 html = "no results";
78 }
79 if (! status) {
80 html = "Enter "+this.min_len+" letter"+(this.min_len == 1 ? '' : 's')+" to filter entries";
81 status = "";
82 }
83 }
84 if (! query) {
85 html = "Enter "+this.min_len+" letter"+(this.min_len == 1 ? '' : 's')+" to filter entries";
86 query = "";
87 } else {
88 query = " for <em>"+query+"</em>";
89 }
90
91 var el = this.element_id("status");
92 el.innerHTML = html+query+"\n";
93
94 return true;
95 }
96
97 // this function is called to clean old results list
98 BFilter.prototype.clear_results = function () {
99 var results_div = this.element_id("results");
100 results_div.innerHTML = '';
101 this.results_html = '';
102 return true;
103 }
104
105 // this function is called for each result
106 BFilter.prototype.result = function (arr) {
107 this.results_html += '<li><a href="'+arr[1]+'">'+
108 (this.hits % 2 == 0 ? '<span style="background: #e0e0e0;">' : '') +
109 arr[0] +
110 (this.hits % 2 == 0 ? '</span>' : '') +
111 '</a></li>';
112 return true;
113 }
114
115 // this function is called when updating innerHTML with results
116 BFilter.prototype.show_results = function () {
117 var results_div = this.element_id("results");
118 if (this.results_html) {
119 results_div.innerHTML = '<ul>'+this.results_html+'</ul>';
120 }
121 return true;
122 }
123
124 BFilter.prototype.debug = function (html) {
125
126 //return;
127
128 // check if debugging is on
129 if (! html) { return 1; }
130
131 var debug_div = this.element_id("debug",1);
132 if (! debug_div) { return null; }
133
134 debug_div.innerHTML += html + "<br>\n";
135
136 return null;
137 }
138
139
140 // modified binary search to find first element with substring
141 BFilter.prototype.binarySearch = function (arr, user_filter) {
142 if (!arr || typeof (user_filter) == "undefined" || !arr.length) {
143 return null;
144 }
145 var low = 0;
146 var high = arr.length - 1;
147 var middlearr = parseInt(arr.length / 2);
148 this.debug("binarySearch: "+low+"-("+middlearr+")-"+high+" for "+user_filter);
149 var lastTry;
150 while (low <= high) {
151 var mid = (low + high) / 2;
152 var aTry = (mid < 1) ? 0 : parseInt(mid);
153
154 var curr = arr[aTry][0].substr(0,user_filter.length).toLowerCase();
155 this.debug(low+"-"+high+", "+aTry+"="+curr+" last="+lastTry);
156 if (curr < user_filter) {
157 low = aTry + 1;
158 continue;
159 }
160 if (curr > user_filter) {
161 high = aTry - 1;
162 continue;
163 }
164 if (curr == user_filter) {
165 high = aTry - 1;
166 lastTry = aTry;
167 continue;
168 }
169 return aTry;
170 }
171 this.debug("lastTry="+lastTry);
172
173 if (typeof (lastTry) != "undefined") {
174 return lastTry;
175 } else {
176 return null;
177 }
178 }
179
180 BFilter.prototype.filter = function (user_filter) {
181 this.debug("set timeout for "+this.obj_count+" to "+this.timeout);
182 if (this.timeout_handle) {
183 clearTimeout(this.timeout_handle);
184 this.timeout_handle = null;
185 }
186 this.timeout_handle=setTimeout("top.__bfilter_obj["+this.obj_count+"].show_filter('"+user_filter.replace(/'/,"\\'")+"');", this.timeout);
187 return true;
188 }
189
190 BFilter.prototype.show_filter = function (user_filter) {
191
192 this.show_status("Showing entries with <em>"+user_filter+"</em>\n");
193
194 if (this.timeout_handle) {
195 clearTimeout(this.timeout_handle);
196 this.timeout_handle = null;
197 this.debug("timeout cleared");
198 }
199
200 this.clear_results();
201 this.hits = 0;
202
203 if (user_filter.length < this.min_len) {
204 this.show_status();
205 return;
206 }
207
208 var user_filter_lc = user_filter.toLowerCase();
209
210 this.debug("filter: '"+user_filter_lc+"'");
211
212 var part = user_filter_lc.substr(0,this.min_len);
213
214 // no part found
215 if (! this.arr[part]) {
216 this.show_status(user_filter);
217 this.debug("no part "+part);
218 return;
219 }
220
221 // start anim icon
222 //<img src=\"pie.gif\">&nbsp;Please wait, filtering...
223
224 var i;
225
226 // full part? (optimization)
227 if (user_filter.length == this.min_len) {
228 for (i = 0; i < this.arr[part].length; i++) {
229 this.result(this.arr[part][i]);
230 this.hits++;
231 }
232 this.show_results();
233 } else {
234
235 var from = this.binarySearch(this.arr[part], user_filter_lc);
236
237 if (from != null) {
238
239 this.debug("loop "+from+" ... "+this.arr[part].length);
240
241 for(i = from ; i < this.arr[part].length ; i++) {
242 if (this.arr[part][i][0].substring(0,user_filter.length).toLowerCase() != user_filter_lc) {
243 this.debug("loop exit at "+i);
244 break;
245 }
246 this.result(this.arr[part][i]);
247 this.hits++;
248 }
249
250 this.show_results();
251 }
252
253 }
254
255 this.show_status(user_filter);
256
257 }
258
259 BFilter.prototype.show_all = function (user_filter) {
260
261 this.show_status("Showing all entries\n");
262
263 if (this.timeout_handle) {
264 clearTimeout(this.timeout_handle);
265 this.timeout_handle = null;
266 this.debug("timeout cleared");
267 }
268
269 this.clear_results();
270 this.hits = 0;
271
272 for (part in this.arr) {
273 // skip elements which are not really parts
274 if (part == 'length' || part == 'min_len') continue;
275
276 this.debug("part: "+part);
277 for(var i = 0 ; i < this.arr[part].length ; i++) {
278 this.result(this.arr[part][i]);
279 this.hits++;
280 }
281
282 this.show_results();
283 }
284
285 this.show_status(user_filter);
286
287 }
288

  ViewVC Help
Powered by ViewVC 1.1.26