Use promises at startup
authorJack Allnutt <jack@allnutt.eu>
Sat, 19 Jul 2014 11:50:27 +0000 (12:50 +0100)
committerJack Allnutt <jack@allnutt.eu>
Thu, 21 Aug 2014 10:51:08 +0000 (11:51 +0100)
Refactored the js code in index.html.tmpl and app.js to use promises
instead of the custom JobManager object.

client/assets/libs/promise.min.js [new file with mode: 0644]
client/src/app.js
client/src/index.html.tmpl

diff --git a/client/assets/libs/promise.min.js b/client/assets/libs/promise.min.js
new file mode 100644 (file)
index 0000000..a6ed593
--- /dev/null
@@ -0,0 +1,5 @@
+/*
+    (c) Yehuda Katz, Tom Dale, Stefan Penner and contributors
+    License: https://github.com/jakearchibald/es6-promise/blob/1.0.0/LICENSE
+*/
+!function(){var a,b,c,d;!function(){var e={},f={};a=function(a,b,c){e[a]={deps:b,callback:c}},d=c=b=function(a){function c(b){if("."!==b.charAt(0))return b;for(var c=b.split("/"),d=a.split("/").slice(0,-1),e=0,f=c.length;f>e;e++){var g=c[e];if(".."===g)d.pop();else{if("."===g)continue;d.push(g)}}return d.join("/")}if(d._eak_seen=e,f[a])return f[a];if(f[a]={},!e[a])throw new Error("Could not find module "+a);for(var g,h=e[a],i=h.deps,j=h.callback,k=[],l=0,m=i.length;m>l;l++)"exports"===i[l]?k.push(g={}):k.push(b(c(i[l])));var n=j.apply(this,k);return f[a]=g||n}}(),a("promise/all",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to all.");return new b(function(b,c){function d(a){return function(b){f(a,b)}}function f(a,c){h[a]=c,0===--i&&b(h)}var g,h=[],i=a.length;0===i&&b([]);for(var j=0;j<a.length;j++)g=a[j],g&&e(g.then)?g.then(d(j),c):f(j,g)})}var d=a.isArray,e=a.isFunction;b.all=c}),a("promise/asap",["exports"],function(a){"use strict";function b(){return function(){process.nextTick(e)}}function c(){var a=0,b=new i(e),c=document.createTextNode("");return b.observe(c,{characterData:!0}),function(){c.data=a=++a%2}}function d(){return function(){j.setTimeout(e,1)}}function e(){for(var a=0;a<k.length;a++){var b=k[a],c=b[0],d=b[1];c(d)}k=[]}function f(a,b){var c=k.push([a,b]);1===c&&g()}var g,h="undefined"!=typeof window?window:{},i=h.MutationObserver||h.WebKitMutationObserver,j="undefined"!=typeof global?global:void 0===this?window:this,k=[];g="undefined"!=typeof process&&"[object process]"==={}.toString.call(process)?b():i?c():d(),a.asap=f}),a("promise/config",["exports"],function(a){"use strict";function b(a,b){return 2!==arguments.length?c[a]:(c[a]=b,void 0)}var c={instrument:!1};a.config=c,a.configure=b}),a("promise/polyfill",["./promise","./utils","exports"],function(a,b,c){"use strict";function d(){var a;a="undefined"!=typeof global?global:"undefined"!=typeof window&&window.document?window:self;var b="Promise"in a&&"resolve"in a.Promise&&"reject"in a.Promise&&"all"in a.Promise&&"race"in a.Promise&&function(){var b;return new a.Promise(function(a){b=a}),f(b)}();b||(a.Promise=e)}var e=a.Promise,f=b.isFunction;c.polyfill=d}),a("promise/promise",["./config","./utils","./all","./race","./resolve","./reject","./asap","exports"],function(a,b,c,d,e,f,g,h){"use strict";function i(a){if(!v(a))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof i))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._subscribers=[],j(a,this)}function j(a,b){function c(a){o(b,a)}function d(a){q(b,a)}try{a(c,d)}catch(e){d(e)}}function k(a,b,c,d){var e,f,g,h,i=v(c);if(i)try{e=c(d),g=!0}catch(j){h=!0,f=j}else e=d,g=!0;n(b,e)||(i&&g?o(b,e):h?q(b,f):a===D?o(b,e):a===E&&q(b,e))}function l(a,b,c,d){var e=a._subscribers,f=e.length;e[f]=b,e[f+D]=c,e[f+E]=d}function m(a,b){for(var c,d,e=a._subscribers,f=a._detail,g=0;g<e.length;g+=3)c=e[g],d=e[g+b],k(b,c,d,f);a._subscribers=null}function n(a,b){var c,d=null;try{if(a===b)throw new TypeError("A promises callback cannot return that same promise.");if(u(b)&&(d=b.then,v(d)))return d.call(b,function(d){return c?!0:(c=!0,b!==d?o(a,d):p(a,d),void 0)},function(b){return c?!0:(c=!0,q(a,b),void 0)}),!0}catch(e){return c?!0:(q(a,e),!0)}return!1}function o(a,b){a===b?p(a,b):n(a,b)||p(a,b)}function p(a,b){a._state===B&&(a._state=C,a._detail=b,t.async(r,a))}function q(a,b){a._state===B&&(a._state=C,a._detail=b,t.async(s,a))}function r(a){m(a,a._state=D)}function s(a){m(a,a._state=E)}var t=a.config,u=(a.configure,b.objectOrFunction),v=b.isFunction,w=(b.now,c.all),x=d.race,y=e.resolve,z=f.reject,A=g.asap;t.async=A;var B=void 0,C=0,D=1,E=2;i.prototype={constructor:i,_state:void 0,_detail:void 0,_subscribers:void 0,then:function(a,b){var c=this,d=new this.constructor(function(){});if(this._state){var e=arguments;t.async(function(){k(c._state,d,e[c._state-1],c._detail)})}else l(this,d,a,b);return d},"catch":function(a){return this.then(null,a)}},i.all=w,i.race=x,i.resolve=y,i.reject=z,h.Promise=i}),a("promise/race",["./utils","exports"],function(a,b){"use strict";function c(a){var b=this;if(!d(a))throw new TypeError("You must pass an array to race.");return new b(function(b,c){for(var d,e=0;e<a.length;e++)d=a[e],d&&"function"==typeof d.then?d.then(b,c):b(d)})}var d=a.isArray;b.race=c}),a("promise/reject",["exports"],function(a){"use strict";function b(a){var b=this;return new b(function(b,c){c(a)})}a.reject=b}),a("promise/resolve",["exports"],function(a){"use strict";function b(a){if(a&&"object"==typeof a&&a.constructor===this)return a;var b=this;return new b(function(b){b(a)})}a.resolve=b}),a("promise/utils",["exports"],function(a){"use strict";function b(a){return c(a)||"object"==typeof a&&null!==a}function c(a){return"function"==typeof a}function d(a){return"[object Array]"===Object.prototype.toString.call(a)}var e=Date.now||function(){return(new Date).getTime()};a.objectOrFunction=b,a.isFunction=c,a.isArray=d,a.now=e}),b("promise/polyfill").polyfill()}();
\ No newline at end of file
index e7d643d626c28f6ac4e901e4d4070a9ce52ddf09..2c740aecbf32fa6c8ae4cec430da62f41d8c4ac2 100644 (file)
@@ -184,13 +184,41 @@ _kiwi.global = {
 \r
     // Entry point to start the kiwi application\r
     init: function (opts, callback) {\r
-        var jobs, locale, localeLoaded, textThemeLoaded, text_theme;\r
+        var localePromise, themePromise,\r
+            that = this;\r
         opts = opts || {};\r
 \r
         this.initUtils();\r
 \r
-        jobs = new JobManager();\r
-        jobs.onFinish(function(locale, s, xhr) {\r
+        // Set up the settings datastore\r
+        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');\r
+        _kiwi.global.settings.load();\r
+\r
+        // Set the window title\r
+        window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';\r
+\r
+        localePromise = new Promise(function (resolve) {\r
+            var locale = _kiwi.global.settings.get('locale') || 'magic';\r
+            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', function (locale) {\r
+                if (locale) {\r
+                    that.i18n = new Jed(locale);\r
+                } else {\r
+                    that.i18n = new Jed();\r
+                }\r
+                resolve();\r
+            });\r
+        });\r
+\r
+        themePromise = new Promise(function (resolve) {\r
+            var text_theme = opts.server_settings.client.settings.text_theme || 'default';\r
+            $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', function(text_theme) {\r
+                opts.text_theme = text_theme;\r
+                resolve();\r
+            });\r
+        });\r
+\r
+\r
+        Promise.all([localePromise, themePromise]).then(function () {\r
             _kiwi.app = new _kiwi.model.Application(opts);\r
 \r
             // Start the client up\r
@@ -204,41 +232,6 @@ _kiwi.global = {
 \r
             callback();\r
         });\r
-\r
-        textThemeLoaded = function(text_theme, s, xhr) {\r
-            opts.text_theme = text_theme;\r
-\r
-            jobs.finishJob('load_text_theme');\r
-        };\r
-\r
-        localeLoaded = function(locale, s, xhr) {\r
-            if (locale) {\r
-                _kiwi.global.i18n = new Jed(locale);\r
-            } else {\r
-                _kiwi.global.i18n = new Jed();\r
-            }\r
-\r
-            jobs.finishJob('load_locale');\r
-        };\r
-\r
-        // Set up the settings datastore\r
-        _kiwi.global.settings = _kiwi.model.DataStore.instance('kiwi.settings');\r
-        _kiwi.global.settings.load();\r
-\r
-        // Set the window title\r
-        window.document.title = opts.server_settings.client.window_title || 'Kiwi IRC';\r
-\r
-        jobs.registerJob('load_locale');\r
-        locale = _kiwi.global.settings.get('locale');\r
-        if (!locale) {\r
-            $.getJSON(opts.base_path + '/assets/locales/magic.json', localeLoaded);\r
-        } else {\r
-            $.getJSON(opts.base_path + '/assets/locales/' + locale + '.json', localeLoaded);\r
-        }\r
-\r
-        jobs.registerJob('load_text_theme');\r
-        text_theme = opts.server_settings.client.settings.text_theme || 'default';\r
-        $.getJSON(opts.base_path + '/assets/text_themes/' + text_theme + '.json', textThemeLoaded);\r
     },\r
 \r
     start: function() {\r
index 1d796fc7c5b9b2836b1f31eb3dd8405b6bfbf5ab..d6694f36fdf4f3e2dd975cf759d3adcb916663fd 100644 (file)
                 console[method] = noop;\r
             }\r
         }\r
-    };\r
+    }\r
+\r
+    normalizeConsole();\r
 \r
     function getQueryVariable(variable) {\r
         var query = window.location.search.substring(1);\r
         }\r
     }\r
 \r
-    /**\r
-     * Get alerted when a group of jobs have been completed.\r
-     * Eg. .registerJob('job1'); .registerJob('job2');\r
-     *     .onFinish(function(){ alert('Jobs finished!'); });\r
-     *     .finishJob('job1');\r
-     *     .finishJob('job2');\r
-     */\r
-    function JobManager() {\r
-        var completed_jobs = {  };\r
-\r
-        // Functions to call once all jobs have completed\r
-        var completed_callbacks = [];\r
-\r
+    (function afterPromiseAvailable() {\r
+        var base_path = '<%base_path%>', // Entry path for the kiwi application\r
+            scripts = [],\r
+            opts = {\r
+                container: $('body'),\r
+                base_path: base_path,\r
+                settings_path: base_path + '/assets/settings.json'\r
+            },\r
+            script_promise, script_promise_resolve,\r
+            onload_promise,\r
+            settings_promise;\r
+\r
+        // If the browser doesn't natively support promises load up the polyfill and try again.\r
+        if (!window.Promise) {\r
+            $script(base_path + "/assets/libs/promise.min.js", afterPromiseAvailable);\r
+            return;\r
+        }\r
 \r
-        function callCompletedFunctions(fn) {\r
-            $.each(completed_callbacks, function(idx, fn) {\r
-                fn();\r
+        function loadScripts(scripts) {\r
+            return new Promise(function (resolve, reject) {\r
+                var to_load, idx,\r
+                    base = base_path + '/';\r
+                if (typeof scripts === 'string') {\r
+                    to_load = base + scripts;\r
+                } else {\r
+                    to_load = [];\r
+                    for (idx in scripts) {\r
+                        to_load.push(base + scripts[idx]);\r
+                    }\r
+                }\r
+                $script(to_load, resolve, reject);\r
             });\r
+        }\r
 \r
-            completed_callbacks = [];\r
-        };\r
-\r
+        onload_promise = new Promise(function (resolve) {\r
+            window.onload = resolve;\r
+        });\r
 \r
-        this.finishJob = function(job_name) {\r
-            if (typeof completed_jobs[job_name] === 'undefined') {\r
-                return;\r
+        script_promise = new Promise(function (resolve) {\r
+            script_promise_resolve = resolve;\r
+        });\r
+        script_promise = script_promise.then(function () {\r
+            var idx;\r
+            for (idx = 0; idx < scripts.length; idx++) {\r
+                (function (idx) {\r
+                    script_promise = script_promise.then(function () {\r
+                        return loadScripts(scripts[idx]);\r
+                    });\r
+                })(idx);\r
             }\r
+        });\r
 \r
-            completed_jobs[job_name] = true;\r
-\r
-            // Check if all our jobs have completed\r
-            var all_jobs_completed = true;\r
-            $.each(completed_jobs, function(idx, completed) {\r
-                if (!completed) {\r
-                    all_jobs_completed = false;\r
-                    return false;\r
-                }\r
-            });\r
-\r
-            if (all_jobs_completed) {\r
-                callCompletedFunctions();\r
-            }\r
-        };\r
+        if (!getQueryVariable('debug')) {\r
+            scripts.push(['assets/libs/lodash.min.js?t=<%build_time%>']);\r
+            scripts.push([\r
+                'assets/libs/backbone.min.js?t=<%build_time%>',\r
+                'assets/libs/jed.js?t=<%build_time%>'\r
+            ]);\r
+            scripts.push([\r
+                'assets/kiwi.min.js?t=<%build_time%>',\r
+                'assets/libs/engine.io.bundle.min.js?t=<%build_time%>'\r
+            ]);\r
 \r
+            script_promise_resolve();\r
+        }\r
 \r
-        this.onFinish = function(fn) {\r
-            completed_callbacks.push(fn);\r
-        };\r
+        settings_promise = new Promise(function (resolve) {\r
+            $.getJSON(opts.settings_path, function (data) {\r
+                opts.server_settings = data.server_settings;\r
+                opts.client_plugins = data.client_plugins;\r
+                opts.translations = data.translations;\r
+                opts.locale = data.locale;\r
+                opts.themes = data.themes;\r
 \r
+                if (typeof data.kiwi_server !== 'undefined') {\r
+                    opts.kiwi_server = data.kiwi_server;\r
+                }\r
 \r
-        this.registerJob = function(job_name) {\r
-            completed_jobs[job_name] = false;\r
-        };\r
-    }\r
+                resolve();\r
 \r
-    (function () {\r
-        var base_path = '<%base_path%>', // Entry path for the kiwi application\r
-            scripts = [],\r
-            opts = {\r
-                container: $('body'),\r
-                base_path: base_path,\r
-                settings_path: base_path + '/assets/settings.json'\r
-            };\r
+                // If debugging, grab the debug scripts and load them\r
+                if (getQueryVariable('debug')) {\r
+                    scripts = scripts.concat(data.scripts);\r
+                    script_promise_resolve();\r
+                }\r
 \r
-        normalizeConsole();\r
+                // Load themes\r
+                if (opts.themes) {\r
+                    $.each(opts.themes, function (theme_idx, theme) {\r
+                        var disabled = (opts.server_settings.client.settings.theme.toLowerCase() !== theme.name.toLowerCase()),\r
+                            rel = (disabled?'alternate ':'') + 'stylesheet';\r
 \r
-        /**\r
-         * Job bootup manager\r
-         * Once all jobs have completed, call any registered completed functions\r
-         */\r
-        var jobs = new JobManager();\r
+                        var link = $.parseHTML('<link rel="' + rel + '" type="text/css" data-theme href="'+ opts.base_path + '/assets/themes/' + theme.name.toLowerCase() + '/style.css" title="' + theme.name.toLowerCase() + '" ' + (disabled?'disabled':'') + '/>');\r
+                        link.disabled = disabled;\r
 \r
+                        $(link).appendTo($('head'));\r
+                    });\r
+                }\r
+            });\r
+        });\r
 \r
-        // Run after all dependancies have been loaded\r
-        jobs.onFinish(function startApp() {\r
+        Promise.all([onload_promise, settings_promise]).then(function(){return script_promise}).then(function startApp() {\r
             // Kiwi IRC version this is built from\r
             kiwi.build_version = '<%build_version%>';\r
 \r
                 }\r
             });\r
         });\r
-\r
-\r
-        // Load each script\r
-        jobs.registerJob('load_scripts');\r
-        var cur_script = 0;\r
-        function loadNextScript () {\r
-            var to_load,\r
-                base = base_path + '/';\r
-\r
-            // Start the kiwi app if all scripts have been loaded\r
-            if (cur_script === scripts.length) {\r
-                jobs.finishJob('load_scripts');\r
-                return;\r
-            }\r
-\r
-            if (typeof scripts[cur_script] === 'string') {\r
-                to_load = base + scripts[cur_script];\r
-            } else {\r
-                to_load = [];\r
-                for(var idx in scripts[cur_script]) {\r
-                    to_load.push(base + scripts[cur_script][idx]);\r
-                }\r
-            }\r
-\r
-            $script(to_load, loadNextScript);\r
-\r
-            cur_script++;\r
-        }\r
-\r
-        // If we're not interested in debug libs, start loading production files\r
-        if (!getQueryVariable('debug')) {\r
-            scripts.push(['assets/libs/lodash.min.js?t=<%build_time%>']);\r
-            scripts.push([\r
-                'assets/libs/backbone.min.js?t=<%build_time%>',\r
-                'assets/libs/jed.js?t=<%build_time%>'\r
-            ]);\r
-            scripts.push([\r
-                'assets/kiwi.min.js?t=<%build_time%>',\r
-                'assets/libs/engine.io.bundle.min.js?t=<%build_time%>'\r
-            ]);\r
-\r
-            loadNextScript();\r
-        }\r
-\r
-        // Load application settings\r
-        jobs.registerJob('load_settings');\r
-        $.getJSON(opts.settings_path, function (data) {\r
-            opts.server_settings = data.server_settings;\r
-            opts.client_plugins = data.client_plugins;\r
-            opts.translations = data.translations;\r
-            opts.locale = data.locale;\r
-            opts.themes = data.themes;\r
-\r
-            if (typeof data.kiwi_server !== 'undefined')\r
-                opts.kiwi_server = data.kiwi_server;\r
-\r
-            jobs.finishJob('load_settings');\r
-\r
-            // If debugging, grab the debug scripts and load them\r
-            if (getQueryVariable('debug')) {\r
-                scripts = scripts.concat(data.scripts);\r
-                loadNextScript();\r
-            }\r
-\r
-            // Load themes\r
-            if (opts.themes) {\r
-                $.each(opts.themes, function (theme_idx, theme) {\r
-                    var disabled = (opts.server_settings.client.settings.theme.toLowerCase() !== theme.name.toLowerCase()),\r
-                        rel = (disabled?'alternate ':'') + 'stylesheet' /*+ (disabled?' prefetch':'')*/;\r
-\r
-                    var link = $.parseHTML('<link rel="' + rel + '" type="text/css" data-theme href="'+ opts.base_path + '/assets/themes/' + theme.name.toLowerCase() + '/style.css" title="' + theme.name.toLowerCase() + '" ' + (disabled?'disabled':'') + '/>');\r
-                    link.disabled = disabled;\r
-\r
-                    $(link).appendTo($('head'));\r
-                });\r
-            }\r
-        });\r
-\r
-        jobs.registerJob('window_load');\r
-        window.onload = function() {\r
-            jobs.finishJob('window_load');\r
-        };\r
     })();\r
 </script>\r
 </body>\r