couchdb-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ryanram...@apache.org
Subject [03/51] [partial] Restructure to simpler jam/erica style.
Date Sat, 11 May 2013 05:48:16 GMT
http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/less/index.html
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/less/index.html b/src/fauxton/jam/codemirror/mode/less/index.html
new file mode 100644
index 0000000..69467bb
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/less/index.html
@@ -0,0 +1,740 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CodeMirror: LESS mode</title>
+    <link rel="stylesheet" href="../../lib/codemirror.css">
+    <script src="../../lib/codemirror.js"></script>
+    <script src="less.js"></script>
+    <style>.CodeMirror {background: #f8f8f8; border: 1px solid #ddd; font-size:12px} .CodeMirror-scroll {height: 400px}</style>
+    <link rel="stylesheet" href="../../doc/docs.css">
+    <link rel="stylesheet" href="../../theme/lesser-dark.css">
+  </head>
+  <body>
+    <h1>CodeMirror: LESS mode</h1>
+    <form><textarea id="code" name="code">@media screen and (device-aspect-ratio: 16/9) { … }
+@media screen and (device-aspect-ratio: 32/18) { … }
+@media screen and (device-aspect-ratio: 1280/720) { … }
+@media screen and (device-aspect-ratio: 2560/1440) { … }
+
+html:lang(fr-be)
+html:lang(de)
+:lang(fr-be) > q
+:lang(de) > q
+
+tr:nth-child(2n+1) /* represents every odd row of an HTML table */
+tr:nth-child(odd)  /* same */
+tr:nth-child(2n+0) /* represents every even row of an HTML table */
+tr:nth-child(even) /* same */
+
+/* Alternate paragraph colours in CSS */
+p:nth-child(4n+1) { color: navy; }
+p:nth-child(4n+2) { color: green; }
+p:nth-child(4n+3) { color: maroon; }
+p:nth-child(4n+4) { color: purple; }
+
+:nth-child(10n-1)  /* represents the 9th, 19th, 29th, etc, element */
+:nth-child(10n+9)  /* Same */
+:nth-child(10n+-1) /* Syntactically invalid, and would be ignored */
+
+:nth-child( 3n + 1 )
+:nth-child( +3n - 2 )
+:nth-child( -n+ 6)
+:nth-child( +6 )
+
+html|tr:nth-child(-n+6)  /* represents the 6 first rows of XHTML tables */
+
+img:nth-of-type(2n+1) { float: right; }
+img:nth-of-type(2n) { float: left; }
+
+body > h2:nth-of-type(n+2):nth-last-of-type(n+2)
+body > h2:not(:first-of-type):not(:last-of-type)
+
+html|*:not(:link):not(:visited)
+*|*:not(:hover)
+p::first-line { text-transform: uppercase }
+
+p { color: red; font-size: 12pt }
+p::first-letter { color: green; font-size: 200% }
+p::first-line { color: blue }
+
+p { line-height: 1.1 }
+p::first-letter { font-size: 3em; font-weight: normal }
+span { font-weight: bold }
+
+*               /* a=0 b=0 c=0 -> specificity =   0 */
+LI              /* a=0 b=0 c=1 -> specificity =   1 */
+UL LI           /* a=0 b=0 c=2 -> specificity =   2 */
+UL OL+LI        /* a=0 b=0 c=3 -> specificity =   3 */
+H1 + *[REL=up]  /* a=0 b=1 c=1 -> specificity =  11 */
+UL OL LI.red    /* a=0 b=1 c=3 -> specificity =  13 */
+LI.red.level    /* a=0 b=2 c=1 -> specificity =  21 */
+#x34y           /* a=1 b=0 c=0 -> specificity = 100 */
+#s12:not(FOO)   /* a=1 b=0 c=1 -> specificity = 101 */
+
+@namespace foo url(http://www.example.com);
+foo|h1 { color: blue }  /* first rule */
+foo|* { color: yellow } /* second rule */
+|h1 { color: red }      /* ...*/
+*|h1 { color: green }
+h1 { color: green }
+
+span[hello="Ocean"][goodbye="Land"]
+
+a[rel~="copyright"] { ... }
+a[href="http://www.w3.org/"] { ... }
+
+DIALOGUE[character=romeo]
+DIALOGUE[character=juliet]
+
+[att^=val]
+[att$=val]
+[att*=val]
+
+@namespace foo "http://www.example.com";
+[foo|att=val] { color: blue }
+[*|att] { color: yellow }
+[|att] { color: green }
+[att] { color: green }
+
+
+*:target { color : red }
+*:target::before { content : url(target.png) }
+
+E[foo]{
+  padding:65px;
+}
+E[foo] ~ F{
+  padding:65px;
+}
+E#myid{
+  padding:65px;
+}
+input[type="search"]::-webkit-search-decoration,
+input[type="search"]::-webkit-search-cancel-button {
+  -webkit-appearance: none; // Inner-padding issues in Chrome OSX, Safari 5
+}
+button::-moz-focus-inner,
+input::-moz-focus-inner { // Inner padding and border oddities in FF3/4
+  padding: 0;
+  border: 0;
+}
+.btn {
+  // reset here as of 2.0.3 due to Recess property order
+  border-color: #ccc;
+  border-color: rgba(0,0,0,.1) rgba(0,0,0,.1) rgba(0,0,0,.25);
+}
+fieldset span button, fieldset span input[type="file"] {
+  font-size:12px;
+	font-family:Arial, Helvetica, sans-serif;
+}
+.el tr:nth-child(even):last-child td:first-child{
+	-moz-border-radius-bottomleft:3px;
+	-webkit-border-bottom-left-radius:3px;
+	border-bottom-left-radius:3px;
+}
+
+/* Some LESS code */
+
+button {
+    width:  32px;
+    height: 32px;
+    border: 0;
+    margin: 4px;
+    cursor: pointer;
+}
+button.icon-plus { background: url(http://dahlström.net/tmp/sharp-icons/svg-icon-target.svg#plus) no-repeat; }
+button.icon-chart { background: url(http://dahlström.net/tmp/sharp-icons/svg-icon-target.svg#chart) no-repeat; }
+
+button:hover { background-color: #999; }
+button:active { background-color: #666; }
+
+@test_a: #eeeQQQ;//this is not a valid hex value and thus parsed as an element id
+@test_b: #eeeFFF //this is a valid hex value but the declaration doesn't end with a semicolon and thus parsed as an element id
+
+#eee aaa .box
+{
+  #test bbb {
+    width: 500px;
+    height: 250px;
+    background-image: url(dir/output/sheep.png), url( betweengrassandsky.png );
+    background-position: center bottom, left top;
+    background-repeat: no-repeat;
+  }
+}
+
+@base: #f938ab;
+
+.box-shadow(@style, @c) when (iscolor(@c)) {
+  box-shadow:         @style @c;
+  -webkit-box-shadow: @style @c;
+  -moz-box-shadow:    @style @c;
+}
+.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
+  .box-shadow(@style, rgba(0, 0, 0, @alpha));
+}
+
+@color: #4D926F;
+
+#header {
+  color: @color;
+  color: #000000;
+}
+h2 {
+  color: @color;
+}
+
+.rounded-corners (@radius: 5px) {
+  border-radius: @radius;
+  -webkit-border-radius: @radius;
+  -moz-border-radius: @radius;
+}
+
+#header {
+  .rounded-corners;
+}
+#footer {
+  .rounded-corners(10px);
+}
+
+.box-shadow (@x: 0, @y: 0, @blur: 1px, @alpha) {
+  @val: @x @y @blur rgba(0, 0, 0, @alpha);
+
+  box-shadow:         @val;
+  -webkit-box-shadow: @val;
+  -moz-box-shadow:    @val;
+}
+.box { @base: #f938ab;
+  color:        saturate(@base, 5%);
+  border-color: lighten(@base, 30%);
+  div { .box-shadow(0, 0, 5px, 0.4) }
+}
+
+@import url("something.css");
+
+@light-blue:   hsl(190, 50%, 65%);
+@light-yellow: desaturate(#fefec8, 10%);
+@dark-yellow:  desaturate(darken(@light-yellow, 10%), 40%);
+@darkest:      hsl(20, 0%, 15%);
+@dark:         hsl(190, 20%, 30%);
+@medium:       hsl(10, 60%, 30%);
+@light:        hsl(90, 40%, 20%);
+@lightest:     hsl(90, 20%, 90%);
+@highlight:    hsl(80, 50%, 90%);
+@blue:         hsl(210, 60%, 20%);
+@alpha-blue:   hsla(210, 60%, 40%, 0.5);
+
+.box-shadow (@x, @y, @blur, @alpha) {
+  @value: @x @y @blur rgba(0, 0, 0, @alpha);
+  box-shadow:         @value;
+  -moz-box-shadow:    @value;
+  -webkit-box-shadow: @value;
+}
+.border-radius (@radius) {
+  border-radius: @radius;
+  -moz-border-radius: @radius;
+  -webkit-border-radius: @radius;
+}
+
+.border-radius (@radius, bottom) {
+  border-top-right-radius: 0;
+  border-top-left-radius: 0;
+  -moz-border-top-right-radius: 0;
+  -moz-border-top-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+  -webkit-border-top-right-radius: 0;
+}
+.border-radius (@radius, right) {
+  border-bottom-left-radius: 0;
+  border-top-left-radius: 0;
+  -moz-border-bottom-left-radius: 0;
+  -moz-border-top-left-radius: 0;
+  -webkit-border-bottom-left-radius: 0;
+  -webkit-border-top-left-radius: 0;
+}
+.box-shadow-inset (@x, @y, @blur, @color) {
+  box-shadow: @x @y @blur @color inset;
+  -moz-box-shadow: @x @y @blur @color inset;
+  -webkit-box-shadow: @x @y @blur @color inset;
+}
+.code () {
+  font-family: 'Bitstream Vera Sans Mono',
+               'DejaVu Sans Mono',
+               'Monaco',
+               Courier,
+               monospace !important;
+}
+.wrap () {
+  text-wrap: wrap;
+  white-space: pre-wrap;       /* css-3 */
+  white-space: -moz-pre-wrap;  /* Mozilla, since 1999 */
+  white-space: -pre-wrap;      /* Opera 4-6 */
+  white-space: -o-pre-wrap;    /* Opera 7 */
+  word-wrap: break-word;       /* Internet Explorer 5.5+ */
+}
+
+html { margin: 0 }
+body {
+  background-color: @darkest;
+  margin: 0 auto;
+  font-family: Arial, sans-serif;
+  font-size: 100%;
+  overflow-x: hidden;
+}
+nav, header, footer, section, article {
+  display: block;
+}
+a {
+  color: #b83000;
+}
+h1 a {
+  color: black;
+  text-decoration: none;
+}
+a:hover {
+  text-decoration: underline;
+}
+h1, h2, h3, h4 {
+  margin: 0;
+  font-weight: normal;
+}
+ul, li {
+  list-style-type: none;
+}
+code { .code; }
+code {
+  .string, .regexp { color: @dark }
+  .keyword { font-weight: bold }
+  .comment { color: rgba(0, 0, 0, 0.5) }
+  .number { color: @blue }
+  .class, .special { color: rgba(0, 50, 100, 0.8) }
+}
+pre {
+  padding: 0 30px;
+  .wrap;
+}
+blockquote {
+  font-style: italic;
+}
+body > footer {
+  text-align: left;
+  margin-left: 10px;
+  font-style: italic;
+  font-size: 18px;
+  color: #888;
+}
+
+#logo {
+  margin-top: 30px;
+  margin-bottom: 30px;
+  display: block;
+  width: 199px;
+  height: 81px;
+  background: url(/images/logo.png) no-repeat;
+}
+nav {
+  margin-left: 15px;
+}
+nav a, #dropdown li {
+  display: inline-block;
+  color: white;
+  line-height: 42px;
+  margin: 0;
+  padding: 0px 15px;
+  text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.5);
+  text-decoration: none;
+  border: 2px solid transparent;
+  border-width: 0 2px;
+  &:hover {
+    .dark-red; 
+    text-decoration: none;
+  }
+}
+.dark-red {
+    @red: @medium;
+    border: 2px solid darken(@red, 25%);
+    border-left-color: darken(@red, 15%);
+    border-right-color: darken(@red, 15%);
+    border-bottom: 0;
+    border-top: 0;
+    background-color: darken(@red, 10%);
+}
+
+.content {
+  margin: 0 auto;
+  width: 980px;
+}
+
+#menu {
+  position: absolute;
+  width: 100%;
+  z-index: 3;
+  clear: both;
+  display: block;
+  background-color: @blue;
+  height: 42px;
+  border-top: 2px solid lighten(@alpha-blue, 20%);
+  border-bottom: 2px solid darken(@alpha-blue, 25%);
+  .box-shadow(0, 1px, 8px, 0.6);
+  -moz-box-shadow: 0 0 0 #000; // Because firefox sucks.
+
+  &.docked {
+    background-color: hsla(210, 60%, 40%, 0.4);
+  }
+  &:hover {
+    background-color: @blue;
+  }
+
+  #dropdown {
+    margin: 0 0 0 117px;
+    padding: 0;
+    padding-top: 5px;
+    display: none;
+    width: 190px;
+    border-top: 2px solid @medium;
+    color: @highlight;
+    border: 2px solid darken(@medium, 25%);
+    border-left-color: darken(@medium, 15%);
+    border-right-color: darken(@medium, 15%);
+    border-top-width: 0;
+    background-color: darken(@medium, 10%);
+    ul {
+      padding: 0px;  
+    }
+    li {
+      font-size: 14px;
+      display: block;
+      text-align: left;
+      padding: 0;
+      border: 0;
+      a {
+        display: block;
+        padding: 0px 15px;  
+        text-decoration: none;
+        color: white;  
+        &:hover {
+          background-color: darken(@medium, 15%);
+          text-decoration: none;
+        }
+      }
+    }
+    .border-radius(5px, bottom);
+    .box-shadow(0, 6px, 8px, 0.5);
+  }
+}
+
+#main {
+  margin: 0 auto;
+  width: 100%;
+  background-color: @light-blue;
+  border-top: 8px solid darken(@light-blue, 5%);
+
+  #intro {
+    background-color: lighten(@light-blue, 25%);
+    float: left;
+    margin-top: -8px;
+    margin-right: 5px;
+
+    height: 380px;
+    position: relative;
+    z-index: 2;
+    font-family: 'Droid Serif', 'Georgia';
+    width: 395px;
+    padding: 45px 20px 23px 30px;
+    border: 2px dashed darken(@light-blue, 10%);
+    .box-shadow(1px, 0px, 6px, 0.5);
+    border-bottom: 0;
+    border-top: 0;
+    #download { color: transparent; border: 0; float: left; display: inline-block; margin: 15px 0 15px -5px; }
+    #download img { display: inline-block}
+    #download-info {
+      code {
+        font-size: 13px;  
+      }
+      color: @blue + #333; display: inline; float: left; margin: 36px 0 0 15px }
+  }
+  h2 {
+    span {
+      color: @medium;  
+    }
+    color: @blue;
+    margin: 20px 0;
+    font-size: 24px;
+    line-height: 1.2em;
+  }
+  h3 {
+    color: @blue;
+    line-height: 1.4em;
+    margin: 30px 0 15px 0;
+    font-size: 1em;
+    text-shadow: 0px 0px 0px @lightest;
+    span { color: @medium }
+  }
+  #example {
+    p {
+      font-size: 18px;
+      color: @blue;
+      font-weight: bold;
+      text-shadow: 0px 1px 1px @lightest;
+    }
+    pre {
+      margin: 0;
+      text-shadow: 0 -1px 1px @darkest;
+      margin-top: 20px;
+      background-color: desaturate(@darkest, 8%);
+      border: 0;
+      width: 450px;
+      color: lighten(@lightest, 2%);
+      background-repeat: repeat;
+      padding: 15px;
+      border: 1px dashed @lightest;
+      line-height: 15px;
+      .box-shadow(0, 0px, 15px, 0.5);
+      .code;
+      .border-radius(2px);
+      code .attribute { color: hsl(40, 50%, 70%) }
+      code .variable  { color: hsl(120, 10%, 50%) }
+      code .element   { color: hsl(170, 20%, 50%) }
+
+      code .string, .regexp { color: hsl(75, 50%, 65%) }
+      code .class { color: hsl(40, 40%, 60%); font-weight: normal }
+      code .id { color: hsl(50, 40%, 60%); font-weight: normal }
+      code .comment { color: rgba(255, 255, 255, 0.2) }
+      code .number, .color { color: hsl(10, 40%, 50%) }
+      code .class, code .mixin, .special { color: hsl(190, 20%, 50%) }
+      #time { color: #aaa }
+    }
+    float: right;
+    font-size: 12px;
+    margin: 0;
+    margin-top: 15px;
+    padding: 0;
+    width: 500px;
+  }
+}
+
+
+.page {
+  .content {
+    width: 870px;
+    padding: 45px;
+  }
+  margin: 0 auto;
+  font-family: 'Georgia', serif;
+  font-size: 18px;
+  line-height: 26px;
+  padding: 0 60px;
+  code {
+    font-size: 16px;  
+  }
+  pre {
+    border-width: 1px;
+    border-style: dashed;
+    padding: 15px;
+    margin: 15px 0;
+  }
+  h1 {
+    text-align: left;
+    font-size: 40px;
+    margin-top: 15px;
+    margin-bottom: 35px;
+  }
+  p + h1 { margin-top: 60px }
+  h2, h3 {
+    margin: 30px 0 15px 0;
+  }
+  p + h2, pre + h2, code + h2 {
+    border-top: 6px solid rgba(255, 255, 255, 0.1);
+    padding-top: 30px;
+  }
+  h3 {
+    margin: 15px 0;
+  }
+}
+
+
+#docs {
+  @bg: lighten(@light-blue, 5%);
+  border-top: 2px solid lighten(@bg, 5%);
+  color: @blue;
+  background-color: @light-blue;
+  .box-shadow(0, -2px, 5px, 0.2);
+
+  h1 {
+    font-family: 'Droid Serif', 'Georgia', serif;
+    padding-top: 30px;
+    padding-left: 45px;
+    font-size: 44px;
+    text-align: left;
+    margin: 30px 0 !important;
+    text-shadow: 0px 1px 1px @lightest;
+    font-weight: bold;
+  }
+  .content {
+    clear: both;
+    border-color: transparent;
+    background-color: lighten(@light-blue, 25%);
+    .box-shadow(0, 5px, 5px, 0.4);
+  }
+  pre {
+    @background: lighten(@bg, 30%);
+    color: lighten(@blue, 10%);
+    background-color: @background;
+    border-color: lighten(@light-blue, 25%);
+    border-width: 2px;
+    code .attribute { color: hsl(40, 50%, 30%) }
+    code .variable  { color: hsl(120, 10%, 30%) }
+    code .element   { color: hsl(170, 20%, 30%) }
+
+    code .string, .regexp { color: hsl(75, 50%, 35%) }
+    code .class { color: hsl(40, 40%, 30%); font-weight: normal }
+    code .id { color: hsl(50, 40%, 30%); font-weight: normal }
+    code .comment { color: rgba(0, 0, 0, 0.4) }
+    code .number, .color { color: hsl(10, 40%, 30%) }
+    code .class, code .mixin, .special { color: hsl(190, 20%, 30%) }
+  }
+  pre code                    { font-size: 15px  }
+  p + h2, pre + h2, code + h2 { border-top-color: rgba(0, 0, 0, 0.1) }
+}
+
+td {
+  padding-right: 30px;  
+}
+#synopsis {
+  .box-shadow(0, 5px, 5px, 0.2);
+}
+#synopsis, #about {
+  h2 {
+    font-size: 30px;  
+    padding: 10px 0;
+  }
+  h1 + h2 {
+      margin-top: 15px;  
+  }
+  h3 { font-size: 22px }
+
+  .code-example {
+    border-spacing: 0;
+    border-width: 1px;
+    border-style: dashed;
+    padding: 0;
+    pre { border: 0; margin: 0 }
+    td {
+      border: 0;
+      margin: 0;
+      background-color: desaturate(darken(@darkest, 5%), 20%);
+      vertical-align: top;
+      padding: 0;
+    }
+    tr { padding: 0 }
+  }
+  .css-output {
+    td {
+      border-left: 0;  
+    }
+  }
+  .less-example {
+    //border-right: 1px dotted rgba(255, 255, 255, 0.5) !important;
+  }
+  .css-output, .less-example {
+    width: 390px;
+  }
+  pre {
+    padding: 20px;
+    line-height: 20px;
+    font-size: 14px;
+  }
+}
+#about, #synopsis, #guide {
+  a {
+    text-decoration: none;
+    color: @light-yellow;
+    border-bottom: 1px dashed rgba(255, 255, 255, 0.2);
+    &:hover {
+      text-decoration: none;
+      border-bottom: 1px dashed @light-yellow;
+    }
+  }
+  @bg: desaturate(darken(@darkest, 5%), 20%);
+  text-shadow: 0 -1px 1px lighten(@bg, 5%);
+  color: @highlight;
+  background-color: @bg;
+  .content {
+    background-color: desaturate(@darkest, 20%);
+    clear: both;
+    .box-shadow(0, 5px, 5px, 0.4);
+  }
+  h1, h2, h3 {
+    color: @dark-yellow;
+  }
+  pre {
+      code .attribute { color: hsl(40, 50%, 70%) }
+      code .variable  { color: hsl(120, 10%, 50%) }
+      code .element   { color: hsl(170, 20%, 50%) }
+
+      code .string, .regexp { color: hsl(75, 50%, 65%) }
+      code .class { color: hsl(40, 40%, 60%); font-weight: normal }
+      code .id { color: hsl(50, 40%, 60%); font-weight: normal }
+      code .comment { color: rgba(255, 255, 255, 0.2) }
+      code .number, .color { color: hsl(10, 40%, 50%) }
+      code .class, code .mixin, .special { color: hsl(190, 20%, 50%) }
+    background-color: @bg;
+    border-color: darken(@light-yellow, 5%);
+  }
+  code {
+    color: darken(@dark-yellow, 5%);
+    .string, .regexp { color: desaturate(@light-blue, 15%) }
+    .keyword { color: hsl(40, 40%, 60%); font-weight: normal }
+    .comment { color: rgba(255, 255, 255, 0.2) }
+    .number { color: lighten(@blue, 10%) }
+    .class, .special { color: hsl(190, 20%, 50%) }
+  }
+}
+#guide {
+  background-color: @darkest;
+  .content {
+    background-color: transparent;
+  }
+
+}
+
+#about {
+  background-color: @darkest !important;
+  .content {
+    background-color: desaturate(lighten(@darkest, 3%), 5%);
+  }
+}
+#synopsis {
+  background-color: desaturate(lighten(@darkest, 3%), 5%) !important;
+  .content {
+    background-color: desaturate(lighten(@darkest, 3%), 5%);
+  }
+  pre {}
+}
+#synopsis, #guide {
+  .content {
+    .box-shadow(0, 0px, 0px, 0.0);
+  }
+}
+#about footer {
+  margin-top: 30px;
+  padding-top: 30px;
+  border-top: 6px solid rgba(0, 0, 0, 0.1);
+  text-align: center;
+  font-size: 16px;
+  color: rgba(255, 255, 255, 0.35);
+  #copy { font-size: 12px }
+  text-shadow: -1px -1px 1px rgba(0, 0, 0, 0.02);
+}
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        theme: "lesser-dark",
+        lineNumbers : true,
+        matchBrackets : true
+      });
+    </script>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-less</code>, <code>text/css</code> (if not previously defined).</p>
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/less/less.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/less/less.js b/src/fauxton/jam/codemirror/mode/less/less.js
new file mode 100644
index 0000000..70cd5c9
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/less/less.js
@@ -0,0 +1,266 @@
+/*
+  LESS mode - http://www.lesscss.org/
+  Ported to CodeMirror by Peter Kroon <plakroon@gmail.com>
+  Report bugs/issues here: https://github.com/marijnh/CodeMirror/issues  GitHub: @peterkroon
+*/
+
+CodeMirror.defineMode("less", function(config) {
+  var indentUnit = config.indentUnit, type;
+  function ret(style, tp) {type = tp; return style;}
+  //html tags
+  var tags = "a abbr acronym address applet area article aside audio b base basefont bdi bdo big blockquote body br button canvas caption cite code col colgroup command datalist dd del details dfn dir div dl dt em embed fieldset figcaption figure font footer form frame frameset h1 h2 h3 h4 h5 h6 head header hgroup hr html i iframe img input ins keygen kbd label legend li link map mark menu meta meter nav noframes noscript object ol optgroup option output p param pre progress q rp rt ruby s samp script section select small source span strike strong style sub summary sup table tbody td textarea tfoot th thead time title tr track tt u ul var video wbr".split(' ');
+  
+  function inTagsArray(val){
+    for(var i=0; i<tags.length; i++)if(val === tags[i])return true;
+  }
+   
+  var selectors = /(^\:root$|^\:nth\-child$|^\:nth\-last\-child$|^\:nth\-of\-type$|^\:nth\-last\-of\-type$|^\:first\-child$|^\:last\-child$|^\:first\-of\-type$|^\:last\-of\-type$|^\:only\-child$|^\:only\-of\-type$|^\:empty$|^\:link|^\:visited$|^\:active$|^\:hover$|^\:focus$|^\:target$|^\:lang$|^\:enabled^\:disabled$|^\:checked$|^\:first\-line$|^\:first\-letter$|^\:before$|^\:after$|^\:not$|^\:required$|^\:invalid$)/;
+  
+  function tokenBase(stream, state) {
+    var ch = stream.next();
+    
+    if (ch == "@") {stream.eatWhile(/[\w\-]/); return ret("meta", stream.current());}
+    else if (ch == "/" && stream.eat("*")) {
+      state.tokenize = tokenCComment;
+      return tokenCComment(stream, state);
+    }
+    else if (ch == "<" && stream.eat("!")) {
+      state.tokenize = tokenSGMLComment;
+      return tokenSGMLComment(stream, state);
+    }
+    else if (ch == "=") ret(null, "compare");
+    else if (ch == "|" && stream.eat("=")) return ret(null, "compare");
+    else if (ch == "\"" || ch == "'") {
+      state.tokenize = tokenString(ch);
+      return state.tokenize(stream, state);
+    }
+    else if (ch == "/") { // e.g.: .png will not be parsed as a class
+      if(stream.eat("/")){
+        state.tokenize = tokenSComment;
+        return tokenSComment(stream, state);
+      }else{
+        if(type == "string" || type == "(")return ret("string", "string");
+        if(state.stack[state.stack.length-1] != undefined)return ret(null, ch);
+        stream.eatWhile(/[\a-zA-Z0-9\-_.\s]/);		
+        if( /\/|\)|#/.test(stream.peek() || (stream.eatSpace() && stream.peek() == ")"))  || stream.eol() )return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
+      }
+    }
+    else if (ch == "!") {
+      stream.match(/^\s*\w*/);
+      return ret("keyword", "important");
+    }
+    else if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w.%]/);
+      return ret("number", "unit");
+    }
+    else if (/[,+<>*\/]/.test(ch)) {
+      if(stream.peek() == "=" || type == "a")return ret("string", "string");
+      return ret(null, "select-op");
+    }
+    else if (/[;{}:\[\]()~\|]/.test(ch)) {
+      if(ch == ":"){		
+        stream.eatWhile(/[a-z\\\-]/);
+        if( selectors.test(stream.current()) ){
+          return ret("tag", "tag");
+        }else if(stream.peek() == ":"){//::-webkit-search-decoration
+          stream.next();
+          stream.eatWhile(/[a-z\\\-]/);
+          if(stream.current().match(/\:\:\-(o|ms|moz|webkit)\-/))return ret("string", "string");
+          if( selectors.test(stream.current().substring(1)) )return ret("tag", "tag");
+          return ret(null, ch);
+        }else{
+          return ret(null, ch); 
+        }
+      }else if(ch == "~"){
+        if(type == "r")return ret("string", "string");
+      }else{
+        return ret(null, ch);
+      }
+    }
+    else if (ch == ".") {		
+      if(type == "(" || type == "string")return ret("string", "string"); // allow url(../image.png)
+      stream.eatWhile(/[\a-zA-Z0-9\-_]/);
+      if(stream.peek() == " ")stream.eatSpace();
+      if(stream.peek() == ")")return ret("number", "unit");//rgba(0,0,0,.25);
+      return ret("tag", "tag");
+    }
+    else if (ch == "#") {
+      //we don't eat white-space, we want the hex color and or id only
+      stream.eatWhile(/[A-Za-z0-9]/);
+      //check if there is a proper hex color length e.g. #eee || #eeeEEE
+      if(stream.current().length == 4 || stream.current().length == 7){
+        if(stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false) != null){//is there a valid hex color value present in the current stream
+          //when not a valid hex value, parse as id
+          if(stream.current().substring(1) != stream.current().match(/[A-Fa-f0-9]{6}|[A-Fa-f0-9]{3}/,false))return ret("atom", "tag");
+          //eat white-space
+          stream.eatSpace();
+          //when hex value declaration doesn't end with [;,] but is does with a slash/cc comment treat it as an id, just like the other hex values that don't end with[;,]
+          if( /[\/<>.(){!$%^&*_\-\\?=+\|#'~`]/.test(stream.peek()) )return ret("atom", "tag");
+          //#time { color: #aaa }
+          else if(stream.peek() == "}" )return ret("number", "unit");
+          //we have a valid hex color value, parse as id whenever an element/class is defined after the hex(id) value e.g. #eee aaa || #eee .aaa
+          else if( /[a-zA-Z\\]/.test(stream.peek()) )return ret("atom", "tag");
+          //when a hex value is on the end of a line, parse as id
+          else if(stream.eol())return ret("atom", "tag");
+          //default
+          else return ret("number", "unit");
+        }else{//when not a valid hexvalue in the current stream e.g. #footer
+          stream.eatWhile(/[\w\\\-]/);
+          return ret("atom", "tag"); 
+        }
+      }else{//when not a valid hexvalue length
+        stream.eatWhile(/[\w\\\-]/);
+        return ret("atom", "tag");
+      }
+    }
+    else if (ch == "&") {
+      stream.eatWhile(/[\w\-]/);
+      return ret(null, ch);
+    }
+    else {
+      stream.eatWhile(/[\w\\\-_%.{]/);
+      if(type == "string"){
+        return ret("string", "string");
+      }else if(stream.current().match(/(^http$|^https$)/) != null){
+        stream.eatWhile(/[\w\\\-_%.{:\/]/);
+        return ret("string", "string");
+      }else if(stream.peek() == "<" || stream.peek() == ">"){
+        return ret("tag", "tag");
+      }else if( /\(/.test(stream.peek()) ){																	  
+        return ret(null, ch);
+      }else if (stream.peek() == "/" && state.stack[state.stack.length-1] != undefined){ // url(dir/center/image.png)
+        return ret("string", "string");
+      }else if( stream.current().match(/\-\d|\-.\d/) ){ // match e.g.: -5px -0.4 etc... only colorize the minus sign
+        //commment out these 2 comment if you want the minus sign to be parsed as null -500px
+        //stream.backUp(stream.current().length-1);
+        //return ret(null, ch); //console.log( stream.current() );		
+        return ret("number", "unit");
+      }else if( inTagsArray(stream.current().toLowerCase()) ){ // match html tags
+        return ret("tag", "tag");
+      }else if( /\/|[\s\)]/.test(stream.peek() || stream.eol() || (stream.eatSpace() && stream.peek() == "/")) && stream.current().indexOf(".") !== -1){
+        if(stream.current().substring(stream.current().length-1,stream.current().length) == "{"){
+          stream.backUp(1);
+          return ret("tag", "tag");
+        }//end if
+        stream.eatSpace();
+        if( /[{<>.a-zA-Z\/]/.test(stream.peek())  || stream.eol() )return ret("tag", "tag"); // e.g. button.icon-plus
+        return ret("string", "string"); // let url(/images/logo.png) without quotes return as string
+      }else if( stream.eol() || stream.peek() == "[" || stream.peek() == "#" || type == "tag" ){
+        if(stream.current().substring(stream.current().length-1,stream.current().length) == "{")stream.backUp(1);
+        return ret("tag", "tag");
+      }else if(type == "compare" || type == "a" || type == "("){
+        return ret("string", "string");
+      }else if(type == "|" || stream.current() == "-" || type == "["){
+        return ret(null, ch);
+      }else if(stream.peek() == ":") {
+        stream.next();
+        var t_v = stream.peek() == ":" ? true : false;
+        if(!t_v){
+      	  var old_pos = stream.pos;
+      	  var sc = stream.current().length;
+      	  stream.eatWhile(/[a-z\\\-]/);
+      	  var new_pos = stream.pos;
+      	  if(stream.current().substring(sc-1).match(selectors) != null){
+      	    stream.backUp(new_pos-(old_pos-1));
+      		return ret("tag", "tag");
+      	  } else stream.backUp(new_pos-(old_pos-1));
+      	}else{
+      	  stream.backUp(1);	
+      	}
+      	if(t_v)return ret("tag", "tag"); else return ret("variable", "variable");
+      }else{		
+        return ret("variable", "variable");		
+      }
+    }    
+  }
+  
+  function tokenSComment(stream, state) { // SComment = Slash comment
+    stream.skipToEnd();
+    state.tokenize = tokenBase;
+    return ret("comment", "comment");
+  }
+  
+  function tokenCComment(stream, state) {
+    var maybeEnd = false, ch;
+    while ((ch = stream.next()) != null) {
+      if (maybeEnd && ch == "/") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      maybeEnd = (ch == "*");
+    }
+    return ret("comment", "comment");
+  }
+  
+  function tokenSGMLComment(stream, state) {
+    var dashes = 0, ch;
+    while ((ch = stream.next()) != null) {
+      if (dashes >= 2 && ch == ">") {
+        state.tokenize = tokenBase;
+        break;
+      }
+      dashes = (ch == "-") ? dashes + 1 : 0;
+    }
+    return ret("comment", "comment");
+  }
+  
+  function tokenString(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped)
+          break;
+        escaped = !escaped && ch == "\\";
+      }
+      if (!escaped) state.tokenize = tokenBase;
+      return ret("string", "string");
+    };
+  }
+  
+  return {
+    startState: function(base) { 
+      return {tokenize: tokenBase,
+              baseIndent: base || 0,
+              stack: []};
+    },
+    
+    token: function(stream, state) {
+      if (stream.eatSpace()) return null;
+      var style = state.tokenize(stream, state);
+      
+      var context = state.stack[state.stack.length-1];
+      if (type == "hash" && context == "rule") style = "atom";
+      else if (style == "variable") {
+        if (context == "rule") style = null; //"tag"
+        else if (!context || context == "@media{") {
+          style = stream.current() == "when"  ? "variable" :
+          /[\s,|\s\)|\s]/.test(stream.peek()) ? "tag"      : type;
+        }
+      }
+      
+      if (context == "rule" && /^[\{\};]$/.test(type))
+        state.stack.pop();
+      if (type == "{") {
+        if (context == "@media") state.stack[state.stack.length-1] = "@media{";
+        else state.stack.push("{");
+      }
+      else if (type == "}") state.stack.pop();
+      else if (type == "@media") state.stack.push("@media");
+      else if (context == "{" && type != "comment") state.stack.push("rule");
+      return style;
+    },
+    
+    indent: function(state, textAfter) {
+      var n = state.stack.length;
+      if (/^\}/.test(textAfter))
+        n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
+      return state.baseIndent + n * indentUnit;
+    },
+    
+    electricChars: "}"
+  };
+});
+
+CodeMirror.defineMIME("text/x-less", "less");
+if (!CodeMirror.mimeModes.hasOwnProperty("text/css"))
+  CodeMirror.defineMIME("text/css", "less");
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/lua/index.html
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/lua/index.html b/src/fauxton/jam/codemirror/mode/lua/index.html
new file mode 100644
index 0000000..6e984f4
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/lua/index.html
@@ -0,0 +1,73 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CodeMirror: Lua mode</title>
+    <link rel="stylesheet" href="../../lib/codemirror.css">
+    <script src="../../lib/codemirror.js"></script>
+    <script src="lua.js"></script>
+    <link rel="stylesheet" href="../../theme/neat.css">
+    <style>.CodeMirror {border: 1px solid black;}</style>
+    <link rel="stylesheet" href="../../doc/docs.css">
+  </head>
+  <body>
+    <h1>CodeMirror: Lua mode</h1>
+    <form><textarea id="code" name="code">
+--[[
+example useless code to show lua syntax highlighting
+this is multiline comment
+]]
+
+function blahblahblah(x)
+
+  local table = {
+    "asd" = 123,
+    "x" = 0.34,  
+  }
+  if x ~= 3 then
+    print( x )
+  elseif x == "string"
+    my_custom_function( 0x34 )
+  else
+    unknown_function( "some string" )
+  end
+
+  --single line comment
+  
+end
+
+function blablabla3()
+
+  for k,v in ipairs( table ) do
+    --abcde..
+    y=[=[
+  x=[[
+      x is a multi line string
+   ]]
+  but its definition is iside a highest level string!
+  ]=]
+    print(" \"\" ")
+
+    s = math.sin( x )
+  end
+
+end
+</textarea></form>
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        tabMode: "indent",
+        matchBrackets: true,
+        theme: "neat"
+      });
+    </script>
+
+    <p>Loosely based on Franciszek
+    Wawrzak's <a href="http://codemirror.net/1/contrib/lua">CodeMirror
+    1 mode</a>. One configuration parameter is
+    supported, <code>specials</code>, to which you can provide an
+    array of strings to have those identifiers highlighted with
+    the <code>lua-special</code> style.</p>
+    <p><strong>MIME types defined:</strong> <code>text/x-lua</code>.</p>
+
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/lua/lua.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/lua/lua.js b/src/fauxton/jam/codemirror/mode/lua/lua.js
new file mode 100644
index 0000000..97fb2c6
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/lua/lua.js
@@ -0,0 +1,140 @@
+// LUA mode. Ported to CodeMirror 2 from Franciszek Wawrzak's
+// CodeMirror 1 mode.
+// highlights keywords, strings, comments (no leveling supported! ("[==[")), tokens, basic indenting
+ 
+CodeMirror.defineMode("lua", function(config, parserConfig) {
+  var indentUnit = config.indentUnit;
+
+  function prefixRE(words) {
+    return new RegExp("^(?:" + words.join("|") + ")", "i");
+  }
+  function wordRE(words) {
+    return new RegExp("^(?:" + words.join("|") + ")$", "i");
+  }
+  var specials = wordRE(parserConfig.specials || []);
+ 
+  // long list of standard functions from lua manual
+  var builtins = wordRE([
+    "_G","_VERSION","assert","collectgarbage","dofile","error","getfenv","getmetatable","ipairs","load",
+    "loadfile","loadstring","module","next","pairs","pcall","print","rawequal","rawget","rawset","require",
+    "select","setfenv","setmetatable","tonumber","tostring","type","unpack","xpcall",
+
+    "coroutine.create","coroutine.resume","coroutine.running","coroutine.status","coroutine.wrap","coroutine.yield",
+
+    "debug.debug","debug.getfenv","debug.gethook","debug.getinfo","debug.getlocal","debug.getmetatable",
+    "debug.getregistry","debug.getupvalue","debug.setfenv","debug.sethook","debug.setlocal","debug.setmetatable",
+    "debug.setupvalue","debug.traceback",
+
+    "close","flush","lines","read","seek","setvbuf","write",
+
+    "io.close","io.flush","io.input","io.lines","io.open","io.output","io.popen","io.read","io.stderr","io.stdin",
+    "io.stdout","io.tmpfile","io.type","io.write",
+
+    "math.abs","math.acos","math.asin","math.atan","math.atan2","math.ceil","math.cos","math.cosh","math.deg",
+    "math.exp","math.floor","math.fmod","math.frexp","math.huge","math.ldexp","math.log","math.log10","math.max",
+    "math.min","math.modf","math.pi","math.pow","math.rad","math.random","math.randomseed","math.sin","math.sinh",
+    "math.sqrt","math.tan","math.tanh",
+
+    "os.clock","os.date","os.difftime","os.execute","os.exit","os.getenv","os.remove","os.rename","os.setlocale",
+    "os.time","os.tmpname",
+
+    "package.cpath","package.loaded","package.loaders","package.loadlib","package.path","package.preload",
+    "package.seeall",
+
+    "string.byte","string.char","string.dump","string.find","string.format","string.gmatch","string.gsub",
+    "string.len","string.lower","string.match","string.rep","string.reverse","string.sub","string.upper",
+
+    "table.concat","table.insert","table.maxn","table.remove","table.sort"
+  ]);
+  var keywords = wordRE(["and","break","elseif","false","nil","not","or","return",
+			 "true","function", "end", "if", "then", "else", "do", 
+			 "while", "repeat", "until", "for", "in", "local" ]);
+
+  var indentTokens = wordRE(["function", "if","repeat","do", "\\(", "{"]);
+  var dedentTokens = wordRE(["end", "until", "\\)", "}"]);
+  var dedentPartial = prefixRE(["end", "until", "\\)", "}", "else", "elseif"]);
+
+  function readBracket(stream) {
+    var level = 0;
+    while (stream.eat("=")) ++level;
+    stream.eat("[");
+    return level;
+  }
+
+  function normal(stream, state) {
+    var ch = stream.next();
+    if (ch == "-" && stream.eat("-")) {
+      if (stream.eat("[") && stream.eat("["))
+        return (state.cur = bracketed(readBracket(stream), "comment"))(stream, state);
+      stream.skipToEnd();
+      return "comment";
+    } 
+    if (ch == "\"" || ch == "'")
+      return (state.cur = string(ch))(stream, state);
+    if (ch == "[" && /[\[=]/.test(stream.peek()))
+      return (state.cur = bracketed(readBracket(stream), "string"))(stream, state);
+    if (/\d/.test(ch)) {
+      stream.eatWhile(/[\w.%]/);
+      return "number";
+    }
+    if (/[\w_]/.test(ch)) {
+      stream.eatWhile(/[\w\\\-_.]/);
+      return "variable";
+    }
+    return null;
+  }
+
+  function bracketed(level, style) {
+    return function(stream, state) {
+      var curlev = null, ch;
+      while ((ch = stream.next()) != null) {
+        if (curlev == null) {if (ch == "]") curlev = 0;}
+        else if (ch == "=") ++curlev;
+        else if (ch == "]" && curlev == level) { state.cur = normal; break; }
+        else curlev = null;
+      }
+      return style;
+    };
+  }
+
+  function string(quote) {
+    return function(stream, state) {
+      var escaped = false, ch;
+      while ((ch = stream.next()) != null) {
+        if (ch == quote && !escaped) break;
+        escaped = !escaped && ch == "\\";
+      }
+      if (!escaped) state.cur = normal;
+      return "string";
+    };
+  }
+    
+  return {
+    startState: function(basecol) {
+      return {basecol: basecol || 0, indentDepth: 0, cur: normal};
+    },
+
+    token: function(stream, state) {
+      if (stream.eatSpace()) return null;
+      var style = state.cur(stream, state);
+      var word = stream.current();
+      if (style == "variable") {
+        if (keywords.test(word)) style = "keyword";
+        else if (builtins.test(word)) style = "builtin";
+	else if (specials.test(word)) style = "variable-2";
+      }
+      if ((style != "comment") && (style != "string")){
+        if (indentTokens.test(word)) ++state.indentDepth;
+        else if (dedentTokens.test(word)) --state.indentDepth;
+      }
+      return style;
+    },
+
+    indent: function(state, textAfter) {
+      var closing = dedentPartial.test(textAfter);
+      return state.basecol + indentUnit * (state.indentDepth - (closing ? 1 : 0));
+    }
+  };
+});
+
+CodeMirror.defineMIME("text/x-lua", "lua");

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/markdown/index.html
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/markdown/index.html b/src/fauxton/jam/codemirror/mode/markdown/index.html
new file mode 100644
index 0000000..92d5f1f
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/markdown/index.html
@@ -0,0 +1,343 @@
+<!doctype html>
+<html>
+  <head>
+    <meta charset="utf-8">
+    <title>CodeMirror: Markdown mode</title>
+    <link rel="stylesheet" href="../../lib/codemirror.css">
+    <script src="../../lib/codemirror.js"></script>
+    <script src="../xml/xml.js"></script>
+    <script src="markdown.js"></script>
+    <style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
+    <link rel="stylesheet" href="../../doc/docs.css">
+  </head>
+  <body>
+    <h1>CodeMirror: Markdown mode</h1>
+
+<!-- source: http://daringfireball.net/projects/markdown/basics.text -->
+<form><textarea id="code" name="code">
+Markdown: Basics
+================
+
+&lt;ul id="ProjectSubmenu"&gt;
+    &lt;li&gt;&lt;a href="/projects/markdown/" title="Markdown Project Page"&gt;Main&lt;/a&gt;&lt;/li&gt;
+    &lt;li&gt;&lt;a class="selected" title="Markdown Basics"&gt;Basics&lt;/a&gt;&lt;/li&gt;
+    &lt;li&gt;&lt;a href="/projects/markdown/syntax" title="Markdown Syntax Documentation"&gt;Syntax&lt;/a&gt;&lt;/li&gt;
+    &lt;li&gt;&lt;a href="/projects/markdown/license" title="Pricing and License Information"&gt;License&lt;/a&gt;&lt;/li&gt;
+    &lt;li&gt;&lt;a href="/projects/markdown/dingus" title="Online Markdown Web Form"&gt;Dingus&lt;/a&gt;&lt;/li&gt;
+&lt;/ul&gt;
+
+
+Getting the Gist of Markdown's Formatting Syntax
+------------------------------------------------
+
+This page offers a brief overview of what it's like to use Markdown.
+The [syntax page] [s] provides complete, detailed documentation for
+every feature, but Markdown should be very easy to pick up simply by
+looking at a few examples of it in action. The examples on this page
+are written in a before/after style, showing example syntax and the
+HTML output produced by Markdown.
+
+It's also helpful to simply try Markdown out; the [Dingus] [d] is a
+web application that allows you type your own Markdown-formatted text
+and translate it to XHTML.
+
+**Note:** This document is itself written using Markdown; you
+can [see the source for it by adding '.text' to the URL] [src].
+
+  [s]: /projects/markdown/syntax  "Markdown Syntax"
+  [d]: /projects/markdown/dingus  "Markdown Dingus"
+  [src]: /projects/markdown/basics.text
+
+
+## Paragraphs, Headers, Blockquotes ##
+
+A paragraph is simply one or more consecutive lines of text, separated
+by one or more blank lines. (A blank line is any line that looks like
+a blank line -- a line containing nothing but spaces or tabs is
+considered blank.) Normal paragraphs should not be indented with
+spaces or tabs.
+
+Markdown offers two styles of headers: *Setext* and *atx*.
+Setext-style headers for `&lt;h1&gt;` and `&lt;h2&gt;` are created by
+"underlining" with equal signs (`=`) and hyphens (`-`), respectively.
+To create an atx-style header, you put 1-6 hash marks (`#`) at the
+beginning of the line -- the number of hashes equals the resulting
+HTML header level.
+
+Blockquotes are indicated using email-style '`&gt;`' angle brackets.
+
+Markdown:
+
+    A First Level Header
+    ====================
+    
+    A Second Level Header
+    ---------------------
+
+    Now is the time for all good men to come to
+    the aid of their country. This is just a
+    regular paragraph.
+
+    The quick brown fox jumped over the lazy
+    dog's back.
+    
+    ### Header 3
+
+    &gt; This is a blockquote.
+    &gt; 
+    &gt; This is the second paragraph in the blockquote.
+    &gt;
+    &gt; ## This is an H2 in a blockquote
+
+
+Output:
+
+    &lt;h1&gt;A First Level Header&lt;/h1&gt;
+    
+    &lt;h2&gt;A Second Level Header&lt;/h2&gt;
+    
+    &lt;p&gt;Now is the time for all good men to come to
+    the aid of their country. This is just a
+    regular paragraph.&lt;/p&gt;
+    
+    &lt;p&gt;The quick brown fox jumped over the lazy
+    dog's back.&lt;/p&gt;
+    
+    &lt;h3&gt;Header 3&lt;/h3&gt;
+    
+    &lt;blockquote&gt;
+        &lt;p&gt;This is a blockquote.&lt;/p&gt;
+        
+        &lt;p&gt;This is the second paragraph in the blockquote.&lt;/p&gt;
+        
+        &lt;h2&gt;This is an H2 in a blockquote&lt;/h2&gt;
+    &lt;/blockquote&gt;
+
+
+
+### Phrase Emphasis ###
+
+Markdown uses asterisks and underscores to indicate spans of emphasis.
+
+Markdown:
+
+    Some of these words *are emphasized*.
+    Some of these words _are emphasized also_.
+    
+    Use two asterisks for **strong emphasis**.
+    Or, if you prefer, __use two underscores instead__.
+
+Output:
+
+    &lt;p&gt;Some of these words &lt;em&gt;are emphasized&lt;/em&gt;.
+    Some of these words &lt;em&gt;are emphasized also&lt;/em&gt;.&lt;/p&gt;
+    
+    &lt;p&gt;Use two asterisks for &lt;strong&gt;strong emphasis&lt;/strong&gt;.
+    Or, if you prefer, &lt;strong&gt;use two underscores instead&lt;/strong&gt;.&lt;/p&gt;
+   
+
+
+## Lists ##
+
+Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
+`+`, and `-`) as list markers. These three markers are
+interchangable; this:
+
+    *   Candy.
+    *   Gum.
+    *   Booze.
+
+this:
+
+    +   Candy.
+    +   Gum.
+    +   Booze.
+
+and this:
+
+    -   Candy.
+    -   Gum.
+    -   Booze.
+
+all produce the same output:
+
+    &lt;ul&gt;
+    &lt;li&gt;Candy.&lt;/li&gt;
+    &lt;li&gt;Gum.&lt;/li&gt;
+    &lt;li&gt;Booze.&lt;/li&gt;
+    &lt;/ul&gt;
+
+Ordered (numbered) lists use regular numbers, followed by periods, as
+list markers:
+
+    1.  Red
+    2.  Green
+    3.  Blue
+
+Output:
+
+    &lt;ol&gt;
+    &lt;li&gt;Red&lt;/li&gt;
+    &lt;li&gt;Green&lt;/li&gt;
+    &lt;li&gt;Blue&lt;/li&gt;
+    &lt;/ol&gt;
+
+If you put blank lines between items, you'll get `&lt;p&gt;` tags for the
+list item text. You can create multi-paragraph list items by indenting
+the paragraphs by 4 spaces or 1 tab:
+
+    *   A list item.
+    
+        With multiple paragraphs.
+
+    *   Another item in the list.
+
+Output:
+
+    &lt;ul&gt;
+    &lt;li&gt;&lt;p&gt;A list item.&lt;/p&gt;
+    &lt;p&gt;With multiple paragraphs.&lt;/p&gt;&lt;/li&gt;
+    &lt;li&gt;&lt;p&gt;Another item in the list.&lt;/p&gt;&lt;/li&gt;
+    &lt;/ul&gt;
+    
+
+
+### Links ###
+
+Markdown supports two styles for creating links: *inline* and
+*reference*. With both styles, you use square brackets to delimit the
+text you want to turn into a link.
+
+Inline-style links use parentheses immediately after the link text.
+For example:
+
+    This is an [example link](http://example.com/).
+
+Output:
+
+    &lt;p&gt;This is an &lt;a href="http://example.com/"&gt;
+    example link&lt;/a&gt;.&lt;/p&gt;
+
+Optionally, you may include a title attribute in the parentheses:
+
+    This is an [example link](http://example.com/ "With a Title").
+
+Output:
+
+    &lt;p&gt;This is an &lt;a href="http://example.com/" title="With a Title"&gt;
+    example link&lt;/a&gt;.&lt;/p&gt;
+
+Reference-style links allow you to refer to your links by names, which
+you define elsewhere in your document:
+
+    I get 10 times more traffic from [Google][1] than from
+    [Yahoo][2] or [MSN][3].
+
+    [1]: http://google.com/        "Google"
+    [2]: http://search.yahoo.com/  "Yahoo Search"
+    [3]: http://search.msn.com/    "MSN Search"
+
+Output:
+
+    &lt;p&gt;I get 10 times more traffic from &lt;a href="http://google.com/"
+    title="Google"&gt;Google&lt;/a&gt; than from &lt;a href="http://search.yahoo.com/"
+    title="Yahoo Search"&gt;Yahoo&lt;/a&gt; or &lt;a href="http://search.msn.com/"
+    title="MSN Search"&gt;MSN&lt;/a&gt;.&lt;/p&gt;
+
+The title attribute is optional. Link names may contain letters,
+numbers and spaces, but are *not* case sensitive:
+
+    I start my morning with a cup of coffee and
+    [The New York Times][NY Times].
+
+    [ny times]: http://www.nytimes.com/
+
+Output:
+
+    &lt;p&gt;I start my morning with a cup of coffee and
+    &lt;a href="http://www.nytimes.com/"&gt;The New York Times&lt;/a&gt;.&lt;/p&gt;
+
+
+### Images ###
+
+Image syntax is very much like link syntax.
+
+Inline (titles are optional):
+
+    ![alt text](/path/to/img.jpg "Title")
+
+Reference-style:
+
+    ![alt text][id]
+
+    [id]: /path/to/img.jpg "Title"
+
+Both of the above examples produce the same output:
+
+    &lt;img src="/path/to/img.jpg" alt="alt text" title="Title" /&gt;
+
+
+
+### Code ###
+
+In a regular paragraph, you can create code span by wrapping text in
+backtick quotes. Any ampersands (`&amp;`) and angle brackets (`&lt;` or
+`&gt;`) will automatically be translated into HTML entities. This makes
+it easy to use Markdown to write about HTML example code:
+
+    I strongly recommend against using any `&lt;blink&gt;` tags.
+
+    I wish SmartyPants used named entities like `&amp;mdash;`
+    instead of decimal-encoded entites like `&amp;#8212;`.
+
+Output:
+
+    &lt;p&gt;I strongly recommend against using any
+    &lt;code&gt;&amp;lt;blink&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
+    
+    &lt;p&gt;I wish SmartyPants used named entities like
+    &lt;code&gt;&amp;amp;mdash;&lt;/code&gt; instead of decimal-encoded
+    entites like &lt;code&gt;&amp;amp;#8212;&lt;/code&gt;.&lt;/p&gt;
+
+
+To specify an entire block of pre-formatted code, indent every line of
+the block by 4 spaces or 1 tab. Just like with code spans, `&amp;`, `&lt;`,
+and `&gt;` characters will be escaped automatically.
+
+Markdown:
+
+    If you want your page to validate under XHTML 1.0 Strict,
+    you've got to put paragraph tags in your blockquotes:
+
+        &lt;blockquote&gt;
+            &lt;p&gt;For example.&lt;/p&gt;
+        &lt;/blockquote&gt;
+
+Output:
+
+    &lt;p&gt;If you want your page to validate under XHTML 1.0 Strict,
+    you've got to put paragraph tags in your blockquotes:&lt;/p&gt;
+    
+    &lt;pre&gt;&lt;code&gt;&amp;lt;blockquote&amp;gt;
+        &amp;lt;p&amp;gt;For example.&amp;lt;/p&amp;gt;
+    &amp;lt;/blockquote&amp;gt;
+    &lt;/code&gt;&lt;/pre&gt;
+</textarea></form>
+
+    <script>
+      var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
+        mode: 'markdown',
+        lineNumbers: true,
+        matchBrackets: true,
+        theme: "default"
+      });
+    </script>
+
+    <p>Optionally depends on the XML mode for properly highlighted inline XML blocks.</p>
+
+    <p><strong>MIME types defined:</strong> <code>text/x-markdown</code>.</p>
+
+    <p><strong>Parsing/Highlighting Tests:</strong> <a href="../../test/index.html#markdown_*">normal</a>,  <a href="../../test/index.html#verbose,markdown_*">verbose</a>.</p>
+
+  </body>
+</html>

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/markdown/markdown.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/markdown/markdown.js b/src/fauxton/jam/codemirror/mode/markdown/markdown.js
new file mode 100644
index 0000000..d227fc9
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/markdown/markdown.js
@@ -0,0 +1,481 @@
+CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
+
+  var htmlFound = CodeMirror.mimeModes.hasOwnProperty("text/html");
+  var htmlMode = CodeMirror.getMode(cmCfg, htmlFound ? "text/html" : "text/plain");
+  var aliases = {
+    html: "htmlmixed",
+    js: "javascript",
+    json: "application/json",
+    c: "text/x-csrc",
+    "c++": "text/x-c++src",
+    java: "text/x-java",
+    csharp: "text/x-csharp",
+    "c#": "text/x-csharp"
+  };
+
+  var getMode = (function () {
+    var i, modes = {}, mimes = {}, mime;
+
+    var list = CodeMirror.listModes();
+    for (i = 0; i < list.length; i++) {
+      modes[list[i]] = list[i];
+    }
+    var mimesList = CodeMirror.listMIMEs();
+    for (i = 0; i < mimesList.length; i++) {
+      mime = mimesList[i].mime;
+      mimes[mime] = mimesList[i].mime;
+    }
+
+    for (var a in aliases) {
+      if (aliases[a] in modes || aliases[a] in mimes)
+        modes[a] = aliases[a];
+    }
+    
+    return function (lang) {
+      return modes[lang] ? CodeMirror.getMode(cmCfg, modes[lang]) : null;
+    };
+  }());
+  
+  // Should underscores in words open/close em/strong?
+  if (modeCfg.underscoresBreakWords === undefined)
+    modeCfg.underscoresBreakWords = true;
+  
+  // Turn on fenced code blocks? ("```" to start/end)
+  if (modeCfg.fencedCodeBlocks === undefined) modeCfg.fencedCodeBlocks = false;
+  
+  var codeDepth = 0;
+  var prevLineHasContent = false
+  ,   thisLineHasContent = false;
+
+  var header   = 'header'
+  ,   code     = 'comment'
+  ,   quote    = 'quote'
+  ,   list     = 'string'
+  ,   hr       = 'hr'
+  ,   image    = 'tag'
+  ,   linkinline = 'link'
+  ,   linkemail = 'link'
+  ,   linktext = 'link'
+  ,   linkhref = 'string'
+  ,   em       = 'em'
+  ,   strong   = 'strong'
+  ,   emstrong = 'emstrong';
+
+  var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
+  ,   ulRE = /^[*\-+]\s+/
+  ,   olRE = /^[0-9]+\.\s+/
+  ,   headerRE = /^(?:\={1,}|-{1,})$/
+  ,   textRE = /^[^!\[\]*_\\<>` "'(]+/;
+
+  function switchInline(stream, state, f) {
+    state.f = state.inline = f;
+    return f(stream, state);
+  }
+
+  function switchBlock(stream, state, f) {
+    state.f = state.block = f;
+    return f(stream, state);
+  }
+
+
+  // Blocks
+
+  function blankLine(state) {
+    // Reset linkTitle state
+    state.linkTitle = false;
+    // Reset EM state
+    state.em = false;
+    // Reset STRONG state
+    state.strong = false;
+    // Reset state.quote
+    state.quote = false;
+    if (!htmlFound && state.f == htmlBlock) {
+      state.f = inlineNormal;
+      state.block = blockNormal;
+    }
+    return null;
+  }
+
+  function blockNormal(stream, state) {
+    
+    if (state.list !== false && state.indentationDiff >= 0) { // Continued list
+      if (state.indentationDiff < 4) { // Only adjust indentation if *not* a code block
+        state.indentation -= state.indentationDiff;
+      }
+      state.list = null;
+    } else { // No longer a list
+      state.list = false;
+    }
+    
+    if (state.indentationDiff >= 4) {
+      state.indentation -= 4;
+      stream.skipToEnd();
+      return code;
+    } else if (stream.eatSpace()) {
+      return null;
+    } else if (stream.peek() === '#' || (prevLineHasContent && stream.match(headerRE)) ) {
+      state.header = true;
+    } else if (stream.eat('>')) {
+      state.indentation++;
+      state.quote = true;
+    } else if (stream.peek() === '[') {
+      return switchInline(stream, state, footnoteLink);
+    } else if (stream.match(hrRE, true)) {
+      return hr;
+    } else if (stream.match(ulRE, true) || stream.match(olRE, true)) {
+      state.indentation += 4;
+      state.list = true;
+    } else if (modeCfg.fencedCodeBlocks && stream.match(/^```([\w+#]*)/, true)) {
+      // try switching mode
+      state.localMode = getMode(RegExp.$1);
+      if (state.localMode) state.localState = state.localMode.startState();
+      switchBlock(stream, state, local);
+      return code;
+    }
+    
+    return switchInline(stream, state, state.inline);
+  }
+
+  function htmlBlock(stream, state) {
+    var style = htmlMode.token(stream, state.htmlState);
+    if (htmlFound && style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
+      state.f = inlineNormal;
+      state.block = blockNormal;
+    }
+    if (state.md_inside && stream.current().indexOf(">")!=-1) {
+      state.f = inlineNormal;
+      state.block = blockNormal;
+      state.htmlState.context = undefined;
+    }
+    return style;
+  }
+
+  function local(stream, state) {
+    if (stream.sol() && stream.match(/^```/, true)) {
+      state.localMode = state.localState = null;
+      state.f = inlineNormal;
+      state.block = blockNormal;
+      return code;
+    } else if (state.localMode) {
+      return state.localMode.token(stream, state.localState);
+    } else {
+      stream.skipToEnd();
+      return code;
+    }
+  }
+
+  function codeBlock(stream, state) {
+    if(stream.match(codeBlockRE, true)){
+      state.f = inlineNormal;
+      state.block = blockNormal;
+      switchInline(stream, state, state.inline);
+      return code;
+    }
+    stream.skipToEnd();
+    return code;
+  }
+
+  // Inline
+  function getType(state) {
+    var styles = [];
+    
+    if (state.strong) { styles.push(state.em ? emstrong : strong); }
+    else if (state.em) { styles.push(em); }
+    
+    if (state.linkText) { styles.push(linktext); }
+    
+    if (state.code) { styles.push(code); }
+    
+    if (state.header) { styles.push(header); }
+    if (state.quote) { styles.push(quote); }
+    if (state.list !== false) { styles.push(list); }
+
+    return styles.length ? styles.join(' ') : null;
+  }
+
+  function handleText(stream, state) {
+    if (stream.match(textRE, true)) {
+      return getType(state);
+    }
+    return undefined;        
+  }
+
+  function inlineNormal(stream, state) {
+    var style = state.text(stream, state);
+    if (typeof style !== 'undefined')
+      return style;
+    
+    if (state.list) { // List marker (*, +, -, 1., etc)
+      state.list = null;
+      return list;
+    }
+    
+    var ch = stream.next();
+    
+    if (ch === '\\') {
+      stream.next();
+      return getType(state);
+    }
+    
+    // Matches link titles present on next line
+    if (state.linkTitle) {
+      state.linkTitle = false;
+      var matchCh = ch;
+      if (ch === '(') {
+        matchCh = ')';
+      }
+      matchCh = (matchCh+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+      var regex = '^\\s*(?:[^' + matchCh + '\\\\]+|\\\\\\\\|\\\\.)' + matchCh;
+      if (stream.match(new RegExp(regex), true)) {
+        return linkhref;
+      }
+    }
+    
+    // If this block is changed, it may need to be updated in GFM mode
+    if (ch === '`') {
+      var t = getType(state);
+      var before = stream.pos;
+      stream.eatWhile('`');
+      var difference = 1 + stream.pos - before;
+      if (!state.code) {
+        codeDepth = difference;
+        state.code = true;
+        return getType(state);
+      } else {
+        if (difference === codeDepth) { // Must be exact
+          state.code = false;
+          return t;
+        }
+        return getType(state);
+      }
+    } else if (state.code) {
+      return getType(state);
+    }
+    
+    if (ch === '!' && stream.match(/\[.*\] ?(?:\(|\[)/, false)) {
+      stream.match(/\[.*\]/);
+      state.inline = state.f = linkHref;
+      return image;
+    }
+
+    if (ch === '[' && stream.match(/.*\](\(| ?\[)/, false)) {
+      state.linkText = true;
+      return getType(state);
+    }
+
+    if (ch === ']' && state.linkText) {
+      var type = getType(state);
+      state.linkText = false;
+      state.inline = state.f = linkHref;
+      return type;
+    }
+    
+    if (ch === '<' && stream.match(/^(https?|ftps?):\/\/(?:[^\\>]|\\.)+>/, true)) {
+      return switchInline(stream, state, inlineElement(linkinline, '>'));
+    }
+    
+    if (ch === '<' && stream.match(/^[^> \\]+@(?:[^\\>]|\\.)+>/, true)) {
+      return switchInline(stream, state, inlineElement(linkemail, '>'));
+    }
+    
+    if (ch === '<' && stream.match(/^\w/, false)) {
+      var md_inside = false;
+      if (stream.string.indexOf(">")!=-1) {
+        var atts = stream.string.substring(1,stream.string.indexOf(">"));
+        if (/markdown\s*=\s*('|"){0,1}1('|"){0,1}/.test(atts)) {
+          state.md_inside = true;
+        }
+      }
+      stream.backUp(1);
+      return switchBlock(stream, state, htmlBlock);
+    }
+      
+    if (ch === '<' && stream.match(/^\/\w*?>/)) {
+      state.md_inside = false;
+      return "tag";
+    }
+      
+    var ignoreUnderscore = false;
+    if (!modeCfg.underscoresBreakWords) {
+      if (ch === '_' && stream.peek() !== '_' && stream.match(/(\w)/, false)) {
+        var prevPos = stream.pos - 2;
+        if (prevPos >= 0) {
+          var prevCh = stream.string.charAt(prevPos);
+          if (prevCh !== '_' && prevCh.match(/(\w)/, false)) {
+            ignoreUnderscore = true;
+          }
+        }
+      }
+    }
+    var t = getType(state);
+    if (ch === '*' || (ch === '_' && !ignoreUnderscore)) {
+      if (state.strong === ch && stream.eat(ch)) { // Remove STRONG
+        state.strong = false;
+        return t;
+      } else if (!state.strong && stream.eat(ch)) { // Add STRONG
+        state.strong = ch;
+        return getType(state);
+      } else if (state.em === ch) { // Remove EM
+        state.em = false;
+        return t;
+      } else if (!state.em) { // Add EM
+        state.em = ch;
+        return getType(state);
+      }
+    } else if (ch === ' ') {
+      if (stream.eat('*') || stream.eat('_')) { // Probably surrounded by spaces
+        if (stream.peek() === ' ') { // Surrounded by spaces, ignore
+          return getType(state);
+        } else { // Not surrounded by spaces, back up pointer
+          stream.backUp(1);
+        }
+      }
+    }
+    
+    return getType(state);
+  }
+
+  function linkHref(stream, state) {
+    // Check if space, and return NULL if so (to avoid marking the space)
+    if(stream.eatSpace()){
+      return null;
+    }
+    var ch = stream.next();
+    if (ch === '(' || ch === '[') {
+      return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
+    }
+    return 'error';
+  }
+
+  function footnoteLink(stream, state) {
+    if (stream.match(/^[^\]]*\]:/, true)) {
+      state.f = footnoteUrl;
+      return linktext;
+    }
+    return switchInline(stream, state, inlineNormal);
+  }
+
+  function footnoteUrl(stream, state) {
+    // Check if space, and return NULL if so (to avoid marking the space)
+    if(stream.eatSpace()){
+      return null;
+    }
+    // Match URL
+    stream.match(/^[^\s]+/, true);
+    // Check for link title
+    if (stream.peek() === undefined) { // End of line, set flag to check next line
+      state.linkTitle = true;
+    } else { // More content on line, check if link title
+      stream.match(/^(?:\s+(?:"(?:[^"\\]|\\\\|\\.)+"|'(?:[^'\\]|\\\\|\\.)+'|\((?:[^)\\]|\\\\|\\.)+\)))?/, true);
+    }
+    state.f = state.inline = inlineNormal;
+    return linkhref;
+  }
+
+  var savedInlineRE = [];
+  function inlineRE(endChar) {
+    if (!savedInlineRE[endChar]) {
+      // Escape endChar for RegExp (taken from http://stackoverflow.com/a/494122/526741)
+      endChar = (endChar+'').replace(/([.?*+^$[\]\\(){}|-])/g, "\\$1");
+      // Match any non-endChar, escaped character, as well as the closing 
+      // endChar.
+      savedInlineRE[endChar] = new RegExp('^(?:[^\\\\]|\\\\.)*?(' + endChar + ')');
+    }
+    return savedInlineRE[endChar];
+  }
+
+  function inlineElement(type, endChar, next) {
+    next = next || inlineNormal;
+    return function(stream, state) {
+      stream.match(inlineRE(endChar));
+      state.inline = state.f = next;
+      return type;
+    };
+  }
+
+  return {
+    startState: function() {
+      prevLineHasContent = false;
+      thisLineHasContent = false;
+      return {
+        f: blockNormal,
+        
+        block: blockNormal,
+        htmlState: CodeMirror.startState(htmlMode),
+        indentation: 0,
+        
+        inline: inlineNormal,
+        text: handleText,
+        
+        linkText: false,
+        linkTitle: false,
+        em: false,
+        strong: false,
+        header: false,
+        list: false,
+        quote: false
+      };
+    },
+
+    copyState: function(s) {
+      return {
+        f: s.f,
+        
+        block: s.block,
+        htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
+        indentation: s.indentation,
+        
+        localMode: s.localMode,
+        localState: s.localMode ? CodeMirror.copyState(s.localMode, s.localState) : null,
+          
+        inline: s.inline,
+        text: s.text,
+        linkTitle: s.linkTitle,
+        em: s.em,
+        strong: s.strong,
+        header: s.header,
+        list: s.list,
+        quote: s.quote,
+        md_inside: s.md_inside
+      };
+    },
+
+    token: function(stream, state) {
+      if (stream.sol()) {
+        if (stream.match(/^\s*$/, true)) {
+          prevLineHasContent = false;
+          return blankLine(state);
+        } else {
+          if(thisLineHasContent){
+            prevLineHasContent = true;
+            thisLineHasContent = false;
+          }
+          thisLineHasContent = true;
+        }
+
+        // Reset state.header
+        state.header = false;
+        
+        // Reset state.code
+        state.code = false;
+
+        state.f = state.block;
+        var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
+        var difference = Math.floor((indentation - state.indentation) / 4) * 4;
+        if (difference > 4) difference = 4;
+        indentation = state.indentation + difference;
+        state.indentationDiff = indentation - state.indentation;
+        state.indentation = indentation;
+        if (indentation > 0) { return null; }
+      }
+      return state.f(stream, state);
+    },
+
+    blankLine: blankLine,
+
+    getType: getType
+  };
+
+}, "xml");
+
+CodeMirror.defineMIME("text/x-markdown", "markdown");

http://git-wip-us.apache.org/repos/asf/couchdb/blob/4615a788/src/fauxton/jam/codemirror/mode/markdown/test.js
----------------------------------------------------------------------
diff --git a/src/fauxton/jam/codemirror/mode/markdown/test.js b/src/fauxton/jam/codemirror/mode/markdown/test.js
new file mode 100644
index 0000000..7572db5
--- /dev/null
+++ b/src/fauxton/jam/codemirror/mode/markdown/test.js
@@ -0,0 +1,1266 @@
+// Initiate ModeTest and set defaults
+var MT = ModeTest;
+MT.modeName = 'markdown';
+MT.modeOptions = {};
+
+MT.testMode(
+  'plainText',
+  'foo',
+  [
+    null, 'foo'
+  ]
+);
+
+// Code blocks using 4 spaces (regardless of CodeMirror.tabSize value)
+MT.testMode(
+  'codeBlocksUsing4Spaces',
+  '    foo',
+  [
+    null, '    ',
+    'comment', 'foo'
+  ]
+);
+// Code blocks using 4 spaces with internal indentation
+MT.testMode(
+  'codeBlocksUsing4SpacesIndentation',
+  '    bar\n        hello\n            world\n    foo\nbar',
+  [
+    null, '    ',
+    'comment', 'bar',
+    null, '        ',
+    'comment', 'hello',
+    null, '            ',
+    'comment', 'world',
+    null, '    ',
+    'comment', 'foo',
+    null, 'bar'
+  ]
+);
+// Code blocks using 4 spaces with internal indentation
+MT.testMode(
+  'codeBlocksUsing4SpacesIndentation',
+  ' foo\n    bar\n        hello\n    world',
+  [
+    null, ' foo',
+    null, '    ',
+    'comment', 'bar',
+    null, '        ',
+    'comment', 'hello',
+    null, '    ',
+    'comment', 'world'
+  ]
+);
+
+// Code blocks using 1 tab (regardless of CodeMirror.indentWithTabs value)
+MT.testMode(
+  'codeBlocksUsing1Tab',
+  '\tfoo',
+  [
+    null, '\t',
+    'comment', 'foo'
+  ]
+);
+
+// Inline code using backticks
+MT.testMode(
+  'inlineCodeUsingBackticks',
+  'foo `bar`',
+  [
+    null, 'foo ',
+    'comment', '`bar`'
+  ]
+);
+
+// Block code using single backtick (shouldn't work)
+MT.testMode(
+  'blockCodeSingleBacktick',
+  '`\nfoo\n`',
+  [
+    'comment', '`',
+    null, 'foo',
+    'comment', '`'
+  ]
+);
+
+// Unclosed backticks
+// Instead of simply marking as CODE, it would be nice to have an 
+// incomplete flag for CODE, that is styled slightly different.
+MT.testMode(
+  'unclosedBackticks',
+  'foo `bar',
+  [
+    null, 'foo ',
+    'comment', '`bar'
+  ]
+);
+
+// Per documentation: "To include a literal backtick character within a 
+// code span, you can use multiple backticks as the opening and closing 
+// delimiters"
+MT.testMode(
+  'doubleBackticks',
+  '``foo ` bar``',
+  [
+    'comment', '``foo ` bar``'
+  ]
+);
+
+// Tests based on Dingus
+// http://daringfireball.net/projects/markdown/dingus
+// 
+// Multiple backticks within an inline code block
+MT.testMode(
+  'consecutiveBackticks',
+  '`foo```bar`',
+  [
+    'comment', '`foo```bar`'
+  ]
+);
+// Multiple backticks within an inline code block with a second code block
+MT.testMode(
+  'consecutiveBackticks',
+  '`foo```bar` hello `world`',
+  [
+    'comment', '`foo```bar`',
+    null, ' hello ',
+    'comment', '`world`'
+  ]
+);
+// Unclosed with several different groups of backticks
+MT.testMode(
+  'unclosedBackticks',
+  '``foo ``` bar` hello',
+  [
+    'comment', '``foo ``` bar` hello'
+  ]
+);
+// Closed with several different groups of backticks
+MT.testMode(
+  'closedBackticks',
+  '``foo ``` bar` hello`` world',
+  [
+    'comment', '``foo ``` bar` hello``',
+    null, ' world'
+  ]
+);
+
+// atx headers
+// http://daringfireball.net/projects/markdown/syntax#header
+// 
+// H1
+MT.testMode(
+  'atxH1',
+  '# foo',
+  [
+    'header', '# foo'
+  ]
+);
+// H2
+MT.testMode(
+  'atxH2',
+  '## foo',
+  [
+    'header', '## foo'
+  ]
+);
+// H3
+MT.testMode(
+  'atxH3',
+  '### foo',
+  [
+    'header', '### foo'
+  ]
+);
+// H4
+MT.testMode(
+  'atxH4',
+  '#### foo',
+  [
+    'header', '#### foo'
+  ]
+);
+// H5
+MT.testMode(
+  'atxH5',
+  '##### foo',
+  [
+    'header', '##### foo'
+  ]
+);
+// H6
+MT.testMode(
+  'atxH6',
+  '###### foo',
+  [
+    'header', '###### foo'
+  ]
+);
+// H6 - 7x '#' should still be H6, per Dingus
+// http://daringfireball.net/projects/markdown/dingus
+MT.testMode(
+  'atxH6NotH7',
+  '####### foo',
+  [
+    'header', '####### foo'
+  ]
+);
+
+// Setext headers - H1, H2
+// Per documentation, "Any number of underlining =’s or -’s will work."
+// http://daringfireball.net/projects/markdown/syntax#header
+// Ideally, the text would be marked as `header` as well, but this is 
+// not really feasible at the moment. So, instead, we're testing against 
+// what works today, to avoid any regressions.
+// 
+// Check if single underlining = works
+MT.testMode(
+  'setextH1',
+  'foo\n=',
+  [
+    null, 'foo',
+    'header', '='
+  ]
+);
+// Check if 3+ ='s work
+MT.testMode(
+  'setextH1',
+  'foo\n===',
+  [
+    null, 'foo',
+    'header', '==='
+  ]
+);
+// Check if single underlining - works
+MT.testMode(
+  'setextH2',
+  'foo\n-',
+  [
+    null, 'foo',
+    'header', '-'
+  ]
+);
+// Check if 3+ -'s work
+MT.testMode(
+  'setextH2',
+  'foo\n---',
+  [
+    null, 'foo',
+    'header', '---'
+  ]
+);
+
+// Single-line blockquote with trailing space
+MT.testMode(
+  'blockquoteSpace',
+  '> foo',
+  [
+    'quote', '> foo'
+  ]
+);
+
+// Single-line blockquote
+MT.testMode(
+  'blockquoteNoSpace',
+  '>foo',
+  [
+    'quote', '>foo'
+  ]
+);
+
+// Single-line blockquote followed by normal paragraph
+MT.testMode(
+  'blockquoteThenParagraph',
+  '>foo\n\nbar',
+  [
+    'quote', '>foo',
+    null, 'bar'
+  ]
+);
+
+// Multi-line blockquote (lazy mode)
+MT.testMode(
+  'multiBlockquoteLazy',
+  '>foo\nbar',
+  [
+    'quote', '>foo',
+    'quote', 'bar'
+  ]
+);
+
+// Multi-line blockquote followed by normal paragraph (lazy mode)
+MT.testMode(
+  'multiBlockquoteLazyThenParagraph',
+  '>foo\nbar\n\nhello',
+  [
+    'quote', '>foo',
+    'quote', 'bar',
+    null, 'hello'
+  ]
+);
+
+// Multi-line blockquote (non-lazy mode)
+MT.testMode(
+  'multiBlockquote',
+  '>foo\n>bar',
+  [
+    'quote', '>foo',
+    'quote', '>bar'
+  ]
+);
+
+// Multi-line blockquote followed by normal paragraph (non-lazy mode)
+MT.testMode(
+  'multiBlockquoteThenParagraph',
+  '>foo\n>bar\n\nhello',
+  [
+    'quote', '>foo',
+    'quote', '>bar',
+    null, 'hello'
+  ]
+);
+
+// Check list types
+MT.testMode(
+  'listAsterisk',
+  '* foo\n* bar',
+  [
+    'string', '* foo',
+    'string', '* bar'
+  ]
+);
+MT.testMode(
+  'listPlus',
+  '+ foo\n+ bar',
+  [
+    'string', '+ foo',
+    'string', '+ bar'
+  ]
+);
+MT.testMode(
+  'listDash',
+  '- foo\n- bar',
+  [
+    'string', '- foo',
+    'string', '- bar'
+  ]
+);
+MT.testMode(
+  'listNumber',
+  '1. foo\n2. bar',
+  [
+    'string', '1. foo',
+    'string', '2. bar'
+  ]
+);
+
+// Formatting in lists (*)
+MT.testMode(
+  'listAsteriskFormatting',
+  '* *foo* bar\n\n* **foo** bar\n\n* ***foo*** bar\n\n* `foo` bar',
+  [
+    'string', '* ',
+    'string em', '*foo*',
+    'string', ' bar',
+    'string', '* ',
+    'string strong', '**foo**',
+    'string', ' bar',
+    'string', '* ',
+    'string strong', '**',
+    'string emstrong', '*foo**',
+    'string em', '*',
+    'string', ' bar',
+    'string', '* ',
+    'string comment', '`foo`',
+    'string', ' bar'
+  ]
+);
+// Formatting in lists (+)
+MT.testMode(
+  'listPlusFormatting',
+  '+ *foo* bar\n\n+ **foo** bar\n\n+ ***foo*** bar\n\n+ `foo` bar',
+  [
+    'string', '+ ',
+    'string em', '*foo*',
+    'string', ' bar',
+    'string', '+ ',
+    'string strong', '**foo**',
+    'string', ' bar',
+    'string', '+ ',
+    'string strong', '**',
+    'string emstrong', '*foo**',
+    'string em', '*',
+    'string', ' bar',
+    'string', '+ ',
+    'string comment', '`foo`',
+    'string', ' bar'
+  ]
+);
+// Formatting in lists (-)
+MT.testMode(
+  'listDashFormatting',
+  '- *foo* bar\n\n- **foo** bar\n\n- ***foo*** bar\n\n- `foo` bar',
+  [
+    'string', '- ',
+    'string em', '*foo*',
+    'string', ' bar',
+    'string', '- ',
+    'string strong', '**foo**',
+    'string', ' bar',
+    'string', '- ',
+    'string strong', '**',
+    'string emstrong', '*foo**',
+    'string em', '*',
+    'string', ' bar',
+    'string', '- ',
+    'string comment', '`foo`',
+    'string', ' bar'
+  ]
+);
+// Formatting in lists (1.)
+MT.testMode(
+  'listNumberFormatting',
+  '1. *foo* bar\n\n2. **foo** bar\n\n3. ***foo*** bar\n\n4. `foo` bar',
+  [
+    'string', '1. ',
+    'string em', '*foo*',
+    'string', ' bar',
+    'string', '2. ',
+    'string strong', '**foo**',
+    'string', ' bar',
+    'string', '3. ',
+    'string strong', '**',
+    'string emstrong', '*foo**',
+    'string em', '*',
+    'string', ' bar',
+    'string', '4. ',
+    'string comment', '`foo`',
+    'string', ' bar'
+  ]
+);
+
+// Paragraph lists
+MT.testMode(
+  'listParagraph',
+  '* foo\n\n* bar',
+  [
+    'string', '* foo',
+    'string', '* bar'
+  ]
+);
+
+// Multi-paragraph lists
+//
+// 4 spaces
+MT.testMode(
+  'listMultiParagraph',
+  '* foo\n\n* bar\n\n    hello',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '    ',
+    'string', 'hello'
+  ]
+);
+// 4 spaces, extra blank lines (should still be list, per Dingus)
+MT.testMode(
+  'listMultiParagraphExtra',
+  '* foo\n\n* bar\n\n\n    hello',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '    ',
+    'string', 'hello'
+  ]
+);
+// 4 spaces, plus 1 space (should still be list, per Dingus)
+MT.testMode(
+  'listMultiParagraphExtraSpace',
+  '* foo\n\n* bar\n\n     hello\n\n    world',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '     ',
+    'string', 'hello',
+    null, '    ',
+    'string', 'world'
+  ]
+);
+// 1 tab
+MT.testMode(
+  'listTab',
+  '* foo\n\n* bar\n\n\thello',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '\t',
+    'string', 'hello'
+  ]
+);
+// No indent
+MT.testMode(
+  'listNoIndent',
+  '* foo\n\n* bar\n\nhello',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, 'hello'
+  ]
+);
+// Blockquote
+MT.testMode(
+  'blockquote',
+  '* foo\n\n* bar\n\n    > hello',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '    ',
+    'string quote', '> hello'
+  ]
+);
+// Code block
+MT.testMode(
+  'blockquoteCode',
+  '* foo\n\n* bar\n\n        > hello\n\n    world',
+  [
+    'string', '* foo',
+    'string', '* bar',
+    null, '        ',
+    'comment', '> hello',
+    null, '    ',
+    'string', 'world'
+  ]
+);
+// Code block followed by text
+MT.testMode(
+  'blockquoteCodeText',
+  '* foo\n\n    bar\n\n        hello\n\n    world',
+  [
+    'string', '* foo',
+    null, '    ',
+    'string', 'bar',
+    null, '        ',
+    'comment', 'hello',
+    null, '    ',
+    'string', 'world'
+  ]
+);
+
+// Nested list
+// 
+// *
+MT.testMode(
+  'listAsteriskNested',
+  '* foo\n\n    * bar',
+  [
+    'string', '* foo',
+    null, '    ',
+    'string', '* bar'
+  ]
+);
+// +
+MT.testMode(
+  'listPlusNested',
+  '+ foo\n\n    + bar',
+  [
+    'string', '+ foo',
+    null, '    ',
+    'string', '+ bar'
+  ]
+);
+// -
+MT.testMode(
+  'listDashNested',
+  '- foo\n\n    - bar',
+  [
+    'string', '- foo',
+    null, '    ',
+    'string', '- bar'
+  ]
+);
+// 1.
+MT.testMode(
+  'listNumberNested',
+  '1. foo\n\n    2. bar',
+  [
+    'string', '1. foo',
+    null, '    ',
+    'string', '2. bar'
+  ]
+);
+// Mixed
+MT.testMode(
+  'listMixed',
+  '* foo\n\n    + bar\n\n        - hello\n\n            1. world',
+  [
+    'string', '* foo',
+    null, '    ',
+    'string', '+ bar',
+    null, '        ',
+    'string', '- hello',
+    null, '            ',
+    'string', '1. world'
+  ]
+);
+// Blockquote
+MT.testMode(
+  'listBlockquote',
+  '* foo\n\n    + bar\n\n        > hello',
+  [
+    'string', '* foo',
+    null, '    ',
+    'string', '+ bar',
+    null, '        ',
+    'quote string', '> hello'
+  ]
+);
+// Code
+MT.testMode(
+  'listCode',
+  '* foo\n\n    + bar\n\n            hello',
+  [
+    'string', '* foo',
+    null, '    ',
+    'string', '+ bar',
+    null, '            ',
+    'comment', 'hello'
+  ]
+);
+// Code with internal indentation
+MT.testMode(
+  'listCodeIndentation',
+  '* foo\n\n        bar\n            hello\n                world\n        foo\n    bar',
+  [
+    'string', '* foo',
+    null, '        ',
+    'comment', 'bar',
+    null, '            ',
+    'comment', 'hello',
+    null, '                ',
+    'comment', 'world',
+    null, '        ',
+    'comment', 'foo',
+    null, '    ',
+    'string', 'bar'
+  ]
+);
+// Code followed by text
+MT.testMode(
+  'listCodeText',
+  '* foo\n\n        bar\n\nhello',
+  [
+    'string', '* foo',
+    null, '        ',
+    'comment', 'bar',
+    null, 'hello'
+  ]
+);
+
+// Following tests directly from official Markdown documentation
+// http://daringfireball.net/projects/markdown/syntax#hr
+MT.testMode(
+  'hrSpace',
+  '* * *',
+  [
+    'hr', '* * *'
+  ]
+);
+
+MT.testMode(
+  'hr',
+  '***',
+  [
+    'hr', '***'
+  ]
+);
+
+MT.testMode(
+  'hrLong',
+  '*****',
+  [
+    'hr', '*****'
+  ]
+);
+
+MT.testMode(
+  'hrSpaceDash',
+  '- - -',
+  [
+    'hr', '- - -'
+  ]
+);
+
+MT.testMode(
+  'hrDashLong',
+  '---------------------------------------',
+  [
+    'hr', '---------------------------------------'
+  ]
+);
+
+// Inline link with title
+MT.testMode(
+  'linkTitle',
+  '[foo](http://example.com/ "bar") hello',
+  [
+    'link', '[foo]',
+    'string', '(http://example.com/ "bar")',
+    null, ' hello'
+  ]
+);
+
+// Inline link without title
+MT.testMode(
+  'linkNoTitle',
+  '[foo](http://example.com/) bar',
+  [
+    'link', '[foo]',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Inline link with Em
+MT.testMode(
+  'linkEm',
+  '[*foo*](http://example.com/) bar',
+  [
+    'link', '[',
+    'link em', '*foo*',
+    'link', ']',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Inline link with Strong
+MT.testMode(
+  'linkStrong',
+  '[**foo**](http://example.com/) bar',
+  [
+    'link', '[',
+    'link strong', '**foo**',
+    'link', ']',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Inline link with EmStrong
+MT.testMode(
+  'linkEmStrong',
+  '[***foo***](http://example.com/) bar',
+  [
+    'link', '[',
+    'link strong', '**',
+    'link emstrong', '*foo**',
+    'link em', '*',
+    'link', ']',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Image with title
+MT.testMode(
+  'imageTitle',
+  '![foo](http://example.com/ "bar") hello',
+  [
+    'tag', '![foo]',
+    'string', '(http://example.com/ "bar")',
+    null, ' hello'
+  ]
+);
+
+// Image without title
+MT.testMode(
+  'imageNoTitle',
+  '![foo](http://example.com/) bar',
+  [
+    'tag', '![foo]',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Image with asterisks
+MT.testMode(
+  'imageAsterisks',
+  '![*foo*](http://example.com/) bar',
+  [
+    'tag', '![*foo*]',
+    'string', '(http://example.com/)',
+    null, ' bar'
+  ]
+);
+
+// Not a link. Should be normal text due to square brackets being used
+// regularly in text, especially in quoted material, and no space is allowed
+// between square brackets and parentheses (per Dingus).
+MT.testMode(
+  'notALink',
+  '[foo] (bar)',
+  [
+    null, '[foo] (bar)'
+  ]
+);
+
+// Reference-style links
+MT.testMode(
+  'linkReference',
+  '[foo][bar] hello',
+  [
+    'link', '[foo]',
+    'string', '[bar]',
+    null, ' hello'
+  ]
+);
+// Reference-style links with Em
+MT.testMode(
+  'linkReferenceEm',
+  '[*foo*][bar] hello',
+  [
+    'link', '[',
+    'link em', '*foo*',
+    'link', ']',
+    'string', '[bar]',
+    null, ' hello'
+  ]
+);
+// Reference-style links with Strong
+MT.testMode(
+  'linkReferenceStrong',
+  '[**foo**][bar] hello',
+  [
+    'link', '[',
+    'link strong', '**foo**',
+    'link', ']',
+    'string', '[bar]',
+    null, ' hello'
+  ]
+);
+// Reference-style links with EmStrong
+MT.testMode(
+  'linkReferenceEmStrong',
+  '[***foo***][bar] hello',
+  [
+    'link', '[',
+    'link strong', '**',
+    'link emstrong', '*foo**',
+    'link em', '*',
+    'link', ']',
+    'string', '[bar]',
+    null, ' hello'
+  ]
+);
+
+// Reference-style links with optional space separator (per docuentation)
+// "You can optionally use a space to separate the sets of brackets"
+MT.testMode(
+  'linkReferenceSpace',
+  '[foo] [bar] hello',
+  [
+    'link', '[foo]',
+    null, ' ',
+    'string', '[bar]',
+    null, ' hello'
+  ]
+);
+// Should only allow a single space ("...use *a* space...")
+MT.testMode(
+  'linkReferenceDoubleSpace',
+  '[foo]  [bar] hello',
+  [
+    null, '[foo]  [bar] hello'
+  ]
+);
+
+// Reference-style links with implicit link name
+MT.testMode(
+  'linkImplicit',
+  '[foo][] hello',
+  [
+    'link', '[foo]',
+    'string', '[]',
+    null, ' hello'
+  ]
+);
+
+// @todo It would be nice if, at some point, the document was actually
+// checked to see if the referenced link exists
+
+// Link label, for reference-style links (taken from documentation)
+//
+// No title
+MT.testMode(
+  'labelNoTitle',
+  '[foo]: http://example.com/',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/'
+  ]
+);
+// Space in ID and title
+MT.testMode(
+  'labelSpaceTitle',
+  '[foo bar]: http://example.com/ "hello"',
+  [
+    'link', '[foo bar]:',
+    null, ' ',
+    'string', 'http://example.com/ "hello"'
+  ]
+);
+// Double title
+MT.testMode(
+  'labelDoubleTitle',
+  '[foo bar]: http://example.com/ "hello" "world"',
+  [
+    'link', '[foo bar]:',
+    null, ' ',
+    'string', 'http://example.com/ "hello"',
+    null, ' "world"'
+  ]
+);
+// Double quotes around title
+MT.testMode(
+  'labelTitleDoubleQuotes',
+  '[foo]: http://example.com/  "bar"',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/  "bar"'
+  ]
+);
+// Single quotes around title
+MT.testMode(
+  'labelTitleSingleQuotes',
+  '[foo]: http://example.com/  \'bar\'',
+  [
+    'link', '[foo]:',
+    null, ' ',
+  'string', 'http://example.com/  \'bar\''
+  ]
+);
+// Parentheses around title
+MT.testMode(
+  'labelTitleParenthese',
+  '[foo]: http://example.com/  (bar)',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/  (bar)'
+  ]
+);
+// Invalid title
+MT.testMode(
+  'labelTitleInvalid',
+  '[foo]: http://example.com/ bar',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/',
+    null, ' bar'
+  ]
+);
+// Angle brackets around URL
+MT.testMode(
+  'labelLinkAngleBrackets',
+  '[foo]: <http://example.com/>  "bar"',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', '<http://example.com/>  "bar"'
+  ]
+);
+// Title on next line per documentation (double quotes)
+MT.testMode(
+  'labelTitleNextDoubleQuotes',
+  '[foo]: http://example.com/\n"bar" hello',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/',
+    'string', '"bar"',
+    null, ' hello'
+  ]
+);
+// Title on next line per documentation (single quotes)
+MT.testMode(
+  'labelTitleNextSingleQuotes',
+  '[foo]: http://example.com/\n\'bar\' hello',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/',
+  'string', '\'bar\'',
+  null, ' hello'
+  ]
+);
+// Title on next line per documentation (parentheses)
+MT.testMode(
+  'labelTitleNextParenthese',
+  '[foo]: http://example.com/\n(bar) hello',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/',
+    'string', '(bar)',
+    null, ' hello'
+  ]
+);
+// Title on next line per documentation (mixed)
+MT.testMode(
+  'labelTitleNextMixed',
+  '[foo]: http://example.com/\n(bar" hello',
+  [
+    'link', '[foo]:',
+    null, ' ',
+    'string', 'http://example.com/',
+    null, '(bar" hello'
+  ]
+);
+
+// Automatic links
+MT.testMode(
+  'linkWeb',
+  '<http://example.com/> foo',
+  [
+    'link', '<http://example.com/>',
+    null, ' foo'
+  ]
+);
+
+// Automatic email links
+MT.testMode(
+  'linkEmail',
+  '<user@example.com> foo',
+  [
+    'link', '<user@example.com>',
+    null, ' foo'
+  ]
+);
+
+// Single asterisk
+MT.testMode(
+  'emAsterisk',
+  '*foo* bar',
+  [
+    'em', '*foo*',
+    null, ' bar'
+  ]
+);
+
+// Single underscore
+MT.testMode(
+  'emUnderscore',
+  '_foo_ bar',
+  [
+    'em', '_foo_',
+    null, ' bar'
+  ]
+);
+
+// Emphasis characters within a word
+MT.testMode(
+  'emInWordAsterisk',
+  'foo*bar*hello',
+  [
+    null, 'foo',
+    'em', '*bar*',
+    null, 'hello'
+  ]
+);
+MT.testMode(
+  'emInWordUnderscore',
+  'foo_bar_hello',
+  [
+    null, 'foo',
+    'em', '_bar_',
+    null, 'hello'
+  ]
+);
+// Per documentation: "...surround an * or _ with spaces, it’ll be 
+// treated as a literal asterisk or underscore."
+// 
+// Inside EM
+MT.testMode(
+  'emEscapedBySpaceIn',
+  'foo _bar _ hello_ world',
+  [
+    null, 'foo ',
+    'em', '_bar _ hello_',
+    null, ' world'
+  ]
+);
+// Outside EM
+MT.testMode(
+  'emEscapedBySpaceOut',
+  'foo _ bar_hello_world',
+  [
+    null, 'foo _ bar',
+    'em', '_hello_',
+    null, 'world'
+  ]
+);
+
+// Unclosed emphasis characters
+// Instead of simply marking as EM / STRONG, it would be nice to have an 
+// incomplete flag for EM and STRONG, that is styled slightly different.
+MT.testMode(
+  'emIncompleteAsterisk',
+  'foo *bar',
+  [
+    null, 'foo ',
+    'em', '*bar'
+  ]
+);
+MT.testMode(
+  'emIncompleteUnderscore',
+  'foo _bar',
+  [
+    null, 'foo ',
+    'em', '_bar'
+  ]
+);
+
+// Double asterisk
+MT.testMode(
+  'strongAsterisk',
+  '**foo** bar',
+  [
+    'strong', '**foo**',
+    null, ' bar'
+  ]
+);
+
+// Double underscore
+MT.testMode(
+  'strongUnderscore',
+  '__foo__ bar',
+  [
+    'strong', '__foo__',
+    null, ' bar'
+  ]
+);
+
+// Triple asterisk
+MT.testMode(
+  'emStrongAsterisk',
+  '*foo**bar*hello** world',
+  [
+    'em', '*foo',
+    'emstrong', '**bar*',
+    'strong', 'hello**',
+    null, ' world'
+  ]
+);
+
+// Triple underscore
+MT.testMode(
+  'emStrongUnderscore',
+  '_foo__bar_hello__ world',
+  [
+    'em', '_foo',
+    'emstrong', '__bar_',
+    'strong', 'hello__',
+    null, ' world'
+  ]
+);
+
+// Triple mixed
+// "...same character must be used to open and close an emphasis span.""
+MT.testMode(
+  'emStrongMixed',
+  '_foo**bar*hello__ world',
+  [
+    'em', '_foo',
+    'emstrong', '**bar*hello__ world'
+  ]
+);
+
+MT.testMode(
+  'emStrongMixed',
+  '*foo__bar_hello** world',
+  [
+    'em', '*foo',
+    'emstrong', '__bar_hello** world'
+  ]
+);
+
+// These characters should be escaped:
+// \   backslash
+// `   backtick
+// *   asterisk
+// _   underscore
+// {}  curly braces
+// []  square brackets
+// ()  parentheses
+// #   hash mark
+// +   plus sign
+// -   minus sign (hyphen)
+// .   dot
+// !   exclamation mark
+// 
+// Backtick (code)
+MT.testMode(
+  'escapeBacktick',
+  'foo \\`bar\\`',
+  [
+    null, 'foo \\`bar\\`'
+  ]
+);
+MT.testMode(
+  'doubleEscapeBacktick',
+  'foo \\\\`bar\\\\`',
+  [
+    null, 'foo \\\\',
+    'comment', '`bar\\\\`'
+  ]
+);
+// Asterisk (em)
+MT.testMode(
+  'escapeAsterisk',
+  'foo \\*bar\\*',
+  [
+    null, 'foo \\*bar\\*'
+  ]
+);
+MT.testMode(
+  'doubleEscapeAsterisk',
+  'foo \\\\*bar\\\\*',
+  [
+    null, 'foo \\\\',
+    'em', '*bar\\\\*'
+  ]
+);
+// Underscore (em)
+MT.testMode(
+  'escapeUnderscore',
+  'foo \\_bar\\_',
+  [
+    null, 'foo \\_bar\\_'
+  ]
+);
+MT.testMode(
+  'doubleEscapeUnderscore',
+  'foo \\\\_bar\\\\_',
+  [
+    null, 'foo \\\\',
+    'em', '_bar\\\\_'
+  ]
+);
+// Hash mark (headers)
+MT.testMode(
+  'escapeHash',
+  '\\# foo',
+  [
+    null, '\\# foo'
+  ]
+);
+MT.testMode(
+  'doubleEscapeHash',
+  '\\\\# foo',
+  [
+    null, '\\\\# foo'
+  ]
+);


Mime
View raw message