Mercurial > google_bookmarks_incsearch
annotate chrome/content/incsearch.js @ 6:ac5648afee47 default tip
change hot key from ctrl to alt.
author | Yoshiki Yazawa <yaz@honeyplanet.jp> |
---|---|
date | Sat, 23 May 2009 11:56:41 +0900 |
parents | c47ec96326ad |
children |
rev | line source |
---|---|
0 | 1 var IncSearch = function() { |
2 this.initialize.apply(this, arguments); | |
3 }; | |
4 | |
5 /*-- Utils --------------------------------------------*/ | |
6 IncSearch._copyProperties = function(dest, src) { | |
7 for (var property in src) { | |
8 dest[property] = src[property]; | |
9 } | |
10 return dest; | |
11 }; | |
12 | |
13 IncSearch._copyProperties = function(dest, src) { | |
14 for (var property in src) { | |
15 dest[property] = src[property]; | |
16 } | |
17 return dest; | |
18 }; | |
19 | |
20 IncSearch._getElement = function(element) { | |
21 return (typeof element == 'string') ? document.getElementById(element) : element; | |
22 }; | |
23 | |
24 IncSearch._addEvent = function(element, type, func) { | |
25 element.addEventListener(type, func, false); | |
26 }; | |
27 | |
28 IncSearch._stopEvent = function(event) { | |
29 event.preventDefault(); | |
30 event.stopPropagation(); | |
31 }; | |
32 | |
33 /*-----------------------------------------------------*/ | |
34 IncSearch.prototype = { | |
35 initialize: function(input, viewArea) { | |
36 this.input = IncSearch._getElement(input); | |
37 this.viewArea = IncSearch._getElement(viewArea); | |
38 | |
39 this.checkLoopTimer = null; | |
40 this.setOptions(arguments[2] || {}); | |
41 | |
42 this.reset(); | |
43 | |
44 // check loop start | |
45 this.checkLoop(); | |
46 }, | |
47 | |
48 reset: function() { | |
49 this.oldInput = null; | |
50 this.results = null; | |
51 this.resultCount = null; | |
52 | |
53 this.nowPage = 0; | |
54 this.nowRow = 0; | |
55 | |
56 this.resetTotalCount(); | |
57 }, | |
58 | |
59 // options | |
60 interval: 500, | |
61 delay: 0, | |
62 dispMax: 10, | |
63 initDispNon: false, | |
64 ignoreCase: true, | |
65 highlight: true, | |
66 highClassName: 'high', | |
67 highClassNum: 4, | |
68 delim: ' ', | |
69 escape: true, | |
70 pagePrevName: 'prev', | |
71 pageNextName: 'next', | |
72 useHotkey: true, | |
73 urlTarget: '_blank', | |
74 editTarget: '_blank', | |
75 | |
76 startElementText: '<table><tr><th></th><th width="60%">Description</th><th width="20%">Tags</th><th width="20%">Time</th><th></th>', | |
77 | |
78 setOptions: function(options) { | |
79 | |
80 IncSearch._copyProperties(this, options); | |
81 | |
82 if (this.useHotkey) { | |
83 IncSearch._addEvent(document, 'keydown', this._bindEvent(this.hotkey)); | |
84 } | |
85 }, | |
86 | |
87 checkLoop: function() { | |
88 var input = this.getInput(); | |
89 if (this.isChange(input)) { | |
90 this.oldInput = input; | |
91 if (this.delay == 0) { | |
92 this.startSearch(input); | |
93 } else { | |
94 if (this.startSearchTimer) clearTimeout(this.startSearchTimer); | |
95 this.startSearchTimer = setTimeout(this._bind(this.startSearch, input), this.delay); | |
96 } | |
97 } | |
98 if (this.checkLoopTimer) clearTimeout(this.checkLoopTimer); | |
99 this.checkLoopTimer = setTimeout(this._bind(this.checkLoop), this.interval); | |
100 }, | |
101 | |
102 isChange: function(input) { | |
103 return (!this.oldInput || (input.join(this.delim) != this.oldInput.join(this.delim))); | |
104 }, | |
105 | |
106 startSearch: function(input) { | |
107 // init | |
108 this.clearViewArea(); | |
109 if (!this.initDispNon || input.length != 0) { | |
110 if (this.searchBefore) this.searchBefore(); | |
111 this.count(input); | |
112 this.search(input, 1); | |
113 this.createViewArea(input); | |
114 this.nowPage = 1; | |
115 this.changeRow(1); | |
116 this.createPageLink(1, this.pageLinkTop); | |
117 this.createPageLink(1, this.pageLinkBottom); | |
118 if (this.searchAfter) this.searchAfter(); | |
119 } | |
120 }, | |
121 changePage: function(pageNo) { | |
122 | |
123 var start = (pageNo - 1) * this.dispMax + 1; | |
124 | |
125 if (start > this.resultCount) return false; | |
126 | |
127 if (this.changePageBefore) this.changePageBefore(pageNo); | |
128 this.search(this.oldInput, start); | |
129 this.createViewArea(this.oldInput); | |
130 this.nowPage = pageNo; | |
131 this.nowRow = 0; | |
132 this.changeRow(1); | |
133 this.createPageLink(pageNo, this.pageLinkTop); | |
134 this.createPageLink(pageNo, this.pageLinkBottom); | |
135 if (this.changePageAfter) this.changePageAfter(pageNo); | |
136 return true; | |
137 }, | |
138 changeRow: function(rowNo) { | |
139 | |
140 if (this.results.length == 0) { | |
141 return; | |
142 } | |
143 | |
144 if (rowNo < 1) { | |
145 if (this.nowPage > 1) { | |
146 this.changePage(this.nowPage - 1); | |
147 this.changeRow(this.results.length); | |
148 } | |
149 return; | |
150 } | |
151 | |
152 if (rowNo > this.results.length) { | |
153 if (this.nowPage < this.getPageCount()) { | |
154 this.changePage(this.nowPage + 1); | |
155 } | |
156 return; | |
157 } | |
158 | |
159 var table = this.viewArea.getElementsByTagName('table')[0]; | |
160 | |
161 if (this.nowRow != 0 && table.rows[this.nowRow]) { | |
162 table.rows[this.nowRow].className = ''; | |
163 } | |
164 var row = table.rows[rowNo]; | |
165 row.className = 'focus'; | |
166 | |
167 var margin = 0; | |
168 var topPos = (this.viewArea.offsetTop + row.offsetTop) - (this.viewArea.offsetTop + table.rows[1].offsetTop); | |
169 var bottomPos = (this.viewArea.offsetTop + row.offsetTop + row.offsetHeight) + 5; | |
170 | |
171 if (topPos < document.documentElement.scrollTop) { | |
172 window.scrollTo(0, topPos); | |
173 } else if (bottomPos > document.documentElement.clientHeight + document.documentElement.scrollTop) { | |
174 window.scrollTo(0, bottomPos - document.documentElement.clientHeight); | |
175 } | |
176 | |
177 this.nowRow = rowNo; | |
178 }, | |
179 openUrl: function(rowNo) { | |
180 | |
181 if (this.results.length == 0) { | |
182 return; | |
183 } | |
184 | |
185 window.open(this.results[rowNo - 1].url, this.urlTarget); | |
186 }, | |
187 openEditWindow: function(rowNo) { | |
188 | |
189 if (this.results.length == 0) { | |
190 return; | |
191 } | |
192 | |
193 window.open(this.createEditUrl(this.results[rowNo - 1]), this.editTarget); | |
194 }, | |
195 | |
196 countSql: 'SELECT COUNT(*) count FROM bookmark', | |
197 resetTotalCount: function() { | |
198 | |
199 var handler = new ResultArrayHandler(this.database, this.countSql); | |
200 handler.execute(); | |
201 this.totalCount = handler.result[0].count; | |
202 }, | |
203 | |
204 count: function(patternList) { | |
205 var where = this.createWhere(patternList); | |
206 var sql = this.countSql + where.where; | |
207 | |
208 try{ | |
209 var handler = new ResultArrayHandler(this.database, sql); | |
210 handler.execute(where.params); | |
211 this.resultCount = handler.result[0].count; | |
212 } catch(e) { | |
213 alert(e.message || e); | |
214 throw e; | |
215 } | |
216 }, | |
217 | |
218 searchSql: 'SELECT url, title, info, tags, time FROM bookmark', | |
219 search: function(patternList, start) { | |
220 | |
221 var where = this.createWhere(patternList); | |
222 var sql = [ | |
223 this.searchSql, where.where, | |
224 ' ORDER BY id', | |
225 ' LIMIT ', this.dispMax, | |
226 ' OFFSET ', (start - 1)].join(''); | |
227 | |
228 try { | |
229 var handler = new ResultArrayHandler(this.database, sql); | |
230 handler.execute(where.params); | |
231 this.results = handler.result; | |
232 } catch(e) { | |
233 alert(e.message || e); | |
234 throw e; | |
235 } | |
236 }, | |
237 createWhere: function(patternList) { | |
238 | |
239 var where = []; | |
240 var params = {}; | |
241 | |
242 if (patternList.length != 0) { | |
243 for (var i = 0, len = patternList.length; i < len; i++) { | |
244 var temp = this.createCondOne(patternList[i], params, 'param' + i); | |
245 if (temp != '') { | |
246 if (where.length != 0) where.push(' AND'); | |
247 where.push(temp); | |
248 } | |
249 } | |
250 } | |
251 | |
252 var whereString = where.join(''); | |
253 if (whereString.length > 0) whereString = ' WHERE' + whereString; | |
254 | |
255 return { | |
256 where: whereString, | |
257 params: params}; | |
258 }, | |
259 createCondOne: function(pattern, params, paramName) { | |
260 | |
261 var where = []; | |
262 if (pattern.indexOf('|') > -1) { | |
263 var patterns = this.getSplitPatterns(pattern, '|'); | |
264 if (patterns.length != 0) { | |
265 for (var i = 0, len = patterns.length; i < len; i++) { | |
266 var temp = this.createCondOne(patterns[i], params, paramName + '_' + i); | |
267 if (temp != '') { | |
268 if (where.length != 0) where.push(' OR'); | |
269 where.push(temp); | |
270 } | |
271 } | |
272 if (where.length != 0) { | |
273 where.unshift(' ('); | |
274 where.push(')'); | |
275 } | |
276 } | |
277 } else if (pattern.indexOf('!') == 0) { | |
278 if (pattern.length != 1) { | |
279 where.push(this._createCondOne(pattern.substr(1), params, paramName, true)); | |
280 } | |
281 } else { | |
282 where.push(this._createCondOne(pattern, params, paramName)); | |
283 } | |
284 return where.join(''); | |
285 }, | |
286 _createCondOne: function(pattern, params, paramName, not) { | |
287 params[paramName] = ['%', pattern.toUpperCase().replace(/\\/g, '\\\\').replace(/\%/g, '\\%').replace(/\_/g, '\\_'), '%'].join(''); | |
288 return [" search_text ", (not ? "NOT " : ""), "LIKE :", paramName, " ESCAPE '\\'"].join(''); | |
289 }, | |
290 | |
291 getSplitPatterns: function(pattern, separator) { | |
292 var temp = pattern.split(separator); | |
293 var patterns = []; | |
294 for (var i = 0, len = temp.length; i < len; i++) { | |
295 if (temp[i] != '') patterns.push(temp[i]); | |
296 } | |
297 return patterns; | |
298 }, | |
299 | |
300 createPageLink: function(pageNo, pageLinkElm) { | |
301 | |
302 pageLinkElm = IncSearch._getElement(pageLinkElm); | |
303 | |
304 var pageCount = this.getPageCount(); | |
305 | |
306 var prev_page = false; | |
307 var next_page = false; | |
308 | |
309 if (pageCount > 1) { | |
310 | |
311 if (pageNo == 1) { | |
312 next_page = true; | |
313 } else if (pageNo == pageCount) { | |
314 prev_page = true; | |
315 } else { | |
316 next_page = true; | |
317 prev_page = true; | |
318 } | |
319 } | |
320 | |
321 pageLinkElm.innerHTML = ''; | |
322 | |
323 if (prev_page) { | |
324 this.createPageAnchor(pageLinkElm, this.pagePrevName, pageNo - 1); | |
325 } | |
326 if (next_page) { | |
327 if (prev_page) { | |
328 pageLinkElm.appendChild(document.createTextNode(' | ')); | |
329 } | |
330 | |
331 this.createPageAnchor(pageLinkElm, this.pageNextName, pageNo + 1); | |
332 } | |
333 }, | |
334 | |
335 createPageAnchor: function(parent, text, page) { | |
336 | |
337 var a = parent.appendChild(document.createElement('a')); | |
338 a.setAttribute('href', 'javascript:void(0)'); | |
339 a.appendChild(document.createTextNode(text)); | |
340 | |
341 IncSearch._addEvent(a, 'click', this._bind(this.changePage, page)); | |
342 }, | |
343 | |
344 getPageCount: function() { | |
345 var pageCount = 0; | |
346 | |
347 if (this.resultCount && this.resultCount != 0) { | |
348 if (this.dispMax == 0) { | |
349 pageCount = 1; | |
350 } else { | |
351 pageCount = Math.floor((this.resultCount + this.dispMax - 1) / this.dispMax); | |
352 } | |
353 } | |
354 return pageCount; | |
355 }, | |
356 | |
357 createInfo: function() { | |
358 var displayInfo = ''; | |
359 | |
360 if (this.resultCount != 0) { | |
361 var start = (this.nowPage - 1) * this.dispMax + 1; | |
362 var end = start + this.dispMax - 1; | |
363 | |
364 if (this.dispMax == 0 || end > this.resultCount) { | |
365 end = this.resultCount; | |
366 } | |
367 displayInfo = ['(display :', start, '-', end, ')'].join(''); | |
368 } | |
369 this.status.innerHTML = [this.resultCount.toString(), ' hits ', | |
370 displayInfo, ' / total : ', this.totalCount].join(''); | |
371 }, | |
372 searchAfter: function() { | |
373 this.createInfo(); | |
374 window.scrollTo(0, 0); | |
375 }, | |
376 searchBefore: function() { | |
377 this.status.innerHTML = 'Search...'; | |
378 }, | |
379 changePageAfter: function(pageNo) { | |
380 this.createInfo(); | |
381 window.scrollTo(0, 0); | |
382 }, | |
383 | |
384 hotkey: function(event) { | |
6
ac5648afee47
change hot key from ctrl to alt.
Yoshiki Yazawa <yaz@honeyplanet.jp>
parents:
0
diff
changeset
|
385 if (event.altKey) { |
0 | 386 switch(event.keyCode) { |
387 case 13: // Enter | |
388 case 77: // m (Enter Max OS X) | |
389 this.openUrl(this.nowRow); | |
390 IncSearch._stopEvent(event); | |
391 break; | |
392 case 37: // Left | |
393 if (this.nowPage > 1) { | |
394 this.changePage(this.nowPage - 1); | |
395 } | |
396 IncSearch._stopEvent(event); | |
397 break; | |
398 case 38: // Up | |
399 this.changeRow(this.nowRow - 1); | |
400 IncSearch._stopEvent(event); | |
401 break; | |
402 case 39: // Right | |
403 if (this.nowPage < this.getPageCount()) { | |
404 this.changePage(this.nowPage + 1); | |
405 } | |
406 IncSearch._stopEvent(event); | |
407 break; | |
408 case 40: // Down | |
409 this.changeRow(this.nowRow + 1); | |
410 IncSearch._stopEvent(event); | |
411 break; | |
412 case 69: // e | |
413 this.openEditWindow(this.nowRow); | |
414 IncSearch._stopEvent(event); | |
415 break; | |
416 default: | |
417 break; | |
418 } | |
419 } | |
420 }, | |
421 | |
422 createViewArea: function(patternList) { | |
423 var elementText = []; | |
424 | |
425 patternList = this.getHighlightPatterns(patternList); | |
426 | |
427 for (var i = 0, len = this.results.length; i < len; i++) { | |
428 elementText.push(this.createLineElement(this.results[i], patternList)); | |
429 } | |
430 | |
431 if (elementText.length > 0) { | |
432 if (this.startElementText) elementText.unshift(this.startElementText); | |
433 if (this.endElementText) elementText.push(this.endElementText); | |
434 this.viewArea.innerHTML = elementText.join(''); | |
435 } | |
436 | |
437 if (this.afterHookCreateView) { | |
438 this.afterHookCreateView(patternList); | |
439 } | |
440 }, | |
441 | |
442 getHighlightPatterns: function(patternList) { | |
443 var highlightPatterns = []; | |
444 | |
445 for (var i = 0, len = patternList.length; i < len; i++) { | |
446 var pattern = patternList[i]; | |
447 if (pattern.indexOf('|') > -1) { | |
448 var patterns = this.getSplitPatterns(pattern, '|'); | |
449 highlightPatterns = highlightPatterns.concat(this.getHighlightPatterns(patterns)); | |
450 } else if (pattern.indexOf('!') != 0) { | |
451 highlightPatterns.push(pattern); | |
452 } | |
453 } | |
454 return highlightPatterns; | |
455 }, | |
456 | |
457 clearViewArea: function() { | |
458 this.viewArea.innerHTML = ''; | |
459 this.results = null; | |
460 this.resultCount = null; | |
461 this.nowPage = 0; | |
462 this.nowRow = 0; | |
463 }, | |
464 | |
465 createLineElement: function(bookmark, patternList) { | |
466 | |
467 var text = ['<tr><td></td><td>']; | |
468 | |
469 // url, title | |
470 text.push(this.createTitleElement(bookmark, patternList)); | |
471 | |
472 // info | |
473 if (bookmark.info) { | |
474 text.push(this.createElement(bookmark.info, patternList, 'p')); | |
475 } | |
476 text.push('</td>'); | |
477 | |
478 // tags | |
479 text.push(this.createElement(this.tagsString(bookmark.tags), patternList, 'td')); | |
480 | |
481 // time | |
482 text.push(this.createElement(bookmark.time, patternList, 'td', false)); | |
483 | |
484 // edit | |
485 text.push(this.createEditElement(bookmark, patternList)); | |
486 text.push('</tr>'); | |
487 | |
488 return text.join(''); | |
489 }, | |
490 | |
491 createElement: function(value, patternList, tagName, highlight) { | |
492 | |
493 return ['<', tagName, '>', | |
494 this.createText(value, patternList, highlight), | |
495 '</', tagName, '>'].join(''); | |
496 }, | |
497 | |
498 createText: function(value, patternList, highlight) { | |
499 | |
500 var textList = []; | |
501 | |
502 if (highlight == null) highlight = this.highlight; | |
503 | |
504 if (highlight) { | |
505 | |
506 var first = this.getFirstMatch(value, patternList); | |
507 | |
508 while (first.listIndex != -1) { | |
509 textList.push(this._escapeHTML(value.substr(0, first.matchIndex))); | |
510 textList.push('<strong class="'); | |
511 textList.push(this.highClassName); | |
512 textList.push((first.listIndex % this.highClassNum) + 1); | |
513 textList.push('">'); | |
514 textList.push(this._escapeHTML(value.substr(first.matchIndex, patternList[first.listIndex].length))); | |
515 textList.push('</strong>'); | |
516 | |
517 value = value.substr(first.matchIndex + patternList[first.listIndex].length); | |
518 first = this.getFirstMatch(value, patternList); | |
519 } | |
520 } | |
521 | |
522 textList.push(this._escapeHTML(value)); | |
523 | |
524 return textList.join(''); | |
525 }, | |
526 | |
527 tagsString: function(tags, sep) { | |
528 | |
529 if (typeof(tags) == 'string') return tags; | |
530 | |
531 sep = sep || ' '; | |
532 if (this.tagBracket && tags.length != 0) { | |
533 return ['[', tags.join(']' + sep + '['), ']'].join(''); | |
534 } else { | |
535 return tags.join(sep); | |
536 } | |
537 }, | |
538 | |
539 createTitleElement: function(bookmark, patternList) { | |
540 var text = ['<a href="', bookmark.url, '"']; | |
541 if (this.urlTarget) { | |
542 text.push(' target="', this.urlTarget, '" '); | |
543 } | |
544 text.push('>'); | |
545 text.push(this.createText(bookmark.title, patternList)); | |
546 text.push('</a>'); | |
547 | |
548 if (this.addTitleText) { | |
549 text.push(this.addTitleText(bookmark, patternList)); | |
550 } | |
551 | |
552 text.push('<br />'); | |
553 return text.join(''); | |
554 }, | |
555 | |
556 createEditElement: function(bookmark, patternList) { | |
557 | |
558 var text = ['<td><a href="', this.createEditUrl(bookmark), '"']; | |
559 if (this.editTarget) { | |
560 text.push(' target="', this.editTarget, '" '); | |
561 } | |
562 | |
563 text.push('>edit</a></td>'); | |
564 | |
565 return text.join(''); | |
566 }, | |
567 | |
568 matchIndex: function(value, pattern) { | |
569 | |
570 if (this.ignoreCase) { | |
571 return value.toLowerCase().indexOf(pattern.toLowerCase()); | |
572 } else { | |
573 return value.indexOf(pattern); | |
574 } | |
575 }, | |
576 | |
577 getFirstMatch: function(value, patternList) { | |
578 | |
579 var first = {}; | |
580 first.listIndex = -1; | |
581 first.matchIndex = value.length; | |
582 | |
583 for (var i = 0, len = patternList.length; i < len; i++) { | |
584 var index = this.matchIndex(value, patternList[i]); | |
585 if (index != -1 && index < first.matchIndex) { | |
586 first.listIndex = i; | |
587 first.matchIndex = index; | |
588 } | |
589 } | |
590 | |
591 return first; | |
592 }, | |
593 | |
594 getInput: function() { | |
595 | |
596 var value = this.input.value; | |
597 | |
598 if (!value) { | |
599 return []; | |
600 } else if (this.delim) { | |
601 var list = value.split(this.delim); | |
602 var inputs = []; | |
603 for (var i = 0, len = list.length; i < len; i++) { | |
604 if (list[i]) inputs.push(list[i]); | |
605 } | |
606 return inputs; | |
607 } else { | |
608 return [value]; | |
609 } | |
610 }, | |
611 | |
612 // Utils | |
613 _bind: function(func) { | |
614 var self = this; | |
615 var args = Array.prototype.slice.call(arguments, 1); | |
616 return function(){ func.apply(self, args); }; | |
617 }, | |
618 _bindEvent: function(func) { | |
619 var self = this; | |
620 var args = Array.prototype.slice.call(arguments, 1); | |
621 return function(event){ event = event || window.event; func.apply(self, [event].concat(args)); }; | |
622 }, | |
623 _escapeHTML: function(value) { | |
624 if (this.escape) { | |
625 return value.replace(/\&/g, '&').replace( /</g, '<').replace(/>/g, '>') | |
626 .replace(/\"/g, '"').replace(/\'/g, ''').replace(/\n|\r\n/g, '<br />'); | |
627 } else { | |
628 return value; | |
629 } | |
630 } | |
631 } |