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