Skip to content
This repository
Browse code

Refactored theming system

  • Loading branch information...
commit 109ba2a754f352067dcd51b0cd68ebb3f3b558e5 1 parent 06c676c
Clifton Cunningham authored

Showing 31 changed files with 1,830 additions and 985 deletions. Show diff stats Hide diff stats

  1. 45  app.js
  2. 7  lib/README
  3. 98  lib/calipso.js
  4. 70  lib/{moduleRouter.js → calipsoRouter.js}
  5. 299  lib/calipsoTheme.js
  6. 656  lib/inflection.js
  7. 1,193  logs/calipso.log
  8. 40  modules/admin/admin.module.js
  9. 75  modules/content/content.module.js
  10. 2  modules/content/templates/list.html
  11. 16  modules/news/news.module.js
  12. 47  modules/scheduler/scheduler.module.js
  13. 23  modules/template/template.module.js
  14. 2  modules/template/templates/template-all.html
  15. 2  modules/template/templates/template.html
  16. 6  modules/user/templates/login.html
  17. 52  modules/user/user.module.js
  18. 52  themes/default/layout.html
  19. 6  themes/default/pages/_body.html
  20. 5  themes/default/pages/_right.html
  21. 1  themes/default/pages/index.html
  22. 29  themes/default/templates/admin.html
  23. 4  themes/default/templates/admin/body.html
  24. 29  themes/default/templates/default.html
  25. 3  themes/default/templates/default/body.html
  26. 4  themes/default/templates/default/footer.html
  27. 2  themes/default/templates/default/header.html
  28. 8  themes/default/{pages/_menu.html → templates/default/menu.html}
  29. 3  themes/default/templates/default/right.html
  30. 30  themes/default/theme.json
  31. 6  utils/pager.js
45  app.js
@@ -57,51 +57,20 @@ function bootApplication(app) {
57 57
   app.use(express.static(path + '/themes/' + theme + '/public'));  // Before router to enable dynamic routing
58 58
   app.use(calipso.calipsoRouter(app,app.set('config')));
59 59
 
60  
-  // Setup ejs views as default, with .html as the extension      
61  
-  app.set('views', path + '/themes/' + theme);
62  
-  app.register('.html', require('ejs'));
63  
-  app.set('view engine', 'html');
64  
-
65  
-  // Some dynamic view helpers
66  
-  app.dynamicHelpers({
67  
-  
68  
-	request: function(req){
69  
-	   return req;
70  
-	},	 
71  
-  user: function(req){
72  
-     return req.session.user;
73  
-  },
74  
-	showDebug: function(req,res){
75  
-	  return "Raw: \r\n\r\n" + sys.inspect(res.blocks,false,10,false) + "\r\n\r\nRendered:\r\n\r\n" + sys.inspect(res.renderedBlocks,false,10,false);
76  
-	},
77  
-	hasMessages: function(req){
78  
-      return Object.keys(req.session.flash || {}).length;
79  
-    },
80  
-
81  
-    messages: function(req){
82  
-      return function(){
83  
-        var msgs = req.flash();
84  
-        return Object.keys(msgs).reduce(function(arr, type){
85  
-          return arr.concat(msgs[type]);
86  
-        }, []);        
87  
-      }
88  
-    }
89  
-  });
90 60
 }
91 61
 
92 62
 // allow normal node loading if appropriate
