Commits

Anonymous committed 0007def

tests on lists and shows

Comments (0)

Files changed (27)

+{
+    "env": {
+        "default": {
+            "db": "http://127.0.0.1:5984/benoitc"
+        }
+    }
+}

_attachments/index.html

+<!DOCTYPE html>
+<html>
+  <head>
+    <title>Generated CouchApp</title>
+    <link rel="stylesheet" href="style/main.css" type="text/css">
+  </head>
+  <body>
+    <h1>Generated CouchApp</h1>
+    <ul id="view"></ul>
+  </body>
+  <script src="/_utils/script/json2.js"></script>
+  <script src="/_utils/script/jquery.js?1.2.6"></script>
+  <script src="/_utils/script/jquery.couch.js?0.8.0"></script>
+  <script type="text/javascript" charset="utf-8">
+    $(function() {
+      var dbname = document.location.href.split('/')[3];
+      var design = unescape(document.location.href).split('/')[5];
+      var DB = $.couch.db(dbname);
+      DB.view(design+"/example",{success: function(json) {
+        $("#view").html(json.rows.map(function(row) {
+          return '<li>'+row.key+'</li>';
+        }).join(''));
+      }});
+    });
+  </script>
+</html>

_attachments/style/main.css

+/* add styles here */

_local/feeder/__init__.py

Empty file added.

_local/feeder/fetch_delicious.py

+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com> 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+from xml.etree import ElementTree as ET
+
+from simplecouchdb import Server
+from simplecouchdb.schema import *
+
+from resources.delicious import DeliciousResource
+
+server = Server()
+db = server.create_session('benoitc')
+
+delicious = DeliciousResource('bchesneau', 'as61zx')
+
+class FeedItem(Document):
+    resource = StringProperty()
+    created_at = DateTimeProperty()
+    fetched = DateTimeProperty()
+    item = DictProperty()
+
+FeedItem = db(FeedItem)
+
+
+def main():
+    posts = delicious.get_all()
+    print posts[0]
+
+if __name__ == '__main__':
+    main()
+

_local/feeder/fetch_twitter.py

+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com> 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+
+import calendar
+import rfc822
+from datetime import datetime
+import xml.etree.ElementTree as ET
+
+from simplecouchdb import Server
+from simplecouchdb.schema import *
+
+from resources.twitter import TwitterResource
+
+
+server = Server()
+db = server.create_session('benoitc')
+
+
+class FeedItem(Document):
+    resource = StringProperty()
+    created_at = DateTimeProperty()
+    fetched = DateTimeProperty()
+    item = DictProperty()
+
+FeedItem = db(FeedItem)
+
+twitter = TwitterResource('benoitc', 'as61zx')
+
+
+def main():
+    for i in xrange(1, 17):
+        page = twitter.user_timeline(page=str(i))
+        for item in page:
+            created_at = datetime.datetime.utcfromtimestamp(
+                    calendar.timegm(rfc822.parsedate_tz(item['created_at'])))
+            tweet = FeedItem(
+                    resource='twitter', 
+                    created_at = created_at,
+                    fetched = datetime.datetime.utcnow(),
+                    item = item)
+            tweet.save()
+        time.sleep(1)
+                    
+   
+
+if __name__ == '__main__':
+    main()

_local/feeder/resources/__init__.py

Empty file added.

_local/feeder/resources/__init__.pyc

Binary file added.

_local/feeder/resources/delicious.py

+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com> 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+import httplib
+import socket
+from calendar import timegm
+import decimal
+import datetime
+import time
+from xml.etree import ElementTree as ET
+
+from restclient import Resource, RequestFailed, ResourceNotFound
+from restclient.transport import createHTTPTransport
+
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
+
+__version__ = '0.1'
+USER_AGENT = 'benoitc.org/%s' % __version__
+
+
+class DeliciousResource(Resource):
+
+    DELICIOUS_URL = 'https://api.del.icio.us'
+
+    def __init__(self, username, password):
+        # create transport
+        transport = createHTTPTransport()
+        transport.add_credentials(username, password)
+
+        # init resource
+        Resource.__init__(self, uri=self.DELICIOUS_URL, transport=transport)
+
+    def get_all(self):
+        rst = self.get('/v1/posts/all', meta='yes')
+        tree = ET.XML(str(rst))
+        posts = []
+        for post in tree:
+            tag = post.get('tag', '')
+            post['tag'] = tag.split(' ')
+            post['time'] = DateTimeProperty.to_python(post['time'])
+            posts.append(post.attrib)
+
+        return posts

_local/feeder/resources/delicious.pyc

Binary file added.

_local/feeder/resources/twitter.py

+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com> 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+
+import httplib
+import socket
+
+from restclient import Resource, RequestFailed, ResourceNotFound
+from restclient.transport import createHTTPTransport
+
+
+
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
+
+__version__ = '0.1'
+USER_AGENT = 'benoitc.org/%s' % __version__
+
+
+class TwitterResource(Resource):
+
+    TWITTER_URL = 'http://twitter.com'
+
+    def __init__(self, username, password):
+        # create transport
+        transport = createHTTPTransport()
+        transport.add_credentials(username, password)
+
+        # init resource
+        Resource.__init__(self, uri=self.TWITTER_URL, transport=transport)
+
+    def request(self, method, path=None, payload=None, headers=None, **params):    
+        headers = headers or {}
+        headers.setdefault('Accept', 'application/json')
+        headers.setdefault('User-Agent', USER_AGENT)
+
+        def _make_request(retry=3):
+            try:
+                return Resource.request(self, method, path=path,
+                        payload=payload, headers=headers, **params)
+            except (socket.error, httplib.BadStatusLine), e:
+                if retry > 0:
+                    time.sleep(0.4)
+                    return _make_request(retry - 1)
+                raise
+            except:
+                raise
+        
+        # we just let connexion raise if anything wrong happend
+        data = _make_request()
+        if data:
+            try:
+                data = json.loads(data)
+            except ValueError:
+                pass
+        return data
+
+    def friends_timeline(self, headers=None, **kwargs):
+        return self.get('/statuses/friends_timeline.json',
+                headers=headers)
+
+    def replies(self, headers=None, **kwargs):
+        return self.get('/statuses/replies.json')
+
+    def user_timeline(self, headers=None, **kwargs):
+        return self.get('/statuses/user_timeline.json') 

