new module system
[conkeror.git] / modules / casual-spelling.js
blob9a3bacb8845ada4047d4641a5a807648096d67f4
1 /**
2  * (C) Copyright 2009 John J. Foerch
3  *
4  * Use, modification, and distribution are subject to the terms specified in the
5  * COPYING file.
6 **/
8 /**
9  * Contributors:
10  *   Kris Maglione
11  *   John J. Foerch
12  */
16 This module provides a pattern matcher for hints_text_match which lets you
17 type ascii characters in the hints minibuffer to match unicode characters
18 such as accented letters and ligatures.
22 in_module(null);
24 function casual_spelling_entry_features (entry) {
25     var ret = { ligatures: false, multiples: false };
26     if (typeof entry == "object") {
27         for each (var t in entry) {
28             if (typeof t == "string" && t.length > 1)
29                 ret.ligatures = true;
30             else if (typeof t == "object") {
31                 ret.multiples = true;
32                 if (t.some(function (x) (x.length > 1)))
33                     ret.ligatures = true;
34             }
35         }
36     }
37     return ret;
40 function casual_spelling_table (table) {
41     for (var k in table) {
42         var features = casual_spelling_entry_features(table[k]);
43         if (features.ligatures) table.ligatures = true;
44         if (features.multiples) table.multiples = true;
45     }
46     return table;
50 /**
51  * casual_spelling_from_range_table is a constructor of
52  * casual_spelling_table which generates the table from a shorthand form
53  * called a "range table".  A range table is an array where each element
54  * is an array of three elements: range-low, range-high, and range-spec.
55  * Low and high are integer codepoints of unicode characters to be
56  * translated.  The spec can be a string, or an array.  If it is a string,
57  * it should be a single character.  The designated range will have that
58  * character as its low-point translation, and each next character in the
59  * range will be incremented from that point.  This is how you can
60  * compactly denote alphabetic ranges, for example.  If the spec is an
61  * array, the strings in that array will be repeated over the designated
62  * range.  Multi-character translations, such as for ligatures, must be
63  * given in the array form of range-spec.
64  */
65 function casual_spelling_from_range_table (table) {
66     var ret = {};
67     table.map(function (a) {
68         var features = casual_spelling_entry_features(a[2]);
69         if (features.ligatures) ret.ligatures = true;
70         if (features.multiples) ret.multiples = true;
71         for (var c = a[0]; c <= a[1]; c++) {
72             var chr = String.fromCharCode(c);
73             if (typeof a[2] == "string")
74                 ret[chr] = String.fromCharCode(a[2].charCodeAt(0) + c - a[0]);
75             else
76                 ret[chr] = a[2][(c - a[0]) % a[2].length];
77         }
78     });
79     return ret;
83 function casual_spelling_translate (chr) {
84     return casual_spelling_tables[chr] || chr;
89  * Pattern Matchers
90  */
92 function casual_spelling_hints_text_match (text, pattern) {
93     if (pattern == "")
94         return [0, 0];
95     var plen = pattern.length;
96     for (var i = 0, tlen = text.length - plen; i <= tlen; i++) {
97         for (var j = 0;; j++) {
98             if (pattern[j] != text[i+j] &&
99                 pattern[j] != casual_spelling_translate(text[i+j]))
100                 break;
101             if (j == plen - 1)
102                 return [i, i+plen];
103         }
104     }
105     return false;
108 function casual_spelling_hints_text_match_ligatures (text, pattern) {
109     if (pattern == "")
110         return [0, 0];
111     var tlen = text.length;
112     var plen = pattern.length;
113     var decoded = Array.map(text, casual_spelling_translate);
114     for (var i = 0; i < tlen; i++) {
115         for (var e = 0, j = 0; i + e < tlen; e++) {
116             var elen = 1;
117             if (pattern[j] != text[i+e] &&
118                 pattern.substring(j, j+(elen = decoded[i+e].length)) != decoded[i+e])
119                 break;
120             j += elen;
121             if (j == plen)
122                 return [i, i+e+1];
123         }
124     }
125     return false;
128 function casual_spelling_hints_text_match_multiples (text, pattern) {
129     if (pattern == "")
130         return [0, 0];
131     var plen = pattern.length;
132     var decoded = Array.map(text, function (x) Array.concat(x, casual_spelling_translate(x)));
133     for (var i = 0, tlen = text.length - plen; i < tlen; i++) {
134         for (var j = 0; j < plen; j++) {
135             if (! decoded[i+j].some(function (x) x == pattern[j]))
136                 break;
137             if (j == plen - 1)
138                 return [i, i+plen];
139         }
140     }
141     return false;
144 function casual_spelling_hints_text_match_ligatures_multiples (text, pattern) {
145     if (pattern == "")
146         return [0, 0];
147     var tlen = text.length;
148     var plen = pattern.length;
149     var decoded = Array.map(text, function (x) Array.concat(x, casual_spelling_translate(x)));
150     var matched, mlen;
151     for (var i = 0; i < tlen; i++) {
152         for (var e = 0, j = 0; i + e < tlen; e++) {
153             if (! decoded[i+e].some(function (x) (pattern.substring(j, j+(mlen = x.length)) == (matched = x))))
154                 break;
155             j += mlen;
156             if (j == plen)
157                 return [i, i+e+1];
158         }
159     }
160     return false;
166  * Table Installation
167  */
169 var casual_spelling_tables;
171 function casual_spelling_table_add (table) {
172     table.__proto__ = casual_spelling_tables;
173     casual_spelling_tables = table;
174     if (casual_spelling_tables.ligatures && casual_spelling_tables.multiples)
175         hints_text_match = casual_spelling_hints_text_match_ligatures_multiples;
176     else if (casual_spelling_tables.ligatures)
177         hints_text_match = casual_spelling_hints_text_match_ligatures;
178     else if (casual_spelling_tables.multiples)
179         hints_text_match = casual_spelling_hints_text_match_multiples;
180     else
181         hints_text_match = casual_spelling_hints_text_match;
185 casual_spelling_table_add(casual_spelling_table({}));
189 var casual_spelling_accents = casual_spelling_from_range_table(//no special requirements for pattern matcher
190     [[0x00a9, 0x00a9, "C"],//copyright
191      [0x00c0, 0x00c5, ["A"]],
192      [0x00c7, 0x00c7, "C"],
193      [0x00c8, 0x00cb, ["E"]],
194      [0x00cc, 0x00cf, ["I"]],
195      [0x00d1, 0x00d1, "N"],
196      [0x00d2, 0x00d6, ["O"]],
197      [0x00d8, 0x00d8, "O"],
198      [0x00d9, 0x00dc, ["U"]],
199      [0x00dd, 0x00dd, "Y"],
200      [0x00e0, 0x00e5, ["a"]],
201      [0x00e7, 0x00e7, "c"],
202      [0x00e8, 0x00eb, ["e"]],
203      [0x00ec, 0x00ef, ["i"]],
204      [0x00f1, 0x00f1, "n"],
205      [0x00f2, 0x00f6, ["o"]],
206      [0x00f8, 0x00f8, "o"],
207      [0x00f9, 0x00fc, ["u"]],
208      [0x00fd, 0x00fd, "y"],
209      [0x00ff, 0x00ff, "y"],
210      [0x0100, 0x0105, ["A", "a"]],
211      [0x0106, 0x010d, ["C", "c"]],
212      [0x010e, 0x0111, ["D", "d"]],
213      [0x0112, 0x011b, ["E", "e"]],
214      [0x011c, 0x0123, ["G", "g"]],
215      [0x0124, 0x0127, ["H", "h"]],
216      [0x0128, 0x0130, ["I", "i"]],
217      [0x0134, 0x0135, ["J", "j"]],
218      [0x0136, 0x0136, ["K", "k"]],
219      [0x0139, 0x0142, ["L", "l"]],
220      [0x0143, 0x0148, ["N", "n"]],
221      [0x0149, 0x0149, "n"],
222      [0x014c, 0x0151, ["O", "o"]],
223      [0x0154, 0x0159, ["R", "r"]],
224      [0x015a, 0x0161, ["S", "s"]],
225      [0x0162, 0x0167, ["T", "t"]],
226      [0x0168, 0x0173, ["U", "u"]],
227      [0x0174, 0x0175, ["W", "w"]],
228      [0x0176, 0x0178, ["Y", "y", "Y"]],
229      [0x0179, 0x017e, ["Z", "z"]],
230      [0x0180, 0x0183, ["b", "B", "B", "b"]],
231      [0x0187, 0x0189, ["C", "c", "D"]],
232      [0x018a, 0x0192, ["D", "D", "d", "F", "f"]],
233      [0x0193, 0x0194, ["G"]],
234      [0x0197, 0x019b, ["I", "K", "k", "l", "l"]],
235      [0x019d, 0x01a1, ["N", "n", "O", "O", "o"]],
236      [0x01a4, 0x01a5, ["P", "p"]],
237      [0x01ab, 0x01ab, ["t"]],
238      [0x01ac, 0x01b0, ["T", "t", "T", "U", "u"]],
239      [0x01b2, 0x01d2, ["V", "Y", "y", "Z", "z", "D", "L",
240                        "N", "A", "a", "I", "i", "O", "o"]],
241      [0x01d3, 0x01dc, ["U", "u"]],
242      [0x01de, 0x01e1, ["A", "a"]],
243      [0x01e4, 0x01ed, ["G", "g", "G", "g", "K", "k", "O", "o", "O", "o"]],
244      [0x01f0, 0x01f5, ["j", "D", "G", "g"]],
245      [0x01fa, 0x01fb, ["A", "a"]],
246      [0x01fe, 0x0217, ["O", "o", "A", "a", "A", "a", "E", "e", "E",
247                        "e", "I", "i", "I", "i", "O", "o", "O", "o",
248                        "R", "r", "R", "r", "U", "u", "U", "u"]],
249      [0x0253, 0x0257, ["b", "c", "d", "d"]],
250      [0x0260, 0x0269, ["g", "h", "h", "i", "i"]],
251      [0x026b, 0x0273, ["l", "l", "l", "l", "m", "n", "n"]],
252      [0x027c, 0x028b, ["r", "r", "r", "r", "s", "t", "u", "u", "v"]],
253      [0x0290, 0x0291, ["z"]],
254      [0x029d, 0x02a0, ["j", "q"]],
255      [0x1e00, 0x1e09, ["A", "a", "B", "b", "B", "b", "B", "b", "C", "c"]],
256      [0x1e0a, 0x1e13, ["D", "d"]],
257      [0x1e14, 0x1e1d, ["E", "e"]],
258      [0x1e1e, 0x1e21, ["F", "f", "G", "g"]],
259      [0x1e22, 0x1e2b, ["H", "h"]],
260      [0x1e2c, 0x1e8f, ["I", "i", "I", "i", "K", "k", "K", "k", "K", "k",
261                        "L", "l", "L", "l", "L", "l", "L", "l", "M", "m",
262                        "M", "m", "M", "m", "N", "n", "N", "n", "N", "n",
263                        "N", "n", "O", "o", "O", "o", "O", "o", "O", "o",
264                        "P", "p", "P", "p", "R", "r", "R", "r", "R", "r",
265                        "R", "r", "S", "s", "S", "s", "S", "s", "S", "s",
266                        "S", "s", "T", "t", "T", "t", "T", "t", "T", "t",
267                        "U", "u", "U", "u", "U", "u", "U", "u", "U", "u",
268                        "V", "v", "V", "v", "W", "w", "W", "w", "W", "w",
269                        "W", "w", "W", "w", "X", "x", "X", "x", "Y", "y"]],
270      [0x1e90, 0x1e9a, ["Z", "z", "Z", "z", "Z", "z", "h", "t", "w", "y", "a"]],
271      [0x1ea0, 0x1eb7, ["A", "a"]],
272      [0x1eb8, 0x1ec7, ["E", "e"]],
273      [0x1ec8, 0x1ecb, ["I", "i"]],
274      [0x1ecc, 0x1ee3, ["O", "o"]],
275      [0x1ee4, 0x1ef1, ["U", "u"]],
276      [0x1ef2, 0x1ef9, ["Y", "y"]],
277      [0x2071, 0x2071, "i"],
278      [0x207f, 0x207f, "n"],
279      [0x249c, 0x24b5, "a"],
280      [0x24b6, 0x24cf, "A"],
281      [0x24d0, 0x24e9, "a"],
282      [0xff21, 0xff3a, "A"],
283      [0xff41, 0xff5a, "a"]]);
285 var casual_spelling_ligatures = casual_spelling_from_range_table(
286     [[0x00c6, 0x00c6, ["AE"]],
287      [0x00df, 0x00df, ["ss"]],
288      [0x00e6, 0x00e6, ["ae"]],
289      [0x0132, 0x0132, ["IJ"]],
290      [0x0133, 0x0133, ["ij"]],
291      [0x0152, 0x0152, ["OE"]],
292      [0x0153, 0x0153, ["oe"]],
293      [0x01e2, 0x01e2, ["AE"]],
294      [0x01e3, 0x01e3, ["ae"]],
295      [0x01fc, 0x01fc, ["AE"]],
296      [0x01fd, 0x01fd, ["ae"]],
297      [0xfb00, 0xfb00, ["ff"]],
298      [0xfb01, 0xfb01, ["fi"]],
299      [0xfb02, 0xfb02, ["fl"]],
300      [0xfb03, 0xfb03, ["ffi"]],
301      [0xfb04, 0xfb04, ["ffl"]],
302      [0xfb05, 0xfb05, ["st"]],
303      [0xfb06, 0xfb06, ["st"]]]);
305 casual_spelling_table_add(casual_spelling_accents);
306 casual_spelling_table_add(casual_spelling_ligatures);
308 provide("casual-spelling");