93 63
 if (!module.parent) {
94 64
   
95 65
   console.log("");
96  
-  console.log("\x1b[36m   ###   ##   ##   ## ##### ####   ####  \x1b[0m");
97  
-  console.log("\x1b[36m ###    ####  ##      ##  # ##    ##  ## \x1b[0m");
98  
-  console.log("\x1b[36m ##    ##  ## ##   ## ##  # ##    ##  ## \x1b[0m");
99  
-  console.log("\x1b[36m ##    ##  ## ##   ## #####  ###  ##  ## \x1b[0m");
100  
-  console.log("\x1b[36m ##    ###### ##   ## ##       ## ##  ## \x1b[0m");
101  
-  console.log("\x1b[36m ###   ##  ## ##   ## ##       ## ##  ## \x1b[0m");
102  
-  console.log("\x1b[36m   ### ##  ## #### ## ##    ####   ####  \x1b[0m");
  66
+  console.log("\x1b[36m            _ _                    \x1b[0m");
  67
+  console.log("\x1b[36m  ___  __ _| (_)_ __  ___  ___     \x1b[0m");
  68
+  console.log("\x1b[36m / __|/ _` | | | '_ \\/ __|/ _ \\  \x1b[0m");
  69
+  console.log("\x1b[36m| (__| (_| | | | |_) \\__ \\ (_) | \x1b[0m");
  70
+  console.log("\x1b[36m \\___|\\__,_|_|_| .__/|___/\\___/ \x1b[0m");
  71
+  console.log("\x1b[36m               |_|                 \x1b[0m");
103 72
   console.log("");
104  
-  
  73
+                          
105 74
   exports.boot(function(app) {
106 75
     
107 76
     app.listen(3000);          
7  lib/README
... ...
@@ -1,8 +1,3 @@
1  
-Place any dependent libraries in here
2  
-
3  
-
4  
-* inflection.js : http://code.google.com/p/inflection-js/
5  
-
6  
-    Modified to remove the link to window.
  1
+Core Calipso libraries
7 2
 
8 3
 
98  lib/calipso.js
@@ -13,6 +13,7 @@ module.exports = {
13 13
      */  
14 14
     lib: { 
15 15
       fs: require('fs'), 
  16
+      path: require('path'), 
16 17
       express: require('express'), 
17 18
       step: require('step'), 
18 19
       sys: require('sys'),
@@ -20,8 +21,9 @@ module.exports = {
20 21
       url: require('url'),        
21 22
       ejs: require('ejs'),
22 23
       pager: require("../utils/pager.js")         
23  
-    },            
24  
-    data: {},     // Holds temporary config data
  24
+    },
  25
+    theme: {},
  26
+    data: {},     // Holds global config data
25 27
     modules: {},    
26 28
     
27 29
     /**
@@ -41,7 +43,12 @@ module.exports = {
41 43
       // Load all the modules
42 44
       loadModules(calipso,options);
43 45
 
44  
-      initModules(calipso,app,function() {
  46
+      // Theme
  47
+      configureTheme(calipso, function() {
  48
+        // Do nothing
  49
+      });      
  50
+      
  51
+      initModules(calipso, function() {
45 52
         // Do nothing
46 53
       });
47 54
                     
@@ -49,17 +56,8 @@ module.exports = {
49 56
              
50 57
          res.menu = {admin:[],primary:[],secondary:[]};
51 58
          
52  
-         res.blocks = {header:[],
53  
-             footer:[],
54  
-             left:[],
55  
-             body:[],
56  
-             right:[]};
57  
-         
58  
-         res.renderedBlocks = {header:[],
59  
-             footer:[],
60  
-             left:[],
61  
-             body:[],
62  
-             right:[]};          
  59
+         res.blocks = {};
  60
+         res.renderedBlocks = {};
63 61
          
64 62
          routeModules(req,res,next,calipso,app);
65 63
                   
@@ -82,13 +80,18 @@ function routeModules(req,res,next,calipso,app) {
82 80
       },
83 81
       function reloadConfig() {        
84 82
         if(res.reloadConfig) {                                                         
  83
+          
  84
+          var self = this;
85 85
           app.set('config',calipso.config);               
86 86
           calipso.config = null;          
87 87
           configureLogging(calipso,app.set('config'));  
88 88
           loadModules(calipso,app.set('config'));            
89 89
           res.reloadConfig = null;          
90  
-          initModules(calipso,app,this);          
91  
-        } else {          
  90
+          initModules(calipso,function() {
  91
+            configureTheme(calipso, self);                  
  92
+          });      
  93
+          
  94
+        } else {         
92 95
           this();          
93 96
         }
94 97
         
@@ -104,16 +107,18 @@ function routeModules(req,res,next,calipso,app) {
104 107
           app.install = false;
105 108
           res.redirect("/admin/install");
106 109
         } else {          
  110
+          
107 111
           // If we have now reached the end!
108 112
           switch(res.statusCode) {
109 113
             case 404:              
110  
-              res.render("404",{layout:'error'});
  114
+              res.send("404");
111 115
               break;
112 116
             case 500:
113  
-              res.render("500",{menu:res.menu,blocks:res.blocks,renderedBlocks:res.renderedBlocks});              
  117
+              res.send("500");              
114 118
               break;
115  
-            case 200:            
116  
-              res.render("pages/index",{menu:res.menu,blocks:res.blocks,renderedBlocks:res.renderedBlocks});
  119
+            case 200:      
  120
+              calipso.theme.render(req,res,next);
  121
+              // res.render("pages/index",{menu:res.menu,blocks:res.blocks,renderedBlocks:res.renderedBlocks});
117 122
               break;
118 123
             default:
119 124
               // Do Nothing
@@ -169,14 +174,14 @@ function configureLogging(calipso,options) {
169 174
    
170 175
 }
171 176
 
172  
-function initModules(calipso,app,next) {
  177
+function initModules(calipso,next) {
173 178
   
174 179
   calipso.lib.step(             
175 180
       function loadAllModules() {                              
176 181
           var group = this.group();                                                   
177 182
           for(var module in calipso.modules) {
178 183
               if(calipso.modules[module].enabled) {              
179  
-                calipso.modules[module].fn.init(calipso.modules[module],app,group());
  184
+                calipso.modules[module].fn.init(calipso.modules[module],calipso.app,group());
180 185
               }
181 186
           };               
182 187
       },
@@ -224,12 +229,55 @@ function loadModules(calipso,options) {
224 229
         var enabled = configuredModules[name] ? configuredModules[name].enabled : false;
225 230
         
226 231
         calipso.modules[name] = {name:name, 
227  
-                              enabled:enabled,
228  
-                              fn:require('../modules/' + name + '/' + name + '.module'), 
229  
-                              router:require('./moduleRouter').Router()};
  232
+                                enabled:enabled,
  233
+                                fn:require('../modules/' + name + '/' + name + '.module'), 
  234
+                                router:require('./calipsoRouter').Router(name),
  235
+                                templates:loadModuleTemplates(calipso,__dirname + '/../modules/' + name + '/templates')};
230 236
       }
231 237
   });
232 238
   
233 239
   return calipso.modules;
234 240
     
  241
+};
  242
+
  243
+/** 
  244
+ * Pre load all the templates in a module, synch, but only happens on app start up and config reload
  245
+ * This is attached to the templates attribute so used later.
  246
+ * 
  247
+ * @param calipso
  248
+ * @param moduleTemplatePath
  249
+ * @returns template object
  250
+ */
  251
+function loadModuleTemplates(calipso,moduleTemplatePath) {
  252
+  
  253
+  var templates = {};
  254
+  
  255
+  if(calipso.lib.path.existsSync(moduleTemplatePath)) {        
  256
+
  257
+    calipso.lib.fs.readdirSync(moduleTemplatePath).forEach(function(name){
  258
+      
  259
+      var template=calipso.lib.fs.readFileSync(moduleTemplatePath + "/" + name, 'utf8');
  260
+      if(template) {      
  261
+        templates[name.replace(/\.html$/,'')] = calipso.lib.ejs.compile(template);
  262
+      }
  263
+      
  264
+    });
  265
+    
  266
+    return templates;   
  267
+
  268
+  } else {
  269
+    return;
  270
+  }
  271
+  
  272
+};
  273
+
  274
+function configureTheme(calipso, next) {
  275
+  
  276
+  var themeName = calipso.app.set('config').theme;  
  277
+  
  278
+  require('./calipsoTheme').Theme(themeName,function(theme) {
  279
+     calipso.theme = theme;
  280
+     next();
  281
+  });
  282
+  
235 283
 };
70  lib/moduleRouter.js → lib/calipsoRouter.js
... ...
@@ -1,4 +1,3 @@
1  
-
2 1
 /*!
3 2
  * Connect - content loader
4 3
  * Copyright(c) 2011 Clifton Cunningham
@@ -9,34 +8,28 @@
9 8
  */
10 9
 var url = require('url'),Step = require('step'),fs = require('fs'),path = require('path'), calipso = require("./calipso");
11 10
 
12  
-module.exports.Router = function() {
  11
+module.exports.Router = function(moduleName) {
13 12
   
14 13
   return {
15  
-      
  14
+    
  15
+    moduleName: moduleName,
16 16
     routes: [],
17 17
     configured: false,
18 18
     addRoute: function(path, fn, options, next) {  
19 19
       
20 20
       var router = this;
21 21
       
22  
-      options = options ? options : {end:true, admin:false, templatePath:''};     
  22
+      // Default options
  23
+      options = options ? options : {end:true, admin:false};
  24
+      
  25
+      // Can't do any real checking here as everything is initialised in parallel.
  26
+      router.routes.push({path: path, fn: fn, options: options});
  27
+      next();
23 28
       
24  
-      if(options.templatePath) {        
25  
-        loadTemplate(options.templatePath, function(data) {
26  
-          if(data) {            
27  
-            options.templateData = data;
28  
-          }
29  
-          router.routes.push({path: path, fn: fn, options: options});
30  
-          next();
31  
-        });
32  
-      } else {
33  
-        options.templateData = '';
34  
-        router.routes.push({path: path, fn: fn, options: options});
35  
-        next();
36  
-      }            
37 29
     },    
38 30
     route: function(req, res, next) {
39 31
       
  32
+      var router = this;
40 33
       var requestUrl = url.parse(req.url);
41 34
       var routes = this.routes;      
42 35
         
@@ -50,7 +43,8 @@ module.exports.Router = function() {
50 43
               
51 44
               var keys = [];
52 45
               var route = routes[i];
53  
-              var template = route.options.templateData;
  46
+              var template = null, block = "";
  47
+                            
54 48
               var routeMethod = "GET";
55 49
               var routeRegEx; 
56 50
                 
@@ -67,7 +61,21 @@ module.exports.Router = function() {
67 61
                 
68 62
                 // Check to see if we matched a non /* route to flag a 404 later
69 63
                 res.routeMatched = !(routeRegEx.toString() === "/.*/") || res.routeMatched;
  64
+                                
  65
+                // Lookup the template for this route
  66
+                if(route.options.template) {
  67
+                  template = calipso.modules[router.moduleName].templates[route.options.template];
  68
+                  if(!template && route.options.template) {
  69
+                    calipso.error("The specified template: " + route.options.template + " does not exist in the module: " + router.moduleName);
  70
+                  }                  
  71
+                } 
70 72
                 
  73
+                // Initialise the block if it doesn't exist
  74
+                if(route.options.block && !res.renderedBlocks[block]) {
  75
+                  block = route.options.block;                 
  76
+                  res.renderedBlocks[block] = [];
  77
+                }
  78
+                                
71 79
                 // Copy over any params that make sense                      
72 80
                 req.moduleParams = [];                    
73 81
                 
@@ -87,7 +95,7 @@ module.exports.Router = function() {
87 95
                 
88 96
                 // Check to see if it requires admin access
89 97
                 if(!route.options.admin || (route.options.admin && req.session.user && req.session.user.isAdmin)) {                  
90  
-                  route.fn(req,res,group(),template);                                
  98
+                  route.fn(req,res,template,block,group());                                
91 99
                 } else {
92 100
                   res.statusCode = 401;
93 101
                   res.redirect("/");
@@ -112,30 +120,6 @@ module.exports.Router = function() {
112 120
   
113 121
 }
114 122
 
115  
-
116  
-/**
117  
- * Load a template
118  
- */
119  
-
120  
-function loadTemplate(templatePath, next) {
121  
-    
122  
-  path.exists(templatePath,function(exists) {
123  
-    if(exists) {
124  
-      fs.readFile(templatePath, 'utf8', function(err,data) {
125  
-           if(!err) {
126  
-             next(data);               
127  
-           } else {
128  
-             next('');
129  
-           }
130  
-      });
131  
-    } else {
132  
-      calipso.error("CANT FIND " + templatePath);
133  
-      next('');
134  
-    }
135  
-  });
136  
-  
137  
-}
138  
-
139 123
 /**
140 124
  * Normalize the given path string,
141 125
  * returning a regular expression.
299  lib/calipsoTheme.js
... ...
@@ -0,0 +1,299 @@
  1
+/*!
  2
+ * Connect - content loader
  3
+ * Copyright(c) 2011 Clifton Cunningham
  4
+ * MIT Licensed
  5
+ * 
  6
+ * Borrowed liberally from Connect / ExpressJS itself for this, thanks for the great work!
  7
+ * 
  8
+ */
  9
+var fs = require('fs'),path = require('path'), sys=require('sys'), 
  10
+    calipso = require("./calipso"),
  11
+    utils = require('connect').utils,    
  12
+    merge = utils.merge;
  13
+
  14
+module.exports.Theme = function(themeName,next) {
  15
+   
  16
+  var themePath = __dirname + "/../themes/" + themeName + "/";  
  17
+  
  18
+  loadTheme(themeName, themePath, function(err,config) {
  19
+    
  20
+    if(err) {
  21
+      calipso.error(err.message);
  22
+      next();
  23
+    }
  24
+ 
  25
+    cacheTheme(config,themePath,function (themeCache) {
  26
+            
  27
+      // Load the theme configuration file.  
  28
+      var theme = {                
  29
+        theme: themeName,    
  30
+        cache: themeCache,
  31
+        config: config,
  32
+        renderItem: function(req, res, template, block, options) {
  33
+                    
  34
+          if(template && block) {
  35
+            
  36
+            themeOptions = createOptions(req,res,options);
  37
+
  38
+            if(typeof template === 'function') {
  39
+              var output;
  40
+              try {
  41
+                output = template.call({},themeOptions);
  42
+              } catch(ex) {
  43
+                res.renderedBlocks[block].push("Block: " + block + " failed to render because " + ex.message );         
  44
+                return;
  45
+              }                            
  46
+              
  47
+              res.renderedBlocks[block].push(output);
  48
+            } else {
  49
+              
  50
+              // Assume template is processed HTML
  51
+              res.renderedBlocks[block].push(template);
  52
+              
  53
+            }
  54
+            
  55
+          }                          
  56
+        },
  57
+        render: function(req, res, next) {
  58
+          
  59
+            var cache = this.cache;
  60
+            
  61
+            // Scan through each layout
  62
+            var layout = res.layout ? res.layout : "default";            
  63
+            if(!theme.config.layouts[layout]) {
  64
+              layout = "default";    
  65
+              if(!theme.config.layouts[layout]) {
  66
+                calipso.error("Default layout is not defined within the current theme, exiting.");
  67
+                res.send("");
  68
+                return;
  69
+              }
  70
+            }    
  71
+            
  72
+            var options = createOptions(req,res,processTheme(req,res,layout,this));                        
  73
+            res.send(cache[layout].call({},options));
  74
+            
  75
+        }
  76
+      } 
  77
+      
  78
+      next(theme);
  79
+        
  80
+    });
  81
+    
  82
+    
  83
+  });
  84
+  
  85
+}
  86
+
  87
+/**
  88
+ * Copy the current block data over to options to render
  89
+ * @param res
  90
+ * @param config
  91
+ */
  92
+function processTheme(req,res,layout,theme) {
  93
+        
  94
+  var options = {};
  95
+  
  96
+  // Scan through each layout
  97
+  var layoutConfig = theme.config.layouts[layout].layout;    
  98
+        
  99
+ 
  100
+    for(var section in layoutConfig.sections) {
  101
+                
  102
+        var themeCache = theme.cache[layout + "." + section];       
  103
+        if(!themeCache) {
  104
+          // Use the default
  105
+          themeCache = theme.cache["default." + section];
  106
+        }
  107
+        
  108
+        options[section] = "";
  109
+        
  110
+        if(section != 'menu') {
  111
+          
  112
+          if(layoutConfig.sections[section].blocks) {
  113
+            
  114
+            var blockData = "";
  115
+            layoutConfig.sections[section].blocks.forEach(function(block) {
  116
+                if(res.renderedBlocks[block]) {
  117
+                  res.renderedBlocks[block].forEach(function(renderedContent) {
  118
+                      blockData += renderedContent;
  119
+                  });  
  120
+                }                                     
  121
+            });
  122
+            
  123
+            themeOptions = createOptions(req,res,{blockData:blockData});
  124
+            options[section] += themeCache.call({},themeOptions);
  125
+            
  126
+          } else {
  127
+              
  128
+            options[section] += themeCache.call({},{});
  129
+            
  130
+          }
  131
+          
  132
+        } else {
  133
+            
  134
+            // Pass the menu over - TODO : Deal with menu types
  135
+            themeOptions = createOptions(req,res,{menu:res.menu});
  136
+            options[section] += themeCache.call({},themeOptions);
  137
+          
  138
+        }
  139
+        
  140
+  }           
  141
+  
  142
+  return options;
  143
+  
  144
+}
  145
+
  146
+/**
  147
+ * Load a theme
  148
+ */
  149
+function loadTheme(theme, themePath, next) {    
  150
+  
  151
+  var themeFile = themePath + "theme.json";
  152
+  
  153
+  path.exists(themeFile,function(exists) {
  154
+    
  155
+    if(exists) {
  156
+      fs.readFile(themeFile, 'utf8', function(err,data) {
  157
+           if(!err) {
  158
+             var jsonData;
  159
+             try {
  160
+               jsonData = JSON.parse(data);
  161
+               next(null,jsonData);
  162
+             } catch(ex) {
  163
+               next(new Error("Error parsing theme configuration: " + ex.message),data);
  164
+             }                            
  165
+           } else {
  166
+             next(err);
  167
+           }
  168
+      });
  169
+    } else {      
  170
+      next(new Error("Can't find specified theme configuration " + themeFile));
  171
+    }
  172
+  });  
  173
+}
  174
+
  175
+/** 
  176
+ * Load all of the theme templates into the theme
  177
+ * @param theme
  178
+ */
  179
+function cacheTheme(theme,themePath,next) {
  180
+  
  181
+  var templates = [];
  182
+  var templateCache = {};
  183
+  
  184
+  // Scan through each layout
  185
+  for(var layout in theme.layouts) {
  186
+    
  187
+    // Scan through each layout
  188
+    var layoutConfig = theme.layouts[layout].layout;
  189
+        
  190
+    templates.push({name:layout,templatePath:"templates/" + layoutConfig.template});
  191
+    
  192
+    for(var section in layoutConfig.sections) {
  193
+        var template = layoutConfig.sections[section].template;
  194
+        templates.push({name:layout + "." + section,templatePath:"templates/" + layout + "/" + template});
  195
+    }        
  196
+    
  197
+  }  
  198
+  
  199
+  // Now load them all into the cache
  200
+  calipso.lib.step(
  201
+      function loadTemplates() {
  202
+        var group = this.group();
  203
+        templates.forEach(function(template) {
  204
+            loadTemplate(template,templateCache,themePath, group());
  205
+        });         
  206
+      },
  207
+      function done(err) {
  208
+        if(err) {
  209
+          // May not be a problem as missing templates default to default
  210
+          calipso.debug(err.message);
  211
+          next();
  212
+        } else {          
  213
+          next(templateCache);
  214
+        }
  215
+      }      
  216
+  )
  217
+  
  218
+  
  219
+  
  220
+}
  221
+
  222
+/**
  223
+ * Load a template
  224
+ */
  225
+function loadTemplate(template, templateCache, themePath, next) {    
  226
+  
  227
+  var templatePath = themePath + template.templatePath;
  228
+    
  229
+  path.exists(templatePath,function(exists) {
  230
+    if(exists) {
  231
+      fs.readFile(templatePath, 'utf8', function(err,data) {
  232
+           if(!err) {
  233
+             // Precompile the view into our cache
  234
+             templateCache[template.name] = calipso.lib.ejs.compile(data);                  
  235
+             next();
  236
+           } else {             
  237
+             next(err);
  238
+           }
  239
+      });
  240
+    } else {  
  241
+      // new Error("Can't find specified template configuration " + templatePath)
  242
+      next();
  243
+    }
  244
+  });
  245
+  
  246
+}
  247
+
  248
+function createOptions(req,res,options) {        
  249
+  
  250
+    // Helper Functions
  251
+    function request(req){
  252
+        return req;
  253
+    }
  254
+    
  255
+    function showDebug(req,res) {
  256
+      return sys.inspect(res.renderedBlocks,false,10,false);
  257
+    }
  258
+        
  259
+    function user(req){        
  260
+        var user;      
  261
+        if(req.session && req.session.user) {
  262
+          user = req.session.user;            
  263
+        } else {
  264
+          user = {username:'',anonymous:true};
  265
+        }
  266
+        return user;        
  267
+    }
  268
+    
  269
+    function hasMessages(req) {
  270
+       return Object.keys(req.session.flash || {}).length;
  271
+    }
  272
+
  273
+    function messages(req){
  274
+       return function() {
  275
+         var msgs = req.flash();
  276
+         return Object.keys(msgs).reduce(function(arr, type){
  277
+           return arr.concat(msgs[type]);
  278
+         }, []);        
  279
+       }
  280
+     }
  281
+
  282
+    // apply 
  283
+    var options = merge(options, 
  284
+          {request:request(req),
  285
+           user:user(req),
  286
+           showDebug:showDebug(req,res),
  287
+           hasMessages:hasMessages(req),
  288
+           messages:messages(req)
  289
+           });
  290
+    
  291
+    // Check to see if we have any data
  292
+    if(calipso.data) {
  293
+      options = merge(options,calipso.data);
  294
+    }
  295
+    
  296
+    return options;
  297
+    
  298
+}
  299
+
656  lib/inflection.js
... ...
@@ -1,656 +0,0 @@
1  
-/*
2  
-Copyright (c) 2010 Ryan Schuft (ryan.schuft@gmail.com)
3  
-
4  
-Permission is hereby granted, free of charge, to any person obtaining a copy
5  
-of this software and associated documentation files (the "Software"), to deal
6  
-in the Software without restriction, including without limitation the rights
7  
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8  
-copies of the Software, and to permit persons to whom the Software is
9  
-furnished to do so, subject to the following conditions:
10  
-
11  
-The above copyright notice and this permission notice shall be included in
12  
-all copies or substantial portions of the Software.
13  
-
14  
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15  
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16  
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17  
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18  
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19  
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20  
-THE SOFTWARE.
21  
-*/
22  
-
23  
-/*
24  
-  This code is based in part on the work done in Ruby to support
25  
-  infection as part of Ruby on Rails in the ActiveSupport's Inflector
26  
-  and Inflections classes.  It was initally ported to Javascript by
27  
-  Ryan Schuft (ryan.schuft@gmail.com) in 2007.
28  
-
29  
-  The code is available at http://code.google.com/p/inflection-js/
30  
-
31  
-  The basic usage is:
32  
-    1. Include this script on your web page.
33  
-    2. Call functions on any String object in Javascript
34  
-
35  
-  Currently implemented functions:
36  
-
37  
-    String.pluralize(plural) == String
38  
-      renders a singular English language noun into its plural form
39  
-      normal results can be overridden by passing in an alternative
40  
-
41  
-    String.singularize(singular) == String
42  
-      renders a plural English language noun into its singular form
43  
-      normal results can be overridden by passing in an alterative
44  
-
45  
-    String.camelize(lowFirstLetter) == String
46  
-      renders a lower case underscored word into camel case
47  
-      the first letter of the result will be upper case unless you pass true
48  
-      also translates "/" into "::" (underscore does the opposite)
49  
-
50  
-    String.underscore() == String
51  
-      renders a camel cased word into words seperated by underscores
52  
-      also translates "::" back into "/" (camelize does the opposite)
53  
-
54  
-    String.humanize(lowFirstLetter) == String
55  
-      renders a lower case and underscored word into human readable form
56  
-      defaults to making the first letter capitalized unless you pass true
57  
-
58  
-    String.capitalize() == String
59  
-      renders all characters to lower case and then makes the first upper
60  
-
61  
-    String.dasherize() == String
62  
-      renders all underbars and spaces as dashes
63  
-
64  
-    String.titleize() == String
65  
-      renders words into title casing (as for book titles)
66  
-
67  
-    String.demodulize() == String
68  
-      renders class names that are prepended by modules into just the class
69  
-
70  
-    String.tableize() == String
71  
-      renders camel cased singular words into their underscored plural form
72  
-
73  
-    String.classify() == String
74  
-      renders an underscored plural word into its camel cased singular form
75  
-
76  
-    String.foreign_key(dropIdUbar) == String
77  
-      renders a class name (camel cased singular noun) into a foreign key
78  
-      defaults to seperating the class from the id with an underbar unless
79  
-      you pass true
80  
-
81  
-    String.ordinalize() == String
82  
-      renders all numbers found in the string into their sequence like "22nd"
83  
-*/
84  
-
85  
-/*
86  
-  This sets up a container for some constants in its own namespace
87  
-  We use the window (if available) to enable dynamic loading of this script
88  
-  Window won't necessarily exist for non-browsers.
89  
-if (window && !window.InflectionJS)
90  
-{
91  
-    window.InflectionJS = null;
92  
-}
93  
-*/
94  
-
95  
-/*
96  
-  This sets up some constants for later use
97  
-  This should use the window namespace variable if available
98  
-*/
99  
-InflectionJS =
100  
-{
101  
-    /*
102  
-      This is a list of nouns that use the same form for both singular and plural.
103  
-      This list should remain entirely in lower case to correctly match Strings.
104  
-    */
105  
-    uncountable_words: [
106  
-        'equipment', 'information', 'rice', 'money', 'species', 'series',
107  
-        'fish', 'sheep', 'moose', 'deer', 'news'
108  
-    ],
109  
-
110  
-    /*
111  
-      These rules translate from the singular form of a noun to its plural form.
112  
-    */
113  
-    plural_rules: [
114  
-        [new RegExp('(m)an$', 'gi'),                 '$1en'],
115  
-        [new RegExp('(pe)rson$', 'gi'),              '$1ople'],
116  
-        [new RegExp('(child)$', 'gi'),               '$1ren'],
117  
-        [new RegExp('^(ox)$', 'gi'),                 '$1en'],
118  
-        [new RegExp('(ax|test)is$', 'gi'),           '$1es'],
119  
-        [new RegExp('(octop|vir)us$', 'gi'),         '$1i'],
120  
-        [new RegExp('(alias|status)$', 'gi'),        '$1es'],
121  
-        [new RegExp('(bu)s$', 'gi'),                 '$1ses'],
122  
-        [new RegExp('(buffal|tomat|potat)o$', 'gi'), '$1oes'],
123  
-        [new RegExp('([ti])um$', 'gi'),              '$1a'],
124  
-        [new RegExp('sis$', 'gi'),                   'ses'],
125  
-        [new RegExp('(?:([^f])fe|([lr])f)$', 'gi'),  '$1$2ves'],
126  
-        [new RegExp('(hive)$', 'gi'),                '$1s'],
127  
-        [new RegExp('([^aeiouy]|qu)y$', 'gi'),       '$1ies'],
128  
-        [new RegExp('(x|ch|ss|sh)$', 'gi'),          '$1es'],
129  
-        [new RegExp('(matr|vert|ind)ix|ex$', 'gi'),  '$1ices'],
130  
-        [new RegExp('([m|l])ouse$', 'gi'),           '$1ice'],
131  
-        [new RegExp('(quiz)$', 'gi'),                '$1zes'],
132  
-        [new RegExp('s$', 'gi'),                     's'],
133  
-        [new RegExp('$', 'gi'),                      's']
134  
-    ],
135  
-
136  
-    /*
137  
-      These rules translate from the plural form of a noun to its singular form.
138  
-    */
139  
-    singular_rules: [
140  
-        [new RegExp('(m)en$', 'gi'),                                                       '$1an'],
141  
-        [new RegExp('(pe)ople$', 'gi'),                                                    '$1rson'],
142  
-        [new RegExp('(child)ren$', 'gi'),                                                  '$1'],
143  
-        [new RegExp('([ti])a$', 'gi'),                                                     '$1um'],
144  
-        [new RegExp('((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$','gi'), '$1$2sis'],
145  
-        [new RegExp('(hive)s$', 'gi'),                                                     '$1'],
146  
-        [new RegExp('(tive)s$', 'gi'),                                                     '$1'],
147  
-        [new RegExp('(curve)s$', 'gi'),                                                    '$1'],
148  
-        [new RegExp('([lr])ves$', 'gi'),                                                   '$1f'],
149  
-        [new RegExp('([^fo])ves$', 'gi'),                                                  '$1fe'],
150  
-        [new RegExp('([^aeiouy]|qu)ies$', 'gi'),                                           '$1y'],
151  
-        [new RegExp('(s)eries$', 'gi'),                                                    '$1eries'],
152  
-        [new RegExp('(m)ovies$', 'gi'),                                                    '$1ovie'],
153  
-        [new RegExp('(x|ch|ss|sh)es$', 'gi'),                                              '$1'],
154  
-        [new RegExp('([m|l])ice$', 'gi'),                                                  '$1ouse'],
155  
-        [new RegExp('(bus)es$', 'gi'),                                                     '$1'],
156  
-        [new RegExp('(o)es$', 'gi'),                                                       '$1'],
157  
-        [new RegExp('(shoe)s$', 'gi'),                                                     '$1'],
158  
-        [new RegExp('(cris|ax|test)es$', 'gi'),                                            '$1is'],
159  
-        [new RegExp('(octop|vir)i$', 'gi'),                                                '$1us'],
160  
-        [new RegExp('(alias|status)es$', 'gi'),                                            '$1'],
161  
-        [new RegExp('^(ox)en', 'gi'),                                                      '$1'],
162  
-        [new RegExp('(vert|ind)ices$', 'gi'),                                              '$1ex'],
163  
-        [new RegExp('(matr)ices$', 'gi'),                                                  '$1ix'],
164  
-        [new RegExp('(quiz)zes$', 'gi'),                                                   '$1'],
165  
-        [new RegExp('s$', 'gi'),                                                           '']
166  
-    ],
167  
-
168  
-    /*
169  
-      This is a list of words that should not be capitalized for title case
170  
-    */
171  
-    non_titlecased_words: [
172  
-        'and', 'or', 'nor', 'a', 'an', 'the', 'so', 'but', 'to', 'of', 'at',
173  
-        'by', 'from', 'into', 'on', 'onto', 'off', 'out', 'in', 'over',
174  
-        'with', 'for'
175  
-    ],
176  
-
177  
-    /*
178  
-      These are regular expressions used for converting between String formats
179  
-    */
180  
-    id_suffix: new RegExp('(_ids|_id)$', 'g'),
181  
-    underbar: new RegExp('_', 'g'),
182  
-    space_or_underbar: new RegExp('[\ _]', 'g'),
183  
-    uppercase: new RegExp('([A-Z])', 'g'),
184  
-    underbar_prefix: new RegExp('^_'),
185  
-    
186  
-    /*
187  
-      This is a helper method that applies rules based replacement to a String
188  
-      Signature:
189  
-        InflectionJS.apply_rules(str, rules, skip, override) == String
190  
-      Arguments:
191  
-        str - String - String to modify and return based on the passed rules
192  
-        rules - Array: [RegExp, String] - Regexp to match paired with String to use for replacement
193  
-        skip - Array: [String] - Strings to skip if they match
194  
-        override - String (optional) - String to return as though this method succeeded (used to conform to APIs)
195  
-      Returns:
196  
-        String - passed String modified by passed rules
197  
-      Examples:
198  
-        InflectionJS.apply_rules("cows", InflectionJs.singular_rules) === 'cow'
199  
-    */
200  
-    apply_rules: function(str, rules, skip, override)
201  
-    {
202  
-        if (override)
203  
-        {
204  
-            str = override;
205  
-        }
206  
-        else
207  
-        {
208  
-            var ignore = (skip.indexOf(str.toLowerCase()) > -1);
209  
-            if (!ignore)
210  
-            {
211  
-                for (var x = 0; x < rules.length; x++)
212  
-                {
213  
-                    if (str.match(rules[x][0]))
214  
-                    {
215  
-                        str = str.replace(rules[x][0], rules[x][1]);
216  
-                        break;
217  
-                    }
218  
-                }
219  
-            }
220  
-        }
221  
-        return str;
222  
-    }
223  
-};
224  
-
225  
-/*
226  
-  This lets us detect if an Array contains a given element
227  
-  Signature:
228  
-    Array.indexOf(item, fromIndex, compareFunc) == Integer
229  
-  Arguments:
230  
-    item - Object - object to locate in the Array
231  
-    fromIndex - Integer (optional) - starts checking from this position in the Array
232  
-    compareFunc - Function (optional) - function used to compare Array item vs passed item
233  
-  Returns:
234  
-    Integer - index position in the Array of the passed item
235  
-  Examples:
236  
-    ['hi','there'].indexOf("guys") === -1
237  
-    ['hi','there'].indexOf("hi") === 0
238  
-*/
239  
-if (!Array.prototype.indexOf)
240  
-{
241  
-    Array.prototype.indexOf = function(item, fromIndex, compareFunc)
242  
-    {
243  
-        if (!fromIndex)
244  
-        {
245  
-            fromIndex = -1;
246  
-        }
247  
-        var index = -1;
248  
-        for (var i = fromIndex; i < this.length; i++)
249  
-        {
250  
-            if (this[i] === item || compareFunc && compareFunc(this[i], item))
251  
-            {
252  
-                index = i;
253  
-                break;
254  
-            }
255  
-        }
256  
-        return index;
257  
-    };
258  
-}
259  
-
260  
-/*
261  
-  You can override this list for all Strings or just one depending on if you
262  
-  set the new values on prototype or on a given String instance.
263  
-*/
264  
-if (!String.prototype._uncountable_words)
265  
-{
266  
-    String.prototype._uncountable_words = InflectionJS.uncountable_words;
267  
-}
268  
-
269  
-/*
270  
-  You can override this list for all Strings or just one depending on if you
271  
-  set the new values on prototype or on a given String instance.
272  
-*/
273  
-if (!String.prototype._plural_rules)
274  
-{
275  
-    String.prototype._plural_rules = InflectionJS.plural_rules;
276  
-}
277  
-
278  
-/*
279  
-  You can override this list for all Strings or just one depending on if you
280  
-  set the new values on prototype or on a given String instance.
281  
-*/
282  
-if (!String.prototype._singular_rules)
283  
-{
284  
-    String.prototype._singular_rules = InflectionJS.singular_rules;
285  
-}
286  
-
287  
-/*
288  
-  You can override this list for all Strings or just one depending on if you
289  
-  set the new values on prototype or on a given String instance.
290  
-*/
291  
-if (!String.prototype._non_titlecased_words)
292  
-{
293  
-    String.prototype._non_titlecased_words = InflectionJS.non_titlecased_words;
294  
-}
295  
-
296  
-/*
297  
-  This function adds plurilization support to every String object
298  
-    Signature:
299  
-      String.pluralize(plural) == String
300  
-    Arguments:
301  
-      plural - String (optional) - overrides normal output with said String
302  
-    Returns:
303  
-      String - singular English language nouns are returned in plural form
304  
-    Examples:
305  
-      "person".pluralize() == "people"
306  
-      "octopus".pluralize() == "octopi"
307  
-      "Hat".pluralize() == "Hats"
308  
-      "person".pluralize("guys") == "guys"
309  
-*/
310  
-if (!String.prototype.pluralize)
311  
-{
312  
-    String.prototype.pluralize = function(plural)
313  
-    {
314  
-        return InflectionJS.apply_rules(
315  
-            this,
316  
-            this._plural_rules,
317  
-            this._uncountable_words,
318  
-            plural
319  
-        );
320  
-    };
321  
-}
322  
-
323  
-/*
324  
-  This function adds singularization support to every String object
325  
-    Signature:
326  
-      String.singularize(singular) == String
327  
-    Arguments:
328  
-      singular - String (optional) - overrides normal output with said String
329  
-    Returns:
330  
-      String - plural English language nouns are returned in singular form
331  
-    Examples:
332  
-      "people".singularize() == "person"
333  
-      "octopi".singularize() == "octopus"
334  
-      "Hats".singularize() == "Hat"
335  
-      "guys".singularize("person") == "person"
336  
-*/
337  
-if (!String.prototype.singularize)
338  
-{
339  
-    String.prototype.singularize = function(singular)
340  
-    {
341  
-        return InflectionJS.apply_rules(
342  
-            this,
343  
-            this._singular_rules,
344  
-            this._uncountable_words,
345  
-            singular
346  
-        );
347  
-    };
348  
-}
349  
-
350  
-/*
351  
-  This function adds camelization support to every String object
352  
-    Signature:
353  
-      String.camelize(lowFirstLetter) == String
354  
-    Arguments:
355  
-      lowFirstLetter - boolean (optional) - default is to capitalize the first
356  
-        letter of the results... passing true will lowercase it
357  
-    Returns:
358  
-      String - lower case underscored words will be returned in camel case
359  
-        additionally '/' is translated to '::'
360  
-    Examples:
361  
-      "message_properties".camelize() == "MessageProperties"
362  
-      "message_properties".camelize(true) == "messageProperties"
363  
-*/
364  
-if (!String.prototype.camelize)
365  
-{
366  
-     String.prototype.camelize = function(lowFirstLetter)
367  
-     {
368  
-        var str = this.toLowerCase();
369  
-        var str_path = str.split('/');
370  
-        for (var i = 0; i < str_path.length; i++)
371  
-        {
372  
-            var str_arr = str_path[i].split('_');
373  
-            var initX = ((lowFirstLetter && i + 1 === str_path.length) ? (1) : (0));
374  
-            for (var x = initX; x < str_arr.length; x++)
375  
-            {
376  
-                str_arr[x] = str_arr[x].charAt(0).toUpperCase() + str_arr[x].substring(1);
377  
-            }
378  
-            str_path[i] = str_arr.join('');
379  
-        }
380  
-        str = str_path.join('::');
381  
-        return str;
382  
-    };
383  
-}
384  
-
385  
-/*
386  
-  This function adds underscore support to every String object
387  
-    Signature:
388  
-      String.underscore() == String
389  
-    Arguments:
390  
-      N/A
391  
-    Returns:
392  
-      String - camel cased words are returned as lower cased and underscored
393  
-        additionally '::' is translated to '/'
394  
-    Examples:
395  
-      "MessageProperties".camelize() == "message_properties"
396  
-      "messageProperties".underscore() == "message_properties"
397  
-*/
398  
-if (!String.prototype.underscore)
399  
-{
400  
-     String.prototype.underscore = function()
401  
-     {
402  
-        var str = this;
403  
-        var str_path = str.split('::');
404  
-        for (var i = 0; i < str_path.length; i++)
405  
-        {
406  
-            str_path[i] = str_path[i].replace(InflectionJS.uppercase, '_$1');
407  
-            str_path[i] = str_path[i].replace(InflectionJS.underbar_prefix, '');
408  
-        }
409  
-        str = str_path.join('/').toLowerCase();
410  
-        return str;
411  
-    };
412  
-}
413  
-
414  
-/*
415  
-  This function adds humanize support to every String object
416  
-    Signature:
417  
-      String.humanize(lowFirstLetter) == String
418  
-    Arguments:
419  
-      lowFirstLetter - boolean (optional) - default is to capitalize the first
420  
-        letter of the results... passing true will lowercase it
421  
-    Returns:
422  
-      String - lower case underscored words will be returned in humanized form
423  
-    Examples:
424  
-      "message_properties".humanize() == "Message properties"
425  
-      "message_properties".humanize(true) == "message properties"
426  
-*/
427  
-if (!String.prototype.humanize)
428  
-{
429  
-    String.prototype.humanize = function(lowFirstLetter)
430  
-    {
431  
-        var str = this.toLowerCase();
432  
-        str = str.replace(InflectionJS.id_suffix, '');
433  
-        str = str.replace(InflectionJS.underbar, ' ');
434  
-        if (!lowFirstLetter)
435  
-        {
436  
-            str = str.capitalize();
437  
-        }
438  
-        return str;
439  
-    };
440  
-}
441  
-
442  
-/*
443  
-  This function adds capitalization support to every String object
444  
-    Signature:
445  
-      String.capitalize() == String
446  
-    Arguments:
447  
-      N/A
448  
-    Returns:
449  
-      String - all characters will be lower case and the first will be upper
450  
-    Examples:
451  
-      "message_properties".capitalize() == "Message_properties"
452  
-      "message properties".capitalize() == "Message properties"
453  
-*/
454  
-if (!String.prototype.capitalize)
455  
-{
456  
-    String.prototype.capitalize = function()
457  
-    {
458  
-        var str = this.toLowerCase();
459  
-        str = str.substring(0, 1).toUpperCase() + str.substring(1);
460  
-        return str;
461  
-    };
462  
-}
463  
-
464  
-/*
465  
-  This function adds dasherization support to every String object
466  
-    Signature:
467  
-      String.dasherize() == String
468  
-    Arguments:
469  
-      N/A
470  
-    Returns:
471  
-      String - replaces all spaces or underbars with dashes
472  
-    Examples:
473  
-      "message_properties".capitalize() == "message-properties"
474  
-      "Message Properties".capitalize() == "Message-Properties"
475  
-*/
476  
-if (!String.prototype.dasherize)
477  
-{
478  
-    String.prototype.dasherize = function()
479  
-    {
480  
-        var str = this;
481  
-        str = str.replace(InflectionJS.space_or_underbar, '-');
482  
-        return str;
483  
-    };
484  
-}
485  
-
486  
-/*
487  
-  This function adds titleize support to every String object
488  
-    Signature:
489  
-      String.titleize() == String
490  
-    Arguments:
491  
-      N/A
492  
-    Returns:
493  
-      String - capitalizes words as you would for a book title
494  
-    Examples:
495  
-      "message_properties".titleize() == "Message Properties"
496  
-      "message properties to keep".titleize() == "Message Properties to Keep"
497  
-*/
498  
-if (!String.prototype.titleize)
499  
-{
500  
-    String.prototype.titleize = function()
501  
-    {
502  
-        var str = this.toLowerCase();
503  
-        str = str.replace(InflectionJS.underbar, ' ');
504  
-        var str_arr = str.split(' ');
505  
-        for (var x = 0; x < str_arr.length; x++)
506  
-        {
507  
-            var d = str_arr[x].split('-');
508  
-            for (var i = 0; i < d.length; i++)
509  
-            {
510  
-                if (this._non_titlecased_words.indexOf(d[i].toLowerCase()) < 0)
511  
-                {
512  
-                    d[i] = d[i].capitalize();
513  
-                }
514  
-            }
515  
-            str_arr[x] = d.join('-');