Commits

Anonymous committed cfc2b87

Fix login. It depends on
https://issues.apache.org/jira/browse/COUCHDB-492

if you want to use cookie auth with users in local.ini.

  • Participants
  • Parent commits e7a1387
  • Branches couchdb010

Comments (0)

Files changed (25)

 .couchapprc
-.DS_Store
+.DS_Store
+.swp

_attachments/js/blog.js

   });
 }
 
+function parseUri(sourceUri){
+   /* parseUri by Steven Levithan (http://badassery.blogspot.com) */
+    var uriPartNames = ["source","protocol","authority","domain","port","path","directoryPath","fileName","query","anchor"];
+    var uriParts = new RegExp("^(?:([^:/?#.]+):)?(?://)?(([^:/?#]*)(?::(\\d*))?)?((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[\\?#]|$)))*/?)?([^?#/]*))?(?:\\?([^#]*))?(?:#(.*))?").exec(sourceUri);
+    var uri = {};
+    
+    for(var i = 0; i < 10; i++){
+        uri[uriPartNames[i]] = (uriParts[i] ? uriParts[i] : "");
+    }
+    
+    // Always end directoryPath with a trailing backslash if a path was present in the source URI
+    // Note that a trailing backslash is NOT automatically inserted within or appended to the "path" key
+    if(uri.directoryPath.length > 0){
+        uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/");
+    }
+    
+    return uri;
+}
 
