-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathbackground.js
More file actions
148 lines (126 loc) · 4.38 KB
/
Copy pathbackground.js
File metadata and controls
148 lines (126 loc) · 4.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Fix kuromoji XHR in Firefox extension context
// kuromoji uses XHR to load dict files, Firefox generates moz-extension:/
// with a single slash instead of moz-extension:// — this corrects it
XMLHttpRequest.prototype.open = (function (originalOpen) {
return function (method, url, ...rest) {
if (typeof url === "string") {
url = url.replace(/^moz-extension:\/(?!\/)/, "moz-extension://");
}
return originalOpen.call(this, method, url, ...rest);
};
})(XMLHttpRequest.prototype.open);
const KANJI_RE = /[\u4e00-\u9fff]/;
class FuriganaAnnotator {
tokenizer = null;
async init() {
return new Promise((resolve, reject) => {
kuromoji.builder({ dicPath: browser.runtime.getURL("dict/") })
.build((err, tokenizer) => {
if (err) return reject(err);
this.tokenizer = tokenizer;
console.log("[furigana] ready");
resolve();
});
});
}
annotate(text) {
if (!this.tokenizer) return text;
const result = this.tokenizer
.tokenize(text)
.map(token => this.toRuby(token))
.join("");
return result === text ? null : result;
}
toRuby(token) {
const surface = token.surface_form;
const reading = token.reading;
if (!reading || reading === surface) return surface;
if (!KANJI_RE.test(surface)) return surface;
return this.alignReadingToSurface(surface, this.toHiragana(reading));
}
// Build a regex from the surface where consecutive kanji
// groups become (.+) and kana become literals, then match
// against the reading to extract per-kanji-group readings.
alignReadingToSurface(surface, reading) {
let pattern = "^";
let inKanji = false;
for (const char of surface) {
if (KANJI_RE.test(char)) {
if (!inKanji) {
pattern += "(.+)";
inKanji = true;
}
} else {
inKanji = false;
pattern += char.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}
}
pattern += "$";
const match = new RegExp(pattern).exec(reading);
if (!match) {
// Fallback — wrap entire surface
return `<ruby>${surface}<rt>${reading}</rt></ruby>`;
}
let result = "";
let groupIndex = 1;
inKanji = false;
for (const char of surface) {
if (KANJI_RE.test(char)) {
if (!inKanji) {
result += `<ruby>${char}`;
inKanji = true;
} else {
result += char;
}
} else {
if (inKanji) {
result += `<rt>${match[groupIndex++]}</rt></ruby>`;
inKanji = false;
}
result += char;
}
}
if (inKanji) {
result += `<rt>${match[groupIndex]}</rt></ruby>`;
}
return result;
}
toHiragana(text) {
return text.replace(/[\u30a1-\u30f6]/g, char =>
String.fromCharCode(char.charCodeAt(0) - 0x60)
);
}
}
const annotator = new FuriganaAnnotator();
annotator.init().catch(err => {
console.error("[furigana] init failed:", err);
});
if (browser.contextMenus && browser.contextMenus.create) {
browser.contextMenus.create({
id: "add-furigana",
title: "Add furigana",
contexts: ["page"]
});
browser.contextMenus.create({
id: "add-furigana-selection",
title: "Add furigana to selection",
contexts: ["selection"]
});
browser.contextMenus.onClicked.addListener((info, tab) => {
if (info.menuItemId === "add-furigana") {
browser.tabs.sendMessage(tab.id, { type: "ANNOTATE_PAGE" });
} else if (info.menuItemId === "add-furigana-selection") {
browser.tabs.sendMessage(tab.id, { type: "ANNOTATE_SELECTION" });
}
});
}
browser.browserAction.onClicked.addListener((tab) => {
browser.tabs.sendMessage(tab.id, { type: "ANNOTATE_PAGE" });
});
browser.runtime.onMessage.addListener(msg => {
if (msg.type === "ANNOTATE_BATCH") {
return Promise.resolve(
msg.texts.map(text => annotator.annotate(text))
);
}
});