]> git.donarmstrong.com Git - dactyl.git/blob - common/modules/messages.jsm
New upstream version 1.0+hg6948
[dactyl.git] / common / modules / messages.jsm
1 // Copyright (c) 2011-2012 Kris Maglione <maglione.k@gmail.com>
2 //
3 // This work is licensed for reuse under an MIT license. Details are
4 // given in the LICENSE.txt file included with this file.
5 "use strict";
6
7 defineModule("messages", {
8     exports: ["Messages", "messages", "_"],
9     require: ["services", "util"]
10 });
11
12 var Messages = Module("messages", {
13
14     init: function init(name) {
15         let self = this;
16         this.name = name || "messages";
17
18         this._ = Class("_", String, {
19             init: function _(message) {
20                 this.args = arguments;
21             },
22             instance: {},
23             message: Class.Memoize(function () {
24                 let message = this.args[0];
25
26                 if (this.args.length > 1) {
27                     let args = Array.slice(this.args, 1);
28                     return self.format(message + "-" + args.length, args, null) || self.format(message, args);
29                 }
30                 return self.get(message);
31             }),
32             valueOf: function valueOf() this.message,
33             toString: function toString() this.message
34         });
35     },
36
37     cleanup: function cleanup() {
38         services.stringBundle.flushBundles();
39     },
40
41     bundles: Class.Memoize(function ()
42         array.uniq([JSMLoader.getTarget("dactyl://locale/" + this.name + ".properties"),
43                     JSMLoader.getTarget("dactyl://locale-local/" + this.name + ".properties"),
44                     "resource://dactyl-locale/en-US/" + this.name + ".properties",
45                     "resource://dactyl-locale-local/en-US/" + this.name + ".properties"],
46                    true)
47              .map(services.stringBundle.createBundle)
48              .filter(function (bundle) { try { bundle.getSimpleEnumeration(); return true; } catch (e) { return false; } })),
49
50     iterate: function () {
51         let seen = {};
52         for (let bundle in values(this.bundles))
53             for (let { key, value } in iter(bundle.getSimpleEnumeration(), Ci.nsIPropertyElement))
54                 if (!Set.add(seen, key))
55                     yield [key, value];
56     },
57
58     get: function get(value, default_) {
59         for (let bundle in values(this.bundles))
60             try {
61                 let res = bundle.GetStringFromName(value);
62                 if (res.slice(0, 2) == "+ ")
63                     return res.slice(2).replace(/\s+/g, " ");
64                 return res;
65             }
66             catch (e) {}
67
68         // Report error so tests fail, but don't throw
69         if (arguments.length < 2) // Do *not* localize these strings
70             util.reportError(Error("Invalid locale string: " + value));
71         return arguments.length > 1 ? default_ : value;
72     },
73
74     format: function format(value, args, default_) {
75         for (let bundle in values(this.bundles))
76             try {
77                 let res = bundle.formatStringFromName(value, args, args.length);
78                 if (res.slice(0, 2) == "+ ")
79                     return res.slice(2).replace(/\s+/g, " ");
80                 return res;
81             }
82             catch (e) {}
83
84         // Report error so tests fail, but don't throw
85         if (arguments.length < 3) // Do *not* localize these strings
86             util.reportError(Error("Invalid locale string: " + value));
87         return arguments.length > 2 ? default_ : value;
88     },
89
90     /**
91      * Exports known localizable strings to a properties file.
92      *
93      * @param {string|nsIFile} {file} The file to which to export
94      *      the strings.
95      */
96     export: function export_(file) {
97         let { Buffer, commands, hints, io, mappings, modes, options, sanitizer } = overlay.activeModules;
98         file = io.File(file);
99
100         function properties(base, iter_, prop) iter(function _properties() {
101             function key(...args) [base, obj.identifier || obj.name].concat(args).join(".").replace(/[\\:=]/g, "\\$&");
102
103             prop = prop || "description";
104             for (var obj in iter_) {
105                 if (!obj.hive || obj.hive.name !== "user") {
106                     yield key(prop) + " = " + obj[prop];
107
108                     if (iter_.values)
109                         for (let [k, v] in isArray(obj.values) ? array.iterValues(obj.values) : iter(obj.values))
110                             yield key("values", k) + " = " + v;
111
112                     for (let opt in values(obj.options))
113                         yield key("options", opt.names[0]) + " = " + opt.description;
114
115                     if (obj.deprecated)
116                         yield key("deprecated") + " = " + obj.deprecated;
117                 }
118             }
119         }()).toArray();
120
121         file.write(
122             array(commands.allHives.map(function (h) properties("command", h)))
123                           .concat(modes.all.map(function (m)
124                               properties("map", values(mappings.builtin.getStack(m)
125                                                                .filter(function (map) map.modes[0] == m)))))
126                           .concat(properties("mode", values(modes.all.filter(function (m) !m.hidden))))
127                           .concat(properties("option", options))
128                           .concat(properties("hintmode", values(hints.modes), "prompt"))
129                           .concat(properties("pageinfo", values(Buffer.pageInfo), "title"))
130                           .concat(properties("sanitizeitem", values(sanitizer.itemMap)))
131                 .flatten().uniq().join("\n"));
132     }
133 }, {
134     Localized: Class("Localized", Class.Property, {
135         init: function init(prop, obj) {
136             let _prop = "unlocalized_" + prop;
137             if (this.initialized) {
138                 /*
139                 if (config.locale === "en-US")
140                     return { configurable: true, enumerable: true, value: this.default, writable: true };
141                 */
142
143                 if (!Set.has(obj, "localizedProperties"))
144                     obj.localizedProperties = { __proto__: obj.localizedProperties };
145                 obj.localizedProperties[prop] = true;
146
147                 obj[_prop] = this.default;
148                 return {
149                     get: function get() {
150                         let value = this[_prop];
151
152                         function getter(key, default_) function getter() messages.get([name, key].join("."), default_);
153
154                         if (value != null) {
155                             var name = [this.constructor.className.toLowerCase(),
156                                         this.identifier || this.name,
157                                         prop].join(".");
158
159                             if (!isObject(value))
160                                 value = messages.get(name, value);
161                             else if (isArray(value))
162                                 // Deprecated
163                                 iter(value).forEach(function ([k, v]) {
164                                     if (isArray(v))
165                                         memoize(v, 1, getter(v[0], v[1]));
166                                     else
167                                         memoize(value, k, getter(k, v));
168                                 });
169                             else
170                                 iter(value).forEach(function ([k, v]) {
171                                     memoize(value, k, function () messages.get([name, k].join("."), v));
172                                 });
173                         }
174
175                         return Class.replaceProperty(this, prop, value);
176                     },
177
178                     set: function set(val) this[_prop] = val
179                 };
180             }
181             this.default = prop;
182             this.initialized = true;
183         }
184     })
185 }, {
186     javascript: function initJavascript(dactyl, modules, window) {
187         let { JavaScript } = modules;
188
189         JavaScript.setCompleter([this._, this.get, this.format], [
190             function (context) messages.iterate()
191         ]);
192
193         JavaScript.setCompleter([this.export],
194             [function (context, obj, args) {
195                 context.quote[2] = "";
196                 modules.completion.file(context, true);
197             }]);
198     }
199 });
200
201 var { _ } = messages;
202
203 endModule();
204
205 // catch(e){ if (!e.stack) e = Error(e); dump(e.fileName+":"+e.lineNumber+": "+e+"\n" + e.stack); }
206
207 // vim: set fdm=marker sw=4 sts=4 ts=8 et ft=javascript: