Don't embded settings in HTML, serve them as a separate JSON file
authorJack Allnutt <jack@allnutt.eu>
Thu, 8 Aug 2013 23:09:32 +0000 (00:09 +0100)
committerJack Allnutt <jack@allnutt.eu>
Thu, 8 Aug 2013 23:12:28 +0000 (00:12 +0100)
client/assets/src/build.js
client/assets/src/index.html.tmpl
server/httphandler.js

index d06089b52433185db32f18ca7d1f37c469290928..6aa0611371a17d7cd3e0d1f1a86535399bc880ef 100644 (file)
@@ -130,47 +130,13 @@ translation_files.forEach(function (file) {
  * Build the index.html file\r
  */\r
 \r
-var index_src = fs.readFileSync(__dirname + '/index.html.tmpl', FILE_ENCODING);\r
-var vars = {\r
-    base_path: config.get().http_base_path || '/kiwi',\r
-    cache_buster: Math.ceil(Math.random() * 9000).toString(),\r
-    server_settings: {},\r
-    client_plugins: []\r
-};\r
-\r
-// Any restricted server mode set?\r
-if (config.get().restrict_server) {\r
-    vars.server_settings = {\r
-        connection: {\r
-            server: config.get().restrict_server,\r
-            port: config.get().restrict_server_port || 6667,\r
-            ssl: config.get().restrict_server_ssl,\r
-            channel: config.get().restrict_server_channel,\r
-            nick: config.get().restrict_server_nick,\r
-            allow_change: false\r
-        }\r
-    };\r
-}\r
-\r
-// Any client default settings?\r
-if (config.get().client) {\r
-    vars.server_settings.client = config.get().client;\r
-}\r
-\r
-// Any client plugins?\r
-if (config.get().client_plugins && config.get().client_plugins.length > 0) {\r
-    vars.client_plugins = config.get().client_plugins;\r
-}\r
-\r
-// Translations\r
-vars.translations = translations;\r
-\r
-_.each(vars, function(value, key) {\r
-    if (typeof value === 'object') value = JSON.stringify(value);\r
-    index_src = index_src.replace(new RegExp('<%' + key + '%>', 'g'), value);\r
+var index_src = fs.readFileSync(__dirname + '/index.html.tmpl', FILE_ENCODING)\r
+    .replace(new RegExp('<%base_path%>', 'g'), config.get().http_base_path || '/kiwi');\r
+\r
+fs.writeFile(__dirname + '/../../index.html', index_src, { encoding: FILE_ENCODING }, function (err) {\r
+    if (!err) {\r
+        console.log('index.html built');\r
+    } else {\r
+        console.log('Error building index.html');\r
+    }\r
 });\r
-\r
-fs.writeFileSync(__dirname + '/../../index.html', index_src, FILE_ENCODING);\r
-\r
-\r
-console.log('index.html built');\r
index 470814cb188e798eaaec57a2c86edf2864e4190b..e5fab30c2593001c27efd2423cb33d60ee0596bb 100644 (file)
     </script>\r
 \r
 \r
+<script src="<%base_path%>/assets/libs/jquery-1.9.1.min.js"></script>\r
 <script>\r
 /* Script loader (https://github.com/ded/script.js) */\r
 (function(a,b,c){typeof c["module"]!="undefined"&&c.module.exports?c.module.exports=b():typeof c["define"]!="undefined"&&c["define"]=="function"&&c.define.amd?define(a,b):c[a]=b()})("$script",function(){function p(a,b){for(var c=0,d=a.length;c<d;++c)if(!b(a[c]))return j;return 1}function q(a,b){p(a,function(a){return!b(a)})}function r(a,b,i){function o(a){return a.call?a():d[a]}function t(){if(!--n){d[m]=1,l&&l();for(var a in f)p(a.split("|"),o)&&!q(f[a],o)&&(f[a]=[])}}a=a[k]?a:[a];var j=b&&b.call,l=j?b:i,m=j?a.join(""):b,n=a.length;return setTimeout(function(){q(a,function(a){if(h[a])return m&&(e[m]=1),h[a]==2&&t();h[a]=1,m&&(e[m]=1),s(!c.test(a)&&g?g+a+".js":a,t)})},0),r}function s(c,d){var e=a.createElement("script"),f=j;e.onload=e.onerror=e[o]=function(){if(e[m]&&!/^c|loade/.test(e[m])||f)return;e.onload=e[o]=null,f=1,h[c]=2,d()},e.async=1,e.src=c,b.insertBefore(e,b.firstChild)}var a=document,b=a.getElementsByTagName("head")[0],c=/^https?:\/\//,d={},e={},f={},g,h={},i="string",j=!1,k="push",l="DOMContentLoaded",m="readyState",n="addEventListener",o="onreadystatechange";return!a[m]&&a[n]&&(a[n](l,function t(){a.removeEventListener(l,t,j),a[m]="complete"},j),a[m]="loading"),r.get=s,r.order=function(a,b,c){(function d(e){e=a.shift(),a.length?r(e,d):r(e,b,c)})()},r.path=function(a){g=a},r.ready=function(a,b,c){a=a[k]?a:[a];var e=[];return!q(a,function(a){d[a]||e[k](a)})&&p(a,function(a){return d[a]})?b():!function(a){f[a]=f[a]||[],f[a][k](b),c&&c(e)}(a.join("|")),r},r},this)\r
 \r
 \r
-\r
-\r
     function getQueryVariable(variable) {\r
         var query = window.location.search.substring(1);\r
         var vars = query.split('&');\r
     }\r
 \r
     window.onload = function () {\r
-\r
-        // Common dependancies that are required at all times\r
-        var scripts = [\r
-            ['libs/jquery-1.9.1.min.js', 'libs/lodash.min.js'],\r
-            'libs/backbone.min.js', 'libs/jed.js'\r
-        ];\r
-\r
-        // If in debug mode, load each development script\r
-        if (getQueryVariable('debug')) {\r
-            console.log('Loading debugging scripts');\r
-            scripts = scripts.concat([\r
-                'src/app.js',\r
-                [\r
-                    'src/models/application.js',\r
-                    'src/models/gateway.js'\r
-                ],\r
-                [\r
-                    'src/models/newconnection.js',\r
-                    'src/models/panellist.js',\r
-                    'src/models/networkpanellist.js',\r
-                    'src/models/panel.js',\r
-                    'src/models/member.js',\r
-                    'src/models/memberlist.js',\r
-                    'src/models/network.js'\r
-                ],\r
-                \r
-                [\r
-                    'src/models/query.js',\r
-                    'src/models/channel.js',\r
-                    'src/models/server.js',\r
-                    'src/models/applet.js'\r
-                ],\r
-\r
-                [\r
-                    'src/applets/settings.js',\r
-                    'src/applets/chanlist.js',\r
-                    'src/applets/scripteditor.js'\r
-                ],\r
-\r
-                [\r
-                    'src/models/pluginmanager.js',\r
-                    'src/models/datastore.js',\r
-                    'src/helpers/utils.js'\r
-                ],\r
-\r
-                // Some views extend these, so make sure they're loaded beforehand\r
-                [\r
-                    'src/views/panel.js'\r
-                ],\r
-\r
-                [\r
-                    'src/views/channel.js',\r
-                    'src/views/applet.js',\r
-                    'src/views/application.js',\r
-                    'src/views/apptoolbar.js',\r
-                    'src/views/controlbox.js',\r
-                    'src/views/favicon.js',\r
-                    'src/views/mediamessage.js',\r
-                    'src/views/member.js',\r
-                    'src/views/memberlist.js',\r
-                    'src/views/menubox.js',\r
-                    'src/views/networktabs.js',\r
-                    'src/views/nickchangebox.js',\r
-                    'src/views/resizehandler.js',\r
-                    'src/views/serverselect.js',\r
-                    'src/views/statusmessage.js',\r
-                    'src/views/tabs.js',\r
-                    'src/views/topicbar.js',\r
-                    'src/views/userbox.js'\r
-                ]\r
-            ]);\r
-        } else {\r
-            scripts.push('kiwi.min.js?<%cache_buster%>');\r
-        }\r
-\r
-\r
-        // Run after all dependancies have been loaded\r
-        function startApp () {\r
-            var opts = {\r
+        var base_path = '<%base_path%>', // Entry path for the kiwi application\r
+            scripts = [],\r
+            opts = {\r
                 container: $('#kiwi'),\r
                 base_path: base_path,\r
 \r
                 // Override the kiwi_server to use. (Think: running on standalone client..)\r
                 //kiwi_server: 'http://kiwiirc.com:80',\r
-\r
-                server_settings: <%server_settings%>,\r
-                client_plugins: <%client_plugins%>,\r
-                translations: <%translations%>\r
             };\r
 \r
+        // Run after all dependancies have been loaded\r
+        function startApp () {\r
             // Start the app\r
             kiwi.start(opts, function() {\r
                 // Load any plugins\r
             cur_script++;\r
         }\r
 \r
-        // Entry path for the kiwi application\r
-        var base_path = '<%base_path%>';\r
-\r
-        // Start loading scripts\r
-        loadNextScript();\r
+        // Load application settings\r
+        $.getJSON(base_path + '/assets/settings.json', function (data) {\r
+            scripts = scripts.concat(data.scripts);\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
+\r
+            // Start loading scripts\r
+            loadNextScript();\r
+        });\r
     };\r
 </script>\r
 </body>\r
index 045dd2f806b049fd54707cb2cae4d00547c45d36..6a25aab11b2138bd9a8f42b7f26e40da7cb60ac1 100644 (file)
@@ -1,7 +1,9 @@
 var url         = require('url'),
     fs          = require('fs'),
+    crypto      = require('crypto'),
     node_static = require('node-static'),
-    _           = require('lodash');
+    _           = require('lodash'),
+    config      = require('./configuration.js');
 
 
 
@@ -43,6 +45,8 @@ HttpHandler.prototype.serve = function (request, response) {
         } else {
             response.setHeader('Content-Language', request.url.substr(16, request.url.indexOf('.') - 16));
         }
+    } else if (request.url.substr(0, 21) === '/assets/settings.json') {
+        return serveSettings.call(this, request, response);
     }
 
     this.file_server.serve(request, response, function (err) {
@@ -101,3 +105,149 @@ var serveFallbackLocale = function (request, response) {
     //en-gb is our default language, so we serve this as the last possible answer for everything
     this.file_server.serveFile('/assets/locales/en-gb.json', 200, {Vary: 'Accept-Language', 'Content-Language': 'en-gb'}, request, response);
 };
+
+function serveSettings(request, response) {
+    var vars = {
+            server_settings: {},
+            client_plugins: [],
+            translations: [],
+            scripts: [
+                [
+                    'libs/lodash.min.js'
+                ],
+                'libs/backbone.min.js',
+                'libs/jed.js'
+            ]
+        },
+        referrer_url;
+
+    if (request.headers['referer']) {
+        referrer_url = url.parse(request.headers['referer'], true);
+    }
+    if (referrer_url && referrer_url.query && referrer_url.query.debug) {
+        vars.scripts = vars.scripts.concat([
+            'src/app.js',
+            [
+                'src/models/application.js',
+                'src/models/gateway.js'
+            ],
+            [
+                'src/models/newconnection.js',
+                'src/models/panellist.js',
+                'src/models/networkpanellist.js',
+                'src/models/panel.js',
+                'src/models/member.js',
+                'src/models/memberlist.js',
+                'src/models/network.js'
+            ],
+            
+            [
+                'src/models/query.js',
+                'src/models/channel.js',
+                'src/models/server.js',
+                'src/models/applet.js'
+            ],
+
+            [
+                'src/applets/settings.js',
+                'src/applets/chanlist.js',
+                'src/applets/scripteditor.js'
+            ],
+
+            [
+                'src/models/pluginmanager.js',
+                'src/models/datastore.js',
+                'src/helpers/utils.js'
+            ],
+
+            // Some views extend these, so make sure they're loaded beforehand
+            [
+                'src/views/panel.js'
+            ],
+
+            [
+                'src/views/channel.js',
+                'src/views/applet.js',
+                'src/views/application.js',
+                'src/views/apptoolbar.js',
+                'src/views/controlbox.js',
+                'src/views/favicon.js',
+                'src/views/mediamessage.js',
+                'src/views/member.js',
+                'src/views/memberlist.js',
+                'src/views/menubox.js',
+                'src/views/networktabs.js',
+                'src/views/nickchangebox.js',
+                'src/views/resizehandler.js',
+                'src/views/serverselect.js',
+                'src/views/statusmessage.js',
+                'src/views/tabs.js',
+                'src/views/topicbar.js',
+                'src/views/userbox.js'
+            ]
+        ]);
+    } else {
+        vars.scripts.push('kiwi.min.js');
+    }
+
+    // Any restricted server mode set?
+    if (config.get().restrict_server) {
+        vars.server_settings = {
+            connection: {
+                server: config.get().restrict_server,
+                port: config.get().restrict_server_port || 6667,
+                ssl: config.get().restrict_server_ssl,
+                channel: config.get().restrict_server_channel,
+                nick: config.get().restrict_server_nick,
+                allow_change: false
+            }
+        };
+    }
+
+    // Any client default settings?
+    if (config.get().client) {
+        vars.server_settings.client = config.get().client;
+    }
+
+    // Any client plugins?
+    if (config.get().client_plugins && config.get().client_plugins.length > 0) {
+        vars.client_plugins = config.get().client_plugins;
+    }
+
+    fs.readFile(__dirname + '/../client/assets/src/translations/translations.json', function (err, translations) {
+        if (err) {
+            response.statusCode = 500;
+            return response.end();
+        }
+
+        var translation_files;
+        translations = JSON.parse(translations);
+        fs.readdir(__dirname + '/../client/assets/src/translations/', function (err, pofiles) {
+            var hash;
+            if (err) {
+                response.statusCode = 500;
+                return response.end();
+            }
+
+            pofiles.forEach(function (file) {
+                var locale = file.slice(0, -3);
+                if ((file.slice(-3) === '.po') && (locale !== 'template')) {
+                    vars.translations.push({tag: locale, language: translations[locale]});
+                }
+            });
+
+            hash = crypto.createHash('md5').update(JSON.stringify(vars)).digest('hex');
+
+            if (request.headers['if-none-match'] && request.headers['if-none-match'] === hash) {
+                response.writeHead(304, 'Not Modified');
+                return response.end();
+            }
+
+            response.writeHead(200, {
+                'ETag': hash,
+                'Content-Type': 'application/json'
+            });
+            response.end(JSON.stringify(vars));
+        });
+    });
+}