_local/feeder/resources/twitter.pyc

Binary file added.
+# -*- coding: utf-8 -*-
+#
+# Copyright (c) 2008-2009 Benoit Chesneau <benoitc@e-engura.com> 
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+#
+
+"""
+simple script used to display notifications for twitter
+and save them in db
+"""
+from datetime import datetime
+import os
+import httplib
+import socket
+import sys
+import time
+
+from eventlet import api
+import pygtk
+pygtk.require('2.0')
+import pynotify 
+from restclient import Resource, RequestFailed, ResourceNotFound
+from restclient.transport import createHTTPTransport
+
+try:
+    import simplejson as json
+except ImportError:
+    import json
+
+from simplecouchdb import Server
+from simplecouchdb.schema import *
+
+
+__version__ = '0.1'
+USER_AGENT = 'stweet/%s' % __version__
+PIDFILE = '/home/benoitc/.stweet.pid'
+
+
+COUCHDB_NODE = 'http://127.0.0.1:5984'
+COUCHDB_DB = 'benoitc'
+
+
+class TwitterResource(Resource):
+
+    TWITTER_URL = 'http://twitter.com'
+
+    def __init__(self, username, password):
+        # create transport
+        transport = createHTTPTransport()
+        transport.add_credentials(username, password)
+
+        # init resource
+        Resource.__init__(self, uri=self.TWITTER_URL, transport=transport)
+
+    def request(self, method, path=None, payload=None, headers=None, **params):    
+        headers = headers or {}
+        headers.setdefault('Accept', 'application/json')
+        headers.setdefault('User-Agent', USER_AGENT)
+
+        def _make_request(retry=3):
+            try:
+                return Resource.request(self, method, path=path,
+                        payload=payload, headers=headers, **params)
+            except (socket.error, httplib.BadStatusLine), e:
+                if retry > 0:
+                    time.sleep(0.4)
+                    return _make_request(retry - 1)
+                raise
+            except:
+                raise
+        
+        # we just let connexion raise if anything wrong happend
+        data = _make_request()
+        if data:
+            try:
+                data = json.loads(data)
+            except ValueError:
+                pass
+        return data
+
+    def friends_timeline(self, headers=None):
+        return self.get('/statuses/friends_timeline.json',
+                headers=headers)
+
+    def replies(self):
+        return self.get('/statuses/replies.json')
+    
+class TwitterService(object):
+    def __init__(self, username, password, dbname):
+        self.res = TwitterResource(username, password)
+        server = Server()
+        try:
+            self.db = server.create_db(dbname)
+        except:
+            self.db = server[dbname]
+        self.lastmod = None
+
+    def run(self):
+        if not pynotify.init("Stweet"):
+            sys.exit(1)
+        
+        while True:
+            api.spawn(self.get_timeline)
+            api.sleep()
+            time.sleep(60)
+            
+    def get_timeline(self):
+        headers = {}
+        if self.lastmod is not None:
+            headers['If-Modified-Since'] = self.lastmod.strftime("%a, %d %b %Y %H:%M:%S GMT")
+        result = self.res.friends_timeline(headers=headers)
+        
+        self.lastmod = datetime.datetime.utcnow()
+        if result:
+            api.spawn(self.notify, result)
+            api.sleep
+
+    def notify(self, tweets):
+        self.save(tweets)
+   
+        for tweet in tweets:
+            api.spawn(self.desktop_notify, tweet)
+        api.sleep(0.4)
+
+    def desktop_notify(self, tweet):
+        title = "Twitter: %s" % tweet['user']['screen_name']
+        message = tweet['text']
+        n = pynotify.Notification(title, message)
+    
+        if not n.show():
+            print "Failed to send notification"
+            sys.exit(1)
+
+    def save(self, tweets):
+        self.db.save(tweets)
+
+
+def deaemonize():
+    # do the UNIX double-fork magic, see Stevens' "Advanced
+    # Programming in the UNIX Environment" for details (ISBN 0201563177)
+    try:
+        pid = os.fork()
+        if pid > 0:
+            # exit first parent
+            sys.exit(0)
+    except OSError, e:
+        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror)
+        sys.exit(1)
+
+    # decouple from parent environment
+    os.chdir("/")   #don't prevent unmounting....
+    os.setsid()
+    os.umask(0)
+
+    # do second fork
+    try:
+        pid = os.fork()
+        if pid > 0:
+            # exit from second parent, print eventual PID before
+            #print "Daemon PID %d" % pid
+            open(PIDFILE,'w').write("%d"%pid)
+            sys.exit(0)
+    except OSError, e:
+        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror)
+        sys.exit(1)
+
+if __name__ == '__main__':
+    daemonize()
+    service = TwitterService('benoitc', 'as61zx', 'benoitc')
+    service.run()
+
+
+---
+title: De retour sur le net
+summary: 
+language: fr
+filter:
+    - markdown
+---
+
+Et oui il fallair bien que cela arrive, j'ai de nouveau un carnet sur le
+net. Dans la mesure du possible ce carnet sera tenu en 2 langues. Vous y
+trouverez mes notes sur ...
+
+

