Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Re-factoring code to provide usable Connect middleware plugin

  • Loading branch information...
commit 0b557fdd6870a0296bcf7bbe4ddd300040e63d53 1 parent 4aa16e6
authored August 22, 2012
67  README.md
Source Rendered
... ...
@@ -1,14 +1,61 @@
1  
-Experimenting with rendering dijit widgets on the server-side, using NodeJS, Express and JSDOM. 
  1
+Server Side Dijit
  2
+===
2 3
 
3  
-Run the following command line to start the application: 
  4
+Middleware plugin for Connect that renders dijit widget templates, which are normally rendered client-side, on the server. 
  5
+Returned pages are modified to include the generated server-side templates inline, with a client-side JavaScript plugin 
  6
+to hook up the templates to the widget.
4 7
 
5  
-$ node app.js
  8
+Running the demo
  9
+--
6 10
 
7  
-All files in the "./public" directory are automatically made available at http://localhost:3000
8  
-Any HTML pages requested are parsed before returning, looking for declarative widgets in the page
9  
-which contain the "dojo-dojo-mixins='server_side/_TemplatedWidget'" reference. These widgets are 
10  
-instantiated in the server side environment and their rendered template is injected into the returning 
11  
-page.
  11
+Run the following command line to start a test application, demonstrating a variety of widgets rendered
  12
+on the server side. 
12 13
 
13  
-On the client-side, the mixin modifies widget construct to stop templated widgets from trying to 
14  
-do the local rendering, instead simply hooking up the template that's already present.
  14
+    $ export DOJO_SOURCE=/path/to/dojo-release-1.8.0-src
  15
+    $ npm start 
  16
+
  17
+Once the server has started, visit...
  18
+http://localhost:3000
  19
+
  20
+How to use
  21
+--
  22
+
  23
+Using this middleware plugin in your own application requires the use of the middleware plugin
  24
+alongside a custom client-side JavaScript mixin.
  25
+
  26
+The Connect plugin takes a single configuration parameter, *dojo*, which denotes the location of top level
  27
+directory containing the The Dojo Toolkit source (version 1.8).
  28
+
  29
+### Configure middleware and use with Connect server
  30
+    var connect = require('connect'),
  31
+        server_side = require('server_side_dijit');
  32
+
  33
+    var app = connect()
  34
+       .use(server_side({dojo: './path_to_dojo_1.8'}))
  35
+       .use(connect.static('./my_html_files'))
  36
+       .listen(3000);
  37
+
  38
+Every requested HTML page will be scanned for declaratively defined widgets, *data-dojo-type*, 
  39
+that also include the declarative mixin command, *data-dojo-mixin*, for the custom JavaScript library, *server_side/_TemplatedWidget*. 
  40
+
  41
+### Configure AMD loader with client-side library path
  42
+    var dojoConfig = {
  43
+        paths: {
  44
+            server_side: "/public/js/server_side"
  45
+        },
  46
+        async: true
  47
+     };
  48
+
  49
+Custom JavaScript library to assist server-side rendering is provided under *public/js/server_side* in the module source directory.
  50
+
  51
+### Use client-side mixin to denote widgets for server rendering
  52
+    <div data-dojo-type="dijit/CalendarLite" data-dojo-mixins="server_side/_TemplatedMixin"></div>
  53
+
  54
+These widgets are instantiated in the server side environment and their rendered template injected into the returned 
  55
+page. On the client-side, the mixin modifies widget construct to stop templated widgets from trying to 
  56
+do the client-side rendering and simply hooking up the template node that are already present.
  57
+
  58
+Requirements
  59
+--
  60
+
  61
+* The Dojo Toolkit (version 1.8) source distribution available locally.
7  app.js
... ...
@@ -1,7 +0,0 @@
1  
-var connect = require('connect'),
2  
-    server_side = require('./lib/server_side');
3  
-
4  
-var app = connect()
5  
-  .use(server_side)
6  
-  .use(connect.static('public'))
7  
-  .listen(3000);
88  lib/render.js
@@ -2,51 +2,47 @@ var jsdom = require("jsdom").jsdom,
2 2
     document = jsdom("<html></html>"),
3 3
     window = document.createWindow();
4 4
 
