+
From 924a7268b3225c206c19f0bfcdb9301b09982ddf Mon Sep 17 00:00:00 2001
From: Chris Anderson Customize this format here: ddoc.evently.items._changes.mustache {{message}} Protip: If you setup continuous replication between this database and a remote one, this list will reflect remote changes in near real-time. This would be a good place to add pagination. Most applications will customize this template (ddoc.evently.profile.profileReady.mustache) for user input. Not much to see here loggedIn was triggered from another widget, {{name}}. The db name is {{name}} Click to show the full doc source: The full doc source (rerun to hide): Hello world What a crazy world! Hello Joan! Darn, it's Jane... Actually Jane is awesome. Loading CouchDB server info. Running CouchDB version {{version}} How many databases on the local host? Answer: {{number_of_dbs}} Other stuff: {{args}} More: {{allArgs}} What a crazy world! you went to foo you went to foo - "+params.id+" the index. more cowbell! Now that is a lot of cowbell. back to the index Error running #', id,
+ ' code block:Generated CouchApp
+
+
+
+
+
+
+
+
+
diff --git a/_attachments/style/main.css b/_attachments/style/main.css
new file mode 100755
index 0000000..1d61e0e
--- /dev/null
+++ b/_attachments/style/main.css
@@ -0,0 +1,71 @@
+/* add styles here */
+
+body {
+ font:1em Helvetica, sans-serif;
+ padding:4px;
+}
+
+#account {
+ float:right;
+}
+
+#profile {
+ border:4px solid #edd;
+ background:#fee;
+ padding:8px;
+ margin-bottom:8px;
+}
+
+#items {
+ border:4px solid #dde;
+ background:#eef;
+ padding:8px;
+ width:60%;
+ float:left;
+}
+
+#sidebar {
+ border:4px solid #dfd;
+ padding:8px;
+ float:right;
+ width:30%;
+}
+
+#items li {
+ border:4px solid #f5f5ff;
+ background:#fff;
+ padding:8px;
+ margin:4px 0;
+}
+
+form {
+ padding:4px;
+ margin:6px;
+ background-color:#ddd;
+}
+
+div.avatar {
+ padding:2px;
+ padding-bottom:0;
+ margin-right:4px;
+ float:left;
+ font-size:0.78em;
+ width : 60px;
+ height : 60px;
+ text-align: center;
+}
+
+div.avatar .name {
+ padding-top:2px;
+}
+
+div.avatar img {
+ margin:0 auto;
+ padding:0;
+ width : 40px;
+ height : 40px;
+}
+
+#items ul {
+ list-style: none;
+}
diff --git a/_id b/_id
new file mode 100644
index 0000000..703f3c2
--- /dev/null
+++ b/_id
@@ -0,0 +1 @@
+_design/proto
\ No newline at end of file
diff --git a/couchapp.json b/couchapp.json
new file mode 100644
index 0000000..0cc524d
--- /dev/null
+++ b/couchapp.json
@@ -0,0 +1,4 @@
+{
+ "name": "Name of your CouchApp",
+ "description": "CouchApp"
+}
\ No newline at end of file
diff --git a/evently/items/_changes/data.js b/evently/items/_changes/data.js
new file mode 100644
index 0000000..00ad89c
--- /dev/null
+++ b/evently/items/_changes/data.js
@@ -0,0 +1,11 @@
+function(data) {
+ $.log(data)
+ var p;
+ return {
+ items : data.rows.map(function(r) {
+ p = r.value.profile;
+ p.message = r.value.message;
+ return p;
+ })
+ }
+};
\ No newline at end of file
diff --git a/evently/items/_changes/mustache.html b/evently/items/_changes/mustache.html
new file mode 100644
index 0000000..4634034
--- /dev/null
+++ b/evently/items/_changes/mustache.html
@@ -0,0 +1,18 @@
+Recent Messages
+
+ {{#items}}
+
+{{/gravatar_url}}
+
{{/gravatar_url}}
+
+ {{json}}",
+ data : function() {
+ var widget = app.ddoc.vendor.couchapp.evently.account;
+ return {
+ json : JSON.stringify(widget, null, 2)
+ }
+ }
+ }
+ });
+ });
+
+The top level keys are the most important: `loggedIn`, `loggedOut`, `adminParty`, `signupForm`, `loginForm`, `doLogin`, `doSignup`, and `_init`. Each one of these corresponds to an event or state the system can be in. Some of them draw user interface elements, other directly trigger further events.
+
+### _init
+
+The `_init` event is special, in that Evently will automatically trigger it when the widget is created. Here is the code for the account widget's `_init` event handler.
+
+ function() {
+ var elem = $(this);
+ $.couch.session({
+ success : function(r) {
+ var userCtx = r.userCtx;
+ if (userCtx.name) {
+ elem.trigger("loggedIn", [r]);
+ } else if (userCtx.roles.indexOf("_admin") != -1) {
+ elem.trigger("adminParty");
+ } else {
+ elem.trigger("loggedOut");
+ };
+ }
+ });
+ }
+
+This code does one query to CouchDB, to retrieve the session information for the current user. For this we use the `$.couch.session()` function which is part of the [jquery.couch.js](/_utils/script/jquery.couch.js) library which is part of the CouchDB distribution.
+
+The response is handled in one of three ways, depending on the user's session information. Either we trigger the `loggedIn` or `loggedOut` events, or in the special case where we detect that CouchDB's security is not properly configured, we trigger the `adminParty` event to warn the user.
+
+### loggedOut
+
+Because most visitors start logged out, let's now turn our attention to the `loggedOut` event handler to see what will greet a new visitor:
+
+ "loggedOut": {
+ "mustache": "Signup or Login",
+ "selectors": {
+ "a[href=#login]": {
+ "click": "loginForm"
+ },
+ "a[href=#signup]": {
+ "click": "signupForm"
+ }
+ }
+ }
+
+There are two main components to this handler: `mustache` and `selectors`. `mustache` is a template file with two HTML links. `selectors` contains a set of CSS selectors with events bound to them. You can think of each selector as a nested Evently widget. In this case, clicking "Login" will trigger the `loginForm` event, while clicking "Signup" triggers the `signupForm` event.
+
+### signupForm
+
+Let's see what happens during signup. We'll skip showing the whole handler (it should be in the sidebar anyway if you clicked "run" earlier.)
+
+When the `signupForm` event is triggered, a mustache template draws the form. Then the selectors are run, assigning this function to the form's submit event:
+
+ function(e) {
+ var name = $('input[name=name]', this).val(),
+ pass = $('input[name=password]', this).val();
+ $(this).trigger('doSignup', [name, pass]);
+ return false;
+ }
+
+This handler is as simple as possible, all it does is use jQuery to pull the user data from the form, and send the name and password to the `doSignup` event. We could just use a function call here, but it's nice to keep our individual events as small as possible, as this makes customizing Evently widgets simpler.
+
+### doSignup
+
+Here is the `doSignup` handler:
+
+ function(e, name, pass) {
+ var elem = $(this);
+ $.couch.signup({
+ name : name
+ }, pass, {
+ success : function() {
+ elem.trigger("doLogin", [name, pass]);
+ }
+ });
+ }
+
+Again, all the complex signup logic (encrypting passwords, etc) is pushed to the [jquery.couch.js](/_utils/script/jquery.couch.js) library (via the `$.couch.signup()` call), so our application code can stay as simple as possible. When signup is complete, we trigger the `doLogin` event, so new users don't have to go through another action.
+
+### doLogin
+
+The code for `doLogin` isn't much different, just take the name and password, and call a jquery.couch.js library function with it.
+
+ function(e, name, pass) {
+ var elem = $(this);
+ $.couch.login({
+ name : name,
+ password : pass,
+ success : function(r) {
+ elem.trigger("_init")
+ }
+ });
+ }
+
+The last thing that `doLogin` does is trigger `_init`, so we come full circle! This time, `_init` will see that the user is logged in, and trigger the `loggedIn` event. You'll probably want to hook your application to this `loggedIn` event, to activate any features which are reserved for registered users. We'll cover linking events in a later section.
+
+## Customizing the account widget
+
+Evently widgets are built out of JSON objects, which makes it easy to replace bits and pieces of them without having to mess with the entire widget. We'll start by customizing what users see when they are logged in.
+
+ $.couch.app(function(app){
+ var customizedWidget = $.extend(true, {}, app.ddoc.vendor.couchapp.evently.account, {
+ loggedIn : {
+ mustache : 'Hello {{name}} you are logged in! ' +
+ 'Would you like to logout?'
+ }
+ });
+ $("#customWelcome").evently(customizedWidget);
+ });
+
+Take a moment to run this example code and login to see how our custom template has replaced just one screen in the widget. The first time I did this I thought it was pretty cool. Hopefully you can think of a lot of powerful stuff you could do with it. The sky is the limit.
+
+Here's another quick one:
+
+ $.couch.app(function(app){
+ var customizedWidget = $.extend(true, {}, app.ddoc.vendor.couchapp.evently.account, {
+ loggedOut : {
+ after : "function(){alert('Bye bye');}"
+ }
+ });
+ $("#afterAlert").evently(customizedWidget);
+ });
+
+For a deeper reference on what the various parts of an Evently widget are named, and how you can use them, see [the Evently docs page](#/topic/evently).
+
+## Linking two widgets
+
+First, lets create a basic widget. This one just has an `_init` handler and a handler called `loggedIn`. There is nothing in this widget definition that will trigger `loggedIn`, unless something else triggers it, there's no way it will run.
+
+ $("#link_target").evently({
+ _init : {
+ mustache : "{{ddoc}}',
+ data : {
+ ddoc : JSON.stringify(app.ddoc, null, 2).slice(0,100) + '...'
+ }
+ },
+ click : {
+ mustache : '{{ddoc}}',
+ data : {
+ ddoc : JSON.stringify(app.ddoc, null, 2)
+ }
+ }
+ });
+ });
+
diff --git a/vendor/couchapp/docs/docs.md b/vendor/couchapp/docs/docs.md
new file mode 100644
index 0000000..e4c42ab
--- /dev/null
+++ b/vendor/couchapp/docs/docs.md
@@ -0,0 +1,11 @@
+# Docs for the docs system.
+
+You are encouraged to use the couchapp docs system to write documentation for your plugins and applications. Extra bonus points because it's fun.
+
+Docs automatically make divs based on `$("#foo")` pattern matching. That is, we regex the code looking for the first id we see referenced. Remember ids need to be unique on a page. For doc examples you only get one id.
+
+Example Code:
+
+ $("#hide_foo").hide("slow");
+
+That's all it takes. You only get one div in each example for now. Have fun!
\ No newline at end of file
diff --git a/vendor/couchapp/docs/evently.md b/vendor/couchapp/docs/evently.md
new file mode 100644
index 0000000..bed0a70
--- /dev/null
+++ b/vendor/couchapp/docs/evently.md
@@ -0,0 +1,185 @@
+# Evently Docs
+
+Evently is an declarative framework for evented jQuery applications. You write your code as widgets made up of templates and callbacks, while Evently handles the busywork of linking them together.
+
+Evently has special handlers for CouchDB views and `_changes` feeds, and could be easily extended for other server-side frameworks.
+
+## Hello World
+
+At it's simplest an Evently widget is a set of events connected to a single DOM element.
+
+JavaScript:
+
+ $("#hello").evently({
+ _init : {
+ mustache : "
+ {{#docs}}
+
\ No newline at end of file
diff --git a/vendor/couchapp/evently/docs/index/path.txt b/vendor/couchapp/evently/docs/index/path.txt
new file mode 100644
index 0000000..35ec3b9
--- /dev/null
+++ b/vendor/couchapp/evently/docs/index/path.txt
@@ -0,0 +1 @@
+/
\ No newline at end of file
diff --git a/vendor/couchapp/evently/docs/topic/after.js b/vendor/couchapp/evently/docs/topic/after.js
new file mode 100644
index 0000000..f048c27
--- /dev/null
+++ b/vendor/couchapp/evently/docs/topic/after.js
@@ -0,0 +1,15 @@
+function() {
+ var app = $$(this).app;
+ var self = $(this);
+ $("pre", self).each(function() {
+ var pre = $(this);
+ var js = pre.text();
+ var r = js.match(/\$\(\"\#([^\"]*)\"\)/);
+ if (r) {
+ var id = r[1];
+ var code_id = 'code-'+id;
+ pre.wrap('');
+ $('#'+code_id).evently(app.ddoc.vendor.couchapp.evently.docs.topic.edit, app, [id]);
+ }
+ });
+};
diff --git a/vendor/couchapp/evently/docs/topic/data.js b/vendor/couchapp/evently/docs/topic/data.js
new file mode 100644
index 0000000..039efab
--- /dev/null
+++ b/vendor/couchapp/evently/docs/topic/data.js
@@ -0,0 +1,8 @@
+function(e, p) {
+ var doc = $$(this).app.ddoc.vendor.couchapp.docs[p.id];
+ var converter = new Showdown.converter();
+ var html = converter.makeHtml(doc);
+ return {
+ html : html
+ };
+};
\ No newline at end of file
diff --git a/vendor/couchapp/evently/docs/topic/edit/_init/fun.js b/vendor/couchapp/evently/docs/topic/edit/_init/fun.js
new file mode 100644
index 0000000..8571348
--- /dev/null
+++ b/vendor/couchapp/evently/docs/topic/edit/_init/fun.js
@@ -0,0 +1,19 @@
+function(e, id) {
+ var editable = $(this);
+ if ($$(editable)._init_ran) {return false;}
+ // add edit link
+ var edit = $('edit code');
+ editable.append(edit);
+
+ // add run box
+ var example = $('',
+ (y.toSource ? y.toSource() : JSON.stringify(y)),
+ '
Please log in to see your profile.
\ No newline at end of file diff --git a/vendor/couchapp/evently/profile/noProfile/data.js b/vendor/couchapp/evently/profile/noProfile/data.js new file mode 100644 index 0000000..d8fe586 --- /dev/null +++ b/vendor/couchapp/evently/profile/noProfile/data.js @@ -0,0 +1,3 @@ +function(e, userCtx) { + return userCtx; +} \ No newline at end of file diff --git a/vendor/couchapp/evently/profile/noProfile/mustache.html b/vendor/couchapp/evently/profile/noProfile/mustache.html new file mode 100644 index 0000000..8cf6563 --- /dev/null +++ b/vendor/couchapp/evently/profile/noProfile/mustache.html @@ -0,0 +1,11 @@ + \ No newline at end of file diff --git a/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js b/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js new file mode 100644 index 0000000..2f21530 --- /dev/null +++ b/vendor/couchapp/evently/profile/noProfile/selectors/form/submit.js @@ -0,0 +1,36 @@ +function() { + var md5 = $$(this).app.require("vendor/couchapp/lib/md5"); + + // TODO this can be cleaned up with docForm? + // it still needs the workflow to edit an existing profile + var name = $("input[name=userCtxName]",this).val(); + var newProfile = { + rand : Math.random().toString(), + nickname : $("input[name=nickname]",this).val(), + email : $("input[name=email]",this).val(), + url : $("input[name=url]",this).val() + }, widget = $(this); + + // setup gravatar_url + if (md5) { + newProfile.gravatar_url = 'http://www.gravatar.com/avatar/'+md5.hex(newProfile.email || newProfile.rand)+'.jpg?s=40&d=identicon'; + } + + // store the user profile on the user account document + $.couch.userDb(function(db) { + var userDocId = "org.couchdb.user:"+name; + db.openDoc(userDocId, { + success : function(userDoc) { + userDoc["couch.app.profile"] = newProfile; + db.saveDoc(userDoc, { + success : function() { + newProfile.name = userDoc.name; + $$(widget).profile = newProfile; + widget.trigger("profileReady", [newProfile]); + } + }); + } + }); + }); + return false; +} \ No newline at end of file diff --git a/vendor/couchapp/evently/profile/profileReady/after.js b/vendor/couchapp/evently/profile/profileReady/after.js new file mode 100644 index 0000000..02e9bfe --- /dev/null +++ b/vendor/couchapp/evently/profile/profileReady/after.js @@ -0,0 +1,3 @@ +function(e, p) { + $$(this).profile = p; +}; \ No newline at end of file diff --git a/vendor/couchapp/evently/profile/profileReady/data.js b/vendor/couchapp/evently/profile/profileReady/data.js new file mode 100644 index 0000000..2acd253 --- /dev/null +++ b/vendor/couchapp/evently/profile/profileReady/data.js @@ -0,0 +1,3 @@ +function(e, p) { + return p +} diff --git a/vendor/couchapp/evently/profile/profileReady/mustache.html b/vendor/couchapp/evently/profile/profileReady/mustache.html new file mode 100644 index 0000000..62fe274 --- /dev/null +++ b/vendor/couchapp/evently/profile/profileReady/mustache.html @@ -0,0 +1,8 @@ +Hello {{nickname}}!
+ \ No newline at end of file diff --git a/vendor/couchapp/lib/atom.js b/vendor/couchapp/lib/atom.js new file mode 100644 index 0000000..f61092e --- /dev/null +++ b/vendor/couchapp/lib/atom.js @@ -0,0 +1,39 @@ +// atom feed generator +// requries E4X support. + +function f(n) { // Format integers to have at least two digits. + return n < 10 ? '0' + n : n; +} + +function rfc3339(date) { + return date.getUTCFullYear() + '-' + + f(date.getUTCMonth() + 1) + '-' + + f(date.getUTCDate()) + 'T' + + f(date.getUTCHours()) + ':' + + f(date.getUTCMinutes()) + ':' + + f(date.getUTCSeconds()) + 'Z'; +}; + +exports.header = function(data) { + var f =s around + // "paragraphs" that are wrapped in non-block-level tags, such as anchors, + // phrase emphasis, and spans. The list of tags we're looking for is + // hard-coded: + var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del" + var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math" + + // First, look for nested blocks, e.g.: + //
tags around block-level tags.
+ text = _HashHTMLBlocks(text);
+ text = _FormParagraphs(text);
+
+ return text;
+}
+
+
+var _RunSpanGamut = function(text) {
+//
+// These are all the transformations that occur *within* block-level
+// tags like paragraphs, headers, and list items.
+//
+
+ text = _DoCodeSpans(text);
+ text = _EscapeSpecialCharsWithinTagAttributes(text);
+ text = _EncodeBackslashEscapes(text);
+
+ // Process anchor and image tags. Images must come first,
+ // because ![foo][f] looks like an anchor.
+ text = _DoImages(text);
+ text = _DoAnchors(text);
+
+ // Make links out of things like ` Just type tags
+//
+
+ // Strip leading and trailing lines:
+ text = text.replace(/^\n+/g,"");
+ text = text.replace(/\n+$/g,"");
+
+ var grafs = text.split(/\n{2,}/g);
+ var grafsOut = new Array();
+
+ //
+ // Wrap tags.
+ //
+ var end = grafs.length;
+ for (var i=0; i ");
+ str += "
\n");
+
+ return text;
+}
+
+var _EscapeSpecialCharsWithinTagAttributes = function(text) {
+//
+// Within tags -- meaning between < and > -- encode [\ ` * _] so they
+// don't conflict with their use in Markdown for code, italics and strong.
+//
+
+ // Build a regex to find HTML tags and comments. See Friedl's
+ // "Mastering Regular Expressions", 2nd Ed., pp. 200-201.
+ var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;
+
+ text = text.replace(regex, function(wholeMatch) {
+ var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g,"$1`");
+ tag = escapeCharacters(tag,"\\`*_");
+ return tag;
+ });
+
+ return text;
+}
+
+var _DoAnchors = function(text) {
+//
+// Turn Markdown link shortcuts into XHTML tags.
+//
+ //
+ // First, handle reference-style links: [link text] [id]
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ (
+ (?:
+ \[[^\]]*\] // allow brackets nested one level
+ |
+ [^\[] // or anything else
+ )*
+ )
+ \]
+
+ [ ]? // one optional space
+ (?:\n[ ]*)? // one optional newline followed by spaces
+
+ \[
+ (.*?) // id = $3
+ \]
+ )()()()() // pad remaining backreferences
+ /g,_DoAnchors_callback);
+ */
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeAnchorTag);
+
+ //
+ // Next, inline-style links: [link text](url "optional title")
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ (
+ (?:
+ \[[^\]]*\] // allow brackets nested one level
+ |
+ [^\[\]] // or anything else
+ )
+ )
+ \]
+ \( // literal paren
+ [ \t]*
+ () // no id, so leave $3 empty
+ (.*?)>? // href = $4
+ [ \t]*
+ ( // $5
+ (['"]) // quote char = $6
+ (.*?) // Title = $7
+ \6 // matching quote
+ [ \t]* // ignore any spaces/tabs between closing quote and )
+ )? // title is optional
+ \)
+ )
+ /g,writeAnchorTag);
+ */
+ text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()(.*?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeAnchorTag);
+
+ //
+ // Last, handle reference-style shortcuts: [link text]
+ // These must come last in case you've also got [link test][1]
+ // or [link test](/foo)
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ \[
+ ([^\[\]]+) // link text = $2; can't contain '[' or ']'
+ \]
+ )()()()()() // pad rest of backreferences
+ /g, writeAnchorTag);
+ */
+ text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag);
+
+ return text;
+}
+
+var writeAnchorTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
+ if (m7 == undefined) m7 = "";
+ var whole_match = m1;
+ var link_text = m2;
+ var link_id = m3.toLowerCase();
+ var url = m4;
+ var title = m7;
+
+ if (url == "") {
+ if (link_id == "") {
+ // lower-case and turn embedded newlines into spaces
+ link_id = link_text.toLowerCase().replace(/ ?\n/g," ");
+ }
+ url = "#"+link_id;
+
+ if (g_urls[link_id] != undefined) {
+ url = g_urls[link_id];
+ if (g_titles[link_id] != undefined) {
+ title = g_titles[link_id];
+ }
+ }
+ else {
+ if (whole_match.search(/\(\s*\)$/m)>-1) {
+ // Special case for explicit empty url
+ url = "";
+ } else {
+ return whole_match;
+ }
+ }
+ }
+
+ url = escapeCharacters(url,"*_");
+ var result = "" + link_text + "";
+
+ return result;
+}
+
+
+var _DoImages = function(text) {
+//
+// Turn Markdown image shortcuts into tags.
+//
+
+ //
+ // First, handle reference-style labeled images: ![alt text][id]
+ //
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ !\[
+ (.*?) // alt text = $2
+ \]
+
+ [ ]? // one optional space
+ (?:\n[ ]*)? // one optional newline followed by spaces
+
+ \[
+ (.*?) // id = $3
+ \]
+ )()()()() // pad rest of backreferences
+ /g,writeImageTag);
+ */
+ text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,writeImageTag);
+
+ //
+ // Next, handle inline images: 
+ // Don't forget: encode * and _
+
+ /*
+ text = text.replace(/
+ ( // wrap whole match in $1
+ !\[
+ (.*?) // alt text = $2
+ \]
+ \s? // One optional whitespace character
+ \( // literal paren
+ [ \t]*
+ () // no id, so leave $3 empty
+ (\S+?)>? // src url = $4
+ [ \t]*
+ ( // $5
+ (['"]) // quote char = $6
+ (.*?) // title = $7
+ \6 // matching quote
+ [ \t]*
+ )? // title is optional
+ \)
+ )
+ /g,writeImageTag);
+ */
+ text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()(\S+?)>?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,writeImageTag);
+
+ return text;
+}
+
+var writeImageTag = function(wholeMatch,m1,m2,m3,m4,m5,m6,m7) {
+ var whole_match = m1;
+ var alt_text = m2;
+ var link_id = m3.toLowerCase();
+ var url = m4;
+ var title = m7;
+
+ if (!title) title = "";
+
+ if (url == "") {
+ if (link_id == "") {
+ // lower-case and turn embedded newlines into spaces
+ link_id = alt_text.toLowerCase().replace(/ ?\n/g," ");
+ }
+ url = "#"+link_id;
+
+ if (g_urls[link_id] != undefined) {
+ url = g_urls[link_id];
+ if (g_titles[link_id] != undefined) {
+ title = g_titles[link_id];
+ }
+ }
+ else {
+ return whole_match;
+ }
+ }
+
+ alt_text = alt_text.replace(/"/g,""");
+ url = escapeCharacters(url,"*_");
+ var result = "
";
+
+ return result;
+}
+
+
+var _DoHeaders = function(text) {
+
+ // Setext-style headers:
+ // Header 1
+ // ========
+ //
+ // Header 2
+ // --------
+ //
+ text = text.replace(/^(.+)[ \t]*\n=+[ \t]*\n+/gm,
+ function(wholeMatch,m1){return hashBlock("
" + _RunSpanGamut(m1) + "
");});
+
+ text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,
+ function(matchFound,m1){return hashBlock("" + _RunSpanGamut(m1) + "
");});
+
+ // atx-style headers:
+ // # Header 1
+ // ## Header 2
+ // ## Header 2 with closing hashes ##
+ // ...
+ // ###### Header 6
+ //
+
+ /*
+ text = text.replace(/
+ ^(\#{1,6}) // $1 = string of #'s
+ [ \t]*
+ (.+?) // $2 = Header text
+ [ \t]*
+ \#* // optional closing #'s (not counted)
+ \n+
+ /gm, function() {...});
+ */
+
+ text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm,
+ function(wholeMatch,m1,m2) {
+ var h_level = m1.length;
+ return hashBlock("` blocks.
+//
+
+ /*
+ text = text.replace(text,
+ /(?:\n\n|^)
+ ( // $1 = the code block -- one or more lines, starting with a space/tab
+ (?:
+ (?:[ ]{4}|\t) // Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
+ .*\n+
+ )+
+ )
+ (\n*[ ]{0,3}[^ \t\n]|(?=~0)) // attacklab: g_tab_width
+ /g,function(){...});
+ */
+
+ // attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
+ text += "~0";
+
+ text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,
+ function(wholeMatch,m1,m2) {
+ var codeblock = m1;
+ var nextChar = m2;
+
+ codeblock = _EncodeCode( _Outdent(codeblock));
+ codeblock = _Detab(codeblock);
+ codeblock = codeblock.replace(/^\n+/g,""); // trim leading newlines
+ codeblock = codeblock.replace(/\n+$/g,""); // trim trailing whitespace
+
+ codeblock = "
";
+
+ return hashBlock(codeblock) + nextChar;
+ }
+ );
+
+ // attacklab: strip sentinel
+ text = text.replace(/~0/,"");
+
+ return text;
+}
+
+var hashBlock = function(text) {
+ text = text.replace(/(^\n+|\n+$)/g,"");
+ return "\n\n~K" + (g_html_blocks.push(text)-1) + "K\n\n";
+}
+
+
+var _DoCodeSpans = function(text) {
+//
+// * Backtick quotes are used for " + codeblock + "\n spans.
+//
+// * You can use multiple backticks as the delimiters if you want to
+// include literal backticks in the code span. So, this input:
+//
+// Just type ``foo `bar` baz`` at the prompt.
+//
+// Will translate to:
+//
+// foo `bar` baz at the prompt.`bar` ...
+//
+
+ /*
+ text = text.replace(/
+ (^|[^\\]) // Character before opening ` can't be a backslash
+ (`+) // $2 = Opening run of `
+ ( // $3 = The code block
+ [^\r]*?
+ [^`] // attacklab: work around lack of lookbehind
+ )
+ \2 // Matching closer
+ (?!`)
+ /gm, function(){...});
+ */
+
+ text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,
+ function(wholeMatch,m1,m2,m3,m4) {
+ var c = m3;
+ c = c.replace(/^([ \t]*)/g,""); // leading whitespace
+ c = c.replace(/[ \t]*$/g,""); // trailing whitespace
+ c = _EncodeCode(c);
+ return m1+""+c+"";
+ });
+
+ return text;
+}
+
+
+var _EncodeCode = function(text) {
+//
+// Encode/escape certain characters inside Markdown code runs.
+// The point is that in code, these characters are literals,
+// and lose their special Markdown meanings.
+//
+ // Encode all ampersands; HTML entities are not
+ // entities within a Markdown code span.
+ text = text.replace(/&/g,"&");
+
+ // Do the angle bracket song and dance:
+ text = text.replace(//g,">");
+
+ // Now, escape characters that are magic in Markdown:
+ text = escapeCharacters(text,"\*_{}[]\\",false);
+
+// jj the line above breaks this:
+//---
+
+//* Item
+
+// 1. Subitem
+
+// special char: *
+//---
+
+ return text;
+}
+
+
+var _DoItalicsAndBold = function(text) {
+
+ // must go first:
+ text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g,
+ "$2");
+
+ text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g,
+ "$2");
+
+ return text;
+}
+
+
+var _DoBlockQuotes = function(text) {
+
+ /*
+ text = text.replace(/
+ ( // Wrap whole match in $1
+ (
+ ^[ \t]*>[ \t]? // '>' at the start of a line
+ .+\n // rest of the first line
+ (.+\n)* // subsequent consecutive lines
+ \n* // blanks
+ )+
+ )
+ /gm, function(){...});
+ */
+
+ text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,
+ function(wholeMatch,m1) {
+ var bq = m1;
+
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ // "----------bug".replace(/^-/g,"") == "bug"
+
+ bq = bq.replace(/^[ \t]*>[ \t]?/gm,"~0"); // trim one level of quoting
+
+ // attacklab: clean up hack
+ bq = bq.replace(/~0/g,"");
+
+ bq = bq.replace(/^[ \t]+$/gm,""); // trim whitespace-only lines
+ bq = _RunBlockGamut(bq); // recurse
+
+ bq = bq.replace(/(^|\n)/g,"$1 ");
+ // These leading spaces screw with content, so we need to fix that:
+ bq = bq.replace(
+ /(\s*
[^\r]+?<\/pre>)/gm,
+ function(wholeMatch,m1) {
+ var pre = m1;
+ // attacklab: hack around Konqueror 3.5.4 bug:
+ pre = pre.replace(/^ /mg,"~0");
+ pre = pre.replace(/~0/g,"");
+ return pre;
+ });
+
+ return hashBlock("\n" + bq + "\n
");
+ });
+ return text;
+}
+
+
+var _FormParagraphs = function(text) {
+//
+// Params:
+// $text - string to process with html Generated CouchApp