| 23 | | // |
| 24 | | // TODO: |
| 25 | | // FIND_MODE_NATIVE のときうまく動かない。XUL/Migemoの問題? |
| 26 | | // 検索時に取りこぼさないようにする。 |
| 27 | | // (とりあえず検索開始を遅延することで取りこぼしにくくした) |
| 28 | | |
| 29 | | (function () { |
| 30 | | |
| 31 | | // findMode := FIND_MODE_NATIVE | FIND_MODE_MIGEMO | FIND_MODE_REGEXP |
| 32 | | |
| 33 | | const FindToolbar = document.getElementById('FindToolbar') |
| 34 | | const FindbarTextbox = FindToolbar.getElement('findbar-textbox'); |
| 35 | | const DOMUtils = Components.classes["@mozilla.org/inspector/dom-utils;1"]. |
| 36 | | getService(Components.interfaces["inIDOMUtils"]); |
| 37 | | |
| 38 | | let previousKeyword = null; |
| 39 | | let lastKeyword = null; |
| | 19 | |
| | 20 | (function () { try { |
| | 21 | |
| | 22 | let XMigemoCore = Components.classes['@piro.sakura.ne.jp/xmigemo/factory;1'] |
| | 23 | .getService(Components.interfaces.pIXMigemoFactory) |
| | 24 | .getService('ja'); |
| | 25 | |
| | 26 | function getPosition (elem) { |
| | 27 | if (!elem) |
| | 28 | return {x: 0, y: 0}; |
| | 29 | let parent = getPosition(elem.offsetParent); |
| | 30 | return { x: (elem.offsetLeft || 0) + parent.x, |
| | 31 | y: (elem.offsetTop || 0) + parent.y } |
| | 32 | } |
| | 33 | |
| | 34 | let delayCallTimer = null; |
| | 35 | |
| | 36 | let MF = { |
| | 37 | lastSearchText: null, |
| | 38 | previousSearchText: null, |
| | 39 | lastDirection: null, |
| | 40 | |
| | 41 | get buffer function () liberator.buffer, |
| | 42 | |
| | 43 | get document function () content.document, |
| | 44 | |
| | 45 | get storage function () (this.buffer.__migemized_find_storage || (this.buffer.__migemized_find_storage = {})), |
| | 46 | |
| | 47 | get defaultRange function () { |
| | 48 | let range = this.document.createRange(); |
| | 49 | range.selectNodeContents(this.document.body); |
| | 50 | return range; |
| | 51 | }, |
| | 52 | |
| | 53 | get highlightRemover function () (this.storage.highlightRemover || function () void(0)), |
| | 54 | set highlightRemover function (fun) (this.storage.highlightRemover = fun), |
| | 55 | |
| | 56 | MODE_NORMAL: 0, |
| | 57 | MODE_REGEXP: 1, |
| | 58 | MODE_MIGEMO: 2, |
| | 59 | |
| | 60 | // 検索文字列から検索モードと検索文字列を得る。 |
| | 61 | searchTextToRegExpString: function (str) { |
| | 62 | let [head, tail] = [str[0], str.slice(1)]; |
| | 63 | switch (head) { |
| | 64 | case '/': |
| | 65 | return tail; |
| | 66 | case '?': |
| | 67 | return XMigemoCore.getRegExp(tail); |
| | 68 | } |
| | 69 | return XMigemoCore.getRegExp(str); |
| | 70 | }, |
| | 71 | |
| | 72 | removeHighlight: function () { |
| | 73 | this.highlightRemover() |
| | 74 | this.highlightRemover = null; |
| | 75 | }, |
| | 76 | |
| | 77 | highlightRange: function (range, setRemover) { |
| | 78 | let span = this.document.createElement('span'); |
| | 79 | let spanStyle = 'background-color: lightblue; color: black; border: dotted 3px blue;'; |
| | 80 | |
| | 81 | span.setAttribute('style', spanStyle); |
| | 82 | range.surroundContents(span); |
| | 83 | |
| | 84 | let scroll = function () { |
| | 85 | let pos = getPosition(span); |
| | 86 | content.scroll(pos.x - (content.innerWidth / 2), |
| | 87 | pos.y - (content.innerHeight / 2)); |
| | 88 | }; |
| | 89 | setTimeout(scroll, 0); |
| | 90 | |
| | 91 | let remover = function () { |
| | 92 | let range = this.document.createRange(); |
| | 93 | range.selectNodeContents(span); |
| | 94 | let content = range.extractContents(); |
| | 95 | range.setStartBefore(span); |
| | 96 | range.insertNode(content); |
| | 97 | range.selectNode(span); |
| | 98 | range.deleteContents(); |
| | 99 | }; |
| | 100 | |
| | 101 | if (setRemover) |
| | 102 | this.highlightRemover = remover; |
| | 103 | |
| | 104 | return remover; |
| | 105 | }, |
| | 106 | |
| | 107 | find: function (str, backwards, range, start, end) { |
| | 108 | if (!range) |
| | 109 | range = this.defaultRange; |
| | 110 | try { |
| | 111 | return XMigemoCore.regExpFind(str, 'i', range, start, end, backwards); |
| | 112 | } catch (e) { |
| | 113 | return false; |
| | 114 | } |
| | 115 | }, |
| | 116 | |
| | 117 | findFirst: function (str, backwards) { |
| | 118 | let f = function () { |
| | 119 | this.lastDirection = backwards; |
| | 120 | this.lastSearchText = str = this.searchTextToRegExpString(str); |
| | 121 | |
| | 122 | let result = this.storage.lastResult = this.find(str, backwards); |
| | 123 | |
| | 124 | this.removeHighlight(); |
| | 125 | if (result) |
| | 126 | this.highlightRange(result, true); |
| | 127 | |
| | 128 | return result; |
| | 129 | }; |
| | 130 | |
| | 131 | if (delayCallTimer) |
| | 132 | clearTimeout(delayCallTimer); |
| | 133 | |
| | 134 | delayCallTimer = setTimeout(function () f.call(MF), 300); |
| | 135 | }, |
| | 136 | |
| | 137 | findAgain: function (reverse) { |
| | 138 | this.removeHighlight(); |
| | 139 | |
| | 140 | let str = this.lastSearchText; |
| | 141 | let range = this.defaultRange; |
| | 142 | let last = this.storage.lastResult; |
| | 143 | let backwards = !!(!this.lastDirection ^ !reverse); |
| | 144 | let start, end; |
| | 145 | |
| | 146 | if (last) { |
| | 147 | if (backwards) { |
| | 148 | end = last.cloneRange(); |
| | 149 | end.setStart(last.endContainer, last.endOffset); |
| | 150 | } else { |
| | 151 | start = last.cloneRange(); |
| | 152 | start.setStart(last.endContainer, last.endOffset); |
| | 153 | } |
| | 154 | } |
| | 155 | |
| | 156 | let result = this.storage.lastResult = this.find(str, backwards, range, start, end); |
| | 157 | if (!result) |
| | 158 | result = this.storage.lastResult = this.find(str, backwards, range); |
| | 159 | |
| | 160 | if (result) |
| | 161 | this.highlightRange(result, true); |
| | 162 | else |
| | 163 | liberator.echoerr('not found: ' + str); |
| | 164 | |
| | 165 | return result; |
| | 166 | }, |
| | 167 | }; |
| | 168 | |