+function Login(app, options) {
+  var app = app;
+  var options = options || {};
+
+  function login(username, password) {
+    app.login({
+      userdb: "users",
+      username: username,
+      password: password,
+      success: function() {
+        if (options.success) options.success();
+      },
+      error: function() {
+        alert("An error occurred logging in: " + r);
+        if (options.error) options.error();
+      }
+    });
+  }
+
+ $("#flogin").submit(function(e) {
+    e.preventDefault();
+    login($("#username").val(), $("#password").val())
+    return false;
+ });
+}
+    
 function Blog(app) {
 
     function stripScripts(s) {

_attachments/login.html

+<!DOCTYPE html>
+<html lang="fr">
+<head>
+    <meta charset="utf-8"/>
+    <title>benoît chesneau web - login</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/static/benoitc.html" type="text/html">
+
+    <link rel="stylesheet" href="css/benoitc.css" type="text/css" media="screen, projection" />
+
+
+</head>
+
+
+<body>
+    <div id="container">
+        <form name="flogin" id="flogin" action="/_session" method="post">
+            <!--<input type="hidden" id="next" value="_list/index/recent-posts?descending=true&limit=8">-->
+            <ul>
+                <li><label for="username">username</label><input type="text" id="username" name="username" value=""></li>
+                <li><label for="password">password</label><input type="password" id="password" name="password" value=""></li>
+            </ul>
+            <p><input type="submit" id="bsubmit" value="Continue"></p>
+        </form>
+    </div>
+
+    <script src="/_utils/script/json2.js"></script>
+    <script src="/_utils/script/jquery.js"></script>
+    <script src="/_utils/script/jquery.couch.js"></script>
+    <script src="/_utils/script/jquery.cookies.js"></script>
+    <script src="vendor/inditeweb/jquery.couchapp.js"></script>
+    <script src="js/strftime.js"></script>
+    <script src="js/showdown.js"></script>
+
+    <script src="js/blog.js"></script>
+    <script>
+        $(document).ready(function() {       
+            $.CouchApp(function(app) {  
+                var href = document.location;
+                var uri = parseUri(document.location.href);
+                var query = {};
+                if (uri.query) {
+                    query_parts = uri.query.split("&");
+                    for (var i=0; i<query_parts.length; i++) {
+                        p = query_parts[i].split("=");
+                        query[p[0]] = p[1] || "";
+                    }
+                }     
+                new Login(app, {
+                    success: function() {
+                        alert($("#next").val());
+                        document.location.href = query["next"];
+                    },
+                    error: function() {
+                        document.location = href;
+                    }
+                });
+            });
+        });
+
+    </script>    
+</body>
+
+
+</html>
   
   provides("atom", function() {
       // with first row in head you can do updated.
-      var f = <feed xmlns="http://www.w3.org/2005/Atom"/>;
-	  f.title = blog.title; 
-	  f.id = makeAbsolute(req, indexPath);
-	  f.link.@href = makeAbsolute(req, feedPath);
+    var f = <feed xmlns="http://www.w3.org/2005/Atom"/>;
+	f.title = blog.title; 
+	f.id = makeAbsolute(req, indexPath);
+	f.link.@href = makeAbsolute(req, feedPath);
     f.link.@rel = "self";
     f.generator = 'benoitc.org';
     f.updated = new Date().rfc3339();

shows/edit.js

-function(doc, req) {  
-  // !json templates.edit
-  // !json blog
-  // !code vendor/inditeweb/ejs/*.js
-  // !code vendor/inditeweb/template.js
-  // !code vendor/inditeweb/date.js
-  // !code vendor/inditeweb/path.js
-
-  // we only show html
-  //
-
-  return template(templates.edit, {
-    doc : doc,
-    docid : toJSON((doc && doc._id) || null), 
-    assets : assetPath(),
-    index : listPath('index','recent-posts',{descending:true,limit:8})
-    });
-}
 function(doc, req) {  
-  // !json templates.post
+  // !json templates
   // !json blog
   // !code vendor/inditeweb/ejs/*.js
   // !code vendor/inditeweb/template.js
   // !code vendor/inditeweb/date.js
   // !code vendor/inditeweb/path.js
 
+    
+  var is_edit = (!doc || (req.query.edit && req.query.edit == "true")); 
+  var path_level = "../";
+  if (doc)
+      path_level += "../";
 
+  
+  // so want to create/edit a post but isn't logged
+  // we redirect him to login page
+  if (is_edit && !req.userCtx.name) {
+    redirect_url = path_level + "login.html?next=/" + currentPath();
+    return {
+      code: 302,
+      headers: {
+        "Location": redirect_url
+      }
+    }
+  }
+
+  // if we edit/ create a doc display edit page  
+  if (is_edit)
+    return template(templates.edit, {
+        doc : doc,
+        docid : toJSON((doc && doc._id) || null), 
+        assets : assetPath(),
+        path_level: path_level,
+        index : listPath('index','recent-posts',{descending:true,limit:8})
+    });
+ 
+  // else show the post
   var fcreated_at = new Date().setRFC3339(doc.created_at).toLocaleString();
 
   var labels = [];
       }
   }
 
-  var editPath = showPath("edit", doc._id);
-  // we only show html
+  var editPath = showPath("show", doc._id, {edit:true});
   return template(templates.post, {
     doc: doc,
     fcreated_at: fcreated_at,
     assets : assetPath(),
-    editPostPath : showPath('edit', doc._id),
+    editPostPath : editPath,  
     labels: labels.join(', '),
     index : listPath('index','recent-posts',{descending:true, limit:8}),
     editPath: editPath

templates/edit.html

     <link rel="home index" href="http://benoitc.org/" type="text/html">
     <link rel="me author" href="http://benoitc.org/static/benoitc.html" type="text/html">
 
-    <link rel="stylesheet" href="../../css/benoitc.css" type="text/css" media="screen, projection" />
+    <link rel="stylesheet" href="<%= path_level %>css/benoitc.css" type="text/css" media="screen, projection" />
     
 
 
         </div>
 
         <footer id="main-footer">
-        <p>2009 (c) <a href="../../benoitc.html">Benoît Chesneau</a> - <a rel="license" href="http://creativecommons.org/licenses/by/2.0/fr/">Certains droits reservés</a>.</p>
+        <p>2009 (c) <a href="<%= path_level %>benoitc.html">Benoît Chesneau</a> - <a rel="license" href="http://creativecommons.org/licenses/by/2.0/fr/">Certains droits reservés</a>.</p>
         </footer> 
 
     
     <script src="/_utils/script/jquery.js"></script>
     <script src="/_utils/script/jquery.cookies.js"></script>
     <script src="/_utils/script/jquery.couch.js"></script>
-    <script src="../../js/showdown.js"></script>
-    <script src="../../js/textarea.jquery.js"></script>
-    <script src="../../js/jquery.couchapp.js"></script>
-    <script src="../../js/editor.js"></script>
-    <script src="../../js/strftime.js"></script>
-    <script src="../../js/blog.js"></script>
-    <script src="../../js/urlify.js"></script>
+    <script src="<%= path_level %>js/showdown.js"></script>
+    <script src="<%= path_level %>js/textarea.jquery.js"></script>
+    <script src="<%= path_level %>vendor/inditeweb/jquery.couchapp.js"></script>
+    <script src="<%= path_level %>js/editor.js"></script>
+    <script src="<%= path_level %>js/strftime.js"></script>
+    <script src="<%= path_level %>js/blog.js"></script>
+    <script src="<%= path_level %>js/urlify.js"></script>
 
     <script>
         $(document).ready(function() {
 
             $.CouchApp(function(app) {
-                app.loggedInNow(function(login) {
-                    $("header").prepend('<span id="login">'+login+'</span>');
+
+                    var session = app.get_session();
                     
                     var B = new Blog(app);
 
                         template : {
                             doc_type : "post",
                             format : "markdown",
-                            author : login
+                            author : session.name
                         },
                         onLoad : function(doc) {
                             if (doc._id) {
                     });  
 
                     new Editor(); 
-                    }, function() {
-                    // callback
-                    $('body').append('<a href="../../account.html#'+document.location+'">redirect</a>');
-                    var absurl = $('body a:last')[0].href;
-                    document.location = absurl;
-                });
+                    
             });
         
         });

templates/index/tail.html

     <script src="/_utils/script/json2.js"></script>
     <script src="/_utils/script/jquery.js"></script>
     <script src="/_utils/script/jquery.couch.js"></script>
-    <script src="../../js/jquery.couchapp.js"></script>
+    <script src="../../vendor/inditeweb/jquery.couchapp.js"></script>
     <script src="../../js/strftime.js"></script>
     <script src="../../js/blog.js"></script>
     <script>

templates/post.html

     <script src="/_utils/script/jquery.js"></script>
     <script src="/_utils/script/jquery.couch.js"></script>
     <script src="/_utils/script/jquery.cookies.js"></script>
-    <script src="../../js/jquery.couchapp.js"></script>
+    <script src="../../vendor/inditeweb/jquery.couchapp.js"></script>
     <script src="../../js/strftime.js"></script>
     <script src="../../js/showdown.js"></script>
     <script src="../../js/blog.js"></script>

templates/search/tail.html

     <script src="/_utils/script/json2.js"></script>
     <script src="/_utils/script/jquery.js"></script>
     <script src="/_utils/script/jquery.couch.js"></script>
-    <script src="../../js/jquery.couchapp.js"></script>
+    <script src="../../vendor/inditeweb/jquery.couchapp.js"></script>
     <script src="../../js/strftime.js"></script>
     <script src="../../js/blog.js"></script>
     <script>

vendor/couchapp/README.md

-## CouchApp - more than just a filesystem mapper
-
-This is where documentation will go for the client and server JavaScript parts of CouchApp.

vendor/couchapp/_attachments/jquery.couchapp.js

-// 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.
-
-// Usage: The passed in function is called when the page is ready.
-// CouchApp passes in the app object, which takes care of linking to 
-// the proper database, and provides access to the CouchApp helpers.
-// $.CouchApp(function(app) {
-//    app.db.view(...)
-//    ...
-// });
-
-function escapeHTML(st) {                                       
-  return(                                                                 
-    st && st.replace(/&/g,'&amp;').                                         
-      replace(/>/g,'&gt;').                                           
-      replace(/</g,'&lt;').                                           
-      replace(/"/g,'&quot;')                                         
-  );                                                                     
-};
-
-
-function f(n) {    // Format integers to have at least two digits.
-    return n < 10 ? '0' + n : n;
-}
-
-Date.parseRFC3339 = function (string) {
-    var date=new Date(0);
-    var match = string.match(/(\d{4})-(\d\d)-(\d\d)\s*(?:[\sT]\s*(\d\d):(\d\d)(?::(\d\d))?(\.\d*)?\s*(Z|([-+])(\d\d):(\d\d))?)?/);
-    if (!match) return;
-    if (match[2]) match[2]--;
-    if (match[7]) match[7] = (match[7]+'000').substring(1,4);
-    var field = [null,'FullYear','Month','Date','Hours','Minutes','Seconds','Milliseconds'];
-    for (var i=1; i<=7; i++) if (match[i]) date['setUTC'+field[i]](match[i]);
-    if (match[9]) date.setTime(date.getTime()+(match[9]=='-'?1:-1)*(match[10]*3600000+match[11]*60000) );
-    return date.getTime();
-}
-
-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';
-};
-
-Date.prototype.setRFC3339 = function(dString){
-    var regexp = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)(:)?(\d\d)(\.\d+)?(Z|([+-])(\d\d)(:)?(\d\d))/;
-
-    if (dString.toString().match(new RegExp(regexp))) {
-        var d = dString.match(new RegExp(regexp));
-        var offset = 0;
-
-        this.setUTCDate(1);
-        this.setUTCFullYear(parseInt(d[1],10));
-        this.setUTCMonth(parseInt(d[3],10) - 1);
-        this.setUTCDate(parseInt(d[5],10));
-        this.setUTCHours(parseInt(d[7],10));
-        this.setUTCMinutes(parseInt(d[9],10));
-        this.setUTCSeconds(parseInt(d[11],10));
-        if (d[12])
-            this.setUTCMilliseconds(parseFloat(d[12]) * 1000);
-        else
-            this.setUTCMilliseconds(0);
-        if (d[13] != 'Z') {
-            offset = (d[15] * 60) + parseInt(d[17],10);
-            offset *= ((d[14] == '-') ? -1 : 1);
-            this.setTime(this.getTime() - offset * 60 * 1000);
-        }
-    } else {
-        this.setTime(Date.parse(dString));
-    }
-    return this;
-};
-
-/*
-File: Math.uuid.js
-Version: 1.3
-Copyright (c) 2008, Robert Kieffer
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
-    * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
-    * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
-    * Neither the name of Robert Kieffer nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*/
-
-Math.uuid =  function (len, radix) {
-  // Private array of chars to use
-  var CHARS = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(''); 
-
-  
-    var chars = CHARS, uuid = [], rnd = Math.random;
-    radix = radix || chars.length;
-
-    if (len) {
-      // Compact form
-      for (var i = 0; i < len; i++) uuid[i] = chars[0 | rnd()*radix];
-    } else {
-      // rfc4122, version 4 form
-      var r;
-
-      // rfc4122 requires these characters
-      uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-';
-      uuid[14] = '4';
-
-      // Fill in random data.  At i==19 set the high bits of clock sequence as
-      // per rfc4122, sec. 4.1.5
-      for (var i = 0; i < 36; i++) {
-        if (!uuid[i]) {
-          r = 0 | rnd()*16;
-          uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r & 0xf];
-        }
-      }
-    }
-
-    return uuid.join('');
-  };
-  
-Math.uuidHex = function() {
-  return Math.uuid().replace(/-/g, '');
-};
-
-
-Math.uuidInt = function() {
-  return parseInt(Math.uuidHex(), 16);
-};
-
-
-// Convert a options object to an url query string.
-// ex: {key:'value',key2:'value2'} becomes '?key="value"&key2="value2"'
-function encodeOptions(options) {
-  var buf = []
-  if (typeof(options) == "object" && options !== null) {
-    for (var name in options) {
-      if (name == "error" || name == "success") continue;
-      var value = options[name];
-      if (name == "key" || name == "startkey" || name == "endkey" || name == "keys") {
-        value = toJSON(value);
-      }
-      buf.push(encodeURIComponent(name) + "=" + encodeURIComponent(value));
-    }
-  }
-  return buf.length ? "?" + buf.join("&") : "";
-}
-
-function toJSON(obj) {
-  return obj !== null ? JSON.stringify(obj) : null;
-}
-
-(function($) {
-
-  function Design(db, name) {
-    this.view = function(view, opts) {
-      db.view(name+'/'+view, opts);
-    };
-  };
-
-  
-  function init(app) {
-    $(function() {
-      var dbname = document.location.href.split('/')[3];
-      var dname = unescape(document.location.href).split('/')[5];
-      var db = $.couch.db(dbname);
-      var design = new Design(db, dname);
-      
-      // docForm applies CouchDB behavior to HTML forms.
-      function docForm(formSelector, opts) {
-        var localFormDoc = {};
-        var opts = opts || {};
-        opts.fields = opts.fields || [];
-        
-        // turn the form into deep json
-        // field names like 'author-email' get turned into json like
-        // {"author":{"email":"quentin@example.com"}}
-        function formToDeepJSON(form, fields, doc) {
-          var form = $(form);
-          opts.fields.forEach(function(field) {
-            var val = form.find("[name="+field+"]").val()
-            if (!val) return;
-            var parts = field.split('-');
-            var frontObj = doc, frontName = parts.shift();
-            while (parts.length > 0) {
-              frontObj[frontName] = frontObj[frontName] || {}
-              frontObj = frontObj[frontName];
-              frontName = parts.shift();
-            }
-            frontObj[frontName] = val;
-          });
-        };
-        
-        // Apply the behavior
-        $(formSelector).submit(function(e) {
-          e.preventDefault();
-          // formToDeepJSON acts on localFormDoc by reference
-          formToDeepJSON(this, opts.fields, localFormDoc);
-          if (opts.validForm && !opts.validForm(localFormDoc)) return false;
-          if (opts.beforeSave) opts.beforeSave(localFormDoc);
-          db.saveDoc(localFormDoc, {
-            success : function(resp) {
-              if (opts.success) opts.success(resp, localFormDoc);
-            }
-          })
-          
-          return false;
-        });
-
-        // populate form from an existing doc
-        function docToForm(doc) {
-          var form = $(formSelector);
-          // fills in forms
-          opts.fields.forEach(function(field) {
-            var parts = field.split('-');
-            var run = true, frontObj = doc, frontName = parts.shift();
-            while (frontObj && parts.length > 0) {                
-              frontObj = frontObj[frontName];
-              frontName = parts.shift();
-            }
-            if (frontObj && frontObj[frontName])
-              form.find("[name="+field+"]").val(frontObj[frontName]);
-          });            
-        };
-        
-        if (opts.id) {
-          db.openDoc(opts.id, {
-            success: function(doc) {
-              if (opts.onLoad) opts.onLoad(doc);
-              localFormDoc = doc;
-              docToForm(doc);
-          }});
-        } else if (opts.template) {
-          if (opts.onLoad) opts.onLoad(opts.template);
-          localFormDoc = opts.template;
-          docToForm(localFormDoc);
-        }
-        var instance = {
-          deleteDoc : function(opts) {
-            opts = opts || {};
-            if (confirm("Really delete this document?")) {                
-              db.removeDoc(localFormDoc, opts);
-            }
-          },
-          localDoc : function() {
-            formToDeepJSON(formSelector, opts.fields, localFormDoc);
-            return localFormDoc;
-          }
-        }
-        return instance;
-      }
-      
-      function prettyDate(time){
-      	var date = new Date(time),
-      		diff = (((new Date()).getTime() - date.getTime()) / 1000),
-      		day_diff = Math.floor(diff / 86400);
-
-        // if ( isNaN(day_diff) || day_diff < 0 || day_diff >= 31 ) return;
-
-      	return day_diff < 1 && (
-      			diff < 60 && "just now" ||
-      			diff < 120 && "1 minute ago" ||
-      			diff < 3600 && Math.floor( diff / 60 ) + " minutes ago" ||
-      			diff < 7200 && "1 hour ago" ||
-      			diff < 86400 && Math.floor( diff / 3600 ) + " hours ago") ||
-      		day_diff == 1 && "yesterday" ||
-      		day_diff < 21 && day_diff + " days ago" ||
-      		day_diff < 45 && Math.ceil( day_diff / 7 ) + " weeks ago" ||
-      		day_diff < 730 && Math.ceil( day_diff / 31 ) + " months ago" ||
-      		Math.ceil( day_diff / 365 ) + " years ago";
-      };
-      
-      function login(options) {
-        options = options || {};
-        var username = options.username, 
-        password = options.password;
-        
-        var userdb = options.userdb || dbname;
-        $.ajax({
-          type: "POST",
-          url: "/_session",
-          dataType: "json",
-          data: { username: username, password: password },
-          beforeSend: function(req) {
-            req.setRequestHeader('X-CouchDB-WWW-Authenticate', 'Cookie');
-          },
-          success: function(data) {
-            if (data.ok) {
-              if (options.success) options.success(data);
-            } else {
-              options.error(data);
-            } 
-          }
-        });
-      };
-      
-      function logout(options) {
-        options = options || {};
-        var userdb = options.userdb || dbname;
-        $.ajax({
-          type: "DELETE", url: "/_session", dataType: "json",
-          beforeSend: function(req) {
-            req.setRequestHeader('X-CouchDB-WWW-Authenticate', 'Cookie');
-          },
-          complete: function(req) {
-            var resp = $.httpData(req, "json");
-            if (req.status == 200) {
-              if (options.success) options.success(resp);
-            } else if (options.error) {
-              options.error(req.status, resp.error, resp.reason);
-            } else {
-              alert("An error occurred logging out: " + resp.reason);
-            }
-          }
-        });
-      };
-      
-      app({
-        showPath : function(funcname, docid) {
-          // I wish this was shared with path.js...
-          if (docid)
-            return '/'+[dbname, '_design', dname, '_show', funcname, docid].join('/')
-          else
-            return '/'+[dbname, '_design', dname, '_show', funcname].join('/')
-        },
-        listPath : function(funcname, viewname) {
-          return '/'+[dbname, '_design', dname, '_list', funcname, viewname].join('/')
-        },
-        slugifyString : function(string) {
-          return string.replace(/\W/g,'-').
-            replace(/\-*$/,'').replace(/^\-*/,'').
-            replace(/\-{2,}/,'-');
-        },
-        isLogged: function(loggedIn, loggedOut) {
-          $.getJSON("/_session", function(data) {
-            if (data.name) {
-              loggedIn && loggedIn(data);
-            } else {
-              loggedOut && loggedOut();
-            }
-          });
-          
-          
-        },
-        name: dname,
-        db : db,
-        design : design,
-        view : design.view,
-        docForm : docForm,
-        prettyDate : prettyDate,
-        login: login,
-        logout: logout
-      });
-    });
-  };
-
-  $.CouchApp = $.CouchApp || init;
-
-})(jQuery);

vendor/couchapp/_attachments/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
- */
-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.
- */
-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.
- */
-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;
-}

vendor/couchapp/_attachments/sha1.js

-/*
- * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
- * in FIPS PUB 180-1
- * Version 2.1a Copyright Paul Johnston 2000 - 2002.
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
- * Distributed under the BSD License
- * See http://pajhome.org.uk/crypt/md5 for details.
- */
-
-/*
- * 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_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
-function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
-function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
-function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
-function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
-function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
-
-/*
- * Perform a simple self-test to see if the VM is working
- */
-function sha1_vm_test()
-{
-  return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
-}
-
-/*
- * Calculate the SHA-1 of an array of big-endian words, and a bit length
- */
-function core_sha1(x, len)
-{
-  /* append padding */
-  x[len >> 5] |= 0x80 << (24 - len % 32);
-  x[((len + 64 >> 9) << 4) + 15] = len;
-
-  var w = Array(80);
-  var a =  1732584193;
-  var b = -271733879;
-  var c = -1732584194;
-  var d =  271733878;
-  var e = -1009589776;
-
-  for(var i = 0; i < x.length; i += 16)
-  {
-    var olda = a;
-    var oldb = b;
-    var oldc = c;
-    var oldd = d;
-    var olde = e;
-
-    for(var j = 0; j < 80; j++)
-    {
-      if(j < 16) w[j] = x[i + j];
-      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
-      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
-                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
-      e = d;
-      d = c;
-      c = rol(b, 30);
-      b = a;
-      a = t;
-    }
-
-    a = safe_add(a, olda);
-    b = safe_add(b, oldb);
-    c = safe_add(c, oldc);
-    d = safe_add(d, oldd);
-    e = safe_add(e, olde);
-  }
-  return Array(a, b, c, d, e);
-
-}
-
-/*
- * Perform the appropriate triplet combination function for the current
- * iteration
- */
-function sha1_ft(t, b, c, d)
-{
-  if(t < 20) return (b & c) | ((~b) & d);
-  if(t < 40) return b ^ c ^ d;
-  if(t < 60) return (b & c) | (b & d) | (c & d);
-  return b ^ c ^ d;
-}
-
-/*
- * Determine the appropriate additive constant for the current iteration
- */
-function sha1_kt(t)
-{
-  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
-         (t < 60) ? -1894007588 : -899497514;
-}
-
-/*
- * Calculate the HMAC-SHA1 of a key and some data
- */
-function core_hmac_sha1(key, data)
-{
-  var bkey = str2binb(key);
-  if(bkey.length > 16) bkey = core_sha1(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_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
-  return core_sha1(opad.concat(hash), 512 + 160);
-}
-
-/*
- * 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 rol(num, cnt)
-{
-  return (num << cnt) | (num >>> (32 - cnt));
-}
-
-/*
- * Convert an 8-bit or 16-bit string to an array of big-endian words
- * In 8-bit function, characters >255 have their hi-byte silently ignored.
- */
-function str2binb(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) << (32 - chrsz - i%32);
-  return bin;
-}
-
-/*
- * Convert an array of big-endian words to a string
- */
-function binb2str(bin)
-{
-  var str = "";
-  var mask = (1 << chrsz) - 1;
-  for(var i = 0; i < bin.length * 32; i += chrsz)
-    str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
-  return str;
-}
-
-/*
- * Convert an array of big-endian words to a hex string.
- */
-function binb2hex(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] >> ((3 - i%4)*8+4)) & 0xF) +
-           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
-  }
-  return str;
-}
-
-/*
- * Convert an array of big-endian words to a base-64 string
- */
-function binb2b64(binarray)
-{
-  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var str = "";
-  for(var i = 0; i < binarray.length * 4; i += 3)
-  {
-    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
-                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
-                |  ((binarray[i+2 >> 2] >> 8 * (3 - (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;
-}

vendor/couchapp/_attachments/sha256.js

-/*
- * A JavaScript implementation of the Secure Hash Algorithm, SHA-256, as defined
- * in FIPS 180-2
- * Version 2.2-beta Copyright Angel Marin, Paul Johnston 2000 - 2009.
- * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
- * Distributed under the BSD License
- * See http://pajhome.org.uk/crypt/md5 for details.
- * Also http://anmar.eu.org/projects/jssha2/
- */
-
-/*
- * 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   */
-
-/*
- * 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_sha256(s)    { return rstr2hex(rstr_sha256(str2rstr_utf8(s))); }
-function b64_sha256(s)    { return rstr2b64(rstr_sha256(str2rstr_utf8(s))); }
-function any_sha256(s, e) { return rstr2any(rstr_sha256(str2rstr_utf8(s)), e); }
-function hex_hmac_sha256(k, d)
-  { return rstr2hex(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
-function b64_hmac_sha256(k, d)
-  { return rstr2b64(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d))); }
-function any_hmac_sha256(k, d, e)
-  { return rstr2any(rstr_hmac_sha256(str2rstr_utf8(k), str2rstr_utf8(d)), e); }
-
-/*
- * Perform a simple self-test to see if the VM is working
- */
-function sha256_vm_test()
-{
-  return hex_sha256("abc").toLowerCase() ==
-            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad";
-}
-
-/*
- * Calculate the sha256 of a raw string
- */
-function rstr_sha256(s)
-{
-  return binb2rstr(binb_sha256(rstr2binb(s), s.length * 8));
-}
-
-/*
- * Calculate the HMAC-sha256 of a key and some data (raw strings)
- */
-function rstr_hmac_sha256(key, data)
-{
-  var bkey = rstr2binb(key);
-  if(bkey.length > 16) bkey = binb_sha256(bkey, key.length * 8);
-
-  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 = binb_sha256(ipad.concat(rstr2binb(data)), 512 + data.length * 8);
-  return binb2rstr(binb_sha256(opad.concat(hash), 512 + 256));
-}
-
-/*
- * Convert a raw string to a hex string
- */
-function rstr2hex(input)
-{
-  try { hexcase } catch(e) { hexcase=0; }
-  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
-  var output = "";
-  var x;
-  for(var i = 0; i < input.length; i++)
-  {
-    x = input.charCodeAt(i);
-    output += hex_tab.charAt((x >>> 4) & 0x0F)
-           +  hex_tab.charAt( x        & 0x0F);
-  }
-  return output;
-}
-
-/*
- * Convert a raw string to a base-64 string
- */
-function rstr2b64(input)
-{
-  try { b64pad } catch(e) { b64pad=''; }
-  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
-  var output = "";
-  var len = input.length;
-  for(var i = 0; i < len; i += 3)
-  {
-    var triplet = (input.charCodeAt(i) << 16)
-                | (i + 1 < len ? input.charCodeAt(i+1) << 8 : 0)
-                | (i + 2 < len ? input.charCodeAt(i+2)      : 0);
-    for(var j = 0; j < 4; j++)
-    {
-      if(i * 8 + j * 6 > input.length * 8) output += b64pad;
-      else output += tab.charAt((triplet >>> 6*(3-j)) & 0x3F);
-    }
-  }
-  return output;
-}
-
-/*
- * Convert a raw string to an arbitrary string encoding
- */
-function rstr2any(input, encoding)
-{
-  var divisor = encoding.length;
-  var remainders = Array();
-  var i, q, x, quotient;
-
-  /* Convert to an array of 16-bit big-endian values, forming the dividend */
-  var dividend = Array(Math.ceil(input.length / 2));
-  for(i = 0; i < dividend.length; i++)
-  {
-    dividend[i] = (input.charCodeAt(i * 2) << 8) | input.charCodeAt(i * 2 + 1);
-  }
-
-  /*
-   * Repeatedly perform a long division. The binary array forms the dividend,
-   * the length of the encoding is the divisor. Once computed, the quotient
-   * forms the dividend for the next step. We stop when the dividend is zero.
-   * All remainders are stored for later use.
-   */
-  while(dividend.length > 0)
-  {
-    quotient = Array();
-    x = 0;
-    for(i = 0; i < dividend.length; i++)
-    {
-      x = (x << 16) + dividend[i];
-      q = Math.floor(x / divisor);
-      x -= q * divisor;
-      if(quotient.length > 0 || q > 0)
-        quotient[quotient.length] = q;
-    }
-    remainders[remainders.length] = x;
-    dividend = quotient;
-  }
-
-  /* Convert the remainders to the output string */
-  var output = "";
-  for(i = remainders.length - 1; i >= 0; i--)
-    output += encoding.charAt(remainders[i]);
-
-  /* Append leading zero equivalents */
-  var full_length = Math.ceil(input.length * 8 /
-                                    (Math.log(encoding.length) / Math.log(2)))
-  for(i = output.length; i < full_length; i++)
-    output = encoding[0] + output;
-
-  return output;
-}
-
-/*
- * Encode a string as utf-8.
- * For efficiency, this assumes the input is valid utf-16.
- */
-function str2rstr_utf8(input)
-{
-  var output = "";
-  var i = -1;
-  var x, y;
-
-  while(++i < input.length)
-  {
-    /* Decode utf-16 surrogate pairs */
-    x = input.charCodeAt(i);
-    y = i + 1 < input.length ? input.charCodeAt(i + 1) : 0;
-    if(0xD800 <= x && x <= 0xDBFF && 0xDC00 <= y && y <= 0xDFFF)
-    {
-      x = 0x10000 + ((x & 0x03FF) << 10) + (y & 0x03FF);
-      i++;
-    }
-
-    /* Encode output as utf-8 */
-    if(x <= 0x7F)
-      output += String.fromCharCode(x);
-    else if(x <= 0x7FF)
-      output += String.fromCharCode(0xC0 | ((x >>> 6 ) & 0x1F),
-                                    0x80 | ( x         & 0x3F));
-    else if(x <= 0xFFFF)
-      output += String.fromCharCode(0xE0 | ((x >>> 12) & 0x0F),
-                                    0x80 | ((x >>> 6 ) & 0x3F),
-                                    0x80 | ( x         & 0x3F));
-    else if(x <= 0x1FFFFF)
-      output += String.fromCharCode(0xF0 | ((x >>> 18) & 0x07),
-                                    0x80 | ((x >>> 12) & 0x3F),
-                                    0x80 | ((x >>> 6 ) & 0x3F),
-                                    0x80 | ( x         & 0x3F));
-  }
-  return output;
-}
-
-/*
- * Encode a string as utf-16
- */
-function str2rstr_utf16le(input)
-{
-  var output = "";
-  for(var i = 0; i < input.length; i++)
-    output += String.fromCharCode( input.charCodeAt(i)        & 0xFF,
-                                  (input.charCodeAt(i) >>> 8) & 0xFF);
-  return output;
-}
-
-function str2rstr_utf16be(input)
-{
-  var output = "";
-  for(var i = 0; i < input.length; i++)
-    output += String.fromCharCode((input.charCodeAt(i) >>> 8) & 0xFF,
-                                   input.charCodeAt(i)        & 0xFF);
-  return output;
-}
-
-/*
- * Convert a raw string to an array of big-endian words
- * Characters >255 have their high-byte silently ignored.
- */
-function rstr2binb(input)
-{
-  var output = Array(input.length >> 2);
-  for(var i = 0; i < output.length; i++)
-    output[i] = 0;
-  for(var i = 0; i < input.length * 8; i += 8)
-    output[i>>5] |= (input.charCodeAt(i / 8) & 0xFF) << (24 - i % 32);
-  return output;
-}
-
-/*
- * Convert an array of big-endian words to a string
- */
-function binb2rstr(input)
-{
-  var output = "";
-  for(var i = 0; i < input.length * 32; i += 8)
-    output += String.fromCharCode((input[i>>5] >>> (24 - i % 32)) & 0xFF);
-  return output;
-}
-
-/*
- * Main sha256 function, with its support functions
- */
-function sha256_S (X, n) {return ( X >>> n ) | (X << (32 - n));}
-function sha256_R (X, n) {return ( X >>> n );}
-function sha256_Ch(x, y, z) {return ((x & y) ^ ((~x) & z));}
-function sha256_Maj(x, y, z) {return ((x & y) ^ (x & z) ^ (y & z));}
-function sha256_Sigma0256(x) {return (sha256_S(x, 2) ^ sha256_S(x, 13) ^ sha256_S(x, 22));}
-function sha256_Sigma1256(x) {return (sha256_S(x, 6) ^ sha256_S(x, 11) ^ sha256_S(x, 25));}
-function sha256_Gamma0256(x) {return (sha256_S(x, 7) ^ sha256_S(x, 18) ^ sha256_R(x, 3));}
-function sha256_Gamma1256(x) {return (sha256_S(x, 17) ^ sha256_S(x, 19) ^ sha256_R(x, 10));}
-function sha256_Sigma0512(x) {return (sha256_S(x, 28) ^ sha256_S(x, 34) ^ sha256_S(x, 39));}
-function sha256_Sigma1512(x) {return (sha256_S(x, 14) ^ sha256_S(x, 18) ^ sha256_S(x, 41));}
-function sha256_Gamma0512(x) {return (sha256_S(x, 1)  ^ sha256_S(x, 8) ^ sha256_R(x, 7));}
-function sha256_Gamma1512(x) {return (sha256_S(x, 19) ^ sha256_S(x, 61) ^ sha256_R(x, 6));}
-
-var sha256_K = new Array
-(
-  1116352408, 1899447441, -1245643825, -373957723, 961987163, 1508970993,
-  -1841331548, -1424204075, -670586216, 310598401, 607225278, 1426881987,
-  1925078388, -2132889090, -1680079193, -1046744716, -459576895, -272742522,
-  264347078, 604807628, 770255983, 1249150122, 1555081692, 1996064986,
-  -1740746414, -1473132947, -1341970488, -1084653625, -958395405, -710438585,
-  113926993, 338241895, 666307205, 773529912, 1294757372, 1396182291,
-  1695183700, 1986661051, -2117940946, -1838011259, -1564481375, -1474664885,
-  -1035236496, -949202525, -778901479, -694614492, -200395387, 275423344,
-  430227734, 506948616, 659060556, 883997877, 958139571, 1322822218,
-  1537002063, 1747873779, 1955562222, 2024104815, -2067236844, -1933114872,
-  -1866530822, -1538233109, -1090935817, -965641998
-);
-
-function binb_sha256(m, l)
-{
-  var HASH = new Array(1779033703, -1150833019, 1013904242, -1521486534,
-                       1359893119, -1694144372, 528734635, 1541459225);
-  var W = new Array(64);
-  var a, b, c, d, e, f, g, h;
-  var i, j, T1, T2;
-
-  /* append padding */
-  m[l >> 5] |= 0x80 << (24 - l % 32);
-  m[((l + 64 >> 9) << 4) + 15] = l;
-
-  for(i = 0; i < m.length; i += 16)
-  {
-    a = HASH[0];
-    b = HASH[1];
-    c = HASH[2];
-    d = HASH[3];
-    e = HASH[4];
-    f = HASH[5];
-    g = HASH[6];
-    h = HASH[7];
-
-    for(j = 0; j < 64; j++)
-    {
-      if (j < 16) W[j] = m[j + i];
-      else W[j] = safe_add(safe_add(safe_add(sha256_Gamma1256(W[j - 2]), W[j - 7]),
-                                            sha256_Gamma0256(W[j - 15])), W[j - 16]);
-
-      T1 = safe_add(safe_add(safe_add(safe_add(h, sha256_Sigma1256(e)), sha256_Ch(e, f, g)),
-                                                          sha256_K[j]), W[j]);
-      T2 = safe_add(sha256_Sigma0256(a), sha256_Maj(a, b, c));
-      h = g;
-      g = f;
-      f = e;
-      e = safe_add(d, T1);
-      d = c;
-      c = b;
-      b = a;
-      a = safe_add(T1, T2);
-    }
-
-    HASH[0] = safe_add(a, HASH[0]);
-    HASH[1] = safe_add(b, HASH[1]);
-    HASH[2] = safe_add(c, HASH[2]);
-    HASH[3] = safe_add(d, HASH[3]);
-    HASH[4] = safe_add(e, HASH[4]);
-    HASH[5] = safe_add(f, HASH[5]);
-    HASH[6] = safe_add(g, HASH[6]);
-    HASH[7] = safe_add(h, HASH[7]);
-  }
-  return HASH;
-}
-
-function safe_add (x, y)
-{
-  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
-  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
-  return (msw << 16) | (lsw & 0xFFFF);
-}

vendor/couchapp/couchapp.js

-// this stuff should be properly namespaced etc

vendor/couchapp/date.js

-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';
-};
-
-// This is a format that collates in order and tends to work with
-// JavaScript's new Date(string) date parsing capabilities, unlike rfc3339.
-Date.prototype.toJSON = function() {
-    return this.getUTCFullYear()   + '/' +
-         f(this.getUTCMonth() + 1) + '/' +
-         f(this.getUTCDate())      + ' ' +
-         f(this.getUTCHours())     + ':' +
-         f(this.getUTCMinutes())   + ':' +
-         f(this.getUTCSeconds())   + ' +0000';
-};
-
-Date.prototype.setRFC3339 = function(dString){
-    var regexp = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)(:)?(\d\d)(\.\d+)?(Z|([+-])(\d\d)(:)?(\d\d))/;
-
-    if (dString.toString().match(new RegExp(regexp))) {
-        var d = dString.match(new RegExp(regexp));
-        var offset = 0;
-
-        this.setUTCDate(1);
-        this.setUTCFullYear(parseInt(d[1],10));
-        this.setUTCMonth(parseInt(d[3],10) - 1);
-        this.setUTCDate(parseInt(d[5],10));
-        this.setUTCHours(parseInt(d[7],10));
-        this.setUTCMinutes(parseInt(d[9],10));
-        this.setUTCSeconds(parseInt(d[11],10));
-        if (d[12])
-            this.setUTCMilliseconds(parseFloat(d[12]) * 1000);
-        else
-            this.setUTCMilliseconds(0);
-        if (d[13] != 'Z') {
-            offset = (d[15] * 60) + parseInt(d[17],10);
-            offset *= ((d[14] == '-') ? -1 : 1);
-            this.setTime(this.getTime() - offset * 60 * 1000);
-        }
-    } else {
-        this.setTime(Date.parse(dString));
-    }
-    return this;
-};

vendor/couchapp/date.js.orig

-function f(n) {    // Format integers to have at least two digits.
-    return n < 10 ? '0' + n : n;
-}
-
-
-// This is a format that collates in order and tends to work with
-// JavaScript's new Date(string) date parsing capabilities, unlike rfc3339.
-Date.prototype.toJSON = function() {
-    return this.getUTCFullYear()   + '/' +
-         f(this.getUTCMonth() + 1) + '/' +
-         f(this.getUTCDate())      + ' ' +
-         f(this.getUTCHours())     + ':' +
-         f(this.getUTCMinutes())   + ':' +
-         f(this.getUTCSeconds())   + ' +0000';
-};
-
-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';
-};
-
-Date.prototype.setRFC3339 = function(dString){
-    var regexp = /(\d\d\d\d)(-)?(\d\d)(-)?(\d\d)(T)?(\d\d)(:)?(\d\d)(:)?(\d\d)(\.\d+)?(Z|([+-])(\d\d)(:)?(\d\d))/;
-
-    if (dString.toString().match(new RegExp(regexp))) {
-        var d = dString.match(new RegExp(regexp));
-        var offset = 0;
-
-        this.setUTCDate(1);
-        this.setUTCFullYear(parseInt(d[1],10));
-        this.setUTCMonth(parseInt(d[3],10) - 1);
-        this.setUTCDate(parseInt(d[5],10));
-        this.setUTCHours(parseInt(d[7],10));
-        this.setUTCMinutes(parseInt(d[9],10));
-        this.setUTCSeconds(parseInt(d[11],10));
-        if (d[12])
-            this.setUTCMilliseconds(parseFloat(d[12]) * 1000);
-        else
-            this.setUTCMilliseconds(0);
-        if (d[13] != 'Z') {
-            offset = (d[15] * 60) + parseInt(d[17],10);
-            offset *= ((d[14] == '-') ? -1 : 1);
-            this.setTime(this.getTime() - offset * 60 * 1000);
-        }
-    } else {
-        this.setTime(Date.parse(dString));
-    }
-    return this;
-};

vendor/couchapp/json2.js

-/*
-    json2.js
-    2008-03-14
-
-    Public Domain
-
-    No warranty expressed or implied. Use at your own risk.
-
-    See http://www.JSON.org/js.html
-
-    This is a reference implementation. You are free to copy, modify, or
-    redistribute.
-
-    Use your own copy. It is extremely unwise to load third party
-    code into your pages.
-*/
-
-if (!this.JSON) {
-
-    JSON = function () {
-
-        function f(n) {    // Format integers to have at least two digits.
-            return n < 10 ? '0' + n : n;
-        }
-
-        Date.prototype.toJSON = function () {
-            return this.getUTCFullYear()   + '-' +
-                 f(this.getUTCMonth() + 1) + '-' +
-                 f(this.getUTCDate())      + 'T' +
-                 f(this.getUTCHours())     + ':' +
-                 f(this.getUTCMinutes())   + ':' +
-                 f(this.getUTCSeconds())   + 'Z';
-        };
-
-
-        var m = {    // table of character substitutions
-            '\b': '\\b',
-            '\t': '\\t',
-            '\n': '\\n',
-            '\f': '\\f',
-            '\r': '\\r',
-            '"' : '\\"',
-            '\\': '\\\\'
-        };
-
-        function stringify(value, whitelist) {
-            var a,          // The array holding the partial texts.
-                i,          // The loop counter.
-                k,          // The member key.
-                l,          // Length.
-                r = /["\\\x00-\x1f\x7f-\x9f]/g,
-                v;          // The member value.
-
-            switch (typeof value) {
-            case 'string':
-
-                return r.test(value) ?
-                    '"' + value.replace(r, function (a) {
-                        var c = m[a];
-                        if (c) {
-                            return c;
-                        }
-                        c = a.charCodeAt();
-                        return '\\u00' + Math.floor(c / 16).toString(16) +
-                                                   (c % 16).toString(16);
-                    }) + '"' :
-                    '"' + value + '"';
-
-            case 'number':
-
-                return isFinite(value) ? String(value) : 'null';
-
-            case 'boolean':
-            case 'null':
-                return String(value);
-
-            case 'object':
-
-                if (!value) {
-                    return 'null';
-                }
-
-                if (typeof value.toJSON === 'function') {
-                    return stringify(value.toJSON());
-                }
-                a = [];
-                if (typeof value.length === 'number' &&
-                        !(value.propertyIsEnumerable('length'))) {
-
-                    l = value.length;
-                    for (i = 0; i < l; i += 1) {
-                        a.push(stringify(value[i], whitelist) || 'null');
-                    }
-
-                    return '[' + a.join(',') + ']';
-                }
-                if (whitelist) {
-                    l = whitelist.length;
-                    for (i = 0; i < l; i += 1) {
-                        k = whitelist[i];
-                        if (typeof k === 'string') {
-                            v = stringify(value[k], whitelist);
-                            if (v) {
-                                a.push(stringify(k) + ':' + v);
-                            }
-                        }
-                    }
-                } else {
-
-                    for (k in value) {
-                        if (typeof k === 'string') {
-                            v = stringify(value[k], whitelist);
-                            if (v) {
-                                a.push(stringify(k) + ':' + v);
-                            }
-                        }
-                    }
-                }
-
-                return '{' + a.join(',') + '}';
-            }
-        }
-
-        return {
-            stringify: stringify,
-            parse: function (text, filter) {
-                var j;
-
-                function walk(k, v) {
-                    var i, n;
-                    if (v && typeof v === 'object') {
-                        for (i in v) {
-                            if (Object.prototype.hasOwnProperty.apply(v, [i])) {
-                                n = walk(i, v[i]);
-                                if (n !== undefined) {
-                                    v[i] = n;
-                                } else {
-                                    delete v[i];
-                                }
-                            }
-                        }
-                    }
-                    return filter(k, v);
-                }
-
-                if (/^[\],:{}\s]*$/.test(text.replace(/\\["\\\/bfnrtu]/g, '@').
-replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, ']').
-replace(/(?:^|:|,)(?:\s*\[)+/g, ''))) {
-
-                    j = eval('(' + text + ')');
-
-                    return typeof filter === 'function' ? walk('', j) : j;
-                }
-
-                throw new SyntaxError('parseJSON');
-            }
-        };
-    }();
-}

vendor/couchapp/metadata.json

-{"scm": "git", "update_url": "git://github.com/jchris/sofa.git"}

vendor/couchapp/path.js

-// 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("&amp;");
-}
-
-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();
-  }
-  path = array.map(function(item) {return encodeURIComponent(item)}).join('/');
-  if (options) {
-    return path + encodeOptions(options);
-  } else {
-    return path;    
-  }
-};
-
-function assetPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2]];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function showPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2], '_show'];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function listPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2], '_list'];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function olderPath(info) {
-  if (!info) return null;
-  var q = req.query;
-  q.startkey = info.prev_key;
-  q.skip=1;
-  return listPath('index','recent-posts',q);
-}
-
-function makeAbsolute(req, path) {
-  return 'http://' + req.headers.Host + path;
-}

