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