5  
-// Fix window objects in global scope.
6  
-global.document = document;
7  
-global.navigator = window.navigator;
8  
-global.window = window;
9  
-
10  
-dojoConfig = {
11  
-    packages: [{
12  
-        name: "dojo",
13  
-        location: "./public/js/dojo/dojo"
14  
-    }, {
15  
-        name: "dijit",
16  
-        location: "./public/js/dojo/dijit"
17  
-    }, {
18  
-        name: "server_side",
19  
-        location: "./public/js/server_side"
20  
-    }],
21  
-    // _WidgetsInTemplateMixin call parser directly to instantiate children. 
22  
-    // We need it to use our custom parser so use AMD-remapping magic!
23  
-    aliases: [["dojo/parser", "server_side/parser"]],
24  
-    deps: ["server_side/parser", "dojo/has", "dojo/_base/window", "server_side/registry"]
25  
-};
26  
-
27  
-require("../public/js/dojo/dojo/dojo.js");
28  
-
29  
-// Once Dojo has been evalulated, require & define methods 
30  
-// from AMD API as exposed as properties on "global" object.
31  
-
32  
-var has = global.require("dojo/has"),
33  
-    win = global.require("dojo/_base/window"),
34  
-    registry = global.require("server_side/registry"),
35  
-    parser = global.require("server_side/parser");
36  
-
37  
-// Now we need to manually fix a few things to make Dojo 
38  
-// simulate running in a browser.
39  
-
40  
-// Manually add event listener test as this was only included in 
41  
-// the "host-browser" profile.
42  
-has.add("dom-addeventlistener", !!document.addEventListener);
43  
-has.add("dom-attributes-explicit", true);
44  
-
45  
-// Fix global property to point to "window" 
46  
-win.global = window;
47  
-
48  
-module.exports = {
49  
-    render: function (source) {
  5
+module.exports = function (packages) {
  6
+    // Fix window objects in global scope.
  7
+    global.document = document;
  8
+    global.navigator = window.navigator;
  9
+    global.window = window;
  10
+
  11
+    var amd_packages = Object.keys(packages).map(function (key) {
  12
+        return { name: key, location: packages[key] };
  13
+    });
  14
+
  15
+    // Deliberately create global "dojoConfig" variable.
  16
+    dojoConfig = {
  17
+        packages: amd_packages,
  18
+        // _WidgetsInTemplateMixin call parser directly to instantiate children. 
  19
+        // We need it to use our custom parser so use AMD-remapping magic!
  20
+        aliases: [["dojo/parser", "server_side/parser"]],
  21
+        deps: ["server_side/parser", "dojo/has", "dojo/_base/window", "server_side/registry"]
  22
+    };
  23
+
  24
+    require(packages.dojo + "/dojo.js");
  25
+
  26
+    // Once Dojo has been evalulated, require & define methods 
  27
+    // from AMD API as exposed as properties on "global" object.
  28
+
  29
+    var has = global.require("dojo/has"),
  30
+        win = global.require("dojo/_base/window"),
  31
+        registry = global.require("server_side/registry"),
  32
+        parser = global.require("server_side/parser");
  33
+
  34
+    // Now we need to manually fix a few things to make Dojo 
  35
+    // simulate running in a browser.
  36
+
  37
+    // Manually add event listener test as this was only included in 
  38
+    // the "host-browser" profile.
  39
+    has.add("dom-addeventlistener", !!document.addEventListener);
  40
+    has.add("dom-attributes-explicit", true);
  41
+
  42
+    // Fix global property to point to "window" 
  43
+    win.global = window;
  44
+
  45
+    return function (source) {
50 46
         // Clear any previously rendered widgets from registry,
51 47
         // simulate fresh page load.
52 48
         registry.reset();
@@ -57,5 +53,5 @@ module.exports = {
57 53
         parser.parse(document);
58 54
 
59 55
         return document.innerHTML;
60  
-    }
  56
+    };
61 57
 };
59  lib/server_side.js
... ...
@@ -1,35 +1,44 @@
1 1
 var render = require('./render.js');
2 2
 
3  
-module.exports = function serveJsHandle(req, res, next) {
4  
-    var ignore = function (accept) {
5  
-        return accept.indexOf("text/html") === -1;
6  
-    };
  3
+module.exports = function (config) {
  4
+    // Create AMD packages from module configuration.
  5
+    var page = render({
  6
+        dojo: config.dojo + "/dojo",
  7
+        dijit: config.dojo + "/dijit",
  8
+        server_side: __dirname + "/../public/js/server_side"
  9
+    });
7 10
 
8  
-    if (ignore(req.headers.accept)) {
9  
-        return next(); 
10  
-    }
  11
+    return function (req, res, next) {
  12
+        var ignore = function (accept) {
  13
+            return accept.indexOf("text/html") === -1;
  14
+        };
11 15
 
12  
-    var write = res.write,
13  
-        end = res.end, 
14  
-        buffer = "";
  16
+        if (ignore(req.headers.accept)) {
  17
+            return next();
  18
+        }
15 19
 
16  
-    // Only hook into text/html requests....
17  
-    res.write = function(chunk, encoding){
18  
-        buffer = buffer.concat(chunk);
19  
-        return true;
20  
-    };
  20
+        var write = res.write,
  21
+            end = res.end,
  22
+            buffer = "";
21 23
 
22  
-    res.end = function(chunk, encoding){
23  
-        if (chunk) {
24  
-            res.write(chunk);
25  
-        }
  24
+        // Only hook into text/html requests....
  25
+        res.write = function (chunk, encoding) {
  26
+            buffer = buffer.concat(chunk);
  27
+            return true;
  28
+        };
  29
+
  30
+        res.end = function (chunk, encoding) {
  31
+            if (chunk) {
  32
+                res.write(chunk);
  33
+            }
  34
+
  35
+            // Fix content-length, we now have more data to send.
  36
+            var rendered = page(buffer);
  37
+            res.setHeader("Content-Length", rendered.length);
26 38
 
27  
-        // Fix content-length, we now have more data to send.
28  
-        var rendered = render.render(buffer);
29  
-        res.setHeader("Content-Length", rendered.length);
  39
+            return end.call(res, rendered, encoding);
  40
+        };
30 41
 
31  
-        return end.call(res, rendered, encoding);
  42
+        next();
32 43
     };
33  
-    
34  
-    next();
35 44
 };
27  package.json
... ...
@@ -0,0 +1,27 @@
  1
+{
  2
+  "name": "server_side_dijit",
  3
+  "version": "0.0.1",
  4
+  "author": "James Thomas <james@jamesthom.as>",
  5
+  "description": "Connect middleware plugin to render dijit widgets on the server side.",
  6
+  "scripts": {
  7
+    "start": "node test/app.js"
  8
+  },
  9
+  "main": "./lib/server_side",
  10
+  "repository": {
  11
+    "type": "git",
  12
+    "url": "https://github.com/jthomas/server_side_dijit.git"
  13
+  },
  14
+  "keywords": [
  15
+    "dojo",
  16
+    "dijit",
  17
+    "connect"
  18
+  ],
  19
+  "dependencies" : {
  20
+    "connect"   :  "2.3.4",
  21
+    "jsdom" :  "0.2.14"
  22
+  },
  23
+  "license": "MIT",
  24
+  "engine": {
  25
+    "node": "0.6.X"
  26
+  }
  27
+}
1  public/js/dojo/dijit
... ...
@@ -1 +0,0 @@
1  
-Subproject commit f26d07d6576680d402c684f8e850e8bb786b8bf2
1  public/js/dojo/dojo
... ...
@@ -1 +0,0 @@
1  
-Subproject commit 44ede9947c01e6e6c2d32123541176ab303bb0b7
15  test/app.js
... ...
@@ -0,0 +1,15 @@
  1
+var connect = require('connect'),
  2
+    server_side = require('../lib/server_side');
  3
+
  4
+if (!process.env.DOJO_SOURCE) {
  5
+    console.log("Please set top-level directory containing Dojo 1.8 source as the following environment variable: DOJO_SOURCE");
  6
+    process.exit(1);
  7
+}
  8
+
  9
+var app = connect()
  10
+  .use(connect.directory(__dirname + '/public', { icons: true }))
  11
+  .use(server_side({dojo: process.env.DOJO_SOURCE}))
  12
+  .use("/dojo", connect.static(process.env.DOJO_SOURCE))
  13
+  .use("/server_side", connect.static(__dirname + '/../public/js/server_side'))
  14
+  .use(connect.static(__dirname + '/public'))
  15
+  .listen(3000);
10  public/index.html → test/public/dijit_calendar_lite.html
@@ -5,13 +5,13 @@
5 5
         <script>
6 6
             var dojoConfig = {
7 7
                 paths: {
8  
-                    server_side: "../../server_side"
  8
+                    server_side: "/server_side"
9 9
                 },
10 10
                 async: true
11 11
             };
12 12
         </script>
13 13
 
14  
-        <script src="./js/dojo/dojo/dojo.js">
  14
+        <script src="/dojo/dojo/dojo.js">
15 15
         </script>
16 16
         <script>
17 17
         require(["dojo/parser", "dojo/dom"], function (parser, dom) {
@@ -19,7 +19,7 @@
19 19
         }); 
20 20
     
21 21
         </script>
22  
-        <link rel="stylesheet" type="text/css" href="./js/dojo/dijit/themes/claro/claro.css">
  22
+        <link rel="stylesheet" type="text/css" href="/dojo/dijit/themes/claro/claro.css">
23 23
         <style>
24 24
 
25 25
             #parse {
@@ -72,6 +72,7 @@
72 72
                 <label>Sample Button:</label>
73 73
                 <button data-dojo-type="dijit/form/Button" data-dojo-props="label:'Sample Button'" data-dojo-mixins="server_side/_TemplatedMixin"></button>
74 74
                 -->
  75
+                <div data-dojo-type="dijit/CalendarLite" data-dojo-mixins="server_side/_TemplatedMixin"></div>
75 76
             </div>
76 77
             <div class="client_side">
77 78
                 <p>Client Side:</p>
@@ -90,9 +91,8 @@
90 91
                 <button data-dojo-type="dijit/form/Button" data-dojo-props="label:'Sample Button'"></button> 
91 92
                 <div data-dojo-type="server_side/sample" data-dojo-mixins="server_side/_TemplatedMixin"></div>
92 93
                 -->
93  
-                <div data-dojo-type="dijit/CalendarLite" data-dojo-mixins="server_side/_TemplatedMixin"></div>
94 94
 
95  
-                <div data-dojo-type="dijit/CalendarLite" ></div>
  95
+                <div data-dojo-type="dijit/CalendarLite"></div>
96 96
 
97 97
             </div>
98 98
           </div>

0 notes on commit 0b557fd

Please sign in to comment.
Something went wrong with that request. Please try again.