vendor/couchapp/path.js.orig

-// 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 parseUri(sourceUri){
-   /* parseUri by Steven Levithan (http://badassery.blogspot.com) */
-    var uriPartNames = ["source","protocol","authority","domain","port","path","directoryPath","fileName","query","anchor"];
-    var uriParts = new RegExp("^(?:([^:/?#.]+):)?(?://)?(([^:/?#]*)(?::(\\d*))?)?((/(?:[^?#](?![^?#/]*\\.[^?#/.]+(?:[\\?#]|$)))*/?)?([^?#/]*))?(?:\\?([^#]*))?(?:#(.*))?").exec(sourceUri);
-    var uri = {};
-    
-    for(var i = 0; i < 10; i++){
-        uri[uriPartNames[i]] = (uriParts[i] ? uriParts[i] : "");
-    }
-    
-    // Always end directoryPath with a trailing backslash if a path was present in the source URI
-    // Note that a trailing backslash is NOT automatically inserted within or appended to the "path" key
-    if(uri.directoryPath.length > 0){
-        uri.directoryPath = uri.directoryPath.replace(/\/?$/, "/");
-    }
-    
-    return uri;
-}
-
-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();
-  }
-  path = array.map(function(item) {return encodeURIComponent(item)}).join('/');
-  if (options) {
-    return path + encodeOptions(options);
-  } else {
-    return path;    
-  }
-};
-
-function assetPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2]];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function showPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2], '_show'];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function listPath() {
-  var p = req.path, parts = ['', p[0], p[1] , p[2], '_list'];
-  return makePath(concatArgs(parts, arguments));
-};
-
-function olderPath(info) {
-  if (!info) return null;
-  var q = req.query;
-  q.startkey = info.prev_key;
-  q.skip=1;
-  return listPath('index','recent-posts',q);
-}
-
-function makeAbsolute(req, path) {
-  return 'http://' + req.headers.Host + path;
-}

vendor/couchapp/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;