From be7598a08791a22b2d1164d48a82b9835ceafab8 Mon Sep 17 00:00:00 2001 From: Simon Eisenmann Date: Fri, 8 Aug 2014 15:07:58 +0200 Subject: [PATCH] Implemented deferred based initialization to allow async operations to complete before config. --- static/js/app.js | 209 +++++++++++++++++++++++++++------------------- static/js/main.js | 64 +++++++++++--- 2 files changed, 175 insertions(+), 98 deletions(-) diff --git a/static/js/app.js b/static/js/app.js index 62e7b9d2..a5ecda87 100644 --- a/static/js/app.js +++ b/static/js/app.js @@ -61,7 +61,53 @@ define([ })(window.location.search.substr(1).split("&")); }()); - var initialize = function(ms) { + // Base application config shared during initialization. + var appConfig = {}; + + // Implement translation store. + var TranslationData = function() { + // Create data structure. + this.data = { + locale_data: {} + }; + }; + TranslationData.prototype.add = function(domain, data) { + var src; + if (data && data.locale_data) { + src = data.locale_data[domain]; + } + var dst = this.data.locale_data[domain]; + if (!dst) { + dst = this.data.locale_data[domain] = { + "": { + "domain": domain, + "plural_forms": "nplurals=2; plural=(n != 1);" + } + } + _.extend(dst, src); + } + }; + TranslationData.prototype.load = function(domain, url) { + var that = this; + return $.ajax({ + dataType: "json", + url: url, + success: function(data) { + //console.log("loaded translation data", data); + that.add(domain, data); + }, + error: function(err, textStatus, errorThrown) { + console.warn("Failed to load translation data " + url + ": " + errorThrown); + that.add(domain, null); + } + }); + }; + TranslationData.prototype.get = function() { + return this.data; + }; + var translationData = new TranslationData(); + + var create = function(ms) { var modules = ['ui.bootstrap', 'ngSanitize', 'ngAnimate', 'ngHumanize', 'ngRoute']; if (ms && ms.length) { @@ -99,92 +145,16 @@ define([ app.constant("availableLanguages", languages); - angular.element(document).ready(function() { - - var globalContext = JSON.parse($("#globalcontext").text()); - app.constant("globalContext", globalContext); - - // Configure language. - var lang = (function() { - var lang = "en"; - var wanted = []; - var html = document.getElementsByTagName("html")[0]; - // Get from storage. - if (modernizr.localstorage) { - var lsl = localStorage.getItem("mediastream-language"); - if (lsl && lsl !== "undefined") { - wanted.push(lsl); - } - } - // Get from query. - var qsl = urlQuery.lang; - if (qsl) { - wanted.push(qsl); - } - // Expand browser languages with combined fallback. - _.each(globalContext.Languages, function(l) { - wanted.push(l); - if (l.indexOf("-") != -1) { - wanted.push(l.split("-")[0]); - } - }); - // Loop through browser languages and use first one we got. - for (var i = 0; i < wanted.length; i++) { - if (languages.hasOwnProperty(wanted[i])) { - lang = wanted[i]; - break; - } - } - html.setAttribute("lang", lang); - return lang; - }()); + app.provider("translationData", function translationDataProvider() { - // Prepare bootstrap function with injected locale data. - var domain = "messages"; - var catalog = domain + "-" + lang; - var bootstrap = function(translationData) { - if (!translationData) { - // Fallback catalog in case translation could not be loaded. - lang = "en"; - translationData = {}; - translationData.locale_data = {}; - translationData.domain = domain; - translationData.locale_data[domain] = { - "": { - "domain": domain, - "lang": lang, - "plural_forms": "nplurals=2; plural=(n != 1);" - } - }; - } - // Set date language too. - moment.lang([lang, "en"]); - // Inject translation data globally. - app.constant("translationData", translationData); - // Bootstrap AngularJS app. - console.log("Bootstrapping ..."); - angular.bootstrap(document, ['app']); - }; - - if (lang !== "en") { - // Load translation file. - //console.log("Loading translation data: " + lang); - $.ajax({ - dataType: "json", - url: require.toUrl('translation/' + catalog + '.json'), - success: function(data) { - //console.log("Loaded translation data."); - bootstrap(data); - }, - error: function(err, textStatus, errorThrown) { - console.warn("Failed to load translation data " + catalog + ": " + errorThrown); - bootstrap(null); - } - }); - } else { - // No need to load english as this is built in. - _.defer(bootstrap); - } + // Make available functions for config phase. + this.add = _.bind(translationData.add, translationData); + this.load = _.bind(translationData.load, translationData); + + // Out creater returns raw data. + this.$get = [function translationDataFactory() { + return translationData.get(); + }]; }); @@ -192,8 +162,73 @@ define([ }; + var initialize = function(app) { + + var deferred = $.Deferred(); + + var globalContext = JSON.parse($("#globalcontext").text()); + app.constant("globalContext", globalContext); + + // Configure language. + var lang = (function() { + var lang = "en"; + var wanted = []; + var html = document.getElementsByTagName("html")[0]; + // Get from storage. + if (modernizr.localstorage) { + var lsl = localStorage.getItem("mediastream-language"); + if (lsl && lsl !== "undefined") { + wanted.push(lsl); + } + } + // Get from query. + var qsl = urlQuery.lang; + if (qsl) { + wanted.push(qsl); + } + // Expand browser languages with combined fallback. + _.each(globalContext.Languages, function(l) { + wanted.push(l); + if (l.indexOf("-") != -1) { + wanted.push(l.split("-")[0]); + } + }); + // Loop through browser languages and use first one we got. + for (var i = 0; i < wanted.length; i++) { + if (languages.hasOwnProperty(wanted[i])) { + lang = wanted[i]; + break; + } + } + html.setAttribute("lang", lang); + return lang; + }()); + + // Inject language to config. + appConfig.lang = lang; + + if (lang === "en") { + // No need to load english as this is built in. + deferred.resolve(); + } else { + // Load default translation catalog. + var domain = "messages"; + var url = require.toUrl('translation/' + domain + "-" + lang + '.json'); + $.when(translationData.load(domain, url)).always(function() { + deferred.resolve(); + }); + } + + return deferred.promise(); + + }; + return { - initialize: initialize + create: create, + initialize: initialize, + query: urlQuery, + config: appConfig, + translationData: translationData }; }); diff --git a/static/js/main.js b/static/js/main.js index 25f32a1c..79e7f879 100644 --- a/static/js/main.js +++ b/static/js/main.js @@ -193,8 +193,17 @@ if (Object.create) { load.push(dataPlugin); } }); + require(load, function(App) { + + // All other arguments are plugins. var args = Array.prototype.slice.call(arguments, 1); + + // Prepare our promised based initialization. + var promises = []; + var loading = $.Deferred(); + promises.push(loading.promise()); + // Add Angular modules from plugins. var modules = []; _.each(args, function(plugin) { @@ -202,26 +211,59 @@ if (Object.create) { plugin.module(modules); } }); + // External plugin support. - var externalPlugin + var externalPlugin; if (window.externalPlugin) { externalPlugin = window.externalPlugin($, _, angular); if (externalPlugin && externalPlugin.module) { externalPlugin.module(modules); } } - // Init Angular app. - var app = App.initialize(modules); - // Init plugins. - _.each(args, function(plugin) { - if (plugin && plugin.initialize) { - plugin.initialize(app); + + // Create Angular app. + var app = App.create(modules); + + // Helper function to initialize with deferreds. + var initialize = function(obj) { + if (obj && obj.initialize) { + var result = obj.initialize(app); + if (result && result.done) { + // If we got a promise add it to our wait queue. + promises.push(result); + } + } + }; + + // Wait until dom is ready before we initialize. + angular.element(document).ready(function() { + + // Init base application. + initialize(App); + + // Init plugins. + _.each(args, function(plugin) { + initialize(plugin); + }); + + // Init external plugin. + if (externalPlugin) { + initialize(externalPlugin); } + + // Resolve the base loader. + loading.resolve(); + + // Wait for all others to complete and then boostrap the app. + $.when.apply($, promises).done(function() { + console.log("Bootstrapping ..."); + angular.bootstrap(document, ['app'], { + strictDi: true + }); + }); + }); - // Init external plugin. - if (externalPlugin && externalPlugin.initialize) { - externalPlugin.initialize(app); - } + }); });