lib/helpers/couchapp.js

+// this stuff should be properly namespaced etc
+
+// from couch.js
+function encodeOptions(options) {
+  var buf = []
+  if (typeof(options) == "object" && options !== null) {
+    for (var name in options) {
+      if (!options.hasOwnProperty(name)) continue;
+      var value = options[name];
+      if (name == "key" || name == "startkey" || name == "endkey") {
+        value = toJSON(value);
+      }
+      buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
+    }
+  }
+  if (!buf.length) {
+    return "";
+  }
+  return "?" + buf.join("&");
+}
+
+function concatArgs(array, args) {
+  for (var i=0; i < args.length; i++) {
+    array.push(args[i]);
+  };
+  return array;
+};
+
+function makePath(array) {
+  var options, path;
+  
+  if (typeof array[array.length - 1] != "string") {
+    // it's a params hash
+    options = array.pop();
+    log({options:options});
+  }
+  path = array.map(function(item) {return encodeURIComponent(item)}).join('/');
+  if (options) {
+    return path + encodeOptions(options);
+  } else {
+    return path;    
+  }
+};
+
+function assetPath() {
+  var parts = ['',req.path[0], '_design', req.path[2]];
+  return makePath(concatArgs(parts, arguments));
+};
+
+function showPath() {
+  var parts = ['',req.path[0], '_show', req.path[2]];
+  return makePath(concatArgs(parts, arguments));
+};
+
+function listPath() {
+  var parts = ['',req.path[0], '_list', req.path[2]];
+  return makePath(concatArgs(parts, arguments));
+};
+
+function makeAbsolute(req, path) {
+  return 'http://' + req.headers.Host + path;
+}
+
+function f(n) {    // Format integers to have at least two digits.
+    return n < 10 ? '0' + n : n;
+}
+
+Date.prototype.rfc3339 = function() {
+    return this.getUTCFullYear()   + '-' +
+         f(this.getUTCMonth() + 1) + '-' +
+         f(this.getUTCDate())      + 'T' +
+         f(this.getUTCHours())     + ':' +
+         f(this.getUTCMinutes())   + ':' +
+         f(this.getUTCSeconds())   + 'Z';
+};

lib/helpers/ejs/ejs.js

