Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| /* | |
| Rikaichan | |
| Copyright (C) 2005-2014 Jonathan Zarate | |
| http://www.polarcloud.com/ | |
| --- | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 2 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program; if not, write to the Free Software | |
| Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA | |
| --- | |
| Please do not change or remove any of the copyrights or links to web pages | |
| when modifying any of the files. | |
| */ | |
| var rcxData = { | |
| ready: false, | |
| kanjiPos: 0, | |
| dicList: [], | |
| loadConfig: function() { | |
| let reinit = false; | |
| if (this.ready) { | |
| this.done(); | |
| reinit = true; | |
| } | |
| if (typeof(rcxDicList) == 'undefined') { | |
| rcxDicList = {}; | |
| this.missing = true; | |
| } | |
| if (rcxDicList['kanji@local'] == null) { | |
| rcxDicList['kanji@local'] = { | |
| name: 'Kanji', | |
| id: 'kanji@local', | |
| isKanji: true | |
| }; | |
| } | |
| // rcxMain.global().rcxDicList = rcxDicList; | |
| let prefs = new rcxPrefs(); | |
| let order = prefs.getString('dpriority'); | |
| if (order == '') order = 'rikaichan-jpen@polarcloud.com#|rikaichan-jpde@polarcloud.com#|rikaichan-jpfr@polarcloud.com#|rikaichan-jpru@polarcloud.com#|rikaichan-jpnames@polarcloud.com#|kanji@local#'; | |
| this.dicList = []; | |
| this.kanjiPos = 0; | |
| let done = {}; | |
| // arrange dicList based on user setting | |
| let oa = order.split('|'); | |
| for (let i = 0; i < oa.length; ++i) { | |
| if (oa[i].match(/^(.+?)#/)) { | |
| let dic = rcxDicList[RegExp.$1]; | |
| if (dic) { | |
| this.dicList.push(dic); | |
| done[dic.id] = true; | |
| } | |
| } | |
| } | |
| // anything new is added at the end | |
| let addedNew = false; | |
| for (let id in rcxDicList) { | |
| if (!done[id]) { | |
| this.dicList.push(rcxDicList[id]); | |
| addedNew = true; | |
| } | |
| } | |
| let ids = []; | |
| // rebuild dpriority string which is also used by Options | |
| let order2 = []; | |
| for (let i = 0; i < this.dicList.length; ++i) { | |
| let dic = this.dicList[i]; | |
| let s = dic.id + '#' + dic.name; | |
| if (dic.version) s += ' v' + dic.version; | |
| order2.push(s) | |
| if (dic.isKanji) this.kanjiPos = i; // keep track of position | |
| else ids.push(dic.id); | |
| } | |
| order2 = this.missing ? '' : order2.join('|'); | |
| if (order != order2) prefs.setString('dpriority', order2); | |
| if (addedNew) { | |
| // show dictionary tab if we have a new dictionary | |
| window.openDialog('chrome://rikaichan/content/options.xul', '', 'chrome,centerscreen', 'dic'); | |
| } | |
| // FF 3.7a workaround; @@ revisit later | |
| if (!rcxData.dicPath) { | |
| rcxData.dicPath = { ready: false }; | |
| try { | |
| Components.utils.import('resource://gre/modules/AddonManager.jsm'); | |
| // asynchronous | |
| AddonManager.getAddonsByIDs(ids, function(addons) { | |
| for (let i = 0; i < addons.length; ++i) { | |
| let a = addons[i]; | |
| rcxData.dicPath[a.id] = a.getResourceURI('install.rdf') | |
| .QueryInterface(Components.interfaces.nsIFileURL) | |
| .file.parent.path; | |
| // alert(a.id + ': path=' + rcxData.dicPath[a.id]); | |
| } | |
| rcxData.dicPath.ready = true; | |
| rcxMain.rcxObs.notifyState('dready'); | |
| }); | |
| return; | |
| } | |
| catch (ex) { } | |
| rcxData.dicPath.ready = true; | |
| } | |
| if (reinit) this.init(); | |
| }, | |
| init: function() { | |
| if (this.ready) return; | |
| this.kanjiShown = {}; | |
| let a = rcxConfig.kindex.split(','); | |
| for (let i = a.length - 1; i >= 0; --i) { | |
| this.kanjiShown[a[i]] = 1; | |
| } | |
| for (let i = this.dicList.length - 1; i >= 0; --i) { | |
| let dic = this.dicList[i]; | |
| if (dic.isKanji) continue; | |
| if ((!dic.findWord) || (!dic.findText)) this.dicList[i] = dic = new RcxDic(dic); | |
| if (dic.open) dic.open(); | |
| } | |
| this.ready = true; | |
| }, | |
| done: function() { | |
| this.ready = false; | |
| this.kanjiData = null; | |
| this.kanjiShown = null; | |
| this.radData = null; | |
| this.deinflect.done(); | |
| for (let i = this.dicList.length - 1; i >= 0; --i) { | |
| try { | |
| let dic = this.dicList[i]; | |
| if (dic.close) dic.close(); | |
| } | |
| catch (ex) { } | |
| } | |
| }, | |
| selected: 0, | |
| selectNext: function() { | |
| this.selected = (this.selected + this.searchSkipped + 1) % this.dicList.length; | |
| this.searchSkipped = 0; | |
| }, | |
| select: function(n) { | |
| if ((n < 0) || (n >= this.dicList.length)) return; | |
| this.selected = n; | |
| this.searchSkipped = 0; | |
| }, | |
| deinflect: { | |
| init: function() { | |
| this.reasons = []; | |
| this.rules = []; | |
| var buffer = rcxFile.readArray('chrome://rikaichan/content/deinflect.dat'); | |
| var ruleGroup = []; | |
| ruleGroup.fromLen = -1; | |
| // i = 1: skip header | |
| for (var i = 1; i < buffer.length; ++i) { | |
| var f = buffer[i].split('\t'); | |
| if (f.length == 1) { | |
| this.reasons.push(f[0]); | |
| } | |
| else if (f.length == 4) { | |
| var r = { from: f[0], to: f[1], type: f[2], reason: f[3] }; | |
| if (ruleGroup.fromLen != r.from.length) { | |
| ruleGroup = []; | |
| ruleGroup.fromLen = r.from.length; | |
| this.rules.push(ruleGroup); | |
| } | |
| ruleGroup.push(r); | |
| } | |
| } | |
| this.ready = true; | |
| }, | |
| done: function() { | |
| this.reasons = null; | |
| this.rules = null; | |
| this.ready = false; | |
| }, | |
| go: function(word) { | |
| if (!this.ready) this.init(); | |
| var have = []; | |
| have[word] = 0; | |
| var r = [{ word: word, type: 0xFF, reason: '' }]; | |
| var i = 0; | |
| do { | |
| word = r[i].word; | |
| var wordLen = word.length; | |
| var type = r[i].type; | |
| for (var j = 0; j < this.rules.length; ++j) { | |
| var ruleGroup = this.rules[j]; | |
| if (ruleGroup.fromLen <= wordLen) { | |
| var end = word.substr(-ruleGroup.fromLen); | |
| for (var k = 0; k < ruleGroup.length; ++k) { | |
| var rule = ruleGroup[k]; | |
| if ((type & rule.type) && (end == rule.from)) { | |
| var newWord = word.substr(0, word.length - rule.from.length) + rule.to; | |
| if (newWord.length <= 1) continue; | |
| var o = {}; | |
| if ((typeof(have[newWord])) != 'undefined') { | |
| o = r[have[newWord]]; | |
| o.type |= (rule.type >> 8); | |
| continue; | |
| } | |
| have[newWord] = r.length; | |
| if (r[i].reason.length) o.reason = this.reasons[rule.reason] + ' < ' + r[i].reason; | |
| else o.reason = this.reasons[rule.reason]; | |
| o.type = rule.type >> 8; | |
| o.word = newWord; | |
| r.push(o); | |
| } | |
| } | |
| } | |
| } | |
| } while (++i < r.length); | |
| return r; | |
| } | |
| }, | |
| // katakana -> hiragana conversion tables | |
| ch:[0x3092,0x3041,0x3043,0x3045,0x3047,0x3049,0x3083,0x3085,0x3087,0x3063,0x30FC,0x3042,0x3044,0x3046, | |
| 0x3048,0x304A,0x304B,0x304D,0x304F,0x3051,0x3053,0x3055,0x3057,0x3059,0x305B,0x305D,0x305F,0x3061, | |
| 0x3064,0x3066,0x3068,0x306A,0x306B,0x306C,0x306D,0x306E,0x306F,0x3072,0x3075,0x3078,0x307B,0x307E, | |
| 0x307F,0x3080,0x3081,0x3082,0x3084,0x3086,0x3088,0x3089,0x308A,0x308B,0x308C,0x308D,0x308F,0x3093], | |
| cv:[0x30F4,0xFF74,0xFF75,0x304C,0x304E,0x3050,0x3052,0x3054,0x3056,0x3058,0x305A,0x305C,0x305E,0x3060, | |
| 0x3062,0x3065,0x3067,0x3069,0xFF85,0xFF86,0xFF87,0xFF88,0xFF89,0x3070,0x3073,0x3076,0x3079,0x307C], | |
| cs:[0x3071,0x3074,0x3077,0x307A,0x307D], | |
| _wordSearch: function(word, dic, max) { | |
| if (!this.ready) this.init(); | |
| // half & full-width katakana to hiragana conversion | |
| // note: katakana vu is never converted to hiragana | |
| var trueLen = [0]; | |
| var p = 0; | |
| var r = ''; | |
| for (let i = 0; i < word.length; ++i) { | |
| let u = word.charCodeAt(i); | |
| let v = u; | |
| if (u <= 0x3000) break; | |
| // full-width katakana to hiragana | |
| if ((u >= 0x30A1) && (u <= 0x30F3)) { | |
| u -= 0x60; | |
| } | |
| // half-width katakana to hiragana | |
| else if ((u >= 0xFF66) && (u <= 0xFF9D)) { | |
| u = this.ch[u - 0xFF66]; | |
| } | |
| // voiced (used in half-width katakana) to hiragana | |
| else if (u == 0xFF9E) { | |
| if ((p >= 0xFF73) && (p <= 0xFF8E)) { | |
| r = r.substr(0, r.length - 1); | |
| u = this.cv[p - 0xFF73]; | |
| } | |
| } | |
| // semi-voiced (used in half-width katakana) to hiragana | |
| else if (u == 0xFF9F) { | |
| if ((p >= 0xFF8A) && (p <= 0xFF8E)) { | |
| r = r.substr(0, r.length - 1); | |
| u = this.cs[p - 0xFF8A]; | |
| } | |
| } | |
| // ignore J~ | |
| else if (u == 0xFF5E) { | |
| p = 0; | |
| continue; | |
| } | |
| r += String.fromCharCode(u); | |
| trueLen[r.length] = i + 1; // need to keep real length because of the half-width semi/voiced conversion | |
| p = v; | |
| } | |
| word = r; | |
| var result = { data: [] }; | |
| var maxTrim; | |
| if (dic.isName) { | |
| maxTrim = rcxConfig.namax; | |
| result.names = 1; | |
| } | |
| else { | |
| maxTrim = rcxConfig.wmax; | |
| } | |
| if (max != null) maxTrim = max; | |
| var have = []; | |
| var count = 0; | |
| var maxLen = 0; | |
| while (word.length > 0) { | |
| var showInf = (count != 0); | |
| var variants = dic.isName ? [{word: word, type: 0xFF, reason: null}] : this.deinflect.go(word); | |
| for (var i = 0; i < variants.length; i++) { | |
| var v = variants[i]; | |
| var entries = dic.findWord(v.word); | |
| for (var j = 0; j < entries.length; ++j) { | |
| var dentry = entries[j]; | |
| if (have[dentry]) continue; | |
| var ok = true; | |
| if ((dic.hasType) && (i > 0)) { | |
| // i > 0 a de-inflected word | |
| var gloss = dentry.split(/[,()]/); | |
| var y = v.type; | |
| var z; | |
| for (z = gloss.length - 1; z >= 0; --z) { | |
| var g = gloss[z]; | |
| if ((y & 1) && (g == 'v1')) break; | |
| if ((y & 4) && (g == 'adj-i')) break; | |
| if (y & 66) { | |
| if ((g == 'v5k-s') || (g == 'v5u-s')) { | |
| if (y & 64) break; | |
| } | |
| else if (g.substr(0, 2) == 'v5') { | |
| if (y & 2) break; | |
| } | |
| } | |
| if ((y & 8) && (g == 'vk')) break; | |
| if ((y & 16) && (g.substr(0, 3) == 'vs-')) break; | |
| } | |
| ok = (z != -1); | |
| } | |
| if ((ok) && (dic.hasType) && (rcxConfig.hidex)) { | |
| if (dentry.match(/\/\([^\)]*\bX\b.*?\)/)) ok = false; | |
| } | |
| if (ok) { | |
| if (count >= maxTrim) { | |
| result.more = 1; | |
| break; | |
| } | |
| have[dentry] = 1; | |
| ++count; | |
| if (maxLen == 0) maxLen = trueLen[word.length]; | |
| var r = null; | |
| if (v.reason) { | |
| if (showInf) r = '< ' + v.reason + ' < ' + word; | |
| else r = '< ' + v.reason; | |
| } | |
| result.data.push([dentry, r]); | |
| } | |
| } // for j < entries.length | |
| if (count >= maxTrim) break; | |
| } // for i < variants.length | |
| if (count >= maxTrim) break; | |
| word = word.substr(0, word.length - 1); | |
| } // while word.length > 0 | |
| if (result.data.length == 0) return null; | |
| result.matchLen = maxLen; | |
| return result; | |
| }, | |
| wordSearch: function(word, noKanji) { | |
| this.searchSkipped = 0; | |
| let ds = this.selected; | |
| do { | |
| let dic = this.dicList[ds]; | |
| if ((!noKanji) || (!dic.isKanji)) { | |
| let e; | |
| if (dic.isKanji) e = this.kanjiSearch(word.charAt(0)); | |
| else e = this._wordSearch(word, dic, null); | |
| if (e) { | |
| if (ds != 0) e.title = dic.name; | |
| return e; | |
| } | |
| } | |
| this.searchSkipped++; | |
| ds = (ds + 1) % this.dicList.length; | |
| } while (ds != this.selected); | |
| return null; | |
| }, | |
| translate: function(text) { | |
| var result = { data: [], textLen: text.length }; | |
| while (text.length > 0) { | |
| var e = null; | |
| var ds = this.selected; | |
| do { | |
| if (!this.dicList[ds].isKanji) { | |
| e = this._wordSearch(text, this.dicList[ds], 1); | |
| if (e != null) break; | |
| } | |
| ds = (ds + 1) % this.dicList.length; | |
| } while (ds != this.selected); | |
| if (e != null) { | |
| if (result.data.length >= rcxConfig.wmax) { | |
| result.more = 1; | |
| break; | |
| } | |
| result.data.push(e.data[0]); | |
| text = text.substr(e.matchLen); | |
| } | |
| else { | |
| text = text.substr(1); | |
| } | |
| } | |
| this.searchSkipped = (this.selected == this.kanjiPos) ? 1 : 0; | |
| if (result.data.length == 0) return null; | |
| result.textLen -= text.length; | |
| return result; | |
| }, | |
| textSearch: function(text) { | |
| this.searchSkipped = 0; | |
| if (!this.ready) this.init(); | |
| text = text.toLowerCase(); | |
| let ds = this.selected; | |
| do { | |
| let dic = this.dicList[ds]; | |
| if (!dic.isKanji) { | |
| let result = { data: [], reason: [], kanji: 0, more: 0, names: dic.isName }; | |
| let r = dic.findText(text); | |
| // try priorizing | |
| let list = []; | |
| let sW = /[\sW]/; | |
| let slashText = '/' + text + '/'; | |
| for (let i = 0; i < r.length; ++i) { | |
| let t = r[i].replace(/\(.+?\)/g, '').toLowerCase(); | |
| // closer to the beginning = better | |
| let d = t.indexOf(text); | |
| if (d >= 0) { | |
| // the exact text within an entry = best | |
| if (t.replace(/\s+/g, '').indexOf(slashText) != -1) { | |
| d -= 100; | |
| } | |
| // a word within an entry = better | |
| else if (((d == 0) || (sW.test(t.substr(d - 1, 1)))) && | |
| (((d + text.length) >= t.length) || (sW.test(t.substr(d + text.length, 1))))) { | |
| d -= 50; | |
| } | |
| } | |
| else d = 9999; | |
| list.push({ rank: d, text: r[i] }); | |
| } | |
| let max = dic.isName ? rcxConfig.namax : rcxConfig.wmax; | |
| list.sort(function(a, b) { return a.rank - b.rank }); | |
| for (let i = 0; i < list.length; ++i) { | |
| if (result.data.length >= max) { | |
| result.more = 1; | |
| break; | |
| } | |
| // result.data.push([list[i].text + '[' + list[i].rank + ']/', null]); | |
| result.data.push([list[i].text, null]); | |
| } | |
| /* | |
| let j = (list.length > 100) ? 100 : list.length; | |
| for (let i = 0; i < j; ++i) { | |
| rcxDebug.echo(i + ': [' + list[i].rank + '] ' + list[i].text); | |
| }*/ | |
| /* | |
| for (let i = 0; i < r.length; ++i) { | |
| if (result.data.length >= max) { | |
| result.more = 1; | |
| break; | |
| } | |
| result.data.push([r[i], null]); | |
| } | |
| */ | |
| if (result.data.length) { | |
| if (ds != 0) result.title = dic.name; | |
| return result; | |
| } | |
| } | |
| this.searchSkipped++; | |
| ds = (ds + 1) % this.dicList.length; | |
| } while (ds != this.selected); | |
| return null; | |
| }, | |
| // @@@ todo later... | |
| kanjiSearch: function(kanji) { | |
| const hex = '0123456789ABCDEF'; | |
| var kde; | |
| var result; | |
| var a, b; | |
| var i; | |
| i = kanji.charCodeAt(0); | |
| if (i < 0x3000) return null; | |
| if (!this.kanjiData) { | |
| this.kanjiData = rcxFile.read((typeof(rcxKanjiURI) == 'string') ? rcxKanjiURI : 'chrome://rikaichan/content/kanji.dat'); | |
| } | |
| kde = this.find(this.kanjiData, kanji); | |
| if (!kde) return null; | |
| a = kde.split('|'); | |
| if (a.length != 6) return null; | |
| result = { }; | |
| result.kanji = a[0]; | |
| result.misc = {}; | |
| result.misc['U'] = hex[(i >>> 12) & 15] + hex[(i >>> 8) & 15] + hex[(i >>> 4) & 15] + hex[i & 15]; | |
| b = a[1].split(' '); | |
| for (i = 0; i < b.length; ++i) { | |
| if (b[i].match(/^([A-Z]+)(.*)/)) { | |
| if (!result.misc[RegExp.$1]) result.misc[RegExp.$1] = RegExp.$2; | |
| else result.misc[RegExp.$1] += ' ' + RegExp.$2; | |
| } | |
| } | |
| result.onkun = a[2].replace(/\s+/g, '\u3001 '); | |
| result.nanori = a[3].replace(/\s+/g, '\u3001 '); | |
| result.bushumei = a[4].replace(/\s+/g, '\u3001 '); | |
| result.eigo = a[5]; | |
| return result; | |
| }, | |
| // --- | |
| numList: [ | |
| /* | |
| 'C', 'Classical Radical', | |
| 'DR', 'Father Joseph De Roo Index', | |
| 'DO', 'P.G. O\'Neill Index', | |
| 'O', 'P.G. O\'Neill Japanese Names Index', | |
| 'Q', 'Four Corner Code', | |
| 'MN', 'Morohashi Daikanwajiten Index', | |
| 'MP', 'Morohashi Daikanwajiten Volume/Page', | |
| 'K', 'Gakken Kanji Dictionary Index', | |
| 'W', 'Korean Reading', | |
| */ | |
| 'H', 'Halpern', | |
| 'L', 'Heisig', | |
| 'E', 'Henshall', | |
| 'DK', 'Kanji Learners Dictionary', | |
| 'N', 'Nelson', | |
| 'V', 'New Nelson', | |
| 'Y', 'PinYin', | |
| 'P', 'Skip Pattern', | |
| 'IN', 'Tuttle Kanji & Kana', | |
| 'I', 'Tuttle Kanji Dictionary', | |
| 'U', 'Unicode' | |
| ], | |
| makeHtml: function(entry) { | |
| var e; | |
| var b; | |
| var c, s, t; | |
| var i, j, n; | |
| if (entry == null) return ''; | |
| if (!this.ready) this.init(); | |
| if (!this.radData) this.radData = rcxFile.readArray('chrome://rikaichan/content/radicals.dat'); | |
| b = []; | |
| if (entry.kanji) { | |
| var yomi; | |
| var box; | |
| var bn; | |
| var k; | |
| var nums; | |
| yomi = entry.onkun.replace(/\.([^\u3001]+)/g, '<span class="k-yomi-hi">$1</span>'); | |
| if (entry.nanori.length) { | |
| yomi += '<br/><span class="k-yomi-ti">\u540D\u4E57\u308A</span> ' + entry.nanori; | |
| } | |
| if (entry.bushumei.length) { | |
| yomi += '<br/><span class="k-yomi-ti">\u90E8\u9996\u540D</span> ' + entry.bushumei; | |
| } | |
| bn = entry.misc['B'] - 1; | |
| k = entry.misc['G']; | |
| switch (k) { | |
| case 8: | |
| k = 'general<br/>use'; | |
| break; | |
| case 9: | |
| k = 'name<br/>use'; | |
| break; | |
| default: | |
| k = isNaN(k) ? '-' : ('grade<br/>' + k); | |
| break; | |
| } | |
| box = '<table class="k-abox-tb"><tr>' + | |
| '<td class="k-abox-r">radical<br/>' + this.radData[bn].charAt(0) + ' ' + (bn + 1) + '</td>' + | |
| '<td class="k-abox-g">' + k + '</td>' + | |
| '</tr><tr>' + | |
| '<td class="k-abox-f">freq<br/>' + (entry.misc['F'] ? entry.misc['F'] : '-') + '</td>' + | |
| '<td class="k-abox-s">strokes<br/>' + entry.misc['S'] + '</td>' + | |
| '</tr></table>'; | |
| if (this.kanjiShown['COMP']) { | |
| k = this.radData[bn].split('\t'); | |
| box += '<table class="k-bbox-tb">' + | |
| '<tr><td class="k-bbox-1a">' + k[0] + '</td>' + | |
| '<td class="k-bbox-1b">' + k[2] + '</td>' + | |
| '<td class="k-bbox-1b">' + k[3] + '</td></tr>'; | |
| j = 1; | |
| for (i = 0; i < this.radData.length; ++i) { | |
| s = this.radData[i]; | |
| if ((bn != i) && (s.indexOf(entry.kanji) != -1)) { | |
| k = s.split('\t'); | |
| c = ' class="k-bbox-' + (j ^= 1); | |
| box += '<tr><td' + c + 'a">' + k[0] + '</td>' + | |
| '<td' + c + 'b">' + k[2] + '</td>' + | |
| '<td' + c + 'b">' + k[3] + '</td></tr>'; | |
| } | |
| } | |
| box += '</table>'; | |
| } | |
| nums = ''; | |
| j = 0; | |
| for (i = 0; i < this.numList.length; i += 2) { | |
| c = this.numList[i]; | |
| if (this.kanjiShown[c]) { | |
| s = entry.misc[c]; | |
| c = ' class="k-mix-td' + (j ^= 1) + '"'; | |
| nums += '<tr><td' + c + '>' + this.numList[i + 1] + '</td><td' + c + '>' + (s ? s : '-') + '</td></tr>'; | |
| } | |
| } | |
| if (nums.length) nums = '<table class="k-mix-tb">' + nums + '</table>'; | |
| b.push('<table class="k-main-tb"><tr><td valign="top">'); | |
| b.push(box); | |
| b.push('<span class="k-kanji">' + entry.kanji + '</span><br/>'); | |
| if (!rcxConfig.hidedef) b.push('<div class="k-eigo">' + entry.eigo + '</div>'); | |
| b.push('<div class="k-yomi">' + yomi + '</div>'); | |
| b.push('</td></tr><tr><td>' + nums + '</td></tr></table>'); | |
| return b.join(''); | |
| } | |
| s = t = ''; | |
| if (entry.names) { | |
| c = []; | |
| b.push('<div class="w-title">Names Dictionary</div><table class="w-na-tb"><tr><td>'); | |
| for (i = 0; i < entry.data.length; ++i) { | |
| e = entry.data[i][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/([\S\s]+)\//); | |
| if (!e) continue; | |
| if (s != e[3]) { | |
| c.push(t); | |
| t = ''; | |
| } | |
| if (e[2]) c.push('<span class="w-kanji">' + e[1] + '</span>   <span class="w-kana">' + e[2] + '</span><br/> '); | |
| else c.push('<span class="w-kana">' + e[1] + '</span><br/> '); | |
| s = e[3]; | |
| if (rcxConfig.hidedef) t = ''; | |
| else t = '<span class="w-def">' + s.replace(/\//g, '; ').replace(/\n/g, '<br/>') + '</span><br/>'; | |
| } | |
| c.push(t); | |
| if (c.length > 4) { | |
| n = (c.length >> 1) + 1; | |
| b.push(c.slice(0, n + 1).join('')); | |
| t = c[n]; | |
| c = c.slice(n, c.length); | |
| for (i = 0; i < c.length; ++i) { | |
| if (c[i].indexOf('w-def') != -1) { | |
| if (t != c[i]) b.push(c[i]); | |
| if (i == 0) c.shift(); | |
| break; | |
| } | |
| } | |
| b.push('</td><td>'); | |
| b.push(c.join('')); | |
| } | |
| else { | |
| b.push(c.join('')); | |
| } | |
| if (entry.more) b.push('...<br/>'); | |
| b.push('</td></tr></table>'); | |
| } | |
| else { | |
| if (entry.title) { | |
| b.push('<div class="w-title">' + entry.title + '</div>'); | |
| } | |
| var pK = ''; | |
| var k; | |
| for (i = 0; i < entry.data.length; ++i) { | |
| e = entry.data[i][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/([\S\s]+)\//); | |
| if (!e) continue; | |
| /* | |
| e[1] = kanji/kana | |
| e[2] = kana | |
| e[3] = definition | |
| */ | |
| if (s != e[3]) { | |
| b.push(t); | |
| pK = k = ''; | |
| } | |
| else { | |
| k = t.length ? '<br/>' : ''; | |
| } | |
| if (e[2]) { | |
| if (pK == e[1]) k = '\u3001 <span class="w-kana">' + e[2] + '</span>'; | |
| else k += '<span class="w-kanji">' + e[1] + '</span>   <span class="w-kana">' + e[2] + '</span>'; | |
| pK = e[1]; | |
| } | |
| else { | |
| k += '<span class="w-kana">' + e[1] + '</span>'; | |
| pK = ''; | |
| } | |
| b.push(k); | |
| if (entry.data[i][1]) b.push(' <span class="w-conj">(' + entry.data[i][1] + ')</span>'); | |
| s = e[3]; | |
| if (rcxConfig.hidedef) { | |
| t = '<br/>'; | |
| } | |
| else { | |
| t = s.replace(/\//g, '; '); | |
| if (!rcxConfig.wpos) t = t.replace(/^\([^)]+\)\s*/, ''); | |
| if (!rcxConfig.wpop) t = t.replace('; (P)', ''); | |
| t = t.replace(/\n/g, '<br/>'); | |
| t = '<br/><span class="w-def">' + t + '</span><br/>'; | |
| } | |
| } | |
| b.push(t); | |
| if (entry.more) b.push('...<br/>'); | |
| } | |
| return b.join(''); | |
| }, | |
| makeText: function(entry, max) { | |
| var e; | |
| var b; | |
| var i, j; | |
| var t; | |
| if (entry == null) return ''; | |
| if (!this.ready) this.init(); | |
| b = []; | |
| if (entry.kanji) { | |
| b.push(entry.kanji + '\n'); | |
| b.push((entry.eigo.length ? entry.eigo : '-') + '\n'); | |
| b.push(entry.onkun.replace(/\.([^\u3001]+)/g, '\uFF08$1\uFF09') + '\n'); | |
| if (entry.nanori.length) { | |
| b.push('\u540D\u4E57\u308A\t' + entry.nanori + '\n'); | |
| } | |
| if (entry.bushumei.length) { | |
| b.push('\u90E8\u9996\u540D\t' + entry.bushumei + '\n'); | |
| } | |
| for (i = 0; i < this.numList.length; i += 2) { | |
| e = this.numList[i]; | |
| if (this.kanjiShown[e]) { | |
| j = entry.misc[e]; | |
| b.push(this.numList[i + 1].replace('&', '&') + '\t' + (j ? j : '-') + '\n'); | |
| } | |
| } | |
| } | |
| else { | |
| if (max > entry.data.length) max = entry.data.length; | |
| for (i = 0; i < max; ++i) { | |
| e = entry.data[i][0].match(/^(.+?)\s+(?:\[(.*?)\])?\s*\/(.+)\//); | |
| if (!e) continue; | |
| if (e[2]) { | |
| b.push(e[1] + '\t' + e[2]); | |
| } | |
| else { | |
| b.push(e[1]); | |
| } | |
| t = e[3].replace(/\//g, '; '); | |
| if (!rcxConfig.wpos) t = t.replace(/^\([^)]+\)\s*/, ''); | |
| if (!rcxConfig.wpop) t = t.replace('; (P)', ''); | |
| b.push('\t' + t + '\n'); | |
| } | |
| } | |
| return b.join(''); | |
| }, | |
| // --- | |
| find: function(data, text) { | |
| const tlen = text.length; | |
| var beg = 0; | |
| var end = data.length - 1; | |
| var i; | |
| var mi; | |
| var mis; | |
| while (beg < end) { | |
| mi = (beg + end) >> 1; | |
| i = data.lastIndexOf('\n', mi) + 1; | |
| mis = data.substr(i, tlen); | |
| if (text < mis) end = i - 1; | |
| else if (text > mis) beg = data.indexOf('\n', mi + 1) + 1; | |
| else return data.substring(i, data.indexOf('\n', mi + 1)); | |
| } | |
| return null; | |
| } | |
| }; | |
| var rcxFile = { | |
| read: function(uri) { | |
| var inp = Components.classes['@mozilla.org/network/io-service;1'] | |
| .getService(Components.interfaces.nsIIOService) | |
| .newChannel(uri, null, null) | |
| .open(); | |
| var is = Components.classes['@mozilla.org/intl/converter-input-stream;1'] | |
| .createInstance(Components.interfaces.nsIConverterInputStream); | |
| is.init(inp, 'UTF-8', 4 * 1024 * 1024, | |
| Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER); | |
| var buffer = ''; | |
| var s = {}; | |
| while (is.readString(-1, s) > 0) { | |
| buffer += s.value; | |
| } | |
| is.close(); | |
| return buffer; | |
| }, | |
| readArray: function(name) { | |
| var a = this.read(name).split('\n'); | |
| while ((a.length > 0) && (a[a.length - 1].length == 0)) a.pop(); | |
| return a; | |
| } | |
| }; | |
| function RcxDb(name) | |
| { | |
| this.open = function() { | |
| var f; | |
| if (name.match(/(.+)\|(.+)/)) { | |
| let id = RegExp.$1; | |
| let nm = RegExp.$2; | |
| if (typeof(rcxData.dicPath) == 'undefined') throw 'dicPath does not exist'; | |
| if (typeof(rcxData.dicPath[id]) == 'undefined') throw 'dicPath.' + id + ' does not exist'; | |
| f = Components.classes['@mozilla.org/file/local;1'] | |
| .createInstance(Components.interfaces.nsILocalFile); | |
| f.initWithPath(rcxData.dicPath[id]); | |
| f.append(nm); | |
| } | |
| else { | |
| f = Components.classes['@mozilla.org/file/local;1'] | |
| .createInstance(Components.interfaces.nsILocalFile); | |
| f.initWithPath(name); | |
| } | |
| if (!f.exists()) throw 'Could not find ' + id + '/' + nm; | |
| // The files may get installed as read-only, breaking | |
| // index creation. Try changing the file permission. | |
| if (!f.isWritable()) f.permissions |= 0x180; // 0x180=0600 strict mode doesn't like octals | |
| this.db = Components.classes['@mozilla.org/storage/service;1'] | |
| .getService(Components.interfaces.mozIStorageService) | |
| .openDatabase(f); | |
| }; | |
| this.close = function() { | |
| if (this.db) { | |
| try { | |
| this.db.close(); | |
| } | |
| catch (ex) { | |
| } | |
| this.db = null; | |
| } | |
| }; | |
| this.exec = function(stm) { | |
| var rows = []; | |
| if (!this.db) this.open(); | |
| var st = this.db.createStatement(stm); | |
| for (var i = arguments.length - 1; i > 0; --i) { | |
| if (arguments[i] != null) st.bindUTF8StringParameter(i - 1, arguments[i]); | |
| } | |
| while (st.executeStep()) { | |
| var r = []; | |
| for (var i = st.columnCount - 1; i >= 0; --i) { | |
| r[st.getColumnName(i)] = st.getUTF8String(i); | |
| } | |
| rows.push(r); | |
| } | |
| return rows; | |
| }; | |
| this.indexExists = function(index) { | |
| if (!this.db) this.open(); | |
| return this.db.indexExists(index); | |
| }; | |
| this.beginTransaction = function() { | |
| if (!this.db) this.open(); | |
| this.db.beginTransaction(); | |
| }; | |
| this.commitTransaction = function() { | |
| this.db.commitTransaction(); | |
| }; | |
| this.rollbackTransaction = function() { | |
| this.db.rollbackTransaction(); | |
| }; | |
| return this; | |
| } | |
| function RcxDic(dic) | |
| { | |
| this.name = dic.name; | |
| this.version = dic.version; | |
| this.id = dic.id; | |
| this.hasType = dic.hasType; | |
| this.isName = dic.isName; | |
| this.open = function() { | |
| try { | |
| if (this.rdb) return; | |
| this.rdb = new RcxDb(this.id + '|dict.sqlite'); | |
| this.rdb.open(); | |
| this.checkIndex('kanji'); | |
| this.checkIndex('kana'); | |
| } | |
| catch (ex) { | |
| this.close(); | |
| throw ex; | |
| } | |
| }; | |
| this.close = function() { | |
| if (this.rdb) { | |
| try { | |
| this.rdb.close(); | |
| } | |
| catch (ex) { | |
| } | |
| this.rdb = null; | |
| } | |
| }; | |
| this.checkIndex = function(name) { | |
| var ix = 'ix_' + name; | |
| if (this.rdb.indexExists(ix)) return; | |
| if (!rcxData.indexCreateNotice) { | |
| alert('A dictionary index needs to be created. This may take a while on some systems. Click OK to start.'); | |
| rcxData.indexCreateNotice = true; | |
| } | |
| this.rdb.exec('CREATE INDEX ' + ix + ' ON dict (' + name + ' ASC)'); | |
| }; | |
| this.find = function(query, arg1) { | |
| if (!this.rdb) this.open(); | |
| var r = this.rdb.exec(query, arg1); | |
| var entries = []; | |
| for (var i = 0; i < r.length; ++i) { | |
| var x = r[i]; | |
| if (!x.entry.length) continue; | |
| // rcx currently expects an edict-like format | |
| if (x.entry[x.entry.length - 1] == '/') entries.push(x.entry); | |
| else entries.push((x.kanji ? (x.kanji + ' [' + x.kana + ']') : x.kana) + ' /' + x.entry + '/'); | |
| } | |
| return entries; | |
| }; | |
| this.findWord = function(word) { | |
| return this.find('SELECT * FROM dict WHERE kanji=?1 OR kana=?1 LIMIT 100', word); | |
| }; | |
| this.findText = function(text) { | |
| return this.find('SELECT * FROM dict WHERE entry LIKE ?1 LIMIT 300', '%' + text + '%'); | |
| }; | |
| return this; | |
| }; |