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