Permalink
Browse files

0.1.3 for SocketStream 0.3 beta2

  • Loading branch information...
0 parents commit ab32279bfd57882bb7feeccc66faee3e39843749 Owen Barnes committed Apr 10, 2012
Showing with 377 additions and 0 deletions.
  1. +5 −0 .gitignore
  2. +27 −0 HISTORY.md
  3. +43 −0 README.md
  4. +240 −0 client.js
  5. +44 −0 engine.js
  6. +18 −0 package.json
@@ -0,0 +1,5 @@
+.DS_Store
+node_modules
+dump.rdb
+npm-debug.log
+*.tgz
@@ -0,0 +1,27 @@
+0.1.3 / 2012-04-10
+==================
+
+* New API for SocketStream 0.3 beta2
+* Now includes client code (Hogan VM) for client
+* Improved error reporting in console
+* Reset repo to remove rubbish
+
+
+0.1.2 / 2012-03-17
+==================
+
+* Templates are now appended to `ss.tmpl` instead of global variables (e.g. `HT`)
+* Tip: You may put `window.HT = ss.tmpl` into `entry.js` to avoid changing your code
+* Reduced amount of code output for apps with many templates
+
+
+0.1.1 / 2012-03-17
+==================
+
+* Upgraded `hogan` to 2.0.0
+
+
+0.1.0 / 2012-01-14
+==================
+
+* Initial commit
@@ -0,0 +1,43 @@
+# Hogan Template Engine wrapper for SocketStream 0.3
+
+http://twitter.github.com/hogan.js/
+
+Use pre-compiled Hogan (Mustache-compatible) client-side templates in your app to benefit from increased performance and a smaller client-side library download.
+
+
+### Installation
+
+The `ss-hogan` module is installed by default with new apps created by SocketStream 0.3. If you don't already have it installed, add `ss-hogan` to your application's `package.json` file and then add this line to app.js:
+
+```javascript
+ss.client.templateEngine.use(require('ss-hogan'));
+```
+
+Restart the server. From now on all templates will be pre-compiled and accessibale via the `ss.tmpl` object.
+
+Note: Hogan uses a small [client-side VM](https://raw.github.com/twitter/hogan.js/master/lib/template.js) which renders the pre-compiled templates. This file is included and automatically sent to the client.
+
+
+### Usage
+
+E.g. a template placed in
+
+ /client/templates/offers/latest.html
+
+Can be rendered in your browser with
+
+```javascript
+// assumes var ss = require('socketstream')
+var html = ss.tmpl['offers-latest'].render({name: 'Special Offers'})
+```
+
+
+### Options
+
+When experimenting with Hogan, or converting an app from one template type to another, you may find it advantageous to use multiple template engines and confine use of Hogan to a sub-directory of `/client/templates`.
+
+Directory names can be passed to the second argument as so:
+
+```javascript
+ss.client.templateEngine.use(require('ss-hogan'), '/hogan-templates');
+```
240 client.js
@@ -0,0 +1,240 @@
+/*
+ * Copyright 2011 Twitter, Inc.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+var Hogan = {};
+
+(function (Hogan, useArrayBuffer) {
+ Hogan.Template = function (renderFunc, text, compiler, options) {
+ this.r = renderFunc || this.r;
+ this.c = compiler;
+ this.options = options;
+ this.text = text || '';
+ this.buf = (useArrayBuffer) ? [] : '';
+ }
+
+ Hogan.Template.prototype = {
+ // render: replaced by generated code.
+ r: function (context, partials, indent) { return ''; },
+
+ // variable escaping
+ v: hoganEscape,
+
+ // triple stache
+ t: coerceToString,
+
+ render: function render(context, partials, indent) {
+ return this.ri([context], partials || {}, indent);
+ },
+
+ // render internal -- a hook for overrides that catches partials too
+ ri: function (context, partials, indent) {
+ return this.r(context, partials, indent);
+ },
+
+ // tries to find a partial in the curent scope and render it
+ rp: function(name, context, partials, indent) {
+ var partial = partials[name];
+
+ if (!partial) {
+ return '';
+ }
+
+ if (this.c && typeof partial == 'string') {
+ partial = this.c.compile(partial, this.options);
+ }
+
+ return partial.ri(context, partials, indent);
+ },
+
+ // render a section
+ rs: function(context, partials, section) {
+ var tail = context[context.length - 1];
+
+ if (!isArray(tail)) {
+ section(context, partials, this);
+ return;
+ }
+
+ for (var i = 0; i < tail.length; i++) {
+ context.push(tail[i]);
+ section(context, partials, this);
+ context.pop();
+ }
+ },
+
+ // maybe start a section
+ s: function(val, ctx, partials, inverted, start, end, tags) {
+ var pass;
+
+ if (isArray(val) && val.length === 0) {
+ return false;
+ }
+
+ if (typeof val == 'function') {
+ val = this.ls(val, ctx, partials, inverted, start, end, tags);
+ }
+
+ pass = (val === '') || !!val;
+
+ if (!inverted && pass && ctx) {
+ ctx.push((typeof val == 'object') ? val : ctx[ctx.length - 1]);
+ }
+
+ return pass;
+ },
+
+ // find values with dotted names
+ d: function(key, ctx, partials, returnFound) {
+ var names = key.split('.'),
+ val = this.f(names[0], ctx, partials, returnFound),
+ cx = null;
+
+ if (key === '.' && isArray(ctx[ctx.length - 2])) {
+ return ctx[ctx.length - 1];
+ }
+
+ for (var i = 1; i < names.length; i++) {
+ if (val && typeof val == 'object' && names[i] in val) {
+ cx = val;
+ val = val[names[i]];
+ } else {
+ val = '';
+ }
+ }
+
+ if (returnFound && !val) {
+ return false;
+ }
+
+ if (!returnFound && typeof val == 'function') {
+ ctx.push(cx);
+ val = this.lv(val, ctx, partials);
+ ctx.pop();
+ }
+
+ return val;
+ },
+
+ // find values with normal names
+ f: function(key, ctx, partials, returnFound) {
+ var val = false,
+ v = null,
+ found = false;
+
+ for (var i = ctx.length - 1; i >= 0; i--) {
+ v = ctx[i];
+ if (v && typeof v == 'object' && key in v) {
+ val = v[key];
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ return (returnFound) ? false : "";
+ }
+
+ if (!returnFound && typeof val == 'function') {
+ val = this.lv(val, ctx, partials);
+ }
+
+ return val;
+ },
+
+ // higher order templates
+ ho: function(val, cx, partials, text, tags) {
+ var compiler = this.c;
+ var t = val.call(cx, text, function(t) {
+ return compiler.compile(t, {delimiters: tags}).render(cx, partials);
+ });
+ this.b(compiler.compile(t.toString(), {delimiters: tags}).render(cx, partials));
+ return false;
+ },
+
+ // template result buffering
+ b: (useArrayBuffer) ? function(s) { this.buf.push(s); } :
+ function(s) { this.buf += s; },
+ fl: (useArrayBuffer) ? function() { var r = this.buf.join(''); this.buf = []; return r; } :
+ function() { var r = this.buf; this.buf = ''; return r; },
+
+ // lambda replace section
+ ls: function(val, ctx, partials, inverted, start, end, tags) {
+ var cx = ctx[ctx.length - 1],
+ t = null;
+
+ if (!inverted && this.c && val.length > 0) {
+ return this.ho(val, cx, partials, this.text.substring(start, end), tags);
+ }
+
+ t = val.call(cx);
+
+ if (typeof t == 'function') {
+ if (inverted) {
+ return true;
+ } else if (this.c) {
+ return this.ho(t, cx, partials, this.text.substring(start, end), tags);
+ }
+ }
+
+ return t;
+ },
+
+ // lambda replace variable
+ lv: function(val, ctx, partials) {
+ var cx = ctx[ctx.length - 1];
+ var result = val.call(cx);
+ if (typeof result == 'function') {
+ result = result.call(cx);
+ }
+ result = result.toString();
+
+ if (this.c && ~result.indexOf("{\u007B")) {
+ return this.c.compile(result).render(cx, partials);
+ }
+
+ return result;
+ }
+
+ };
+
+ var rAmp = /&/g,
+ rLt = /</g,
+ rGt = />/g,
+ rApos =/\'/g,
+ rQuot = /\"/g,
+ hChars =/[&<>\"\']/;
+
+
+ function coerceToString(val) {
+ return String((val === null || val === undefined) ? '' : val);
+ }
+
+ function hoganEscape(str) {
+ str = coerceToString(str);
+ return hChars.test(str) ?
+ str
+ .replace(rAmp,'&amp;')
+ .replace(rLt,'&lt;')
+ .replace(rGt,'&gt;')
+ .replace(rApos,'&#39;')
+ .replace(rQuot, '&quot;') :
+ str;
+ }
+
+ var isArray = Array.isArray || function(a) {
+ return Object.prototype.toString.call(a) === '[object Array]';
+ };
+
+})(typeof exports !== 'undefined' ? exports : Hogan);
@@ -0,0 +1,44 @@
+// Hogan Template Engine wrapper for SocketStream 0.3
+
+var fs = require('fs'),
+ path = require('path'),
+ hogan = require('hogan.js');
+
+exports.init = function(ss, config) {
+
+ // Send Hogan VM to the client
+ var clientCode = fs.readFileSync(path.join(__dirname, 'client.js'), 'utf8');
+ ss.client.send('lib', 'hogan-template', clientCode);
+
+ return {
+
+ name: 'Hogan',
+
+ // Opening code to use when a Hogan template is called for the first time
+ prefix: function() {
+ return '<script type="text/javascript">(function(){var ht=Hogan.Template,t=require(\'socketstream\').tmpl;'
+ },
+
+ // Closing code once all Hogan templates have been written into the <script> tag
+ suffix: function() {
+ return '}).call(this);</script>';
+ },
+
+ // Compile template into a function and attach it to ss.tmpl
+ process: function(template, path, id) {
+
+ var compiledTemplate;
+
+ try {
+ compiledTemplate = hogan.compile(template, {asString: true});
+ } catch (e) {
+ var message = '! Error compiling the ' + path + ' template into Hogan';
+ console.log(String.prototype.hasOwnProperty('red') && message.red || message);
+ throw new Error(e);
+ compiledTemplate = '<p>Error</p>';
+ }
+
+ return 't[\'' + id + '\']=new ht(' + compiledTemplate + ');';
+ }
+ }
+}
@@ -0,0 +1,18 @@
+{
+ "name": "ss-hogan",
+ "author": "Owen Barnes <owen@socketstream.org>",
+ "description": "Hogan template engine wrapper providing server-side compiled templates for SocketStream apps",
+ "version": "0.1.3",
+ "main": "./engine.js",
+ "repository": {
+ "type" : "git",
+ "url": "https://github.com/socketstream/ss-hogan.git"
+ },
+ "engines": {
+ "node": ">= 0.6.0"
+ },
+ "dependencies": {
+ "hogan.js": "2.0.0"
+ },
+ "devDependencies": {}
+}

0 comments on commit ab32279

Please sign in to comment.