1 /**
  2  * 
  3  * All content on this website (including text, images, source
  4  * code and any other original works), unless otherwise noted,
  5  * is licensed under a Creative Commons License.
  6  * 
  7  * http://creativecommons.org/licenses/by-nc-sa/2.5/
  8  * 
  9  * Copyright (C) Open-Xchange Inc., 2006-2011
 10  * Mail: info@open-xchange.com 
 11  * 
 12  * @author Viktor Pracht <viktor.pracht@open-xchange.com>
 13  * 
 14  */
 15 
 16 var loadPlugins, registerPlugin,
 17     isPluginLoaded;
 18 
 19 (function () {
 20     
 21     var modules = null;
 22     
 23     var getModules = function () {
 24         
 25         // not first time?
 26         if (modules !== null) {
 27             return;
 28         }
 29         
 30         modules = ox.api.config.get("availableModules", []);
 31         
 32         // toolbar?
 33         if (configGetKey("ui.global.toolbar.mode.value") === "simple") {
 34             modules.push("com.openexchange.toolbar");
 35         }
 36         
 37         // turn on/off plugins via url parameter
 38         if (url && url.plugins !== undefined) {
 39             // loop over comma separated list
 40             var i = 0, plugins = url.plugins.split(/,/), $i = plugins.length, plugin, pos;
 41             for (; i < $i; i++) {
 42                 plugin = plugins[i];
 43                 // on/off?
 44                 if (plugin.indexOf("-") === 0) {
 45                     pos = jQuery.inArray(plugin.substr(1), modules);
 46                     // turn off
 47                     if (pos > -1) {
 48                         modules.splice(pos, 1);
 49                     }
 50                 } else {
 51                     pos = jQuery.inArray(plugin, modules);
 52                     if (pos === -1) {
 53                         modules.push(plugin);
 54                     }
 55                 }
 56             }
 57         }
 58     };
 59 
 60     var loadCorePlugins = function (join) {
 61         var file = urlify("plugins/register_concat.jsz");
 62         jQuery.ajax({
 63             url: file,
 64             dataType: "script",
 65             success: join.add(),
 66             cache: url.dev !== "plugin"
 67         });
 68         loadCorePlugins = jQuery.noop;
 69     };
 70     
 71     var isCore = function (id) {
 72         return internalPlugins[id] !== undefined;
 73     };
 74     
 75     loadPlugins = function (cont) {
 76         
 77         getModules();
 78         
 79         var join = new Join(function() {
 80             temporary.services.finish();
 81             if (cont) { cont(); }
 82         });
 83         
 84         var lock = join.add();
 85         
 86         // loop
 87         var i = 0, $i = modules.length, name;
 88         for (; i < $i; i++) {
 89             name = modules[i];
 90             // core plugin?
 91             if (isCore(name) && debugPlugins === false) {
 92                 loadCorePlugins(join);
 93             } else {
 94                 // load external plugins
 95                 loadModule(name, join);
 96             }
 97         }
 98         lock();
 99     };
100     
101     registerPlugin = function (id, fn) {
102         // get modules
103         getModules();
104         // in that list?
105         if (jQuery.inArray(id, modules) > -1) {
106             // wrapper
107             var path = oxProductInfo.build + "/plugins/" + id,
108                 domain = isCore(id) ? "" : id,
109                 gt = function (text) { return dpgettext(domain, "", text); },
110                 pgt = function (context, text) { return dpgettext(domain, context, text); },
111                 ngt = function (singular, plural, n) { return dnpgettext(domain, "", singular, plural, n); },
112                 npgt = function (context, singular, plural, n) { return dnpgettext(domain, context, singular, plural, n); };
113             // call ("_", "gettext", "pgettext", "ngettext", "npgettext", "NAME", "PATH", "JSON")
114             fn(gt, gt, pgt, ngt, npgt, id, path, JSONX);
115         }
116     };
117     
118     isPluginLoaded = function (id) {
119         return loadedModules[id] === true || (url.plugins || "").indexOf(id) > -1;
120     };
121     
122     var TRUE = function () {
123         return true;
124     };
125     
126     defineDeferredFunction = function (namespace, name, pluginId) {
127         // create new function
128         if (namespace && name) {
129             var fn = namespace[name] = function () {
130                 if (fn.loading === false) {
131                     fn.loading = true;
132                     // get code
133                     var path = "plugins/" + pluginId;
134                     jQuery.ajax({
135                         dataType: "text",
136                         url: urlify(path + "/code.js"),
137                         cache: ox.util.getHash("dev") !== "plugin"
138                     }).done(function (js) {
139                         loadI18n(js, pluginId, pluginId, path, function () {
140                             namespace[name].available = TRUE;
141                             namespace[name](); // not fn here!
142                             fn = null;
143                         });
144                     });
145                 }
146             };
147             // init loading flag
148             fn.loading = false;
149             // available?
150             fn.available = function () {
151                 // check for plugin
152                 return isPluginLoaded(pluginId);
153             };
154         }
155     };
156     
157 }());
158 
159 
160 
161   //////////////////////////////////////////
162  //   Global names defined for plugins   //
163 //////////////////////////////////////////
164 
165 /**
166  * Translates a string.
167  * @function
168  * @name _
169  * @param {String} text The original English text to translate.
170  * @type I18nString
171  * @return The translated text.
172  */
173 
174 /**
175  * Translates a string.
176  * @function
177  * @name gettext
178  * @param {String} text The original English text to translate.
179  * @type I18nString
180  * @return The translated text.
181  */
182 
183 /**
184  * Translates a string with context
185  * @function
186  * @name pgettext
187  * @param {String} context A context to differentiate multiple identical texts
188  * with different translations.
189  * @param {String} text The original English text to translate.
190  * @type I18nString
191  * @return The translated text.
192  */
193 
194 /**
195  * Translates a string containing numbers.
196  * @function
197  * @name ngettext
198  * @param {String} singular The original English text for the singular form.
199  * @param {String} plural The original English text for the plural form.
200  * @param {Number} n The number which determines which text form is used.
201  * @type I18nString
202  * @return The translated text.
203  */
204 
205 /**
206  * Translates a string containing numbers with context.
207  * @function
208  * @name npgettext
209  * @param {String} context A context to differentiate multiple identical texts
210  * with different translations.
211  * @param {String} singular The original English text for the singular form.
212  * @param {String} plural The original English text for the plural form.
213  * @param {Number} n The number which determines which text form is used.
214  * @type I18nString
215  * @return The translated text.
216  */
217 
218 /**
219  * Converts a string to I18nString without translation.
220  * @function
221  * @name noI18n
222  * @param {String} text The text to convert.
223  * @type I18nString
224  * @return The untranslated text as I18nString.
225  */
226 
227 //////////////////////////////////////////
228 
229 /**
230  * Loads a module at runtime.
231  * Loading a module which is already (being) loaded has no effect. 
232  * The register.js file from the module's directory is converted to the body of
233  * a function and that function is called.
234  * The file is downloaded asynchronously.
235  * @param {String} name The name of the module to load.
236  * @param {Join} join An optional Join object which is locked until the module
237  * finishes loading.
238  */
239 
240 function loadModule(name, join, noI18n) {
241     if (name in loadedModules) return;
242     loadedModules[name] = true;
243     if (!join) join = new Join(emptyFunction);
244     // inject version
245     var nocache = ox.util.getHash("dev") === "plugin" ? "?" + (new Date().getTime()) : "";
246     var path = oxProductInfo.build + "/plugins/" + name;
247     var url = path + "/register.js" + nocache;
248     var context = internalPlugins[name] || noI18n === true ? "" : name;
249     // get register.js
250     (new JSONX()).get(url, null,
251         join.add(function(js) {
252             if (internalPlugins[name] === undefined && noI18n !== true) {
253                 bindtextdomain(context, "plugins/" + name + "/lang/%s.po");
254             }
255             loadI18n(js, context, name, path, join.add());
256         }),
257         join.alt(function(result, status) {
258             delete loadedModules[name];
259             return status == 404;
260         }), true);
261 };
262 
263 /**
264  * Evaluates a JavaScript program using the specified i18n text domain.
265  * The domain must have been bound using bindtextdomain().
266  * The JavScript program is converted to the body of a function and
267  * that function is called.
268  * @param {String} js The javaScript program to evaluate.
269  * @param {String} name The i18n text domain as used by bindtextdomain().
270  * This value can also be accessed in the evaluated program as NAME.
271  * @param {String} path The path to the file's directory.
272  * This value can be accessed in the evaluated program as PATH.
273  * @param {Function} cb A callback function which is called with the return
274  * value of the evaluated program.
275  * @return The return value of the evaluated program.
276  */
277 function loadI18n(js, domain, name, path, cb) {
278     if (debugPlugins) {
279         loadI18n.params[++loadI18n.index] =
280             [gt, gt, pgt, ngt, npgt, name, path, JSONX, cb];
281         var src =["(function(_,gettext,pgettext,ngettext,npgettext,NAME,PATH,",
282                   "JSON,$done){delete loadI18n.params[",
283                   loadI18n.index, "];try{var $retval=(function(){", js,
284                   "})()}finally{$done($retval);}})("];
285         var params = [];
286         var prefix = "loadI18n.params[" + loadI18n.index + "][";
287         for (var i = 0; i < loadI18n.params[loadI18n.index].length; i++) {
288             params.push(prefix + i + "]");
289         }
290         src.push(params.join());
291         src.push(");");
292         document.getElementsByTagName("head")[0].appendChild(
293             newnode("script", 0, {
294                 type: "text/javascript",
295                 src: "data:text/javascript;charset=utf-8," +
296                     encodeURIComponent(src.join(""))
297             }));
298     } else {
299         cb((Function("_", "gettext", "pgettext", "ngettext", "npgettext",
300             "NAME", "PATH", "JSON", js))
301             (gt, gt, pgt, ngt, npgt, name, path, JSONX));
302     }
303     function gt(text) { return dpgettext(domain, "", text); }
304     function pgt(context, text) {
305         return dpgettext(domain, context, text);
306     }
307     function ngt(singular, plural, n) {
308         return dnpgettext(domain, "", singular, plural, n);
309     }
310     function npgt(context, singular, plural, n) {
311         return dnpgettext(domain, context, singular, plural, n);
312     }
313 }
314 
315 loadI18n.params = {};
316 loadI18n.index = 0;
317 
318 var internalPlugins = {
319     "com.openexchange.settings.folder": true,
320     "com.openexchange.toolbar": true,
321     "com.openexchange.group": true,
322     "com.openexchange.mail.filter": true,
323     "com.openexchange.oxupdater": true,
324     "com.openexchange.publish": true,
325     "com.openexchange.resource": true,
326     "com.openexchange.secret.recovery": true,
327     "com.openexchange.subscribe": true,
328     "com.openexchange.user.passwordchange": true,
329     "com.openexchange.user.personaldata": true,
330     "com.openexchange.threadview": true,
331     "com.openexchange.upsell.demo": true,
332     "uwaWidgets": true
333 };
334 
335 // TODO: fill during initialization of modules.
336 var loadedModules = {
337     calendar: true,
338     contacts: true,
339     folder: true,
340     infostore: true,
341     interfaces: true,
342     mail: true,
343     mailaccount: true,
344     messaging: true,
345     portal: true,
346     tasks: true,
347     themes: true,
348     folderstorage: true
349 };
350