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