+/*--------------------------------------------------------------------------
+ *  EJS - Embedded JavaScript, version 0.1.0
+ *  Copyright (c) 2007 Edward Benson
+ *  http://www.edwardbenson.com/projects/ejs
+ *  ------------------------------------------------------------------------
+ *
+ *  EJS is freely distributable under the terms of an MIT-style license.
+ *
+ *  EJS is a client-side preprocessing engine written in and for JavaScript.
+ *  If you have used PHP, ASP, JSP, or ERB then you get the idea: code embedded
+ *  in <% // Code here %> tags will be executed, and code embedded in <%= .. %> 
+ *  tags will be evaluated and appended to the output. 
+ * 
+ *  This is essentially a direct JavaScript port of Masatoshi Seki's erb.rb 
+ *  from the Ruby Core, though it contains a subset of ERB's functionality. 
+ * 
+ *  Requirements:
+ *      prototype.js
+ * 
+ *  Usage:
+ *      // source should be either a string or a DOM node whose innerHTML
+ *      // contains EJB source.
+ *  	var source = "<% var ejb="EJB"; %><h1>Hello, <%= ejb %>!</h1>"; 
+ *      var compiler = new EjsCompiler(source);		
+ *	    compiler.compile();	
+ *	    var output = eval(compiler.out);
+ *      alert(output); // -> "<h1>Hello, EJB!</h1>"
+ *       
+ *  For a demo:      see demo.html
+ *  For the license: see license.txt
+ *
+ *--------------------------------------------------------------------------*/
+
+/* Make a split function like Ruby's: "abc".split(/b/) -> ['a', 'b', 'c'] */
+String.prototype.rsplit = function(regex) {
+	var item = this;
+	var result = regex.exec(item);
+	var retArr = new Array();
+	while (result != null)
+	{
+		var first_idx = result.index;
+		var last_idx = regex.lastIndex;
+		if ((first_idx) != 0)
+		{
+			var first_bit = item.substring(0,first_idx);
+			retArr.push(item.substring(0,first_idx));
+			item = item.slice(first_idx);
+		}		
+		retArr.push(result[0]);
+		item = item.slice(result[0].length);
+		result = regex.exec(item);	
+	}
+	if (! item == '')
+	{
+		retArr.push(item);
+	}
+	return retArr;
+};
+
+/* Chop is nice to have too */
+String.prototype.chop = function() {
+	return this.substr(0, this.length - 1);
+}
+
+/* Adaptation from the Scanner of erb.rb  */
+var EjsScanner = function(source, left, right) {
+	this.left_delimiter = 	left +'%'	//<%
+	this.right_delimiter = 	'%'+right	//>
+	this.double_left = 		left+'%%'
+	this.double_right = 	'%%'+right
+	this.left_equal = 		left+'%='
+	this.left_comment = 	left+'%#'
+	if(left=='[')
+		this.SplitRegexp = /(\[%%)|(%%\])|(\[%=)|(\[%#)|(\[%)|(%\]\n)|(%\])|(\n)/;
+	else
+		this.SplitRegexp = new RegExp('('+this.double_left+')|(%%'+this.double_right+')|('+this.left_equal+')|('+this.left_comment+')|('+this.left_delimiter+')|('+this.right_delimiter+'\n)|('+this.right_delimiter+')|(\n)') 
+	
+	this.source = source;
+	this.stag = null;
+	this.lines = 0;
+};
+EjsView = function(data){
+	this.data = data
+}
+EjsView.prototype.partial = function(options, data){
+	if(!data) data = this.data;
+	return new EJS(options).render(data);
+}
+
+EjsScanner.to_text = function(input){
+	if(input == null || input === undefined)
+        return '';
+    if(input instanceof Date)
+		return input.toDateString();
+	if(input.toString) 
+        return input.toString()
+	return '';
+}
+
+EjsScanner.prototype = {
+
+  /* For each line, scan! */
+  scan: function(block) {
+     scanline = this.scanline;
+	 regex = this.SplitRegexp;
+	 if (! this.source == '')
+	 {
+	 	 var source_split = this.source.rsplit(/\n/);
+	 	 for(var i=0; i<source_split.length; i++) {
+		 	 var item = source_split[i];
+			 this.scanline(item, regex, block);
+		 }
+	 }
+  },
+  
+  /* For each token, block! */
+  scanline: function(line, regex, block) {
+	 this.lines++
+	 var line_split = line.rsplit(regex);
+ 	 for(var i=0; i<line_split.length; i++) {
+	   var token = line_split[i];
+       if (token != null) {
+		   	try{
+	         	block(token, this);
+		 	}catch(e){
+				throw {type: 'EjsScanner', line: this.lines}
+			}
+       }
+	 }
+  }
+};
+
+/* Adaptation from the Buffer of erb.rb  */
+var EjsBuffer = function(pre_cmd, post_cmd) {
+	this.line = new Array();
+	this.script = "";
+	this.pre_cmd = pre_cmd;
+	this.post_cmd = post_cmd;
+	
+	for (var i=0; i<this.pre_cmd.length; i++)
+	{
+		this.push(pre_cmd[i]);
+	}
+}
+EjsBuffer.prototype = {
+	
+  push: function(cmd) {
+	this.line.push(cmd);
+  },
+
+  cr: function() {
+	this.script = this.script + this.line.join('; ');
+	this.line = new Array();
+	this.script = this.script + "\n";
+  },
+
+  close: function() {
+	if (this.line.length > 0)
+	{
+		for (var i=0; i<this.post_cmd.length; i++)
+		{
+			this.push(pre_cmd[i]);
+		}
+		this.script = this.script + this.line.join('; ');
+		line = null;
+	}
+  }
+ 	
+};
+
+/* Adaptation from the Compiler of erb.rb  */
+EjsCompiler = function(source, left) {
+	this.pre_cmd = ['___ejsO = "";'];
+	this.post_cmd = new Array();
+	this.source = ' ';	
+	if (source != null)
+	{
+		if (typeof source == 'string')
+		{
+		    source = source.replace(/\r\n/g, "\n");
+            source = source.replace(/\r/g,   "\n");
+			this.source = source;
+		}
+		else if (source.innerHTML)
+		{
+			this.source = source.innerHTML;
+		} 
+		if (typeof this.source != 'string')
+		{
+			this.source = "";
+		}
+	}
+	left = left || '<'
+	var right = '>'
+	switch(left) {
+		case '[':
+			right = ']'
+			break;
+		case '<':
+			break;
+		default:
+			throw left+' is not a supported deliminator'
+			break;
+	}
+	this.scanner = new EjsScanner(this.source, left, right);
+	this.out = '';
+}
+EjsCompiler.prototype = {
+  compile: function(options) {
+  	options = options || {};
+	this.out = '';
+	var put_cmd = "___ejsO += ";
+	var insert_cmd = put_cmd;
+	var buff = new EjsBuffer(this.pre_cmd, this.post_cmd);		
+	var content = '';
+	var clean = function(content)
+	{
+	    content = content.replace(/\\/g, '\\\\');
+        content = content.replace(/\n/g, '\\n');
+        content = content.replace(/"/g,  '\\"');
+        return content;
+	} 
+	this.scanner.scan(function(token, scanner) {
+		if (scanner.stag == null)
+		{
+			//alert(token+'|'+(token == "\n"))
+			switch(token) {
+				case '\n':
+					content = content + "\n";
+					buff.push(put_cmd + '"' + clean(content) + '";');
+					buff.cr()
+					content = '';
+					break;
+				case scanner.left_delimiter:
+				case scanner.left_equal:
+				case scanner.left_comment:
+					scanner.stag = token;
+					if (content.length > 0)
+					{
+						// Chould be content.dump in Ruby
+						
+						buff.push(put_cmd + '"' + clean(content) + '"');
+					}
+					content = '';
+					break;
+				case scanner.double_left:
+					content = content + scanner.left_delimiter;
+					break;
+				default:
+					content = content + token;
+					break;
+			}
+		}
+		else {
+			switch(token) {
+				case scanner.right_delimiter:
+					switch(scanner.stag) {
+						case scanner.left_delimiter:
+							if (content[content.length - 1] == '\n')
+							{
+								content = content.chop();
+								buff.push(content);
+								buff.cr();
+							}
+							else {
+								buff.push(content);
+							}
+							break;
+						case scanner.left_equal:
+							buff.push(insert_cmd + "(EjsScanner.to_text(" + content + "))");
+							break;
+					}
+					scanner.stag = null;
+					content = '';
+					break;
+				case scanner.double_right:
+					content = content + scanner.right_delimiter;
+					break;
+				default:
+					content = content + token;
+					break;
+			}
+		}
+	});
+	if (content.length > 0)
+	{
+		// Chould be content.dump in Ruby
+		buff.push(put_cmd + '"' + clean(content) + '"');
+	}
+	buff.close();
+	this.out = buff.script + ";";
+	var to_be_evaled = 'this.process = function(_CONTEXT,_VIEW) { try { with(_VIEW) { with (_CONTEXT) {'+this.out+" return ___ejsO;}}}catch(e){e.lineNumber=null;throw e;}};";
+	
+	try{
+		eval(to_be_evaled);
+	}catch(e){
+		if(typeof JSLINT != 'undefined'){
+			JSLINT(this.out)
+			for(var i = 0; i < JSLINT.errors.length; i++){
+				var error = JSLINT.errors[i];
+				if(error.reason != "Unnecessary semicolon."){
+					error.line++;
+					var e = new Error();
+					e.lineNumber = error.line;
+					e.message = error.reason;
+					if(options.url)
+						e.fileName = options.url;
+					throw e;
+				}
+			}
+		}else{
+			throw e;
+		}
+	}
+  }
+}
+
+
+//type, cache, folder
+EJS = function( options ){
+	this.set_options(options)
+	
+	if(options.url){
+		var template = EJS.get(options.url, this.cache)
+		if (template) return template;
+	    if (template == EJS.INVALID_PATH) return null;
+		this.text = EJS.request(options.url)
+		if(this.text == null){
+			//EJS.update(options.url, this.INVALID_PATH);
+			throw 'There is no template at '+options.url
+		}
+		this.name = options.url
+	}else if(options.element)
+	{
+		if(typeof options.element == 'string'){
+			var name = options.element
+			options.element = document.getElementById(  options.element )
+			if(options.element == null) throw name+'does not exist!'
+		}
+		if(options.element.value){
+			this.text = options.element.value
+		}else{
+			this.text = options.element.innerHTML
+		}
+		this.name = options.element.id
+		this.type = '['
+	}
+	var template = new EjsCompiler(this.text, this.type);
+
+	template.compile(options);
+
+	
+	EJS.update(this.name, this);
+	this.template = template
+}
+EJS.config = function(options){
+	EJS.cache = options.cache != null ? options.cache : EJS.cache
+	EJS.type = options.type != null ? options.type : EJS.type
+	var templates_directory = {} //nice and private container
+	
+	EJS.get = function(path, cache){
+		if(cache == false) return null;
+		if(templates_directory[path]) return templates_directory[path];
+  		return null;
+	}
+	
+	EJS.update = function(path, template) { 
+		if(path == null) return;
+		templates_directory[path] = template 
+	}
+	
+	EJS.INVALID_PATH =  -1;
+	
+	
+}
+EJS.config( {cache: true, type: '<' } )
+
+EJS.prototype = {
+	render : function(object){
+		var v = new EjsView(object);
+		return this.template.process.call(v, object,v);
+	},
+	out : function(){
+		return this.template.out
+	},
+	set_options : function(options){
+		this.type = options.type != null ? options.type : EJS.type
+		this.cache = options.cache != null ? options.cache : EJS.cache
+		this.text = options.text != null ? options.text : null
+		this.name = options.name != null ? options.name : null
+	},
+	// called without options, returns a function that takes the object
+	// called with options being a string, uses that as a url
+	// called with options as an object
+	update : function(element, options){
+		if(typeof element == 'string'){
+			element = document.getElementById(element)
+		}
+		if(options == null){
+			_template = this;
+			return function(object){
+				EJS.prototype.update.call(_template, element, object)
+			}
+		}
+		if(typeof options == 'string'){
+			params = {}
+			params.url = options
+			_template = this;
+			params.onComplete = function(request){
+				var object = eval( request.responseText )
+				EJS.prototype.update.call(_template, element, object)
+			}
+			EJS.ajax_request(params)
+		}else
+		{
+			element.innerHTML = this.render(options)
+		}
+	}
+}
+
+	EJS.newRequest = function(){
+	   var factories = [function() { return new ActiveXObject("Msxml2.XMLHTTP"); },function() { return new XMLHttpRequest(); },function() { return new ActiveXObject("Microsoft.XMLHTTP"); }];
+	   for(var i = 0; i < factories.length; i++) {
+	        try {
+	            var request = factories[i]();
+	            if (request != null)  return request;
+	        }
+	        catch(e) { continue;}
+	   }
+	}
+	
+	EJS.request = function(path){
+	   var request = new EJS.newRequest()
+	   request.open("GET", path, false);
+	   
+	   try{request.send(null);}
+	   catch(e){return null;}
+	   
+	   if ( request.status == 404 || request.status == 2 ||(request.status == 0 && request.responseText == '') ) return null;
+	   
+	   return request.responseText
+	}
+	EJS.ajax_request = function(params){
+		params.method = ( params.method ? params.method : 'GET')
+		
+		var request = new EJS.newRequest();
+		request.onreadystatechange = function(){
+			if(request.readyState == 4){
+				if(request.status == 200){
+					params.onComplete(request)
+				}else
+				{
+					params.onComplete(request)
+				}
+			}
+		}
+		request.open(params.method, params.url)
+		request.send(null)
+	}
+//}
+
+

lib/helpers/ejs/view.js

+EjsView.prototype.date_tag = function(name, value , html_options) {
+    if(! (value instanceof Date))
+		value = new Date()
+	
+	var month_names = ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"];
+	var years = [], months = [], days =[];
+	var year = value.getFullYear();
+	var month = value.getMonth();
+	var day = value.getDate();
+	for(var y = year - 15; y < year+15 ; y++)
+	{
+		years.push({value: y, text: y})
+	}
+	for(var m = 0; m < 12; m++)
+	{
+		months.push({value: (m), text: month_names[m]})
+	}
+	for(var d = 0; d < 31; d++)
+	{
+		days.push({value: (d+1), text: (d+1)})
+	}
+	var year_select = this.select_tag(name+'[year]', year, years, {id: name+'[year]'} )
+	var month_select = this.select_tag(name+'[month]', month, months, {id: name+'[month]'})
+	var day_select = this.select_tag(name+'[day]', day, days, {id: name+'[day]'})
+	
+    return year_select+month_select+day_select;
+}
+
+EjsView.prototype.form_tag = function(action, html_options) {
+                 
+    
+    html_options     = html_options                     || {};
+	html_options.action = action
+    if(html_options.multipart == true) {
+        html_options.method = 'post';
+        html_options.enctype = 'multipart/form-data';
+    }
+    
+    return this.start_tag_for('form', html_options)
+}
+
+EjsView.prototype.form_tag_end = function() { return this.tag_end('form'); }
+
+EjsView.prototype.hidden_field_tag   = function(name, value, html_options) { 
+    return this.input_field_tag(name, value, 'hidden', html_options); 
+}
+
+EjsView.prototype.input_field_tag = function(name, value , inputType, html_options) {
+    
+    html_options = html_options || {};
+    html_options.id  = html_options.id  || name;
+    html_options.value = value || '';
+    html_options.type = inputType || 'text';
+    html_options.name = name;
+    
+    return this.single_tag_for('input', html_options)
+}
+
+EjsView.prototype.is_current_page = function(url) {
+	return (window.location.href == url || window.location.pathname == url ? true : false);
+}
+
+EjsView.prototype.link_to = function(name, url, html_options) {
+    if(!name) var name = 'null';
+    if(!html_options) var html_options = {}
+	
+	if(html_options.confirm){
+		html_options.onclick = 
+		" var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "
+		html_options.confirm = null;
+	}
+    html_options.href=url
+    return this.start_tag_for('a', html_options)+name+ this.tag_end('a');
+}
+
+EjsView.prototype.submit_link_to = function(name, url, html_options){
+	if(!name) var name = 'null';
+    if(!html_options) var html_options = {}
+    html_options.onclick = html_options.onclick  || '' ;
+	
+	if(html_options.confirm){
+		html_options.onclick = 
+		" var ret_confirm = confirm(\""+html_options.confirm+"\"); if(!ret_confirm){ return false;} "
+		html_options.confirm = null;
+	}
+	
+    html_options.value = name;
+	html_options.type = 'submit'
+    html_options.onclick=html_options.onclick+
+		(url ? this.url_for(url) : '')+'return false;';
+    //html_options.href='#'+(options ? Routes.url_for(options) : '')
+	return this.start_tag_for('input', html_options)
+}
+
+EjsView.prototype.link_to_if = function(condition, name, url, html_options, post, block) {
+	return this.link_to_unless((condition == false), name, url, html_options, post, block);
+}
+
+EjsView.prototype.link_to_unless = function(condition, name, url, html_options, block) {
+	html_options = html_options || {};
+	if(condition) {
+		if(block && typeof block == 'function') {
+			return block(name, url, html_options, block);
+		} else {
+			return name;
+		}
+	} else
+		return this.link_to(name, url, html_options);
+}
+
+EjsView.prototype.link_to_unless_current = function(name, url, html_options, block) {
+	html_options = html_options || {};
+	return this.link_to_unless(this.is_current_page(url), name, url, html_options, block)
+}
+
+
+EjsView.prototype.password_field_tag = function(name, value, html_options) { return this.input_field_tag(name, value, 'password', html_options); }
+
+EjsView.prototype.select_tag = function(name, value, choices, html_options) {     
+    html_options = html_options || {};
+    html_options.id  = html_options.id  || name;
+    html_options.value = value;
+	html_options.name = name;
+    
+    var txt = ''
+    txt += this.start_tag_for('select', html_options)
+    
+    for(var i = 0; i < choices.length; i++)
+    {
+        var choice = choices[i];
+        var optionOptions = {value: choice.value}
+        if(choice.value == value)
+            optionOptions.selected ='selected'
+        txt += this.start_tag_for('option', optionOptions )+choice.text+this.tag_end('option')
+    }
+    txt += this.tag_end('select');
+    return txt;
+}
+
+EjsView.prototype.single_tag_for = function(tag, html_options) { return this.tag(tag, html_options, '/>');}
+
+EjsView.prototype.start_tag_for = function(tag, html_options)  { return this.tag(tag, html_options); }
+
+EjsView.prototype.submit_tag = function(name, html_options) {  
+    html_options = html_options || {};
+    //html_options.name  = html_options.id  || 'commit';
+    html_options.type = html_options.type  || 'submit';
+    html_options.value = name || 'Submit';
+    return this.single_tag_for('input', html_options);
+}
+
+EjsView.prototype.tag = function(tag, html_options, end) {
+    if(!end) var end = '>'
+    var txt = ' '
+    for(var attr in html_options) { 
+	   if(html_options[attr] != null)
+        var value = html_options[attr].toString();
+       else
+        var value=''
+       if(attr == "Class") // special case because "class" is a reserved word in IE
+        attr = "class";
+       if( value.indexOf("'") != -1 )
+            txt += attr+'=\"'+value+'\" ' 
+       else
+            txt += attr+"='"+value+"' " 
+    }
+    return '<'+tag+txt+end;
+}
+
+EjsView.prototype.tag_end = function(tag)             { return '</'+tag+'>'; }
+
+EjsView.prototype.text_area_tag = function(name, value, html_options) { 
+    html_options = html_options || {};
+    html_options.id  = html_options.id  || name;
+    html_options.name  = html_options.name  || name;
+	value = value || ''
+    if(html_options.size) {
+        html_options.cols = html_options.size.split('x')[0]
+        html_options.rows = html_options.size.split('x')[1]
+        delete html_options.size
+    }
+    
+    html_options.cols = html_options.cols  || 50;
+    html_options.rows = html_options.rows  || 4;
+    
+    return  this.start_tag_for('textarea', html_options)+value+this.tag_end('textarea')
+}
+EjsView.prototype.text_tag = EjsView.prototype.text_area_tag
+
+EjsView.prototype.text_field_tag     = function(name, value, html_options) { return this.input_field_tag(name, value, 'text', html_options); }
+
+EjsView.prototype.url_for = function(url) {
+        return 'window.location="'+url+'";'
+}
+EjsView.prototype.img_tag = function(image_location, alt, options){
+	options = options || {};
+	options.src = image_location
+	options.alt = alt
+	return this.single_tag_for('img', options)
+}

lib/helpers/md5.js

+/*
+ * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message
+ * Digest Algorithm, as defined in RFC 1321.
+ * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.
+ * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
+ * Distributed under the BSD License
+ * See http://pajhome.org.uk/crypt/md5 for more info.
+ */
+
+/*
+ * Configurable variables. You may need to tweak these to be compatible with
+ * the server-side, but the defaults work in most cases.
+ */
+var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
+var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
+var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */
+
+/*
+ * These are the functions you'll usually want to call
+ * They take string arguments and return either hex or base-64 encoded strings
+ */
+function hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}
+function b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}
+function str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}
+function hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }
+function b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }
+function str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }
+
+/*
+ * Perform a simple self-test to see if the VM is working
+ */
+function md5_vm_test()
+{
+  return hex_md5("abc") == "900150983cd24fb0d6963f7d28e17f72";
+}
+
+/*
+ * Calculate the MD5 of an array of little-endian words, and a bit length
+ keep
+ */
+function core_md5(x, len)
+{
+  /* append padding */
+  x[len >> 5] |= 0x80 << ((len) % 32);
+  x[(((len + 64) >>> 9) << 4) + 14] = len;
+
+  var a =  1732584193;
+  var b = -271733879;
+  var c = -1732584194;
+  var d =  271733878;
+
+  for(var i = 0; i < x.length; i += 16)
+  {
+    var olda = a;
+    var oldb = b;
+    var oldc = c;
+    var oldd = d;
+
+    a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);
+    d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);
+    c = md5_ff(c, d, a, b, x[i+ 2], 17,  606105819);
+    b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);
+    a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);
+    d = md5_ff(d, a, b, c, x[i+ 5], 12,  1200080426);
+    c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);
+    b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);
+    a = md5_ff(a, b, c, d, x[i+ 8], 7 ,  1770035416);
+    d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);
+    c = md5_ff(c, d, a, b, x[i+10], 17, -42063);
+    b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);
+    a = md5_ff(a, b, c, d, x[i+12], 7 ,  1804603682);
+    d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);
+    c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);
+    b = md5_ff(b, c, d, a, x[i+15], 22,  1236535329);
+
+    a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);
+    d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);
+    c = md5_gg(c, d, a, b, x[i+11], 14,  643717713);
+    b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);
+    a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);
+    d = md5_gg(d, a, b, c, x[i+10], 9 ,  38016083);
+    c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);
+    b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);
+    a = md5_gg(a, b, c, d, x[i+ 9], 5 ,  568446438);
+    d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);
+    c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);
+    b = md5_gg(b, c, d, a, x[i+ 8], 20,  1163531501);
+    a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);
+    d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);
+    c = md5_gg(c, d, a, b, x[i+ 7], 14,  1735328473);
+    b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);
+
+    a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);
+    d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);
+    c = md5_hh(c, d, a, b, x[i+11], 16,  1839030562);
+    b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);
+    a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);
+    d = md5_hh(d, a, b, c, x[i+ 4], 11,  1272893353);
+    c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);
+    b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);
+    a = md5_hh(a, b, c, d, x[i+13], 4 ,  681279174);
+    d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);
+    c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);
+    b = md5_hh(b, c, d, a, x[i+ 6], 23,  76029189);
+    a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);
+    d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);
+    c = md5_hh(c, d, a, b, x[i+15], 16,  530742520);
+    b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);
+
+    a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);
+    d = md5_ii(d, a, b, c, x[i+ 7], 10,  1126891415);
+    c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);
+    b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);
+    a = md5_ii(a, b, c, d, x[i+12], 6 ,  1700485571);
+    d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);
+    c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);
+    b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);
+    a = md5_ii(a, b, c, d, x[i+ 8], 6 ,  1873313359);
+    d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);
+    c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);
+    b = md5_ii(b, c, d, a, x[i+13], 21,  1309151649);
+    a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);
+    d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);
+    c = md5_ii(c, d, a, b, x[i+ 2], 15,  718787259);
+    b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);
+
+    a = safe_add(a, olda);
+    b = safe_add(b, oldb);
+    c = safe_add(c, oldc);
+    d = safe_add(d, oldd);
+  }
+  return Array(a, b, c, d);
+
+}
+
+/*
+ * These functions implement the four basic operations the algorithm uses.
+ */
+function md5_cmn(q, a, b, x, s, t)
+{
+  return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);
+}
+function md5_ff(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);
+}
+function md5_gg(a, b, c, d, x, s, t)
+{
+  return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);
+}
+function md5_hh(a, b, c, d, x, s, t)
+{
+  return md5_cmn(b ^ c ^ d, a, b, x, s, t);
+}
+function md5_ii(a, b, c, d, x, s, t)
+{
+  return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);
+}
+
+/*
+ * Calculate the HMAC-MD5, of a key and some data
+ */
+function core_hmac_md5(key, data)
+{
+  var bkey = str2binl(key);
+  if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);
+
+  var ipad = Array(16), opad = Array(16);
+  for(var i = 0; i < 16; i++)
+  {
+    ipad[i] = bkey[i] ^ 0x36363636;
+    opad[i] = bkey[i] ^ 0x5C5C5C5C;
+  }
+
+  var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);
+  return core_md5(opad.concat(hash), 512 + 128);
+}
+
+/*
+ * Add integers, wrapping at 2^32. This uses 16-bit operations internally
+ * to work around bugs in some JS interpreters.
+ */
+function safe_add(x, y)
+{
+  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
+  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
+  return (msw << 16) | (lsw & 0xFFFF);
+}
+
+/*
+ * Bitwise rotate a 32-bit number to the left.
+ */
+function bit_rol(num, cnt)
+{
+  return (num << cnt) | (num >>> (32 - cnt));
+}
+
+/*
+ * Convert a string to an array of little-endian words
+ * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.
+ keep
+ */
+function str2binl(str)
+{
+  var bin = Array();
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < str.length * chrsz; i += chrsz)
+    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);
+  return bin;
+}
+
+/*
+ * Convert an array of little-endian words to a string
+ */
+function binl2str(bin)
+{
+  var str = "";
+  var mask = (1 << chrsz) - 1;
+  for(var i = 0; i < bin.length * 32; i += chrsz)
+    str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a hex string.
+ keep
+ */
+function binl2hex(binarray)
+{
+  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i++)
+  {
+    str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +
+           hex_tab.charAt((binarray[i>>2] >> ((i%4)*8  )) & 0xF);
+  }
+  return str;
+}
+
+/*
+ * Convert an array of little-endian words to a base-64 string
+ */
+function binl2b64(binarray)
+{
+  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+  var str = "";
+  for(var i = 0; i < binarray.length * 4; i += 3)
+  {
+    var triplet = (((binarray[i   >> 2] >> 8 * ( i   %4)) & 0xFF) << 16)
+                | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )
+                |  ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);
+    for(var j = 0; j < 4; j++)
+    {
+      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
+      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
+    }
+  }
+  return str;
+}

