{"_id":"_design/sofa","_rev":"56-e718766fbe413d05b5bb58ae64446cf6","templates":{"edit":"\n\n \n {{ blog.title }}\n \n \n \n {{>header}}\n
\n
\n \n
\n

{{pageTitle}}

\n

\n

\n

\n

\n

\n \n

\n

\n \n Saved\n

\n
\n Markdown help\n
\n
\n \n {{>scripts}}\n \n \n","index":"\n\n \n {{ title }}\n \n \n \n \n {{>header}}\n
\n
\n

Recently...

\n \n
\n older posts\n 5\n 10\n 25 \n
\n
\n\n{{>scripts}}\n\n","partials":{"comment":"
  • \n

    \n by {{#url}}{{/url}}{{name}}{{#url}}{{/url}},\n {{created_at}}\n

    \n \n

    {{{html}}}

    \n
  • ","header":"
    \n
    \n

    {{ blogName }}

    \n
    \n Atom feeds of\n posts &\n comments.\n
    \n
    ","scripts":"\n\n\n\n\n\n\n\n\n"},"post":"\n\n \n {{ title }} : {{ blogName }}\n \n \n \n {{>header}}\n
    \n

    {{ title }}

    \n
    \n {{ date }}\n
    {{{ html }}}
    \n
    \n
    \n
    \n \n
    \n
    \n
    \n \n {{>scripts}}\n \n"},"vendor":{"textile":{"textile":"/*\n * This is the orginial function from Stuart Langridge at http://www.kryogenix.org/\n */\n \n/*\n* This is the update function from Jeff Minard - http://www.jrm.cc/\n*/\nfunction superTextile(s) {\n var r = s;\n // quick tags first\n qtags = [['\\\\*', 'strong'],\n ['\\\\?\\\\?', 'cite'],\n ['\\\\+', 'ins'], //fixed\n ['~', 'sub'], \n ['\\\\^', 'sup'], // me\n ['@', 'code']];\n for (var i=0;i'+'$1'+'');\n }\n // underscores count as part of a word, so do them separately\n re = new RegExp('\\\\b_(.+?)_\\\\b','g');\n r = r.replace(re,'$1');\n\t\n\t//jeff: so do dashes\n re = new RegExp('[\\\\s\\\\n]-(.+?)-[\\\\s\\\\n]','g');\n r = r.replace(re,'$1');\n\n // links\n re = new RegExp('\"\\\\b(.+?)\\\\(\\\\b(.+?)\\\\b\\\\)\":([^\\\\s]+)','g');\n r = r.replace(re,'$1');\n re = new RegExp('\"(.+?)\":([^\\\\s]+)','g');\n r = r.replace(re,'$1');\n\n // images\n re = new RegExp('!\\\\b(.+?)\\\\(\\\\b(.+?)\\\\b\\\\)!','g');\n r = r.replace(re,'\"$2\"');\n re = new RegExp('!\\\\b(.+?)\\\\b!','g');\n r = r.replace(re,'');\n \n // block level formatting\n\t\n\t\t// Jeff's hack to show single line breaks as they should.\n\t\t// insert breaks - but you get some....stupid ones\n\t re = new RegExp('(.*)\\\\n([^#\\\\*\\\\n].*)','g');\n\t r = r.replace(re,'$1
    $2');\n\t\t// remove the stupid breaks.\n\t re = new RegExp('\\\\n
    ','g');\n\t r = r.replace(re,'\\n');\n\t\n lines = r.split('\\n');\n nr = '';\n for (i=0;i')+''; \n\t\t\tchanged = 1; \n\t\t}\n\t\t\n\t\t// jeff adds h#.\n if (line.search(/^\\s*h[1|2|3|4|5|6]\\.\\s+/) != -1) { \n\t \tre = new RegExp('h([1|2|3|4|5|6])\\\\.(.+)','g');\n\t \tline = line.replace(re,'$2');\n\t\t\tchanged = 1; \n\t\t}\n\t\t\n\t\tif (line.search(/^\\s*\\*\\s+/) != -1) { line = line.replace(/^\\s*\\*\\s+/,'\\t') + ''; changed = 1; } // * for bullet list; make up an liu tag to be fixed later\n if (line.search(/^\\s*#\\s+/) != -1) { line = line.replace(/^\\s*#\\s+/,'\\t') + ''; changed = 1; } // # for numeric list; make up an lio tag to be fixed later\n if (!changed && (line.replace(/\\s/g,'').length > 0)) {line = '

    '+line+'

    ';}\n lines[i] = line + '\\n';\n }\n\t\n // Second pass to do lists\n inlist = 0; \n\tlisttype = '';\n for (i=0;i AND \n r = r.replace(/li[o|u]>/g,'li>');\n\n return r;\n}\n\nexports.encode = superTextile;"},"markdown":{"lib":{}},"couchapp":{"evently":{"profile":{"profileReady":{"after":"function(e, p) {\n $$(this).profile = p;\n};","data":"function(e, p) {\n return p\n}","mustache":"
    \n {{#gravatar_url}}{{/gravatar_url}}\n
    \n {{nickname}}\n
    \n
    \n

    Hello {{nickname}}!

    \n
    "},"noProfile":{"data":"function(e, userCtx) {\n return userCtx;\n}","mustache":"
    \n

    Hello {{name}}, Please setup your user profile.

    \n \n \n \n \n \n
    ","selectors":{"form":{"submit":"function() {\n var md5 = $$(this).app.require(\"vendor/couchapp/lib/md5\");\n \n // TODO this can be cleaned up with docForm?\n // it still needs the workflow to edit an existing profile\n var name = $(\"input[name=userCtxName]\",this).val();\n var newProfile = {\n rand : Math.random().toString(), \n nickname : $(\"input[name=nickname]\",this).val(),\n email : $(\"input[name=email]\",this).val(),\n url : $(\"input[name=url]\",this).val()\n }, widget = $(this);\n\n // setup gravatar_url\n if (md5) {\n newProfile.gravatar_url = 'http://www.gravatar.com/avatar/'+md5.hex(newProfile.email || newProfile.rand)+'.jpg?s=40&d=identicon'; \n }\n\n // store the user profile on the user account document\n $.couch.userDb(function(db) {\n var userDocId = \"org.couchdb.user:\"+name;\n db.openDoc(userDocId, {\n success : function(userDoc) {\n userDoc[\"couch.app.profile\"] = newProfile;\n db.saveDoc(userDoc, {\n success : function() {\n newProfile.name = userDoc.name;\n $$(widget).profile = newProfile;\n widget.trigger(\"profileReady\", [newProfile]);\n }\n });\n }\n });\n });\n return false;\n}"}}},"loggedIn":"function(e, r) {\n var userCtx = r.userCtx;\n var widget = $(this);\n // load the profile from the user doc\n var db = $.couch.db(r.info.authentication_db);\n var userDocId = \"org.couchdb.user:\"+userCtx.name;\n db.openDoc(userDocId, {\n success : function(userDoc) {\n var profile = userDoc[\"couch.app.profile\"];\n if (profile) {\n // we copy the name to the profile so it can be used later\n // without publishing the entire userdoc (roles, pass, etc)\n profile.name = userDoc.name;\n $$(widget).profile = profile;\n widget.trigger(\"profileReady\", [profile]);\n } else {\n widget.trigger(\"noProfile\", [userCtx]);\n }\n }\n });\n}","loggedOut":{"after":"function() {\n $$(this).profile = null;\n};","mustache":"

    Please log in to see your profile.

    "}},"README":"## Starting the Document this code challenge\n\nI need help on this code. I only have so many hours in the day. Please be liberal about patching and hacking (and sharing code!) so we can all benefit. \n\nDocs patches are deeply appreciated. For now you can just stick Markdown files in the Docs directory.\n\n# Evently\n\nThese are some vendor Evently widgets that are running on the CouchApp system.\n\n## Account\n This is how you signup, login and logout without worry about the code.\n Todo, we could have this work against remote APIs like that Facebook stuff or whatever.\n\n\n## Profile\n Use this to load the local users profile for the logged in user. Useful if you're going to be posting new messages. Most applications end up customizing `profile.profileReady` to render the primary data-entry form. This gets you benefits like refreshing on login / logout, etc, automatically.\n\n\n## Docs\n This needs to be moved to it's own app.\n I have this vision of a docs app designed for offline editing, that involves each Markdown paragraph being it's own document, with automatic use of Bespin for code samples. Any help on this would be thanked much.","account":{"loggedIn":{"after":"function(e, r) {\n $$(this).userCtx = r.userCtx;\n $$(this).info = r.info;\n};","data":"function(e, r) {\n return {\n name : r.userCtx.name,\n uri_name : encodeURIComponent(r.userCtx.name),\n auth_db : encodeURIComponent(r.info.authentication_db)\n };\n}","mustache":"Welcome \n{{name}}! \nLogout?\n","selectors":{"a[href=#logout]":{"click":["doLogout"]}}},"signupForm":{"after":"function() {\n $(\"input[name=name]\", this).focus();\n}","mustache":"
    \n \n \n \n or Login\n
    ","selectors":{"a[href=#login]":{"click":["loginForm"]},"form":{"submit":"function(e) {\n var name = $('input[name=name]', this).val(),\n pass = $('input[name=password]', this).val(); \n $(this).trigger('doSignup', [name, pass]);\n return false;\n}"}}},"_init":"function() {\n var elem = $(this);\n $$(this).userCtx = null;\n $.couch.session({\n success : function(r) {\n var userCtx = r.userCtx;\n if (userCtx.name) {\n elem.trigger(\"loggedIn\", [r]);\n } else if (userCtx.roles.indexOf(\"_admin\") != -1) {\n elem.trigger(\"adminParty\");\n } else {\n elem.trigger(\"loggedOut\");\n };\n }\n });\n}","doLogout":"function() {\n var elem = $(this);\n $.couch.logout({\n success : function() {\n elem.trigger(\"_init\");\n }\n });\n}","doSignup":"function(e, name, pass) {\n var elem = $(this);\n $.couch.signup({\n name : name\n }, pass, {\n success : function() {\n elem.trigger(\"doLogin\", [name, pass]);\n }\n });\n}","adminParty":{"mustache":"

    Admin party, everyone is admin! Fix this in Futon before proceeding.

    "},"loggedOut":{"mustache":"Signup or Login","selectors":{"a[href=#login]":{"click":["loginForm"]},"a[href=#signup]":{"click":["signupForm"]}}},"loginForm":{"after":"function() {\n $(\"input[name=name]\", this).focus();\n}","mustache":"
    \n \n \n \n or Signup\n
    ","selectors":{"form":{"submit":"function(e) {\n var name = $('input[name=name]', this).val(),\n pass = $('input[name=password]', this).val(); \n $(this).trigger('doLogin', [name, pass]);\n return false;\n}"},"a[href=#signup]":{"click":["signupForm"]}}},"doLogin":"function(e, name, pass) {\n var elem = $(this);\n $.couch.login({\n name : name,\n password : pass,\n success : function(r) {\n elem.trigger(\"_init\")\n }\n }); \n}"}},"lib":{"redirect":"exports.permanent = function(redirect) {\n return {\n code : 301,\n headers : {\n \"Location\" : redirect\n }\n };\n};","code":"exports.ddoc = function(ddoc) {\n // only return the parts of the app that we use\n var i, j, path, key, obj, ref, out = {},\n resources = ddoc.couchapp && ddoc.couchapp.load && ddoc.couchapp.load.app || [];\n for (i=0; i < resources.length; i++) {\n path = resources[i].split('/');\n obj = ddoc;\n ref = out;\n for (j=0; j < path.length; j++) {\n key = path[j];\n ref[key] = ref[key] || {};\n if (j < path.length - 1) {\n obj = obj[key];\n ref = ref[key];\n }\n };\n ref[key] = obj[key];\n };\n return out;\n};","cache":"exports.get = function(db, docid, setFun, getFun) {\n db.openDoc(docid, {\n success : function(doc) {\n getFun(doc.cache);\n },\n error : function() {\n setFun(function(cache) {\n db.saveDoc({\n _id : docid,\n cache : cache\n });\n getFun(cache);\n });\n }\n });\n};\n\nexports.clear = function(db, docid) {\n db.openDoc(docid, {\n success : function(doc) {\n db.removeDoc(doc);\n },\n error : function() {}\n });\n};","list":"// Helpers for writing server-side _list functions in CouchDB\nexports.withRows = function(fun) {\n var f = function() {\n var row = getRow();\n return row && fun(row);\n };\n f.iterator = true;\n return f;\n}\n\nexports.send = function(chunk) {\n send(chunk + \"\\n\")\n}","markdown":"//\n// showdown.js -- A javascript port of Markdown.\n//\n// Copyright (c) 2007 John Fraser.\n//\n// Original Markdown Copyright (c) 2004-2005 John Gruber\n// \n//\n// Redistributable under a BSD-style open source license.\n// See license.txt for more information.\n//\n// The full source distribution is at:\n//\n//\t\t\t\tA A L\n//\t\t\t\tT C A\n//\t\t\t\tT K B\n//\n// \n//\n\n//\n// Wherever possible, Showdown is a straight, line-by-line port\n// of the Perl version of Markdown.\n//\n// This is not a normal parser design; it's basically just a\n// series of string substitutions. It's hard to read and\n// maintain this way, but keeping Showdown close to the original\n// design makes it easier to port new features.\n//\n// More importantly, Showdown behaves like markdown.pl in most\n// edge cases. So web applications can do client-side preview\n// in Javascript, and then build identical HTML on the server.\n//\n// This port needs the new RegExp functionality of ECMA 262,\n// 3rd Edition (i.e. Javascript 1.5). Most modern web browsers\n// should do fine. Even with the new regular expression features,\n// We do a lot of work to emulate Perl's regex functionality.\n// The tricky changes in this file mostly have the \"attacklab:\"\n// label. Major or self-explanatory changes don't.\n//\n// Smart diff tools like Araxis Merge will be able to match up\n// this file with markdown.pl in a useful way. A little tweaking\n// helps: in a copy of markdown.pl, replace \"#\" with \"//\" and\n// replace \"$text\" with \"text\". Be sure to ignore whitespace\n// and line endings.\n//\n\n\n//\n// Showdown usage:\n//\n// var text = \"Markdown *rocks*.\";\n//\n// var markdown = require(\"markdown\");\n// var html = markdown.encode(text);\n//\n// print(html);\n//\n// Note: move the sample code to the bottom of this\n// file before uncommenting it.\n//\n\n\n//\n// Globals:\n//\n\n// Global hashes, used by various utility routines\nvar g_urls;\nvar g_titles;\nvar g_html_blocks;\n\n// Used to track when we're inside an ordered or unordered list\n// (see _ProcessListItems() for details):\nvar g_list_level = 0;\n\n\nexports.makeHtml = function(text) {\n//\n// Main function. The order in which other subs are called here is\n// essential. Link and image substitutions need to happen before\n// _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the \n// and tags get encoded.\n//\n\n\t// Clear the global hashes. If we don't clear these, you get conflicts\n\t// from other articles when generating a page which contains more than\n\t// one article (e.g. an index page that shows the N most recent\n\t// articles):\n\tg_urls = new Array();\n\tg_titles = new Array();\n\tg_html_blocks = new Array();\n\n\t// attacklab: Replace ~ with ~T\n\t// This lets us use tilde as an escape char to avoid md5 hashes\n\t// The choice of character is arbitray; anything that isn't\n // magic in Markdown will work.\n\ttext = text.replace(/~/g,\"~T\");\n\n\t// attacklab: Replace $ with ~D\n\t// RegExp interprets $ as a special character\n\t// when it's in a replacement string\n\ttext = text.replace(/\\$/g,\"~D\");\n\n\t// Standardize line endings\n\ttext = text.replace(/\\r\\n/g,\"\\n\"); // DOS to Unix\n\ttext = text.replace(/\\r/g,\"\\n\"); // Mac to Unix\n\n\t// Make sure text begins and ends with a couple of newlines:\n\ttext = \"\\n\\n\" + text + \"\\n\\n\";\n\n\t// Convert all tabs to spaces.\n\ttext = _Detab(text);\n\n\t// Strip any lines consisting only of spaces and tabs.\n\t// This makes subsequent regexen easier to write, because we can\n\t// match consecutive blank lines with /\\n+/ instead of something\n\t// contorted like /[ \\t]*\\n+/ .\n\ttext = text.replace(/^[ \\t]+$/mg,\"\");\n\n\t// Turn block-level HTML blocks into hash entries\n\ttext = _HashHTMLBlocks(text);\n\n\t// Strip link definitions, store in hashes.\n\ttext = _StripLinkDefinitions(text);\n\n\ttext = _RunBlockGamut(text);\n\n\ttext = _UnescapeSpecialChars(text);\n\n\t// attacklab: Restore dollar signs\n\ttext = text.replace(/~D/g,\"$$\");\n\n\t// attacklab: Restore tildes\n\ttext = text.replace(/~T/g,\"~\");\n\treturn text;\n}\n\n\nvar _StripLinkDefinitions = function(text) {\n//\n// Strips link definitions from text, stores the URLs and titles in\n// hash references.\n//\n\n\t// Link defs are in the form: ^[id]: url \"optional title\"\n\n\t/*\n\t\tvar text = text.replace(/\n\t\t\t\t^[ ]{0,3}\\[(.+)\\]: // id = $1 attacklab: g_tab_width - 1\n\t\t\t\t [ \\t]*\n\t\t\t\t \\n?\t\t\t\t// maybe *one* newline\n\t\t\t\t [ \\t]*\n\t\t\t\t?\t\t\t// url = $2\n\t\t\t\t [ \\t]*\n\t\t\t\t \\n?\t\t\t\t// maybe one newline\n\t\t\t\t [ \\t]*\n\t\t\t\t(?:\n\t\t\t\t (\\n*)\t\t\t\t// any lines skipped = $3 attacklab: lookbehind removed\n\t\t\t\t [\"(]\n\t\t\t\t (.+?)\t\t\t\t// title = $4\n\t\t\t\t [\")]\n\t\t\t\t [ \\t]*\n\t\t\t\t)?\t\t\t\t\t// title is optional\n\t\t\t\t(?:\\n+|$)\n\t\t\t /gm,\n\t\t\t function(){...});\n\t*/\n\tvar text = text.replace(/^[ ]{0,3}\\[(.+)\\]:[ \\t]*\\n?[ \\t]*?[ \\t]*\\n?[ \\t]*(?:(\\n*)[\"(](.+?)[\")][ \\t]*)?(?:\\n+|\\Z)/gm,\n\t\tfunction (wholeMatch,m1,m2,m3,m4) {\n\t\t\tm1 = m1.toLowerCase();\n\t\t\tg_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive\n\t\t\tif (m3) {\n\t\t\t\t// Oops, found blank lines, so it's not a title.\n\t\t\t\t// Put back the parenthetical statement we stole.\n\t\t\t\treturn m3+m4;\n\t\t\t} else if (m4) {\n\t\t\t\tg_titles[m1] = m4.replace(/\"/g,\""\");\n\t\t\t}\n\t\t\t\n\t\t\t// Completely remove the definition from the text\n\t\t\treturn \"\";\n\t\t}\n\t);\n\n\treturn text;\n}\n\n\nvar _HashHTMLBlocks = function(text) {\n\t// attacklab: Double up blank lines to reduce lookaround\n\ttext = text.replace(/\\n/g,\"\\n\\n\");\n\n\t// Hashify HTML blocks:\n\t// We only want to do this for block-level HTML tags, such as headers,\n\t// lists, and tables. That's because we still want to wrap

    s around\n\t// \"paragraphs\" that are wrapped in non-block-level tags, such as anchors,\n\t// phrase emphasis, and spans. The list of tags we're looking for is\n\t// hard-coded:\n\tvar block_tags_a = \"p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del\"\n\tvar block_tags_b = \"p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math\"\n\n\t// First, look for nested blocks, e.g.:\n\t//

    \n\t//
    \n\t// tags for inner block must be indented.\n\t//
    \n\t//
    \n\t//\n\t// The outermost tags must start at the left margin for this to match, and\n\t// the inner nested divs must be indented.\n\t// We need to do this before the next, more liberal match, because the next\n\t// match will start at the first `
    ` and stop at the first `
    `.\n\n\t// attacklab: This regex can be expensive when it fails.\n\t/*\n\t\tvar text = text.replace(/\n\t\t(\t\t\t\t\t\t// save in $1\n\t\t\t^\t\t\t\t\t// start of line (with /m)\n\t\t\t<($block_tags_a)\t// start tag = $2\n\t\t\t\\b\t\t\t\t\t// word break\n\t\t\t\t\t\t\t\t// attacklab: hack around khtml/pcre bug...\n\t\t\t[^\\r]*?\\n\t\t\t// any number of lines, minimally matching\n\t\t\t\t\t\t\t// the matching end tag\n\t\t\t[ \\t]*\t\t\t\t// trailing spaces/tabs\n\t\t\t(?=\\n+)\t\t\t\t// followed by a newline\n\t\t)\t\t\t\t\t\t// attacklab: there are sentinel newlines at end of document\n\t\t/gm,function(){...}};\n\t*/\n\ttext = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\\b[^\\r]*?\\n<\\/\\2>[ \\t]*(?=\\n+))/gm,hashElement);\n\n\t//\n\t// Now match more liberally, simply from `\\n` to `\\n`\n\t//\n\n\t/*\n\t\tvar text = text.replace(/\n\t\t(\t\t\t\t\t\t// save in $1\n\t\t\t^\t\t\t\t\t// start of line (with /m)\n\t\t\t<($block_tags_b)\t// start tag = $2\n\t\t\t\\b\t\t\t\t\t// word break\n\t\t\t\t\t\t\t\t// attacklab: hack around khtml/pcre bug...\n\t\t\t[^\\r]*?\t\t\t\t// any number of lines, minimally matching\n\t\t\t.*\t\t\t\t// the matching end tag\n\t\t\t[ \\t]*\t\t\t\t// trailing spaces/tabs\n\t\t\t(?=\\n+)\t\t\t\t// followed by a newline\n\t\t)\t\t\t\t\t\t// attacklab: there are sentinel newlines at end of document\n\t\t/gm,function(){...}};\n\t*/\n\ttext = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\\b[^\\r]*?.*<\\/\\2>[ \\t]*(?=\\n+)\\n)/gm,hashElement);\n\n\t// Special case just for
    . It was easier to make a special case than\n\t// to make the other regex more complicated. \n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t// save in $1\n\t\t\t\\n\\n\t\t\t\t// Starting after a blank line\n\t\t\t[ ]{0,3}\n\t\t\t(<(hr)\t\t\t\t// start tag = $2\n\t\t\t\\b\t\t\t\t\t// word break\n\t\t\t([^<>])*?\t\t\t// \n\t\t\t\\/?>)\t\t\t\t// the matching end tag\n\t\t\t[ \\t]*\n\t\t\t(?=\\n{2,})\t\t\t// followed by a blank line\n\t\t)\n\t\t/g,hashElement);\n\t*/\n\ttext = text.replace(/(\\n[ ]{0,3}(<(hr)\\b([^<>])*?\\/?>)[ \\t]*(?=\\n{2,}))/g,hashElement);\n\n\t// Special case for standalone HTML comments:\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t// save in $1\n\t\t\t\\n\\n\t\t\t\t// Starting after a blank line\n\t\t\t[ ]{0,3}\t\t\t// attacklab: g_tab_width - 1\n\t\t\t\n\t\t\t[ \\t]*\n\t\t\t(?=\\n{2,})\t\t\t// followed by a blank line\n\t\t)\n\t\t/g,hashElement);\n\t*/\n\ttext = text.replace(/(\\n\\n[ ]{0,3}[ \\t]*(?=\\n{2,}))/g,hashElement);\n\n\t// PHP and ASP-style processor instructions ( and <%...%>)\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(?:\n\t\t\t\\n\\n\t\t\t\t// Starting after a blank line\n\t\t)\n\t\t(\t\t\t\t\t\t// save in $1\n\t\t\t[ ]{0,3}\t\t\t// attacklab: g_tab_width - 1\n\t\t\t(?:\n\t\t\t\t<([?%])\t\t\t// $2\n\t\t\t\t[^\\r]*?\n\t\t\t\t\\2>\n\t\t\t)\n\t\t\t[ \\t]*\n\t\t\t(?=\\n{2,})\t\t\t// followed by a blank line\n\t\t)\n\t\t/g,hashElement);\n\t*/\n\ttext = text.replace(/(?:\\n\\n)([ ]{0,3}(?:<([?%])[^\\r]*?\\2>)[ \\t]*(?=\\n{2,}))/g,hashElement);\n\n\t// attacklab: Undo double lines (see comment at top of this function)\n\ttext = text.replace(/\\n\\n/g,\"\\n\");\n\treturn text;\n}\n\nvar hashElement = function(wholeMatch,m1) {\n\tvar blockText = m1;\n\n\t// Undo double lines\n\tblockText = blockText.replace(/\\n\\n/g,\"\\n\");\n\tblockText = blockText.replace(/^\\n/,\"\");\n\t\n\t// strip trailing blank lines\n\tblockText = blockText.replace(/\\n+$/g,\"\");\n\t\n\t// Replace the element text with a marker (\"~KxK\" where x is its key)\n\tblockText = \"\\n\\n~K\" + (g_html_blocks.push(blockText)-1) + \"K\\n\\n\";\n\t\n\treturn blockText;\n};\n\nvar _RunBlockGamut = function(text) {\n//\n// These are all the transformations that form block-level\n// tags like paragraphs, headers, and list items.\n//\n\ttext = _DoHeaders(text);\n\n\t// Do Horizontal Rules:\n\tvar key = hashBlock(\"
    \");\n\ttext = text.replace(/^[ ]{0,2}([ ]?\\*[ ]?){3,}[ \\t]*$/gm,key);\n\ttext = text.replace(/^[ ]{0,2}([ ]?\\-[ ]?){3,}[ \\t]*$/gm,key);\n\ttext = text.replace(/^[ ]{0,2}([ ]?\\_[ ]?){3,}[ \\t]*$/gm,key);\n\n\ttext = _DoLists(text);\n\ttext = _DoCodeBlocks(text);\n\ttext = _DoBlockQuotes(text);\n\n\t// We already ran _HashHTMLBlocks() before, in Markdown(), but that\n\t// was to escape raw HTML in the original Markdown source. This time,\n\t// we're escaping the markup we've just created, so that we don't wrap\n\t//

    tags around block-level tags.\n\ttext = _HashHTMLBlocks(text);\n\ttext = _FormParagraphs(text);\n\n\treturn text;\n}\n\n\nvar _RunSpanGamut = function(text) {\n//\n// These are all the transformations that occur *within* block-level\n// tags like paragraphs, headers, and list items.\n//\n\n\ttext = _DoCodeSpans(text);\n\ttext = _EscapeSpecialCharsWithinTagAttributes(text);\n\ttext = _EncodeBackslashEscapes(text);\n\n\t// Process anchor and image tags. Images must come first,\n\t// because ![foo][f] looks like an anchor.\n\ttext = _DoImages(text);\n\ttext = _DoAnchors(text);\n\n\t// Make links out of things like ``\n\t// Must come after _DoAnchors(), because you can use < and >\n\t// delimiters in inline links like [this]().\n\ttext = _DoAutoLinks(text);\n\ttext = _EncodeAmpsAndAngles(text);\n\ttext = _DoItalicsAndBold(text);\n\n\t// Do hard breaks:\n\ttext = text.replace(/ +\\n/g,\"
    \\n\");\n\n\treturn text;\n}\n\nvar _EscapeSpecialCharsWithinTagAttributes = function(text) {\n//\n// Within tags -- meaning between < and > -- encode [\\ ` * _] so they\n// don't conflict with their use in Markdown for code, italics and strong.\n//\n\n\t// Build a regex to find HTML tags and comments. See Friedl's \n\t// \"Mastering Regular Expressions\", 2nd Ed., pp. 200-201.\n\tvar regex = /(<[a-z\\/!$](\"[^\"]*\"|'[^']*'|[^'\">])*>|)/gi;\n\n\ttext = text.replace(regex, function(wholeMatch) {\n\t\tvar tag = wholeMatch.replace(/(.)<\\/?code>(?=.)/g,\"$1`\");\n\t\ttag = escapeCharacters(tag,\"\\\\`*_\");\n\t\treturn tag;\n\t});\n\n\treturn text;\n}\n\nvar _DoAnchors = function(text) {\n//\n// Turn Markdown link shortcuts into XHTML
    tags.\n//\n\t//\n\t// First, handle reference-style links: [link text] [id]\n\t//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t\t// wrap whole match in $1\n\t\t\t\\[\n\t\t\t(\n\t\t\t\t(?:\n\t\t\t\t\t\\[[^\\]]*\\]\t\t// allow brackets nested one level\n\t\t\t\t\t|\n\t\t\t\t\t[^\\[]\t\t\t// or anything else\n\t\t\t\t)*\n\t\t\t)\n\t\t\t\\]\n\n\t\t\t[ ]?\t\t\t\t\t// one optional space\n\t\t\t(?:\\n[ ]*)?\t\t\t\t// one optional newline followed by spaces\n\n\t\t\t\\[\n\t\t\t(.*?)\t\t\t\t\t// id = $3\n\t\t\t\\]\n\t\t)()()()()\t\t\t\t\t// pad remaining backreferences\n\t\t/g,_DoAnchors_callback);\n\t*/\n\ttext = text.replace(/(\\[((?:\\[[^\\]]*\\]|[^\\[\\]])*)\\][ ]?(?:\\n[ ]*)?\\[(.*?)\\])()()()()/g,writeAnchorTag);\n\n\t//\n\t// Next, inline-style links: [link text](url \"optional title\")\n\t//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t\t(\t\t\t\t\t\t// wrap whole match in $1\n\t\t\t\t\\[\n\t\t\t\t(\n\t\t\t\t\t(?:\n\t\t\t\t\t\t\\[[^\\]]*\\]\t// allow brackets nested one level\n\t\t\t\t\t|\n\t\t\t\t\t[^\\[\\]]\t\t\t// or anything else\n\t\t\t\t)\n\t\t\t)\n\t\t\t\\]\n\t\t\t\\(\t\t\t\t\t\t// literal paren\n\t\t\t[ \\t]*\n\t\t\t()\t\t\t\t\t\t// no id, so leave $3 empty\n\t\t\t?\t\t\t\t// href = $4\n\t\t\t[ \\t]*\n\t\t\t(\t\t\t\t\t\t// $5\n\t\t\t\t(['\"])\t\t\t\t// quote char = $6\n\t\t\t\t(.*?)\t\t\t\t// Title = $7\n\t\t\t\t\\6\t\t\t\t\t// matching quote\n\t\t\t\t[ \\t]*\t\t\t\t// ignore any spaces/tabs between closing quote and )\n\t\t\t)?\t\t\t\t\t\t// title is optional\n\t\t\t\\)\n\t\t)\n\t\t/g,writeAnchorTag);\n\t*/\n\ttext = text.replace(/(\\[((?:\\[[^\\]]*\\]|[^\\[\\]])*)\\]\\([ \\t]*()?[ \\t]*((['\"])(.*?)\\6[ \\t]*)?\\))/g,writeAnchorTag);\n\n\t//\n\t// Last, handle reference-style shortcuts: [link text]\n\t// These must come last in case you've also got [link test][1]\n\t// or [link test](/foo)\n\t//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t \t\t\t\t\t// wrap whole match in $1\n\t\t\t\\[\n\t\t\t([^\\[\\]]+)\t\t\t\t// link text = $2; can't contain '[' or ']'\n\t\t\t\\]\n\t\t)()()()()()\t\t\t\t\t// pad rest of backreferences\n\t\t/g, writeAnchorTag);\n\t*/\n\ttext = text.replace(/(\\[([^\\[\\]]+)\\])()()()()()/g, writeAnchorTag);\n\n\treturn text;\n}\n\nvar writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\n\tif (m7 == undefined) m7 = \"\";\n\tvar whole_match = m1;\n\tvar link_text = m2;\n\tvar link_id\t = m3.toLowerCase();\n\tvar url\t\t= m4;\n\tvar title\t= m7;\n\t\n\tif (url == \"\") {\n\t\tif (link_id == \"\") {\n\t\t\t// lower-case and turn embedded newlines into spaces\n\t\t\tlink_id = link_text.toLowerCase().replace(/ ?\\n/g,\" \");\n\t\t}\n\t\turl = \"#\"+link_id;\n\t\t\n\t\tif (g_urls[link_id] != undefined) {\n\t\t\turl = g_urls[link_id];\n\t\t\tif (g_titles[link_id] != undefined) {\n\t\t\t\ttitle = g_titles[link_id];\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\tif (whole_match.search(/\\(\\s*\\)$/m)>-1) {\n\t\t\t\t// Special case for explicit empty url\n\t\t\t\turl = \"\";\n\t\t\t} else {\n\t\t\t\treturn whole_match;\n\t\t\t}\n\t\t}\n\t}\t\n\t\n\turl = escapeCharacters(url,\"*_\");\n\tvar result = \"\" + link_text + \"\";\n\t\n\treturn result;\n}\n\n\nvar _DoImages = function(text) {\n//\n// Turn Markdown image shortcuts into tags.\n//\n\n\t//\n\t// First, handle reference-style labeled images: ![alt text][id]\n\t//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t// wrap whole match in $1\n\t\t\t!\\[\n\t\t\t(.*?)\t\t\t\t// alt text = $2\n\t\t\t\\]\n\n\t\t\t[ ]?\t\t\t\t// one optional space\n\t\t\t(?:\\n[ ]*)?\t\t\t// one optional newline followed by spaces\n\n\t\t\t\\[\n\t\t\t(.*?)\t\t\t\t// id = $3\n\t\t\t\\]\n\t\t)()()()()\t\t\t\t// pad rest of backreferences\n\t\t/g,writeImageTag);\n\t*/\n\ttext = text.replace(/(!\\[(.*?)\\][ ]?(?:\\n[ ]*)?\\[(.*?)\\])()()()()/g,writeImageTag);\n\n\t//\n\t// Next, handle inline images: ![alt text](url \"optional title\")\n\t// Don't forget: encode * and _\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t// wrap whole match in $1\n\t\t\t!\\[\n\t\t\t(.*?)\t\t\t\t// alt text = $2\n\t\t\t\\]\n\t\t\t\\s?\t\t\t\t\t// One optional whitespace character\n\t\t\t\\(\t\t\t\t\t// literal paren\n\t\t\t[ \\t]*\n\t\t\t()\t\t\t\t\t// no id, so leave $3 empty\n\t\t\t?\t\t\t// src url = $4\n\t\t\t[ \\t]*\n\t\t\t(\t\t\t\t\t// $5\n\t\t\t\t(['\"])\t\t\t// quote char = $6\n\t\t\t\t(.*?)\t\t\t// title = $7\n\t\t\t\t\\6\t\t\t\t// matching quote\n\t\t\t\t[ \\t]*\n\t\t\t)?\t\t\t\t\t// title is optional\n\t\t\\)\n\t\t)\n\t\t/g,writeImageTag);\n\t*/\n\ttext = text.replace(/(!\\[(.*?)\\]\\s?\\([ \\t]*()?[ \\t]*((['\"])(.*?)\\6[ \\t]*)?\\))/g,writeImageTag);\n\n\treturn text;\n}\n\nvar writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {\n\tvar whole_match = m1;\n\tvar alt_text = m2;\n\tvar link_id\t = m3.toLowerCase();\n\tvar url\t\t= m4;\n\tvar title\t= m7;\n\n\tif (!title) title = \"\";\n\t\n\tif (url == \"\") {\n\t\tif (link_id == \"\") {\n\t\t\t// lower-case and turn embedded newlines into spaces\n\t\t\tlink_id = alt_text.toLowerCase().replace(/ ?\\n/g,\" \");\n\t\t}\n\t\turl = \"#\"+link_id;\n\t\t\n\t\tif (g_urls[link_id] != undefined) {\n\t\t\turl = g_urls[link_id];\n\t\t\tif (g_titles[link_id] != undefined) {\n\t\t\t\ttitle = g_titles[link_id];\n\t\t\t}\n\t\t}\n\t\telse {\n\t\t\treturn whole_match;\n\t\t}\n\t}\t\n\t\n\talt_text = alt_text.replace(/\"/g,\""\");\n\turl = escapeCharacters(url,\"*_\");\n\tvar result = \"\\\"\"\";\n\t\n\treturn result;\n}\n\n\nvar _DoHeaders = function(text) {\n\n\t// Setext-style headers:\n\t//\tHeader 1\n\t//\t========\n\t// \n\t//\tHeader 2\n\t//\t--------\n\t//\n\ttext = text.replace(/^(.+)[ \\t]*\\n=+[ \\t]*\\n+/gm,\n\t\tfunction(wholeMatch,m1){return hashBlock(\"

    \" + _RunSpanGamut(m1) + \"

    \");});\n\n\ttext = text.replace(/^(.+)[ \\t]*\\n-+[ \\t]*\\n+/gm,\n\t\tfunction(matchFound,m1){return hashBlock(\"

    \" + _RunSpanGamut(m1) + \"

    \");});\n\n\t// atx-style headers:\n\t// # Header 1\n\t// ## Header 2\n\t// ## Header 2 with closing hashes ##\n\t// ...\n\t// ###### Header 6\n\t//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t\t^(\\#{1,6})\t\t\t\t// $1 = string of #'s\n\t\t\t[ \\t]*\n\t\t\t(.+?)\t\t\t\t\t// $2 = Header text\n\t\t\t[ \\t]*\n\t\t\t\\#*\t\t\t\t\t\t// optional closing #'s (not counted)\n\t\t\t\\n+\n\t\t/gm, function() {...});\n\t*/\n\n\ttext = text.replace(/^(\\#{1,6})[ \\t]*(.+?)[ \\t]*\\#*\\n+/gm,\n\t\tfunction(wholeMatch,m1,m2) {\n\t\t\tvar h_level = m1.length;\n\t\t\treturn hashBlock(\"\" + _RunSpanGamut(m2) + \"\");\n\t\t});\n\n\treturn text;\n}\n\n// This declaration keeps Dojo compressor from outputting garbage:\nvar _ProcessListItems;\n\nvar _DoLists = function(text) {\n//\n// Form HTML ordered (numbered) and unordered (bulleted) lists.\n//\n\n\t// attacklab: add sentinel to hack around khtml/safari bug:\n\t// http://bugs.webkit.org/show_bug.cgi?id=11231\n\ttext += \"~0\";\n\n\t// Re-usable pattern to match any entirel ul or ol list:\n\n\t/*\n\t\tvar whole_list = /\n\t\t(\t\t\t\t\t\t\t\t\t// $1 = whole list\n\t\t\t(\t\t\t\t\t\t\t\t// $2\n\t\t\t\t[ ]{0,3}\t\t\t\t\t// attacklab: g_tab_width - 1\n\t\t\t\t([*+-]|\\d+[.])\t\t\t\t// $3 = first list item marker\n\t\t\t\t[ \\t]+\n\t\t\t)\n\t\t\t[^\\r]+?\n\t\t\t(\t\t\t\t\t\t\t\t// $4\n\t\t\t\t~0\t\t\t\t\t\t\t// sentinel for workaround; should be $\n\t\t\t|\n\t\t\t\t\\n{2,}\n\t\t\t\t(?=\\S)\n\t\t\t\t(?!\t\t\t\t\t\t\t// Negative lookahead for another list item marker\n\t\t\t\t\t[ \\t]*\n\t\t\t\t\t(?:[*+-]|\\d+[.])[ \\t]+\n\t\t\t\t)\n\t\t\t)\n\t\t)/g\n\t*/\n\tvar whole_list = /^(([ ]{0,3}([*+-]|\\d+[.])[ \\t]+)[^\\r]+?(~0|\\n{2,}(?=\\S)(?![ \\t]*(?:[*+-]|\\d+[.])[ \\t]+)))/gm;\n\n\tif (g_list_level) {\n\t\ttext = text.replace(whole_list,function(wholeMatch,m1,m2) {\n\t\t\tvar list = m1;\n\t\t\tvar list_type = (m2.search(/[*+-]/g)>-1) ? \"ul\" : \"ol\";\n\n\t\t\t// Turn double returns into triple returns, so that we can make a\n\t\t\t// paragraph for the last item in a list, if necessary:\n\t\t\tlist = list.replace(/\\n{2,}/g,\"\\n\\n\\n\");;\n\t\t\tvar result = _ProcessListItems(list);\n\t\n\t\t\t// Trim any trailing whitespace, to put the closing ``\n\t\t\t// up on the preceding line, to get it past the current stupid\n\t\t\t// HTML block parser. This is a hack to work around the terrible\n\t\t\t// hack that is the HTML block parser.\n\t\t\tresult = result.replace(/\\s+$/,\"\");\n\t\t\tresult = \"<\"+list_type+\">\" + result + \"\\n\";\n\t\t\treturn result;\n\t\t});\n\t} else {\n\t\twhole_list = /(\\n\\n|^\\n?)(([ ]{0,3}([*+-]|\\d+[.])[ \\t]+)[^\\r]+?(~0|\\n{2,}(?=\\S)(?![ \\t]*(?:[*+-]|\\d+[.])[ \\t]+)))/g;\n\t\ttext = text.replace(whole_list,function(wholeMatch,m1,m2,m3) {\n\t\t\tvar runup = m1;\n\t\t\tvar list = m2;\n\n\t\t\tvar list_type = (m3.search(/[*+-]/g)>-1) ? \"ul\" : \"ol\";\n\t\t\t// Turn double returns into triple returns, so that we can make a\n\t\t\t// paragraph for the last item in a list, if necessary:\n\t\t\tvar list = list.replace(/\\n{2,}/g,\"\\n\\n\\n\");;\n\t\t\tvar result = _ProcessListItems(list);\n\t\t\tresult = runup + \"<\"+list_type+\">\\n\" + result + \"\\n\";\t\n\t\t\treturn result;\n\t\t});\n\t}\n\n\t// attacklab: strip sentinel\n\ttext = text.replace(/~0/,\"\");\n\n\treturn text;\n}\n\n_ProcessListItems = function(list_str) {\n//\n// Process the contents of a single ordered or unordered list, splitting it\n// into individual list items.\n//\n\t// The $g_list_level global keeps track of when we're inside a list.\n\t// Each time we enter a list, we increment it; when we leave a list,\n\t// we decrement. If it's zero, we're not in a list anymore.\n\t//\n\t// We do this because when we're not inside a list, we want to treat\n\t// something like this:\n\t//\n\t// I recommend upgrading to version\n\t// 8. Oops, now this line is treated\n\t// as a sub-list.\n\t//\n\t// As a single paragraph, despite the fact that the second line starts\n\t// with a digit-period-space sequence.\n\t//\n\t// Whereas when we're inside a list (or sub-list), that line will be\n\t// treated as the start of a sub-list. What a kludge, huh? This is\n\t// an aspect of Markdown's syntax that's hard to parse perfectly\n\t// without resorting to mind-reading. Perhaps the solution is to\n\t// change the syntax rules such that sub-lists must start with a\n\t// starting cardinal number; e.g. \"1.\" or \"a.\".\n\n\tg_list_level++;\n\n\t// trim trailing blank lines:\n\tlist_str = list_str.replace(/\\n{2,}$/,\"\\n\");\n\n\t// attacklab: add sentinel to emulate \\z\n\tlist_str += \"~0\";\n\n\t/*\n\t\tlist_str = list_str.replace(/\n\t\t\t(\\n)?\t\t\t\t\t\t\t// leading line = $1\n\t\t\t(^[ \\t]*)\t\t\t\t\t\t// leading whitespace = $2\n\t\t\t([*+-]|\\d+[.]) [ \\t]+\t\t\t// list marker = $3\n\t\t\t([^\\r]+?\t\t\t\t\t\t// list item text = $4\n\t\t\t(\\n{1,2}))\n\t\t\t(?= \\n* (~0 | \\2 ([*+-]|\\d+[.]) [ \\t]+))\n\t\t/gm, function(){...});\n\t*/\n\tlist_str = list_str.replace(/(\\n)?(^[ \\t]*)([*+-]|\\d+[.])[ \\t]+([^\\r]+?(\\n{1,2}))(?=\\n*(~0|\\2([*+-]|\\d+[.])[ \\t]+))/gm,\n\t\tfunction(wholeMatch,m1,m2,m3,m4){\n\t\t\tvar item = m4;\n\t\t\tvar leading_line = m1;\n\t\t\tvar leading_space = m2;\n\n\t\t\tif (leading_line || (item.search(/\\n{2,}/)>-1)) {\n\t\t\t\titem = _RunBlockGamut(_Outdent(item));\n\t\t\t}\n\t\t\telse {\n\t\t\t\t// Recursion for sub-lists:\n\t\t\t\titem = _DoLists(_Outdent(item));\n\t\t\t\titem = item.replace(/\\n$/,\"\"); // chomp(item)\n\t\t\t\titem = _RunSpanGamut(item);\n\t\t\t}\n\n\t\t\treturn \"
  • \" + item + \"
  • \\n\";\n\t\t}\n\t);\n\n\t// attacklab: strip sentinel\n\tlist_str = list_str.replace(/~0/g,\"\");\n\n\tg_list_level--;\n\treturn list_str;\n}\n\n\nvar _DoCodeBlocks = function(text) {\n//\n// Process Markdown `
    ` blocks.\n//  \n\n\t/*\n\t\ttext = text.replace(text,\n\t\t\t/(?:\\n\\n|^)\n\t\t\t(\t\t\t\t\t\t\t\t// $1 = the code block -- one or more lines, starting with a space/tab\n\t\t\t\t(?:\n\t\t\t\t\t(?:[ ]{4}|\\t)\t\t\t// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width\n\t\t\t\t\t.*\\n+\n\t\t\t\t)+\n\t\t\t)\n\t\t\t(\\n*[ ]{0,3}[^ \\t\\n]|(?=~0))\t// attacklab: g_tab_width\n\t\t/g,function(){...});\n\t*/\n\n\t// attacklab: sentinel workarounds for lack of \\A and \\Z, safari\\khtml bug\n\ttext += \"~0\";\n\t\n\ttext = text.replace(/(?:\\n\\n|^)((?:(?:[ ]{4}|\\t).*\\n+)+)(\\n*[ ]{0,3}[^ \\t\\n]|(?=~0))/g,\n\t\tfunction(wholeMatch,m1,m2) {\n\t\t\tvar codeblock = m1;\n\t\t\tvar nextChar = m2;\n\t\t\n\t\t\tcodeblock = _EncodeCode( _Outdent(codeblock));\n\t\t\tcodeblock = _Detab(codeblock);\n\t\t\tcodeblock = codeblock.replace(/^\\n+/g,\"\"); // trim leading newlines\n\t\t\tcodeblock = codeblock.replace(/\\n+$/g,\"\"); // trim trailing whitespace\n\n\t\t\tcodeblock = \"
    \" + codeblock + \"\\n
    \";\n\n\t\t\treturn hashBlock(codeblock) + nextChar;\n\t\t}\n\t);\n\n\t// attacklab: strip sentinel\n\ttext = text.replace(/~0/,\"\");\n\n\treturn text;\n}\n\nvar hashBlock = function(text) {\n\ttext = text.replace(/(^\\n+|\\n+$)/g,\"\");\n\treturn \"\\n\\n~K\" + (g_html_blocks.push(text)-1) + \"K\\n\\n\";\n}\n\n\nvar _DoCodeSpans = function(text) {\n//\n// * Backtick quotes are used for spans.\n// \n// * You can use multiple backticks as the delimiters if you want to\n//\t include literal backticks in the code span. So, this input:\n//\t \n//\t\t Just type ``foo `bar` baz`` at the prompt.\n//\t \n//\t Will translate to:\n//\t \n//\t\t

    Just type foo `bar` baz at the prompt.

    \n//\t \n//\tThere's no arbitrary limit to the number of backticks you\n//\tcan use as delimters. If you need three consecutive backticks\n//\tin your code, use four for delimiters, etc.\n//\n// * You can use spaces to get literal backticks at the edges:\n//\t \n//\t\t ... type `` `bar` `` ...\n//\t \n//\t Turns to:\n//\t \n//\t\t ... type `bar` ...\n//\n\n\t/*\n\t\ttext = text.replace(/\n\t\t\t(^|[^\\\\])\t\t\t\t\t// Character before opening ` can't be a backslash\n\t\t\t(`+)\t\t\t\t\t\t// $2 = Opening run of `\n\t\t\t(\t\t\t\t\t\t\t// $3 = The code block\n\t\t\t\t[^\\r]*?\n\t\t\t\t[^`]\t\t\t\t\t// attacklab: work around lack of lookbehind\n\t\t\t)\n\t\t\t\\2\t\t\t\t\t\t\t// Matching closer\n\t\t\t(?!`)\n\t\t/gm, function(){...});\n\t*/\n\n\ttext = text.replace(/(^|[^\\\\])(`+)([^\\r]*?[^`])\\2(?!`)/gm,\n\t\tfunction(wholeMatch,m1,m2,m3,m4) {\n\t\t\tvar c = m3;\n\t\t\tc = c.replace(/^([ \\t]*)/g,\"\");\t// leading whitespace\n\t\t\tc = c.replace(/[ \\t]*$/g,\"\");\t// trailing whitespace\n\t\t\tc = _EncodeCode(c);\n\t\t\treturn m1+\"\"+c+\"\";\n\t\t});\n\n\treturn text;\n}\n\n\nvar _EncodeCode = function(text) {\n//\n// Encode/escape certain characters inside Markdown code runs.\n// The point is that in code, these characters are literals,\n// and lose their special Markdown meanings.\n//\n\t// Encode all ampersands; HTML entities are not\n\t// entities within a Markdown code span.\n\ttext = text.replace(/&/g,\"&\");\n\n\t// Do the angle bracket song and dance:\n\ttext = text.replace(//g,\">\");\n\n\t// Now, escape characters that are magic in Markdown:\n\ttext = escapeCharacters(text,\"\\*_{}[]\\\\\",false);\n\n// jj the line above breaks this:\n//---\n\n//* Item\n\n// 1. Subitem\n\n// special char: *\n//---\n\n\treturn text;\n}\n\n\nvar _DoItalicsAndBold = function(text) {\n\n\t// must go first:\n\ttext = text.replace(/(\\*\\*|__)(?=\\S)([^\\r]*?\\S[*_]*)\\1/g,\n\t\t\"$2\");\n\n\ttext = text.replace(/(\\*|_)(?=\\S)([^\\r]*?\\S)\\1/g,\n\t\t\"$2\");\n\n\treturn text;\n}\n\n\nvar _DoBlockQuotes = function(text) {\n\n\t/*\n\t\ttext = text.replace(/\n\t\t(\t\t\t\t\t\t\t\t// Wrap whole match in $1\n\t\t\t(\n\t\t\t\t^[ \\t]*>[ \\t]?\t\t\t// '>' at the start of a line\n\t\t\t\t.+\\n\t\t\t\t\t// rest of the first line\n\t\t\t\t(.+\\n)*\t\t\t\t\t// subsequent consecutive lines\n\t\t\t\t\\n*\t\t\t\t\t\t// blanks\n\t\t\t)+\n\t\t)\n\t\t/gm, function(){...});\n\t*/\n\n\ttext = text.replace(/((^[ \\t]*>[ \\t]?.+\\n(.+\\n)*\\n*)+)/gm,\n\t\tfunction(wholeMatch,m1) {\n\t\t\tvar bq = m1;\n\n\t\t\t// attacklab: hack around Konqueror 3.5.4 bug:\n\t\t\t// \"----------bug\".replace(/^-/g,\"\") == \"bug\"\n\n\t\t\tbq = bq.replace(/^[ \\t]*>[ \\t]?/gm,\"~0\");\t// trim one level of quoting\n\n\t\t\t// attacklab: clean up hack\n\t\t\tbq = bq.replace(/~0/g,\"\");\n\n\t\t\tbq = bq.replace(/^[ \\t]+$/gm,\"\");\t\t// trim whitespace-only lines\n\t\t\tbq = _RunBlockGamut(bq);\t\t\t\t// recurse\n\t\t\t\n\t\t\tbq = bq.replace(/(^|\\n)/g,\"$1 \");\n\t\t\t// These leading spaces screw with
     content, so we need to fix that:\n\t\t\tbq = bq.replace(\n\t\t\t\t\t/(\\s*
    [^\\r]+?<\\/pre>)/gm,\n\t\t\t\tfunction(wholeMatch,m1) {\n\t\t\t\t\tvar pre = m1;\n\t\t\t\t\t// attacklab: hack around Konqueror 3.5.4 bug:\n\t\t\t\t\tpre = pre.replace(/^  /mg,\"~0\");\n\t\t\t\t\tpre = pre.replace(/~0/g,\"\");\n\t\t\t\t\treturn pre;\n\t\t\t\t});\n\t\t\t\n\t\t\treturn hashBlock(\"
    \\n\" + bq + \"\\n
    \");\n\t\t});\n\treturn text;\n}\n\n\nvar _FormParagraphs = function(text) {\n//\n// Params:\n// $text - string to process with html

    tags\n//\n\n\t// Strip leading and trailing lines:\n\ttext = text.replace(/^\\n+/g,\"\");\n\ttext = text.replace(/\\n+$/g,\"\");\n\n\tvar grafs = text.split(/\\n{2,}/g);\n\tvar grafsOut = new Array();\n\n\t//\n\t// Wrap

    tags.\n\t//\n\tvar end = grafs.length;\n\tfor (var i=0; i= 0) {\n\t\t\tgrafsOut.push(str);\n\t\t}\n\t\telse if (str.search(/\\S/) >= 0) {\n\t\t\tstr = _RunSpanGamut(str);\n\t\t\tstr = str.replace(/^([ \\t]*)/g,\"

    \");\n\t\t\tstr += \"

    \"\n\t\t\tgrafsOut.push(str);\n\t\t}\n\n\t}\n\n\t//\n\t// Unhashify HTML blocks\n\t//\n\tend = grafsOut.length;\n\tfor (var i=0; i= 0) {\n\t\t\tvar blockText = g_html_blocks[RegExp.$1];\n\t\t\tblockText = blockText.replace(/\\$/g,\"$$$$\"); // Escape any dollar signs\n\t\t\tgrafsOut[i] = grafsOut[i].replace(/~K\\d+K/,blockText);\n\t\t}\n\t}\n\n\treturn grafsOut.join(\"\\n\\n\");\n}\n\n\nvar _EncodeAmpsAndAngles = function(text) {\n// Smart processing for ampersands and angle brackets that need to be encoded.\n\t\n\t// Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin:\n\t// http://bumppo.net/projects/amputator/\n\ttext = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\\w+);)/g,\"&\");\n\t\n\t// Encode naked <'s\n\ttext = text.replace(/<(?![a-z\\/?\\$!])/gi,\"<\");\n\t\n\treturn text;\n}\n\n\nvar _EncodeBackslashEscapes = function(text) {\n//\n// Parameter: String.\n// Returns:\tThe string, with after processing the following backslash\n//\t\t\t escape sequences.\n//\n\n\t// attacklab: The polite way to do this is with the new\n\t// escapeCharacters() function:\n\t//\n\t// \ttext = escapeCharacters(text,\"\\\\\",true);\n\t// \ttext = escapeCharacters(text,\"`*_{}[]()>#+-.!\",true);\n\t//\n\t// ...but we're sidestepping its use of the (slow) RegExp constructor\n\t// as an optimization for Firefox. This function gets called a LOT.\n\n\ttext = text.replace(/\\\\(\\\\)/g,escapeCharacters_callback);\n\ttext = text.replace(/\\\\([`*_{}\\[\\]()>#+-.!])/g,escapeCharacters_callback);\n\treturn text;\n}\n\n\nvar _DoAutoLinks = function(text) {\n\n\ttext = text.replace(/<((https?|ftp|dict):[^'\">\\s]+)>/gi,\"$1\");\n\n\t// Email addresses: \n\n\t/*\n\t\ttext = text.replace(/\n\t\t\t<\n\t\t\t(?:mailto:)?\n\t\t\t(\n\t\t\t\t[-.\\w]+\n\t\t\t\t\\@\n\t\t\t\t[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+\n\t\t\t)\n\t\t\t>\n\t\t/gi, _DoAutoLinks_callback());\n\t*/\n\ttext = text.replace(/<(?:mailto:)?([-.\\w]+\\@[-a-z0-9]+(\\.[-a-z0-9]+)*\\.[a-z]+)>/gi,\n\t\tfunction(wholeMatch,m1) {\n\t\t\treturn _EncodeEmailAddress( _UnescapeSpecialChars(m1) );\n\t\t}\n\t);\n\n\treturn text;\n}\n\n\nvar _EncodeEmailAddress = function(addr) {\n//\n// Input: an email address, e.g. \"foo@example.com\"\n//\n// Output: the email address as a mailto link, with each character\n//\tof the address encoded as either a decimal or hex entity, in\n//\tthe hopes of foiling most address harvesting spam bots. E.g.:\n//\n//\tfoo\n//\t @example.com\n//\n// Based on a filter by Matthew Wickline, posted to the BBEdit-Talk\n// mailing list: \n//\n\n\t// attacklab: why can't javascript speak hex?\n\tfunction char2hex(ch) {\n\t\tvar hexDigits = '0123456789ABCDEF';\n\t\tvar dec = ch.charCodeAt(0);\n\t\treturn(hexDigits.charAt(dec>>4) + hexDigits.charAt(dec&15));\n\t}\n\n\tvar encode = [\n\t\tfunction(ch){return \"&#\"+ch.charCodeAt(0)+\";\";},\n\t\tfunction(ch){return \"&#x\"+char2hex(ch)+\";\";},\n\t\tfunction(ch){return ch;}\n\t];\n\n\taddr = \"mailto:\" + addr;\n\n\taddr = addr.replace(/./g, function(ch) {\n\t\tif (ch == \"@\") {\n\t\t \t// this *must* be encoded. I insist.\n\t\t\tch = encode[Math.floor(Math.random()*2)](ch);\n\t\t} else if (ch !=\":\") {\n\t\t\t// leave ':' alone (to spot mailto: later)\n\t\t\tvar r = Math.random();\n\t\t\t// roughly 10% raw, 45% hex, 45% dec\n\t\t\tch = (\n\t\t\t\t\tr > .9 ?\tencode[2](ch) :\n\t\t\t\t\tr > .45 ?\tencode[1](ch) :\n\t\t\t\t\t\t\t\tencode[0](ch)\n\t\t\t\t);\n\t\t}\n\t\treturn ch;\n\t});\n\n\taddr = \"\" + addr + \"\";\n\taddr = addr.replace(/\">.+:/g,\"\\\">\"); // strip the mailto: from the visible part\n\n\treturn addr;\n}\n\n\nvar _UnescapeSpecialChars = function(text) {\n//\n// Swap back in all the special characters we've hidden.\n//\n\ttext = text.replace(/~E(\\d+)E/g,\n\t\tfunction(wholeMatch,m1) {\n\t\t\tvar charCodeToReplace = parseInt(m1);\n\t\t\treturn String.fromCharCode(charCodeToReplace);\n\t\t}\n\t);\n\treturn text;\n}\n\n\nvar _Outdent = function(text) {\n//\n// Remove one level of line-leading tabs or spaces\n//\n\n\t// attacklab: hack around Konqueror 3.5.4 bug:\n\t// \"----------bug\".replace(/^-/g,\"\") == \"bug\"\n\n\ttext = text.replace(/^(\\t|[ ]{1,4})/gm,\"~0\"); // attacklab: g_tab_width\n\n\t// attacklab: clean up hack\n\ttext = text.replace(/~0/g,\"\")\n\n\treturn text;\n}\n\nvar _Detab = function(text) {\n// attacklab: Detab's completely rewritten for speed.\n// In perl we could fix it by anchoring the regexp with \\G.\n// In javascript we're less fortunate.\n\n\t// expand first n-1 tabs\n\ttext = text.replace(/\\t(?=\\t)/g,\" \"); // attacklab: g_tab_width\n\n\t// replace the nth with two sentinels\n\ttext = text.replace(/\\t/g,\"~A~B\");\n\n\t// use the sentinel to anchor our regex so it doesn't explode\n\ttext = text.replace(/~B(.+?)~A/g,\n\t\tfunction(wholeMatch,m1,m2) {\n\t\t\tvar leadingText = m1;\n\t\t\tvar numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width\n\n\t\t\t// there *must* be a better way to do this:\n\t\t\tfor (var i=0; i;\n f.title = data.title;\n f.id = data.feed_id;\n f.link.@href = data.feed_link;\n f.link.@rel = \"self\";\n f.generator = \"CouchApp on CouchDB\";\n f.updated = rfc3339(data.updated);\n return f.toXMLString().replace(/\\<\\/feed\\>/,'');\n};\n\nexports.entry = function(data) {\n var entry = ;\n entry.id = data.entry_id;\n entry.title = data.title;\n entry.content = data.content;\n entry.content.@type = (data.content_type || 'html');\n entry.updated = rfc3339(data.updated);\n entry.author = {data.author};\n entry.link.@href = data.alternate;\n entry.link.@rel = \"alternate\";\n return entry;\n}","linkup":"// this code makes http://example.com into a link, \n// and also handles @name and #hashtag\n\n// todo add [[wiki_links]]\n\nvar mustache = require(\"vendor/couchapp/lib/mustache\");\nexports.encode = function(body, person_prefix, tag_prefix) {\n body = mustache.escape(body);\n person_prefix = person_prefix || \"http://twitter.com/\";\n tag_prefix = tag_prefix || \"http://delicious.com/tag/\";\n return body.replace(/((ftp|http|https):\\/\\/(\\w+:{0,1}\\w*@)?(\\S+)(:[0-9]+)?(\\/|\\/([\\w#!:.?+=&%@!\\-\\/]))?)/gi,function(a) {\n return ''+a+'';\n }).replace(/\\@([\\w\\-]+)/g,function(user,name) {\n return ''+user+'';\n }).replace(/\\#([\\w\\-\\.]+)/g,function(word,tag) {\n return ''+word+'';\n });\n};","path":"// from couch.js\nfunction encodeOptions(options) {\n var buf = [];\n if (typeof(options) == \"object\" && options !== null) {\n for (var name in options) {\n if (!options.hasOwnProperty(name)) {continue;}\n var value = options[name];\n if (name == \"key\" || name == \"startkey\" || name == \"endkey\") {\n value = JSON.stringify(value);\n }\n buf.push(encodeURIComponent(name) + \"=\" + encodeURIComponent(value));\n }\n }\n if (!buf.length) {\n return \"\";\n }\n return \"?\" + buf.join(\"&\");\n}\n\nfunction concatArgs(array, args) {\n for (var i=0; i < args.length; i++) {\n array.push(args[i]);\n };\n return array;\n};\n\nfunction makePath(array) {\n var options, path;\n \n if (typeof array[array.length - 1] != \"string\") {\n // it's a params hash\n options = array.pop();\n }\n path = array.map(function(item) {return encodeURIComponent(item)}).join('/');\n if (options) {\n return path + encodeOptions(options);\n } else {\n return path; \n }\n};\n\nexports.init = function(req) {\n return {\n asset : function() {\n var p = req.path, parts = ['', p[0], p[1] , p[2]];\n return makePath(concatArgs(parts, arguments));\n },\n show : function() {\n var p = req.path, parts = ['', p[0], p[1] , p[2], '_show'];\n return makePath(concatArgs(parts, arguments));\n },\n list : function() {\n var p = req.path, parts = ['', p[0], p[1] , p[2], '_list'];\n return makePath(concatArgs(parts, arguments));\n },\n update : function() {\n var p = req.path, parts = ['', p[0], p[1] , p[2], '_update'];\n return makePath(concatArgs(parts, arguments));\n },\n limit : function(limit) {\n var query = req.query;\n var l = query.limit;\n query.limit = limit;\n var view = req.path[req.path.length - 1];\n var list = req.path[req.path.length - 2];\n var link = this.list(list, view, query);\n query.limit = l;\n return link;\n },\n older : function(key) {\n if (!typeof key == \"undefined\") return null;\n var query = req.query;\n query.startkey = key;\n query.skip=1;\n var view = req.path[req.path.length - 1];\n var list = req.path[req.path.length - 2];\n return this.list(list, view, query);\n },\n absolute : function(path) {\n return 'http://' + req.headers.Host + path;\n }\n }\n};","md5":"/*\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n/*\n * Configurable variables. You may need to tweak these to be compatible with\n * the server-side, but the defaults work in most cases.\n */\nvar hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */\nvar b64pad = \"\"; /* base-64 pad character. \"=\" for strict RFC compliance */\nvar chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */\n\n/*\n * These are the functions you'll usually want to call\n * They take string arguments and return either hex or base-64 encoded strings\n */\nfunction hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}\nfunction b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}\nfunction str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}\nfunction hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }\nfunction b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }\nfunction str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }\n\n/*\n * Perform a simple self-test to see if the VM is working\n */\nfunction md5_vm_test()\n{\n return hex_md5(\"abc\") == \"900150983cd24fb0d6963f7d28e17f72\";\n}\n\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length\n keep\n */\nfunction core_md5(x, len)\n{\n /* append padding */\n x[len >> 5] |= 0x80 << ((len) % 32);\n x[(((len + 64) >>> 9) << 4) + 14] = len;\n\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n\n for(var i = 0; i < x.length; i += 16)\n {\n var olda = a;\n var oldb = b;\n var oldc = c;\n var oldd = d;\n\n a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);\n d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);\n c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);\n b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);\n a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);\n d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);\n c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);\n b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);\n a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);\n d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);\n c = md5_ff(c, d, a, b, x[i+10], 17, -42063);\n b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);\n a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);\n d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);\n c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);\n b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);\n\n a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);\n d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);\n c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);\n b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);\n a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);\n d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);\n c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);\n b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);\n a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);\n d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);\n c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);\n b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);\n a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);\n d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);\n c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);\n b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);\n\n a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);\n d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);\n c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);\n b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);\n a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);\n d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);\n c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);\n b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);\n a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);\n d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);\n c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);\n b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);\n a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);\n d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);\n c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);\n b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);\n\n a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);\n d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);\n c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);\n b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);\n a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);\n d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);\n c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);\n b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);\n a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);\n d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);\n c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);\n b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);\n a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);\n d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);\n c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);\n b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);\n\n a = safe_add(a, olda);\n b = safe_add(b, oldb);\n c = safe_add(c, oldc);\n d = safe_add(d, oldd);\n }\n return Array(a, b, c, d);\n\n}\n\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\nfunction md5_cmn(q, a, b, x, s, t)\n{\n return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);\n}\nfunction md5_ff(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);\n}\nfunction md5_gg(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);\n}\nfunction md5_hh(a, b, c, d, x, s, t)\n{\n return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n}\nfunction md5_ii(a, b, c, d, x, s, t)\n{\n return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);\n}\n\n/*\n * Calculate the HMAC-MD5, of a key and some data\n */\nfunction core_hmac_md5(key, data)\n{\n var bkey = str2binl(key);\n if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);\n\n var ipad = Array(16), opad = Array(16);\n for(var i = 0; i < 16; i++)\n {\n ipad[i] = bkey[i] ^ 0x36363636;\n opad[i] = bkey[i] ^ 0x5C5C5C5C;\n }\n\n var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);\n return core_md5(opad.concat(hash), 512 + 128);\n}\n\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\nfunction safe_add(x, y)\n{\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n}\n\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\nfunction bit_rol(num, cnt)\n{\n return (num << cnt) | (num >>> (32 - cnt));\n}\n\n/*\n * Convert a string to an array of little-endian words\n * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.\n keep\n */\nfunction str2binl(str)\n{\n var bin = Array();\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < str.length * chrsz; i += chrsz)\n bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);\n return bin;\n}\n\n/*\n * Convert an array of little-endian words to a string\n */\nfunction binl2str(bin)\n{\n var str = \"\";\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < bin.length * 32; i += chrsz)\n str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a hex string.\n keep\n */\nfunction binl2hex(binarray)\n{\n var hex_tab = hexcase ? \"0123456789ABCDEF\" : \"0123456789abcdef\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i++)\n {\n str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +\n hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);\n }\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a base-64 string\n */\nfunction binl2b64(binarray)\n{\n var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i += 3)\n {\n var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)\n | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )\n | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);\n for(var j = 0; j < 4; j++)\n {\n if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;\n else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);\n }\n }\n return str;\n}\n\nexports.hex = hex_md5;","validate":"// a library for validations\n// over time we expect to extract more helpers and move them here.\nexports.init = function(newDoc, oldDoc, userCtx, secObj) {\n var v = {};\n \n v.forbidden = function(message) { \n throw({forbidden : message});\n };\n\n v.unauthorized = function(message) {\n throw({unauthorized : message});\n };\n\n v.assert = function(should, message) {\n if (!should) v.forbidden(message);\n }\n \n v.isAdmin = function() {\n return userCtx.roles.indexOf('_admin') != -1\n };\n \n v.isRole = function(role) {\n return userCtx.roles.indexOf(role) != -1\n };\n\n v.require = function() {\n for (var i=0; i < arguments.length; i++) {\n var field = arguments[i];\n message = \"The '\"+field+\"' field is required.\";\n if (typeof newDoc[field] == \"undefined\") v.forbidden(message);\n };\n };\n\n v.unchanged = function(field) {\n if (oldDoc && oldDoc[field] != newDoc[field]) \n v.forbidden(\"You may not change the '\"+field+\"' field.\");\n };\n\n v.matches = function(field, regex, message) {\n if (!newDoc[field].match(regex)) {\n message = message || \"Format of '\"+field+\"' field is invalid.\";\n v.forbidden(message); \n }\n };\n\n // this ensures that the date will be UTC, parseable, and collate correctly\n v.dateFormat = function(field) {\n message = \"Sorry, '\"+field+\"' is not a valid date format. Try: 2010-02-24T17:00:03.432Z\";\n v.matches(field, /\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d*)?Z/, message);\n }\n \n return v;\n};","mustache":"/*\n * CommonJS-compatible mustache.js module\n *\n * See http://github.com/janl/mustache.js for more info.\n */\n\n/*\n mustache.js \u2014 Logic-less templates in JavaScript\n\n See http://mustache.github.com/ for more info.\n*/\n\nvar Mustache = function() {\n var Renderer = function() {};\n\n Renderer.prototype = {\n otag: \"{{\",\n ctag: \"}}\",\n pragmas: {},\n buffer: [],\n pragmas_implemented: {\n \"IMPLICIT-ITERATOR\": true\n },\n context: {},\n\n render: function(template, context, partials, in_recursion) {\n // reset buffer & set context\n if(!in_recursion) {\n this.context = context;\n this.buffer = []; // TODO: make this non-lazy\n }\n\n // fail fast\n if(!this.includes(\"\", template)) {\n if(in_recursion) {\n return template;\n } else {\n this.send(template);\n return;\n }\n }\n\n template = this.render_pragmas(template);\n var html = this.render_section(template, context, partials);\n if(in_recursion) {\n return this.render_tags(html, context, partials, in_recursion);\n }\n\n this.render_tags(html, context, partials, in_recursion);\n },\n\n /*\n Sends parsed lines\n */\n send: function(line) {\n if(line != \"\") {\n this.buffer.push(line);\n }\n },\n\n /*\n Looks for %PRAGMAS\n */\n render_pragmas: function(template) {\n // no pragmas\n if(!this.includes(\"%\", template)) {\n return template;\n }\n\n var that = this;\n var regex = new RegExp(this.otag + \"%([\\\\w-]+) ?([\\\\w]+=[\\\\w]+)?\" +\n this.ctag);\n return template.replace(regex, function(match, pragma, options) {\n if(!that.pragmas_implemented[pragma]) {\n throw({message: \n \"This implementation of mustache doesn't understand the '\" +\n pragma + \"' pragma\"});\n }\n that.pragmas[pragma] = {};\n if(options) {\n var opts = options.split(\"=\");\n that.pragmas[pragma][opts[0]] = opts[1];\n }\n return \"\";\n // ignore unknown pragmas silently\n });\n },\n\n /*\n Tries to find a partial in the curent scope and render it\n */\n render_partial: function(name, context, partials) {\n name = this.trim(name);\n if(!partials || partials[name] === undefined) {\n throw({message: \"unknown_partial '\" + name + \"'\"});\n }\n if(typeof(context[name]) != \"object\") {\n return this.render(partials[name], context, partials, true);\n }\n return this.render(partials[name], context[name], partials, true);\n },\n\n /*\n Renders inverted (^) and normal (#) sections\n */\n render_section: function(template, context, partials) {\n if(!this.includes(\"#\", template) && !this.includes(\"^\", template)) {\n return template;\n }\n\n var that = this;\n // CSW - Added \"+?\" so it finds the tighest bound, not the widest\n var regex = new RegExp(this.otag + \"(\\\\^|\\\\#)\\\\s*(.+)\\\\s*\" + this.ctag +\n \"\\n*([\\\\s\\\\S]+?)\" + this.otag + \"\\\\/\\\\s*\\\\2\\\\s*\" + this.ctag +\n \"\\\\s*\", \"mg\");\n\n // for each {{#foo}}{{/foo}} section do...\n return template.replace(regex, function(match, type, name, content) {\n var value = that.find(name, context);\n if(type == \"^\") { // inverted section\n if(!value || that.is_array(value) && value.length === 0) {\n // false or empty list, render it\n return that.render(content, context, partials, true);\n } else {\n return \"\";\n }\n } else if(type == \"#\") { // normal section\n if(that.is_array(value)) { // Enumerable, Let's loop!\n return that.map(value, function(row) {\n return that.render(content, that.create_context(row),\n partials, true);\n }).join(\"\");\n } else if(that.is_object(value)) { // Object, Use it as subcontext!\n return that.render(content, that.create_context(value),\n partials, true);\n } else if(typeof value === \"function\") {\n // higher order section\n return value.call(context, content, function(text) {\n return that.render(text, context, partials, true);\n });\n } else if(value) { // boolean section\n return that.render(content, context, partials, true);\n } else {\n return \"\";\n }\n }\n });\n },\n\n /*\n Replace {{foo}} and friends with values from our view\n */\n render_tags: function(template, context, partials, in_recursion) {\n // tit for tat\n var that = this;\n\n var new_regex = function() {\n return new RegExp(that.otag + \"(=|!|>|\\\\{|%)?([^\\\\/#\\\\^]+?)\\\\1?\" +\n that.ctag + \"+\", \"g\");\n };\n\n var regex = new_regex();\n var tag_replace_callback = function(match, operator, name) {\n switch(operator) {\n case \"!\": // ignore comments\n return \"\";\n case \"=\": // set new delimiters, rebuild the replace regexp\n that.set_delimiters(name);\n regex = new_regex();\n return \"\";\n case \">\": // render partial\n return that.render_partial(name, context, partials);\n case \"{\": // the triple mustache is unescaped\n return that.find(name, context);\n default: // escape the value\n return that.escape(that.find(name, context));\n }\n };\n var lines = template.split(\"\\n\");\n for(var i = 0; i < lines.length; i++) {\n lines[i] = lines[i].replace(regex, tag_replace_callback, this);\n if(!in_recursion) {\n this.send(lines[i]);\n }\n }\n\n if(in_recursion) {\n return lines.join(\"\\n\");\n }\n },\n\n set_delimiters: function(delimiters) {\n var dels = delimiters.split(\" \");\n this.otag = this.escape_regex(dels[0]);\n this.ctag = this.escape_regex(dels[1]);\n },\n\n escape_regex: function(text) {\n // thank you Simon Willison\n if(!arguments.callee.sRE) {\n var specials = [\n '/', '.', '*', '+', '?', '|',\n '(', ')', '[', ']', '{', '}', '\\\\'\n ];\n arguments.callee.sRE = new RegExp(\n '(\\\\' + specials.join('|\\\\') + ')', 'g'\n );\n }\n return text.replace(arguments.callee.sRE, '\\\\$1');\n },\n\n /*\n find `name` in current `context`. That is find me a value\n from the view object\n */\n find: function(name, context) {\n name = this.trim(name);\n\n // Checks whether a value is thruthy or false or 0\n function is_kinda_truthy(bool) {\n return bool === false || bool === 0 || bool;\n }\n\n var value;\n if(is_kinda_truthy(context[name])) {\n value = context[name];\n } else if(is_kinda_truthy(this.context[name])) {\n value = this.context[name];\n }\n\n if(typeof value === \"function\") {\n return value.apply(context);\n }\n if(value !== undefined) {\n return value;\n }\n // silently ignore unkown variables\n return \"\";\n },\n\n // Utility methods\n\n /* includes tag */\n includes: function(needle, haystack) {\n return haystack.indexOf(this.otag + needle) != -1;\n },\n\n /*\n Does away with nasty characters\n */\n escape: function(s) {\n s = String(s === null ? \"\" : s);\n return s.replace(/&(?!\\w+;)|[\"<>\\\\]/g, function(s) {\n switch(s) {\n case \"&\": return \"&\";\n case \"\\\\\": return \"\\\\\\\\\";\n case '\"': return '\\\"';\n case \"<\": return \"<\";\n case \">\": return \">\";\n default: return s;\n }\n });\n },\n\n // by @langalex, support for arrays of strings\n create_context: function(_context) {\n if(this.is_object(_context)) {\n return _context;\n } else {\n var iterator = \".\";\n if(this.pragmas[\"IMPLICIT-ITERATOR\"]) {\n iterator = this.pragmas[\"IMPLICIT-ITERATOR\"].iterator;\n }\n var ctx = {};\n ctx[iterator] = _context;\n return ctx;\n }\n },\n\n is_object: function(a) {\n return a && typeof a == \"object\";\n },\n\n is_array: function(a) {\n return Object.prototype.toString.call(a) === '[object Array]';\n },\n\n /*\n Gets rid of leading and trailing whitespace\n */\n trim: function(s) {\n return s.replace(/^\\s*|\\s*$/g, \"\");\n },\n\n /*\n Why, why, why? Because IE. Cry, cry cry.\n */\n map: function(array, fn) {\n if (typeof array.map == \"function\") {\n return array.map(fn);\n } else {\n var r = [];\n var l = array.length;\n for(var i = 0; i < l; i++) {\n r.push(fn(array[i]));\n }\n return r;\n }\n }\n };\n\n return({\n name: \"mustache.js\",\n version: \"0.3.1-dev\",\n\n /*\n Turns a template and view into HTML\n */\n to_html: function(template, view, partials, send_fun) {\n var renderer = new Renderer();\n if(send_fun) {\n renderer.send = send_fun;\n }\n renderer.render(template, view, partials);\n if(!send_fun) {\n return renderer.buffer.join(\"\\n\");\n }\n },\n escape : function(text) {\n return new Renderer().escape(text);\n }\n });\n}();\n\nexports.name = Mustache.name;\nexports.version = Mustache.version;\n\nexports.to_html = Mustache.to_html;\nexports.escape = Mustache.escape;","docform":"// Licensed under the Apache License, Version 2.0 (the \"License\"); you may not\n// use this file except in compliance with the License. You may obtain a copy\n// of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT\n// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the\n// License for the specific language governing permissions and limitations under\n// the License.\n\n// turn the form into deep json\n// field names like 'author-email' get turned into json like\n// {\"author\":{\"email\":\"quentin@example.com\"}}\n// acts on doc by reference, so you can safely pass non-form fields through\n\nfunction docForm(formSelector, opts) {\n var localFormDoc = {};\n opts = opts || {};\n opts.fields = opts.fields || [];\n\n // turn the form into deep json\n // field names like 'author-email' get turned into json like\n // {\"author\":{\"email\":\"quentin@example.com\"}}\n function formToDeepJSON(form, fields, doc) {\n form = $(form);\n fields.forEach(function(field) {\n var element = form.find(\"[name=\"+field+\"]\");\n if (element.attr('type') === 'checkbox') {\n var val = element.attr('checked');\n } else {\n var val = element.val();\n if (!val) return;\n }\n var parts = field.split('-');\n var frontObj = doc, frontName = parts.shift();\n while (parts.length > 0) {\n frontObj[frontName] = frontObj[frontName] || {};\n frontObj = frontObj[frontName];\n frontName = parts.shift();\n }\n frontObj[frontName] = val;\n });\n }\n \n // Apply the behavior\n $(formSelector).submit(function(e) {\n e.preventDefault();\n if (opts.validate && opts.validate() == false) { return false;}\n // formToDeepJSON acts on localFormDoc by reference\n formToDeepJSON(this, opts.fields, localFormDoc);\n if (opts.beforeSave) {opts.beforeSave(localFormDoc);}\n db.saveDoc(localFormDoc, {\n success : function(resp) {\n if (opts.success) {opts.success(resp, localFormDoc);}\n }\n });\n \n return false;\n });\n\n // populate form from an existing doc\n function docToForm(doc) {\n var form = $(formSelector);\n // fills in forms\n opts.fields.forEach(function(field) {\n var parts = field.split('-');\n var run = true, frontObj = doc, frontName = parts.shift();\n while (frontObj && parts.length > 0) { \n frontObj = frontObj[frontName];\n frontName = parts.shift();\n }\n if (frontObj && frontObj[frontName]) {\n var element = form.find(\"[name=\"+field+\"]\");\n if (element.attr('type') === 'checkbox') {\n element.attr('checked', frontObj[frontName]);\n } else {\n element.val(frontObj[frontName]);\n }\n }\n });\n }\n \n if (opts.id) {\n db.openDoc(opts.id, {\n attachPrevRev : opts.attachPrevRev,\n error: function() {\n if (opts.error) {opts.error.apply(opts, arguments);}\n },\n success: function(doc) {\n if (opts.load || opts.onLoad) {(opts.load || opts.onLoad)(doc);}\n localFormDoc = doc;\n docToForm(doc);\n }});\n } else if (opts.template) {\n if (opts.load || opts.onLoad) {(opts.load || opts.onLoad)(opts.template);}\n localFormDoc = opts.template;\n docToForm(localFormDoc);\n }\n var instance = {\n deleteDoc : function(opts) {\n opts = opts || {};\n if (confirm(\"Really delete this document?\")) { \n db.removeDoc(localFormDoc, opts);\n }\n },\n localDoc : function() {\n formToDeepJSON(formSelector, opts.fields, localFormDoc);\n return localFormDoc;\n }\n };\n return instance;\n}"},"metadata":{"name":"couchapp","fetch_uri":"git://github.com/couchapp/couchapp.git","description":"official couchapp vendor"}}},"views":{"recent-posts":{"map":"function(doc) {\n if (doc.type == \"post\") {\n emit(new Date(doc.created_at), doc);\n }\n};"},"post-page":{"map":"function(doc) {\n // todo make commonjs\n /*\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n/*\n * Configurable variables. You may need to tweak these to be compatible with\n * the server-side, but the defaults work in most cases.\n */\nvar hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */\nvar b64pad = \"\"; /* base-64 pad character. \"=\" for strict RFC compliance */\nvar chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */\n\n/*\n * These are the functions you'll usually want to call\n * They take string arguments and return either hex or base-64 encoded strings\n */\nfunction hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}\nfunction b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}\nfunction str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}\nfunction hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }\nfunction b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }\nfunction str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }\n\n/*\n * Perform a simple self-test to see if the VM is working\n */\nfunction md5_vm_test()\n{\n return hex_md5(\"abc\") == \"900150983cd24fb0d6963f7d28e17f72\";\n}\n\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length\n keep\n */\nfunction core_md5(x, len)\n{\n /* append padding */\n x[len >> 5] |= 0x80 << ((len) % 32);\n x[(((len + 64) >>> 9) << 4) + 14] = len;\n\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n\n for(var i = 0; i < x.length; i += 16)\n {\n var olda = a;\n var oldb = b;\n var oldc = c;\n var oldd = d;\n\n a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);\n d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);\n c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);\n b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);\n a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);\n d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);\n c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);\n b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);\n a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);\n d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);\n c = md5_ff(c, d, a, b, x[i+10], 17, -42063);\n b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);\n a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);\n d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);\n c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);\n b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);\n\n a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);\n d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);\n c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);\n b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);\n a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);\n d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);\n c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);\n b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);\n a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);\n d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);\n c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);\n b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);\n a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);\n d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);\n c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);\n b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);\n\n a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);\n d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);\n c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);\n b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);\n a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);\n d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);\n c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);\n b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);\n a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);\n d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);\n c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);\n b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);\n a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);\n d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);\n c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);\n b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);\n\n a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);\n d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);\n c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);\n b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);\n a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);\n d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);\n c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);\n b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);\n a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);\n d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);\n c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);\n b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);\n a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);\n d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);\n c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);\n b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);\n\n a = safe_add(a, olda);\n b = safe_add(b, oldb);\n c = safe_add(c, oldc);\n d = safe_add(d, oldd);\n }\n return Array(a, b, c, d);\n\n}\n\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\nfunction md5_cmn(q, a, b, x, s, t)\n{\n return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);\n}\nfunction md5_ff(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);\n}\nfunction md5_gg(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);\n}\nfunction md5_hh(a, b, c, d, x, s, t)\n{\n return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n}\nfunction md5_ii(a, b, c, d, x, s, t)\n{\n return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);\n}\n\n/*\n * Calculate the HMAC-MD5, of a key and some data\n */\nfunction core_hmac_md5(key, data)\n{\n var bkey = str2binl(key);\n if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);\n\n var ipad = Array(16), opad = Array(16);\n for(var i = 0; i < 16; i++)\n {\n ipad[i] = bkey[i] ^ 0x36363636;\n opad[i] = bkey[i] ^ 0x5C5C5C5C;\n }\n\n var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);\n return core_md5(opad.concat(hash), 512 + 128);\n}\n\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\nfunction safe_add(x, y)\n{\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n}\n\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\nfunction bit_rol(num, cnt)\n{\n return (num << cnt) | (num >>> (32 - cnt));\n}\n\n/*\n * Convert a string to an array of little-endian words\n * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.\n keep\n */\nfunction str2binl(str)\n{\n var bin = Array();\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < str.length * chrsz; i += chrsz)\n bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);\n return bin;\n}\n\n/*\n * Convert an array of little-endian words to a string\n */\nfunction binl2str(bin)\n{\n var str = \"\";\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < bin.length * 32; i += chrsz)\n str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a hex string.\n keep\n */\nfunction binl2hex(binarray)\n{\n var hex_tab = hexcase ? \"0123456789ABCDEF\" : \"0123456789abcdef\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i++)\n {\n str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +\n hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);\n }\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a base-64 string\n */\nfunction binl2b64(binarray)\n{\n var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i += 3)\n {\n var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)\n | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )\n | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);\n for(var j = 0; j < 4; j++)\n {\n if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;\n else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);\n }\n }\n return str;\n}\n \n if (doc.type == \"post\") {\n emit([doc._id], doc);\n } else if (doc.type == \"comment\") {\n if (doc.commenter && doc.commenter.email && !doc.commenter.gravatar_url) {\n doc.commenter.gravatar = hex_md5(doc.commenter.email); \n }\n emit([doc.post_id, doc.created_at], doc);\n }\n};"},"comments":{"map":"function(doc) {\n /*\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n/*\n * Configurable variables. You may need to tweak these to be compatible with\n * the server-side, but the defaults work in most cases.\n */\nvar hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */\nvar b64pad = \"\"; /* base-64 pad character. \"=\" for strict RFC compliance */\nvar chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */\n\n/*\n * These are the functions you'll usually want to call\n * They take string arguments and return either hex or base-64 encoded strings\n */\nfunction hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}\nfunction b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}\nfunction str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}\nfunction hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }\nfunction b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }\nfunction str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }\n\n/*\n * Perform a simple self-test to see if the VM is working\n */\nfunction md5_vm_test()\n{\n return hex_md5(\"abc\") == \"900150983cd24fb0d6963f7d28e17f72\";\n}\n\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length\n keep\n */\nfunction core_md5(x, len)\n{\n /* append padding */\n x[len >> 5] |= 0x80 << ((len) % 32);\n x[(((len + 64) >>> 9) << 4) + 14] = len;\n\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n\n for(var i = 0; i < x.length; i += 16)\n {\n var olda = a;\n var oldb = b;\n var oldc = c;\n var oldd = d;\n\n a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);\n d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);\n c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);\n b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);\n a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);\n d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);\n c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);\n b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);\n a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);\n d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);\n c = md5_ff(c, d, a, b, x[i+10], 17, -42063);\n b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);\n a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);\n d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);\n c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);\n b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);\n\n a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);\n d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);\n c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);\n b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);\n a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);\n d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);\n c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);\n b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);\n a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);\n d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);\n c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);\n b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);\n a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);\n d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);\n c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);\n b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);\n\n a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);\n d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);\n c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);\n b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);\n a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);\n d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);\n c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);\n b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);\n a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);\n d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);\n c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);\n b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);\n a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);\n d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);\n c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);\n b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);\n\n a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);\n d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);\n c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);\n b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);\n a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);\n d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);\n c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);\n b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);\n a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);\n d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);\n c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);\n b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);\n a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);\n d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);\n c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);\n b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);\n\n a = safe_add(a, olda);\n b = safe_add(b, oldb);\n c = safe_add(c, oldc);\n d = safe_add(d, oldd);\n }\n return Array(a, b, c, d);\n\n}\n\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\nfunction md5_cmn(q, a, b, x, s, t)\n{\n return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);\n}\nfunction md5_ff(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);\n}\nfunction md5_gg(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);\n}\nfunction md5_hh(a, b, c, d, x, s, t)\n{\n return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n}\nfunction md5_ii(a, b, c, d, x, s, t)\n{\n return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);\n}\n\n/*\n * Calculate the HMAC-MD5, of a key and some data\n */\nfunction core_hmac_md5(key, data)\n{\n var bkey = str2binl(key);\n if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);\n\n var ipad = Array(16), opad = Array(16);\n for(var i = 0; i < 16; i++)\n {\n ipad[i] = bkey[i] ^ 0x36363636;\n opad[i] = bkey[i] ^ 0x5C5C5C5C;\n }\n\n var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);\n return core_md5(opad.concat(hash), 512 + 128);\n}\n\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\nfunction safe_add(x, y)\n{\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n}\n\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\nfunction bit_rol(num, cnt)\n{\n return (num << cnt) | (num >>> (32 - cnt));\n}\n\n/*\n * Convert a string to an array of little-endian words\n * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.\n keep\n */\nfunction str2binl(str)\n{\n var bin = Array();\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < str.length * chrsz; i += chrsz)\n bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);\n return bin;\n}\n\n/*\n * Convert an array of little-endian words to a string\n */\nfunction binl2str(bin)\n{\n var str = \"\";\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < bin.length * 32; i += chrsz)\n str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a hex string.\n keep\n */\nfunction binl2hex(binarray)\n{\n var hex_tab = hexcase ? \"0123456789ABCDEF\" : \"0123456789abcdef\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i++)\n {\n str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +\n hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);\n }\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a base-64 string\n */\nfunction binl2b64(binarray)\n{\n var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i += 3)\n {\n var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)\n | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )\n | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);\n for(var j = 0; j < 4; j++)\n {\n if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;\n else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);\n }\n }\n return str;\n}\n if (doc.type == \"comment\") {\n if (doc.commenter && doc.commenter.email && !doc.commenter.gravatar_url) {\n // todo normalize this schema-ness\n doc.commenter.gravatar = hex_md5(doc.commenter.email); \n }\n emit(new Date(doc.created_at), doc);\n }\n};"},"tags":{"map":"function(doc) {\n if(doc.type == \"post\" && doc.tags && doc.tags.length) {\n for(var idx in doc.tags) {\n if (doc.tags[idx]){ \n emit([doc.tags[idx].toLowerCase(), doc.created_at], doc);\n }\n }\n }\n}","reduce":"_count"}},"lists":{"index":"function(head, req) {\n var ddoc = this;\n var Mustache = require(\"lib/mustache\");\n var List = require(\"vendor/couchapp/lib/list\");\n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var Atom = require(\"vendor/couchapp/lib/atom\");\n\n var indexPath = path.list('index','recent-posts',{descending:true, limit:10});\n var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:\"atom\"});\n var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:\"atom\"});\n\n var path_parts = req.path;\n // The provides function serves the format the client requests.\n // The first matching format is sent, so reordering functions changes \n // thier priority. In this case HTML is the preferred format, so it comes first.\n provides(\"html\", function() {\n var key = \"\";\n // render the html head using a template\n var stash = {\n header : {\n index : indexPath,\n blogName : ddoc.blog.title,\n feedPath : feedPath,\n commentsFeed : commentsFeed\n },\n scripts : {},\n db : req.path[0],\n design : req.path[2],\n feedPath : feedPath,\n newPostPath : path.show(\"edit\"),\n assets : path.asset(),\n posts : List.withRows(function(row) {\n var post = row.value;\n key = row.key;\n return {\n title : post.title,\n author : post.author,\n date : post.created_at,\n link : path.list('post','post-page', {startkey : [row.id]}),\n has_tags : post.tags ? true : false,\n tags : post.tags && post.tags.map ? post.tags.map(function(tag) {\n var t = tag.toLowerCase();\n return {\n tag : tag,\n link : path.list(\"index\", \"tags\", {\n descending : true, \n reduce : false, \n startkey : [t, {}], \n endkey : [t]\n })\n }\n }) : []\n };\n }),\n older : function() {\n return path.older(key);\n },\n \"5\" : path.limit(5),\n \"10\" : path.limit(10),\n \"25\" : path.limit(25)\n };\n return Mustache.to_html(ddoc.templates.index, stash, ddoc.templates.partials, List.send);\n });\n\n // if the client requests an atom feed and not html, \n // we run this function to generate the feed.\n provides(\"atom\", function() { \n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var markdown = require(\"vendor/couchapp/lib/markdown\");\n var textile = require(\"vendor/textile/textile\");\n\n // we load the first row to find the most recent change date\n var row = getRow();\n \n // generate the feed header\n var feedHeader = Atom.header({\n updated : (row ? new Date(row.value.created_at) : new Date()),\n title : ddoc.blog.title,\n feed_id : path.absolute(indexPath),\n feed_link : path.absolute(feedPath),\n });\n \n // send the header to the client\n send(feedHeader);\n\n // loop over all rows\n if (row) {\n do {\n if (row.value.format == \"markdown\") {\n var html = markdown.encode(row.value.body);\n } else if (row.value.format == \"textile\") {\n var html = textile.encode(row.value.body);\n } else {\n var html = Mustache.escape(row.value.html);\n }\n // generate the entry for this row\n var feedEntry = Atom.entry({\n entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),\n title : row.value.title,\n content : html,\n updated : new Date(row.value.created_at),\n author : row.value.author,\n alternate : path.absolute(path.show('post', row.id))\n });\n // send the entry to client\n send(feedEntry);\n } while (row = getRow());\n }\n\n // close the loop after all rows are rendered\n return \"\";\n });\n};","post":"function(head, req) {\n var Mustache = require(\"lib/mustache\");\n var ddoc = this;\n var List = require(\"vendor/couchapp/lib/list\");\n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var markdown = require(\"vendor/couchapp/lib/markdown\");\n var textile = require(\"vendor/textile/textile\");\n\n var indexPath = path.list('index','recent-posts',{descending:true, limit:10});\n var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:\"atom\"});\n var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:\"atom\"});\n \n provides(\"html\", function() {\n // get the first row and make sure it's a post\n var post = getRow().value;\n if (post.type != \"post\") {\n throw([\"error\", \"not_found\", \"not a post\"]);\n } else {\n if (post.format == \"markdown\") {\n var html = markdown.encode(post.body);\n } else if (post.format == \"textile\") {\n var html = textile.encode(post.body);\n } else {\n var html = Mustache.escape(post.html);\n }\n\n var stash = {\n header : {\n index : indexPath,\n blogName : ddoc.blog.title,\n feedPath : feedPath,\n commentsFeed : commentsFeed\n },\n scripts : {},\n title : post.title,\n post_id : post._id,\n date : post.created_at,\n html : html,\n comments : List.withRows(function(row) {\n var v = row.value;\n if (v.type != \"comment\") {\n return;\n }\n // keep getting comments until we get to the next post...\n return {\n comment : {\n name : v.commenter.nickname || v.commenter.name,\n url : v.commenter.url,\n avatar : v.commenter.gravatar_url || 'http://www.gravatar.com/avatar/'+v.commenter.gravatar+'.jpg?s=40&d=identicon',\n html : markdown.encode(Mustache.escape(v.comment)),\n created_at : v.created_at\n }\n };\n })\n };\n return Mustache.to_html(ddoc.templates.post, stash, ddoc.templates.partials, List.send); \n }\n });\n}","comments":"function(head, req) {\n var ddoc = this;\n var Mustache = require(\"lib/mustache\");\n var List = require(\"vendor/couchapp/lib/list\");\n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var Atom = require(\"vendor/couchapp/lib/atom\");\n\n var indexPath = path.list('index','recent-posts',{descending:true, limit:10});\n var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:\"atom\"});\n var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:\"atom\"});\n\n // if the client requests an atom feed and not html, \n // we run this function to generate the feed.\n provides(\"atom\", function() { \n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var markdown = require(\"vendor/couchapp/lib/markdown\");\n var textile = require(\"vendor/textile/textile\");\n\n // we load the first row to find the most recent change date\n var row = getRow();\n \n // generate the feed header\n var feedHeader = Atom.header({\n updated : (row ? new Date(row.value.created_at) : new Date()),\n title : ddoc.blog.title + \" comments\",\n feed_id : path.absolute(indexPath),\n feed_link : path.absolute(commentsFeed)\n });\n \n // send the header to the client\n send(feedHeader);\n\n // loop over all rows\n if (row) {\n do {\n var v = row.value;\n \n // generate the entry for this row\n var feedEntry = Atom.entry({\n entry_id : path.absolute('/'+encodeURIComponent(req.info.db_name)+'/'+encodeURIComponent(row.id)),\n title : \"comment on \"+v.post_id,\n content : markdown.encode(Mustache.escape(v.comment)),\n updated : new Date(v.created_at),\n author : v.commenter.nickname || v.commenter.name,\n alternate : path.absolute(path.list('post','post-page', {startkey:[v.post_id]}))\n });\n // send the entry to client\n send(feedEntry);\n } while (row = getRow());\n }\n\n // close the loop after all rows are rendered\n return \"\";\n });\n}"},"evently":{"profile":{"profileReady":{"mustache":"\n
    \n
    \n {{#avatar_url}}{{/avatar_url}}\n
    \n

    Comment on this post, {{nickname}}:

    \n

    \n

    \n

    \n \n \n

    \n
    ","selectors":{"#preview":{"click":"function() {\n var f = $(this), app = $$(this).app;\n var Mustache = app.require(\"lib/mustache\");\n var markdown = app.require(\"vendor/couchapp/lib/markdown\");\n\n var c = {\n name : $$(\"#profile\").profile.nickname,\n url : $$(\"#profile\").profile.url,\n avatar : $$(\"#profile\").profile.gravatar_url,\n html : markdown.encode(Mustache.escape($(\"[name=comment]\", f).val())),\n created_at : JSON.parse(JSON.stringify(new Date()))\n };\n \n $(\"#comment-preview\").html(Mustache.to_html(app.ddoc.templates.partials.comment, c));\n};"},"form":{"submit":"function() {\n var f = $(this), app = $$(this).app;\n var newComment = {\n type : \"comment\",\n post_id : app.post_id,\n format : \"markdown\",\n comment : $(\"[name=comment]\", f).val(),\n commenter : $$(\"#profile\").profile,\n created_at : new Date()\n };\n app.db.saveDoc(newComment, {\n success : function(resp) {\n $(\"#new-comment\").html('

    Success! Your comment has posted.

    Refresh the page to see it.

    ');\n }\n });\n \n return false;\n};"}}},"loggedOut":{"mustache":"
    \n

    Please login (above) to leave comments.

    \n
    "}},"account":{"loggedIn":{"data":"function(e, r) {\n var app = $$(this).app;\n var path = app.require(\"vendor/couchapp/lib/path\").init(app.req);\n var data = {\n name : r.userCtx.name,\n uri_name : encodeURIComponent(r.userCtx.name),\n auth_db : encodeURIComponent(r.info.authentication_db) \n };\n if (r.userCtx.roles.indexOf(\"_admin\") != -1 || r.userCtx.roles.indexOf(\"author\") != -1) {\n if (app.req.path.indexOf(\"post-page\") == -1) {\n data.postPath = path.show(\"edit\")+\"/\";\n data.postMessage = \"New post.\";\n } else {\n data.postPath = path.show(\"edit\", app.req.query.startkey[0]);\n data.postMessage = \"Edit this post.\";\n } \n }\n return data;\n}","mustache":"Welcome \n{{name}}! \nLogout?\n{{#postPath}}\n {{postMessage}}\n{{/postPath}}\n"}},"tagcloud":{"_init":{"query":{"group_level":1,"view":"tags"},"data":"function(resp) {\n var app = $$(this).app;\n var path = app.require(\"vendor/couchapp/lib/path\").init(app.req);\n var tags = [];\n resp.rows.forEach(function(r) {\n var tag = r.key[0];\n // todo remove duplication of link definitions\n var link = path.list(\"index\",\"tags\",{\n descending : true, \n reduce : false,\n limit : 10,\n startkey : [tag, {}], \n endkey : [tag]});\n tags.push({\n tag : tag,\n link : link,\n size : (r.value * 2) + 10\n });\n });\n return {\n tags : tags.sort(function(a, b) {\n return a.size < b.size;\n })\n };\n}","mustache":"{{#tags}}\n {{tag}}\n{{/tags}}"}}},"THANKS":"Sofa THANKS\n=====================\n\nA number of people have contributed to Sofa by reporting problems,\nsuggesting improvements, submitting changes or asking hard questions.\n\nSome of these people are:\n\n* Andy Wenk \n* Jan Lehnardt\n* You","validate_doc_update":"function (newDoc, oldDoc, userCtx, secObj) {\n var v = require(\"lib/validate\").init(newDoc, oldDoc, userCtx, secObj);\n\n v.isAuthor = function() {\n return v.isAdmin() || userCtx.roles.indexOf(\"author\") != -1;\n };\n\n // admins or owner can always delete\n if (v.isAdmin()) return true;\n if (((oldDoc && (oldDoc.author == userCtx.name))) && newDoc._deleted) return true;\n\n v.unchanged(\"type\");\n v.unchanged(\"author\");\n v.unchanged(\"created_at\");\n \n if (newDoc.created_at) v.dateFormat(\"created_at\");\n\n // docs with authors can only be saved by their author\n // admin can author anything...\n if (!v.isAdmin() && newDoc.author && newDoc.author != userCtx.name) { \n v.unauthorized(\"Only \"+newDoc.author+\" may edit this document.\");\n }\n \n if (newDoc.type == 'post') {\n if (!v.isAuthor()) {\n v.unauthorized(\"Only authors may edit posts.\");\n }\n v.require(\"created_at\", \"author\", \"body\", \"format\", \"title\");\n } else if (newDoc.type == 'comment') {\n v.require(\"created_at\", \"post_id\", \"comment\", \"format\", \"commenter\");\n v.assert((newDoc.commenter.name || newDoc.commenter.nickname) && (typeof newDoc.commenter.email != \"undefined\"), \n \"Comments must include name and email.\");\n if (newDoc.commenter.url) {\n v.assert(newDoc.commenter.url.match(/^https?:\\/\\/[^.]*\\..*/), \n \"Commenter URL must start with http://.\"); \n }\n }\n}","blog":{"title":"Daytime Running Lights"},"rewrites":[{"to":"_list/index/recent-posts","from":"","query":{"limit":10,"descending":true}},{"to":"script/*","from":"script/*"},{"to":"style/*","from":"style/*"},{"to":"vendor/*","from":"vendor/*"},{"to":"../../*","from":":db/*"}],"lib":{"blog":"exports.slugifyString = function(string) {\n return string.replace(/\\W/g,'-').\n replace(/\\-*$/,'').replace(/^\\-*/,'').\n replace(/\\-{2,}/,'-');\n}","validate":"// a library for validations\n// over time we expect to extract more helpers and move them here.\nexports.init = function(newDoc, oldDoc, userCtx, secObj) {\n var v = {};\n \n v.forbidden = function(message) { \n throw({forbidden : message});\n };\n\n v.unauthorized = function(message) {\n throw({unauthorized : message});\n };\n\n v.assert = function(should, message) {\n if (!should) v.forbidden(message);\n }\n \n v.isAdmin = function() {\n return userCtx.roles.indexOf('_admin') != -1\n };\n\n v.require = function() {\n for (var i=0; i < arguments.length; i++) {\n var field = arguments[i];\n message = \"The '\"+field+\"' field is required.\";\n if (typeof newDoc[field] == \"undefined\") v.forbidden(message);\n };\n };\n\n v.unchanged = function(field) {\n if (oldDoc && oldDoc[field] != newDoc[field]) \n v.forbidden(\"You may not change the '\"+field+\"' field.\");\n };\n\n v.matches = function(field, regex, message) {\n if (!newDoc[field].match(regex)) {\n message = message || \"Format of '\"+field+\"' field is invalid.\";\n v.forbidden(message); \n }\n };\n\n // this ensures that the date will be UTC, parseable, and collate correctly\n v.dateFormat = function(field) {\n message = \"Sorry, '\"+field+\"' is not a valid date format. Try: 2010-02-24T17:00:03.432Z\";\n v.matches(field, /\\d{4}\\-\\d{2}\\-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d*)?Z/, message);\n }\n \n return v;\n};","mustache":"/*\n Shameless port of http://github.com/defunkt/mustache\n by Jan Lehnardt , \n Alexander Lang ,\n Sebastian Cohnen \n\n Thanks @defunkt for the awesome code.\n\n See http://github.com/defunkt/mustache for more info.\n*/\n\nvar Mustache = function() {\n var Renderer = function() {};\n\n Renderer.prototype = {\n otag: \"{{\",\n ctag: \"}}\",\n pragmas: {},\n buffer: [],\n pragmas_parsed: false,\n\n render: function(template, context, partials, in_recursion) {\n // fail fast\n if(template.indexOf(this.otag) == -1) {\n if(in_recursion) {\n return template;\n } else {\n this.send(template);\n }\n }\n\n if(!in_recursion) {\n this.buffer = [];\n }\n\n if(!this.pragmas_parsed) {\n template = this.render_pragmas(template);\n }\n var html = this.render_section(template, context, partials);\n if(in_recursion) {\n return this.render_tags(html, context, partials, in_recursion);\n }\n\n this.render_tags(html, context, partials, in_recursion);\n },\n\n /*\n Sends parsed lines\n */\n send: function(line) {\n if(line != \"\") {\n this.buffer.push(line);\n }\n },\n\n /*\n Looks for %PRAGMAS\n */\n render_pragmas: function(template) {\n this.pragmas_parsed = true;\n // no pragmas\n if(template.indexOf(this.otag + \"%\") == -1) {\n return template;\n }\n\n var that = this;\n var regex = new RegExp(this.otag + \"%([\\\\w_-]+) ?([\\\\w]+=[\\\\w]+)?\"\n + this.ctag);\n return template.replace(regex, function(match, pragma, options) {\n that.pragmas[pragma] = {};\n if(options) {\n var opts = options.split(\"=\");\n that.pragmas[pragma][opts[0]] = opts[1];\n }\n return \"\";\n // ignore unknown pragmas silently\n });\n },\n\n /*\n Tries to find a partial in the global scope and render it\n */\n render_partial: function(name, context, partials) {\n if(typeof(context[name]) != \"object\") {\n throw({message: \"subcontext for '\" + name + \"' is not an object\"});\n }\n if(!partials || !partials[name]) {\n throw({message: \"unknown_partial '\" + name + \"'\"});\n }\n return this.render(partials[name], context[name], partials, true);\n },\n\n /*\n Renders boolean and enumerable sections\n */\n render_section: function(template, context, partials) {\n if(template.indexOf(this.otag + \"#\") == -1) {\n return template;\n }\n var that = this;\n // CSW - Added \"+?\" so it finds the tighest bound, not the widest\n var regex = new RegExp(this.otag + \"\\\\#(.+)\" + this.ctag +\n \"\\\\s*([\\\\s\\\\S]+?)\" + this.otag + \"\\\\/\\\\1\" + this.ctag + \"\\\\s*\", \"mg\");\n\n // for each {{#foo}}{{/foo}} section do...\n return template.replace(regex, function(match, name, content) {\n var value = that.find(name, context);\n if(that.is_array(value)) { // Enumerable, Let's loop!\n return that.map(value, function(row) {\n return that.render(content, that.merge(context,\n that.create_context(row)), partials, true);\n }).join(\"\");\n } else if (that.is_iterator(value)) {\n var result = [];\n var row;\n while (row = value()) {\n result.push(that.render(content, that.merge(context,\n that.create_context(row)), partials, true));\n } // fuck buffering, works for now though.\n return result.join('');\n } else if(value) { // boolean section\n return that.render(content, context, partials, true);\n } else {\n return \"\";\n }\n });\n },\n\n /*\n Replace {{foo}} and friends with values from our view\n */\n render_tags: function(template, context, partials, in_recursion) {\n // tit for tat\n var that = this;\n\n var new_regex = function() {\n return new RegExp(that.otag + \"(=|!|>|\\\\{|%)?([^\\/#]+?)\\\\1?\" +\n that.ctag + \"+\", \"g\");\n };\n\n var regex = new_regex();\n var lines = template.split(\"\\n\");\n for (var i=0; i < lines.length; i++) {\n lines[i] = lines[i].replace(regex, function(match, operator, name) {\n switch(operator) {\n case \"!\": // ignore comments\n return match;\n case \"=\": // set new delimiters, rebuild the replace regexp\n that.set_delimiters(name);\n regex = new_regex();\n return \"\";\n case \">\": // render partial\n return that.render_partial(name, context, partials);\n case \"{\": // the triple mustache is unescaped\n return that.find(name, context);\n default: // escape the value\n return that.escape(that.find(name, context));\n }\n }, this);\n if(!in_recursion) {\n this.send(lines[i]);\n }\n }\n return lines.join(\"\\n\");\n },\n\n set_delimiters: function(delimiters) {\n var dels = delimiters.split(\" \");\n this.otag = this.escape_regex(dels[0]);\n this.ctag = this.escape_regex(dels[1]);\n },\n\n escape_regex: function(text) {\n // thank you Simon Willison\n if(!arguments.callee.sRE) {\n var specials = [\n '/', '.', '*', '+', '?', '|',\n '(', ')', '[', ']', '{', '}', '\\\\'\n ];\n arguments.callee.sRE = new RegExp(\n '(\\\\' + specials.join('|\\\\') + ')', 'g'\n );\n }\n return text.replace(arguments.callee.sRE, '\\\\$1');\n },\n\n /*\n find `name` in current `context`. That is find me a value\n from the view object\n */\n find: function(name, context) {\n name = this.trim(name);\n if(typeof context[name] === \"function\" && !context[name].iterator) {\n return context[name].apply(context);\n }\n if(context[name] !== undefined) {\n return context[name];\n }\n // silently ignore unkown variables\n return \"\";\n },\n\n // Utility methods\n\n /*\n Does away with nasty characters\n */\n escape: function(s) {\n return ((s == null) ? \"\" : s).toString().replace(/[&\"<>\\\\]/g, function(s) {\n switch(s) {\n case \"&\": return \"&\";\n case \"\\\\\": return \"\\\\\\\\\";;\n case '\"': return '\\\"';;\n case \"<\": return \"<\";\n case \">\": return \">\";\n default: return s;\n }\n });\n },\n\n /*\n Merges all properties of object `b` into object `a`.\n `b.property` overwrites a.property`\n */\n merge: function(a, b) {\n var _new = {};\n for(var name in a) {\n if(a.hasOwnProperty(name)) {\n _new[name] = a[name];\n }\n };\n for(var name in b) {\n if(b.hasOwnProperty(name)) {\n _new[name] = b[name];\n }\n };\n return _new;\n },\n\n // by @langalex, support for arrays of strings\n create_context: function(_context) {\n if(this.is_object(_context)) {\n return _context;\n } else if(this.pragmas[\"IMPLICIT-ITERATOR\"]) {\n var iterator = this.pragmas[\"IMPLICIT-ITERATOR\"].iterator || \".\";\n var ctx = {};\n ctx[iterator] = _context\n return ctx;\n }\n },\n\n is_object: function(a) {\n return a && typeof a == \"object\";\n },\n\n is_array: function(a) {\n return Object.prototype.toString.call(a) === '[object Array]';\n },\n\n is_iterator : function(f) {\n return (typeof f === 'function' && f.iterator);\n },\n\n /*\n Gets rid of leading and trailing whitespace\n */\n trim: function(s) {\n return s.replace(/^\\s*|\\s*$/g, \"\");\n },\n\n /*\n Why, why, why? Because IE. Cry, cry cry.\n */\n map: function(array, fn) {\n if (typeof array.map == \"function\") {\n return array.map(fn)\n } else {\n var r = [];\n var l = array.length;\n for(i=0;i\n urls for links (sammy.js)\n login / logout\n (test suite report?)\n couchapp github project","helpers":{"md5":"/*\n * A JavaScript implementation of the RSA Data Security, Inc. MD5 Message\n * Digest Algorithm, as defined in RFC 1321.\n * Version 2.1 Copyright (C) Paul Johnston 1999 - 2002.\n * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet\n * Distributed under the BSD License\n * See http://pajhome.org.uk/crypt/md5 for more info.\n */\n\n/*\n * Configurable variables. You may need to tweak these to be compatible with\n * the server-side, but the defaults work in most cases.\n */\nvar hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */\nvar b64pad = \"\"; /* base-64 pad character. \"=\" for strict RFC compliance */\nvar chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */\n\n/*\n * These are the functions you'll usually want to call\n * They take string arguments and return either hex or base-64 encoded strings\n */\nfunction hex_md5(s){ return binl2hex(core_md5(str2binl(s), s.length * chrsz));}\nfunction b64_md5(s){ return binl2b64(core_md5(str2binl(s), s.length * chrsz));}\nfunction str_md5(s){ return binl2str(core_md5(str2binl(s), s.length * chrsz));}\nfunction hex_hmac_md5(key, data) { return binl2hex(core_hmac_md5(key, data)); }\nfunction b64_hmac_md5(key, data) { return binl2b64(core_hmac_md5(key, data)); }\nfunction str_hmac_md5(key, data) { return binl2str(core_hmac_md5(key, data)); }\n\n/*\n * Perform a simple self-test to see if the VM is working\n */\nfunction md5_vm_test()\n{\n return hex_md5(\"abc\") == \"900150983cd24fb0d6963f7d28e17f72\";\n}\n\n/*\n * Calculate the MD5 of an array of little-endian words, and a bit length\n keep\n */\nfunction core_md5(x, len)\n{\n /* append padding */\n x[len >> 5] |= 0x80 << ((len) % 32);\n x[(((len + 64) >>> 9) << 4) + 14] = len;\n\n var a = 1732584193;\n var b = -271733879;\n var c = -1732584194;\n var d = 271733878;\n\n for(var i = 0; i < x.length; i += 16)\n {\n var olda = a;\n var oldb = b;\n var oldc = c;\n var oldd = d;\n\n a = md5_ff(a, b, c, d, x[i+ 0], 7 , -680876936);\n d = md5_ff(d, a, b, c, x[i+ 1], 12, -389564586);\n c = md5_ff(c, d, a, b, x[i+ 2], 17, 606105819);\n b = md5_ff(b, c, d, a, x[i+ 3], 22, -1044525330);\n a = md5_ff(a, b, c, d, x[i+ 4], 7 , -176418897);\n d = md5_ff(d, a, b, c, x[i+ 5], 12, 1200080426);\n c = md5_ff(c, d, a, b, x[i+ 6], 17, -1473231341);\n b = md5_ff(b, c, d, a, x[i+ 7], 22, -45705983);\n a = md5_ff(a, b, c, d, x[i+ 8], 7 , 1770035416);\n d = md5_ff(d, a, b, c, x[i+ 9], 12, -1958414417);\n c = md5_ff(c, d, a, b, x[i+10], 17, -42063);\n b = md5_ff(b, c, d, a, x[i+11], 22, -1990404162);\n a = md5_ff(a, b, c, d, x[i+12], 7 , 1804603682);\n d = md5_ff(d, a, b, c, x[i+13], 12, -40341101);\n c = md5_ff(c, d, a, b, x[i+14], 17, -1502002290);\n b = md5_ff(b, c, d, a, x[i+15], 22, 1236535329);\n\n a = md5_gg(a, b, c, d, x[i+ 1], 5 , -165796510);\n d = md5_gg(d, a, b, c, x[i+ 6], 9 , -1069501632);\n c = md5_gg(c, d, a, b, x[i+11], 14, 643717713);\n b = md5_gg(b, c, d, a, x[i+ 0], 20, -373897302);\n a = md5_gg(a, b, c, d, x[i+ 5], 5 , -701558691);\n d = md5_gg(d, a, b, c, x[i+10], 9 , 38016083);\n c = md5_gg(c, d, a, b, x[i+15], 14, -660478335);\n b = md5_gg(b, c, d, a, x[i+ 4], 20, -405537848);\n a = md5_gg(a, b, c, d, x[i+ 9], 5 , 568446438);\n d = md5_gg(d, a, b, c, x[i+14], 9 , -1019803690);\n c = md5_gg(c, d, a, b, x[i+ 3], 14, -187363961);\n b = md5_gg(b, c, d, a, x[i+ 8], 20, 1163531501);\n a = md5_gg(a, b, c, d, x[i+13], 5 , -1444681467);\n d = md5_gg(d, a, b, c, x[i+ 2], 9 , -51403784);\n c = md5_gg(c, d, a, b, x[i+ 7], 14, 1735328473);\n b = md5_gg(b, c, d, a, x[i+12], 20, -1926607734);\n\n a = md5_hh(a, b, c, d, x[i+ 5], 4 , -378558);\n d = md5_hh(d, a, b, c, x[i+ 8], 11, -2022574463);\n c = md5_hh(c, d, a, b, x[i+11], 16, 1839030562);\n b = md5_hh(b, c, d, a, x[i+14], 23, -35309556);\n a = md5_hh(a, b, c, d, x[i+ 1], 4 , -1530992060);\n d = md5_hh(d, a, b, c, x[i+ 4], 11, 1272893353);\n c = md5_hh(c, d, a, b, x[i+ 7], 16, -155497632);\n b = md5_hh(b, c, d, a, x[i+10], 23, -1094730640);\n a = md5_hh(a, b, c, d, x[i+13], 4 , 681279174);\n d = md5_hh(d, a, b, c, x[i+ 0], 11, -358537222);\n c = md5_hh(c, d, a, b, x[i+ 3], 16, -722521979);\n b = md5_hh(b, c, d, a, x[i+ 6], 23, 76029189);\n a = md5_hh(a, b, c, d, x[i+ 9], 4 , -640364487);\n d = md5_hh(d, a, b, c, x[i+12], 11, -421815835);\n c = md5_hh(c, d, a, b, x[i+15], 16, 530742520);\n b = md5_hh(b, c, d, a, x[i+ 2], 23, -995338651);\n\n a = md5_ii(a, b, c, d, x[i+ 0], 6 , -198630844);\n d = md5_ii(d, a, b, c, x[i+ 7], 10, 1126891415);\n c = md5_ii(c, d, a, b, x[i+14], 15, -1416354905);\n b = md5_ii(b, c, d, a, x[i+ 5], 21, -57434055);\n a = md5_ii(a, b, c, d, x[i+12], 6 , 1700485571);\n d = md5_ii(d, a, b, c, x[i+ 3], 10, -1894986606);\n c = md5_ii(c, d, a, b, x[i+10], 15, -1051523);\n b = md5_ii(b, c, d, a, x[i+ 1], 21, -2054922799);\n a = md5_ii(a, b, c, d, x[i+ 8], 6 , 1873313359);\n d = md5_ii(d, a, b, c, x[i+15], 10, -30611744);\n c = md5_ii(c, d, a, b, x[i+ 6], 15, -1560198380);\n b = md5_ii(b, c, d, a, x[i+13], 21, 1309151649);\n a = md5_ii(a, b, c, d, x[i+ 4], 6 , -145523070);\n d = md5_ii(d, a, b, c, x[i+11], 10, -1120210379);\n c = md5_ii(c, d, a, b, x[i+ 2], 15, 718787259);\n b = md5_ii(b, c, d, a, x[i+ 9], 21, -343485551);\n\n a = safe_add(a, olda);\n b = safe_add(b, oldb);\n c = safe_add(c, oldc);\n d = safe_add(d, oldd);\n }\n return Array(a, b, c, d);\n\n}\n\n/*\n * These functions implement the four basic operations the algorithm uses.\n */\nfunction md5_cmn(q, a, b, x, s, t)\n{\n return safe_add(bit_rol(safe_add(safe_add(a, q), safe_add(x, t)), s),b);\n}\nfunction md5_ff(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & c) | ((~b) & d), a, b, x, s, t);\n}\nfunction md5_gg(a, b, c, d, x, s, t)\n{\n return md5_cmn((b & d) | (c & (~d)), a, b, x, s, t);\n}\nfunction md5_hh(a, b, c, d, x, s, t)\n{\n return md5_cmn(b ^ c ^ d, a, b, x, s, t);\n}\nfunction md5_ii(a, b, c, d, x, s, t)\n{\n return md5_cmn(c ^ (b | (~d)), a, b, x, s, t);\n}\n\n/*\n * Calculate the HMAC-MD5, of a key and some data\n */\nfunction core_hmac_md5(key, data)\n{\n var bkey = str2binl(key);\n if(bkey.length > 16) bkey = core_md5(bkey, key.length * chrsz);\n\n var ipad = Array(16), opad = Array(16);\n for(var i = 0; i < 16; i++)\n {\n ipad[i] = bkey[i] ^ 0x36363636;\n opad[i] = bkey[i] ^ 0x5C5C5C5C;\n }\n\n var hash = core_md5(ipad.concat(str2binl(data)), 512 + data.length * chrsz);\n return core_md5(opad.concat(hash), 512 + 128);\n}\n\n/*\n * Add integers, wrapping at 2^32. This uses 16-bit operations internally\n * to work around bugs in some JS interpreters.\n */\nfunction safe_add(x, y)\n{\n var lsw = (x & 0xFFFF) + (y & 0xFFFF);\n var msw = (x >> 16) + (y >> 16) + (lsw >> 16);\n return (msw << 16) | (lsw & 0xFFFF);\n}\n\n/*\n * Bitwise rotate a 32-bit number to the left.\n */\nfunction bit_rol(num, cnt)\n{\n return (num << cnt) | (num >>> (32 - cnt));\n}\n\n/*\n * Convert a string to an array of little-endian words\n * If chrsz is ASCII, characters >255 have their hi-byte silently ignored.\n keep\n */\nfunction str2binl(str)\n{\n var bin = Array();\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < str.length * chrsz; i += chrsz)\n bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (i%32);\n return bin;\n}\n\n/*\n * Convert an array of little-endian words to a string\n */\nfunction binl2str(bin)\n{\n var str = \"\";\n var mask = (1 << chrsz) - 1;\n for(var i = 0; i < bin.length * 32; i += chrsz)\n str += String.fromCharCode((bin[i>>5] >>> (i % 32)) & mask);\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a hex string.\n keep\n */\nfunction binl2hex(binarray)\n{\n var hex_tab = hexcase ? \"0123456789ABCDEF\" : \"0123456789abcdef\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i++)\n {\n str += hex_tab.charAt((binarray[i>>2] >> ((i%4)*8+4)) & 0xF) +\n hex_tab.charAt((binarray[i>>2] >> ((i%4)*8 )) & 0xF);\n }\n return str;\n}\n\n/*\n * Convert an array of little-endian words to a base-64 string\n */\nfunction binl2b64(binarray)\n{\n var tab = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\";\n var str = \"\";\n for(var i = 0; i < binarray.length * 4; i += 3)\n {\n var triplet = (((binarray[i >> 2] >> 8 * ( i %4)) & 0xFF) << 16)\n | (((binarray[i+1 >> 2] >> 8 * ((i+1)%4)) & 0xFF) << 8 )\n | ((binarray[i+2 >> 2] >> 8 * ((i+2)%4)) & 0xFF);\n for(var j = 0; j < 4; j++)\n {\n if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;\n else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);\n }\n }\n return str;\n}"},"couchapp":{"index":"_list/index/recent-posts?descending=true&limit=10","signatures":{"images/icon.png":"8ea16a9f573a8ccf73b9ec3eb4f45710","vendor/couchapp/loader.js":"5c771689c7faebedd7be76f2e1638c6f","script/md5.js":"6d03025f455869185b57b5c138fd1c01","vendor/couchapp/jquery.pathbinder.js":"65ecfa230f539d62f36938aaebed4ac1","script/app.js":"6afed8455f0a6e6c2066bbe10455befc","style/screen.css":"45fdf6d1ee5960f49eed867d02f313f6","script/jquery.scrollTo.js":"14b1a91ee2cf52628dd77a14091fee67","vendor/couchapp/jquery.mustache.js":"0bfbc929b6d5500aacfa860c783550d8","vendor/couchapp/jquery.couch.app.js":"97772894d3feb4310e886c47c34506ca","vendor/couchapp/jquery.evently.js":"428bcff5d4894bb4c2362ecd788f580a","LICENSE.txt":"36d3d5c76a09d7c71d21ba9d8699aebd","vendor/couchapp/jquery.couch.app.util.js":"3dcb7ba87d7eb3bf9dad48acd85355a8","THANKS.txt":"9c68e1f58b9627e92bd16c8cdecc8a0b"},"objects":{"0b16b6ea9e221c0cc407c12e5a2730be":"function(doc) {\n // !code helpers/md5.js\n if (doc.type == \"comment\") {\n if (doc.commenter && doc.commenter.email && !doc.commenter.gravatar_url) {\n // todo normalize this schema-ness\n doc.commenter.gravatar = hex_md5(doc.commenter.email); \n }\n emit(new Date(doc.created_at), doc);\n }\n};","f744f815b2f5e2a06cfd06e4d6f5cd90":"function(doc) {\n // todo make commonjs\n // !code helpers/md5.js\n \n if (doc.type == \"post\") {\n emit([doc._id], doc);\n } else if (doc.type == \"comment\") {\n if (doc.commenter && doc.commenter.email && !doc.commenter.gravatar_url) {\n doc.commenter.gravatar = hex_md5(doc.commenter.email); \n }\n emit([doc.post_id, doc.created_at], doc);\n }\n};"},"manifest":["blog.json","couchapp.json","evently/","evently/account/","evently/account/loggedIn/","evently/account/loggedIn/data.js","evently/account/loggedIn/mustache.html","evently/profile/","evently/profile/loggedOut/","evently/profile/loggedOut/mustache.html","evently/profile/profileReady/","evently/profile/profileReady/mustache.html","evently/profile/profileReady/selectors/","evently/profile/profileReady/selectors/#preview/","evently/profile/profileReady/selectors/#preview/click.js","evently/profile/profileReady/selectors/form/","evently/profile/profileReady/selectors/form/submit.js","evently/tagcloud/","evently/tagcloud/_init/","evently/tagcloud/_init/data.js","evently/tagcloud/_init/mustache.html","evently/tagcloud/_init/query.json","helpers/","helpers/md5.js","lib/","lib/blog.js","lib/mustache.js","lib/validate.js","lists/","lists/comments.js","lists/index.js","lists/post.js","README.md","rewrites.json","shows/","shows/edit.js","shows/post.js","sofa2.txt","templates/","templates/edit.html","templates/index.html","templates/partials/","templates/partials/comment.html","templates/partials/header.html","templates/partials/scripts.html","templates/post.html","THANKS.txt","validate_doc_update.js","vendor/","vendor/couchapp/","vendor/couchapp/evently/","vendor/couchapp/evently/account/","vendor/couchapp/evently/account/_init.js","vendor/couchapp/evently/account/adminParty/","vendor/couchapp/evently/account/adminParty/mustache.html","vendor/couchapp/evently/account/doLogin.js","vendor/couchapp/evently/account/doLogout.js","vendor/couchapp/evently/account/doSignup.js","vendor/couchapp/evently/account/loggedIn/","vendor/couchapp/evently/account/loggedIn/after.js","vendor/couchapp/evently/account/loggedIn/data.js","vendor/couchapp/evently/account/loggedIn/mustache.html","vendor/couchapp/evently/account/loggedIn/selectors.json","vendor/couchapp/evently/account/loggedOut/","vendor/couchapp/evently/account/loggedOut/mustache.html","vendor/couchapp/evently/account/loggedOut/selectors.json","vendor/couchapp/evently/account/loginForm/","vendor/couchapp/evently/account/loginForm/after.js","vendor/couchapp/evently/account/loginForm/mustache.html","vendor/couchapp/evently/account/loginForm/selectors/","vendor/couchapp/evently/account/loginForm/selectors/a[href=#signup].json","vendor/couchapp/evently/account/loginForm/selectors/form/","vendor/couchapp/evently/account/loginForm/selectors/form/submit.js","vendor/couchapp/evently/account/signupForm/","vendor/couchapp/evently/account/signupForm/after.js","vendor/couchapp/evently/account/signupForm/mustache.html","vendor/couchapp/evently/account/signupForm/selectors/","vendor/couchapp/evently/account/signupForm/selectors/a[href=#login].json","vendor/couchapp/evently/account/signupForm/selectors/form/","vendor/couchapp/evently/account/signupForm/selectors/form/submit.js","vendor/couchapp/evently/profile/","vendor/couchapp/evently/profile/loggedIn.js","vendor/couchapp/evently/profile/loggedOut/","vendor/couchapp/evently/profile/loggedOut/after.js","vendor/couchapp/evently/profile/loggedOut/mustache.html","vendor/couchapp/evently/profile/noProfile/","vendor/couchapp/evently/profile/noProfile/data.js","vendor/couchapp/evently/profile/noProfile/mustache.html","vendor/couchapp/evently/profile/noProfile/selectors/","vendor/couchapp/evently/profile/noProfile/selectors/form/","vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js","vendor/couchapp/evently/profile/profileReady/","vendor/couchapp/evently/profile/profileReady/after.js","vendor/couchapp/evently/profile/profileReady/data.js","vendor/couchapp/evently/profile/profileReady/mustache.html","vendor/couchapp/evently/README.md","vendor/couchapp/lib/","vendor/couchapp/lib/atom.js","vendor/couchapp/lib/cache.js","vendor/couchapp/lib/code.js","vendor/couchapp/lib/docform.js","vendor/couchapp/lib/linkup.js","vendor/couchapp/lib/list.js","vendor/couchapp/lib/markdown.js","vendor/couchapp/lib/md5.js","vendor/couchapp/lib/mustache.js","vendor/couchapp/lib/path.js","vendor/couchapp/lib/redirect.js","vendor/couchapp/lib/validate.js","vendor/couchapp/metadata.json","vendor/markdown/","vendor/markdown/lib/","vendor/textile/","vendor/textile/textile.js","views/","views/comments/","views/comments/map.js","views/post-page/","views/post-page/map.js","views/recent-posts/","views/recent-posts/map.js","views/tags/","views/tags/map.js","views/tags/reduce.js"]},"README":"# Sofa: Standalone CouchDB Blog\n\nSofa showcases the [potential of pure CouchDB applications](http://jchris.mfdz.com/code/2008/10/standalone_applications_with_co). It should provide an easy way for people to put thier thoughts online, anywhere there's a running Couch. It's just HTML, JavaScript and the magic of CouchDB.\n\nCurrently supports authoring by anyone with the proper roles, and comments from anyone with a user account.\n\n## Current News\n\nThings are moving crazy fast around here right now as I bring this stuff up to ship-shape for the [CouchDB book](http://books.couchdb.org). I'll be renaming methods and stuff (if I find the time), any API feedback will be appreciated.\n\n## Install CouchDB\n\nYou'll also need CouchDB (verion 0.11 or newer). Once you have that installed and the tests passing, you can install CouchApp\nand the blog software. \n\n## Install CouchApp\n\nCouchApp makes it easy to edit application that are hosted in CouchDB, by keeping a correspondence between a set of files, and a CouchDB design document. You'll use CouchApp to install Sofa in your CouchDB instance.\n\n sudo easy_install couchapp\n\nCouchApp is a set of utilities for developing standalone CouchDB applications You can [learn more about the CouchApp project here](http://github.com/couchapp/couchapp/). Also, [`easy_install` has an unpleasant bug on OSX](http://mail.python.org/pipermail/pythonmac-sig/2008-October/020567.html), so you might end up having to work from git source.\n\n\n### Setup Admin Access\n\nIf you are going to put your blog in public, you'll want to [set up an Admin account (screencast)](http://www.youtube.com/watch?v=oHKvV3Nh-CI).\n\n\n## Install Sofa\n\n git clone git://github.com/jchris/sofa.git\n cd sofa\n couchapp push . http://user:pass@127.0.0.1:5984/myblogdb \n \nYou'll want to edit the HTML and CSS to personalize your site. Don't worry, the markup is pretty basic, so it's easy to rework. Adding new features is just a few lines of JavaScript away.\n\nAnytime you make edits to the on-disk version of Sofa, and want to see them in your browser, just run `couchapp push . http://127.0.0.1:5984/blogdb` again. **You probably want to setup your `.couchapprc` file.** You should read the CouchApp readme to learn about that.\n\nYou can customize the blog title and other stuff in the `blog.json` file.\n\n# Relax\n\n[Visit your new blog.](http://127.0.0.1:5984/blogdb/_design/sofa/_list/index/recent-posts?descending=true&limit=5)","shows":{"edit":"function(doc, req) { \n var ddoc = this;\n var Mustache = require(\"lib/mustache\");\n var path = require(\"vendor/couchapp/lib/path\").init(req);\n\n var indexPath = path.list('index','recent-posts',{descending:true, limit:10});\n var feedPath = path.list('index','recent-posts',{descending:true, limit:10, format:\"atom\"});\n var commentsFeed = path.list('comments','comments',{descending:true, limit:10, format:\"atom\"});\n\n return Mustache.to_html(ddoc.templates.edit, {\n header : {\n index : indexPath,\n blogName : ddoc.blog.title,\n feedPath : feedPath,\n commentsFeed : commentsFeed\n },\n scripts : {},\n doc : doc,\n docid : JSON.stringify((doc && doc._id) || null),\n pageTitle : doc ? \"Edit: \"+doc.title : \"Create a new post\",\n assets : path.asset()\n }, ddoc.templates.partials);\n}","post":"function(doc, req) { \n var path = require(\"vendor/couchapp/lib/path\").init(req);\n var redirect = require(\"vendor/couchapp/lib/redirect\");\n return redirect.permanent(path.list('post','post-page', {startkey : [doc._id]}));\n}"},"_attachments":{"images/icon.png":{"content_type":"image/png","revpos":45,"length":154642,"stub":true},"script/md5.js":{"content_type":"application/javascript","revpos":45,"length":8571,"stub":true},"vendor/couchapp/jquery.pathbinder.js":{"content_type":"application/javascript","revpos":46,"length":4879,"stub":true},"script/app.js":{"content_type":"application/javascript","revpos":45,"length":183,"stub":true},"style/screen.css":{"content_type":"text/css","revpos":45,"length":2134,"stub":true},"vendor/couchapp/loader.js":{"content_type":"application/javascript","revpos":46,"length":457,"stub":true},"vendor/couchapp/jquery.mustache.js":{"content_type":"application/javascript","revpos":46,"length":9466,"stub":true},"vendor/couchapp/jquery.couch.app.js":{"content_type":"application/javascript","revpos":46,"length":7320,"stub":true},"vendor/couchapp/jquery.evently.js":{"content_type":"application/javascript","revpos":46,"length":10851,"stub":true},"script/jquery.scrollTo.js":{"content_type":"application/javascript","revpos":45,"length":2005,"stub":true},"LICENSE.txt":{"content_type":"text/plain","revpos":45,"length":9722,"stub":true},"vendor/couchapp/jquery.couch.app.util.js":{"content_type":"application/javascript","revpos":46,"length":2893,"stub":true},"THANKS.txt":{"content_type":"text/plain","revpos":45,"length":236,"stub":true}}}