lib/helpers/template.js

+// Simple JavaScript Templating
+// John Resig - http://ejohn.org/ - MIT Licensed
+var cache = {};
+
+function template(str, data){
+  // Figure out if we're getting a template, or if we need to
+  // load the template - and be sure to cache the result.
+  var fn = cache[str] ||
+
+  // Generate a reusable function that will serve as a template
+  // generator (and which will be cached).
+    new Function("obj",
+      "var p=[],print=function(){p.push.apply(p,arguments);};" +
+          
+            // Introduce the data as local variables using with(){}
+            "with(obj){p.push('" +
+          
+            // Convert the template into pure JavaScript
+            str
+            .replace(/\n/g, "\\n")
+            .replace(/[\r\t]/g, " ")
+            .replace(/'(?=[^%]*%>)/g,"\t")
+            .split("'").join("\\'")
+            .split("\t").join("'")
+            .replace(/<%=(.+?)%>/g, "',$1,'")
+            .split("<%").join("');")
+            .split("%>").join("p.push('")
+            + "');}return p.join('');");
+  cache[str] = fn;
+  
+  // Provide some basic currying to the user
+  return data ? fn( data ) : fn;
+};

lib/helpers/template2.js

+// simple helper to reder template from ejs
+//
+
+function template(str, data) {
+    return new EJS({text: str}).render(data);
+}
+
+

lib/templates/feeds/head.html

+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="utf-8"/>
+    <title>benoît chesneau web, artisan web.</title>
+
+    <!--[if IE]>
+
+    <script>
+        // allow IE to recognize HTMl5 elements
+        document.createElement('section');
+        document.createElement('article');
+        document.createElement('aside');
+        document.createElement('footer');
+        document.createElement('header');
+        document.createElement('nav');
+        document.createElement('time');
+
+    </script>
+    <![endif]-->
+    <link rel="openid.server" href="http://www.myopenid.com/server">
+    <link rel="openid.delegate" href="http://bchesneau.myopenid.com/">
+    <link rel="home index" href="http://benoitc.org/" type="text/html">
+    <link rel="me author" href="http://benoitc.org/apropos.html" type="text/html">
+
+    <link rel="stylesheet" href="css/screen.css?20090126" type="text/css" media="screen, projection" />
+
+    <script src="/_utils/script/json2.js"></script>
+    <script src="/_utils/script/jquery.js"></script>
+    <script src="/_utils/script/jquery.couch.js"></script>
+
+
+</head>
+test
+<body>

lib/templates/feeds/row.html

Empty file added.

lib/templates/feeds/tail.html

+<footer>
+    <p> Benoît Chesneau</p>    
+</footer>
+</body>
+</html>

lists/.feeds.js.swp

Binary file added.
+function(head, row, req, row_info) {
+  // !json lib.templates.feeds
+  // !code lib.helpers.couchapp
+  // !code lib.helpers.template
+
+    return respondWith(req, {
+        html: function() {
+            if (head) {
+                return template(lib.templates.feeds.head, {});
+            } else if (row) {
+                return '';
+            } else {
+                return template(lib.templates.feeds.tail, {});
+            }
+        }
+    })
+};
+function(head, row, req, row_info) {
+    // !json lib.templates.twitter
+    // !json !code lib.helpers.couchapp
+    // !code lib.helpers.ejs.ejs
+    // !code lib.helpers.ejs.view
+    // !code lib.helpers.template2
+
+    return respondWidth(req, {
+        html: function() {
+            if (head) {
+                return template(lib.templates.index.head, {});
+            } else if (row) {
+            } else {
+                return  template(lib.templates.index.tail, {});
+            }
+        }
+    }

views/twitter/map.js

+function(doc) {
+    if (doc.doc_type == "FeedItem" && (doc.resource && doc.resource == "twitter")) {
+        
+        url = "http://twitter.com/benoitc/status" + doc.item.id;
+        emit(doc.created_at, {
+                text: doc.text,
+                created_at: doc.created_at,
+                url: url });
+    }
+}