~ubuntu-branches/ubuntu/utopic/codemirror-js/utopic

« back to all changes in this revision

Viewing changes to mode/markdown/markdown.js

  • Committer: Package Import Robot
  • Author(s): David Paleino
  • Date: 2012-04-12 12:25:28 UTC
  • Revision ID: package-import@ubuntu.com-20120412122528-8xp5a8frj4h1d3ee
Tags: upstream-2.23
ImportĀ upstreamĀ versionĀ 2.23

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
CodeMirror.defineMode("markdown", function(cmCfg, modeCfg) {
 
2
 
 
3
  var htmlMode = CodeMirror.getMode(cmCfg, { name: 'xml', htmlMode: true });
 
4
 
 
5
  var header   = 'header'
 
6
  ,   code     = 'comment'
 
7
  ,   quote    = 'quote'
 
8
  ,   list     = 'string'
 
9
  ,   hr       = 'hr'
 
10
  ,   linktext = 'link'
 
11
  ,   linkhref = 'string'
 
12
  ,   em       = 'em'
 
13
  ,   strong   = 'strong'
 
14
  ,   emstrong = 'emstrong';
 
15
 
 
16
  var hrRE = /^([*\-=_])(?:\s*\1){2,}\s*$/
 
17
  ,   ulRE = /^[*\-+]\s+/
 
18
  ,   olRE = /^[0-9]+\.\s+/
 
19
  ,   headerRE = /^(?:\={3,}|-{3,})$/
 
20
  ,   textRE = /^[^\[*_\\<>`]+/;
 
21
 
 
22
  function switchInline(stream, state, f) {
 
23
    state.f = state.inline = f;
 
24
    return f(stream, state);
 
25
  }
 
26
 
 
27
  function switchBlock(stream, state, f) {
 
28
    state.f = state.block = f;
 
29
    return f(stream, state);
 
30
  }
 
31
 
 
32
 
 
33
  // Blocks
 
34
 
 
35
  function blankLine(state) {
 
36
    // Reset EM state
 
37
    state.em = false;
 
38
    // Reset STRONG state
 
39
    state.strong = false;
 
40
    return null;
 
41
  }
 
42
 
 
43
  function blockNormal(stream, state) {
 
44
    var match;
 
45
    if (state.indentationDiff >= 4) {
 
46
      state.indentation -= state.indentationDiff;
 
47
      stream.skipToEnd();
 
48
      return code;
 
49
    } else if (stream.eatSpace()) {
 
50
      return null;
 
51
    } else if (stream.peek() === '#' || stream.match(headerRE)) {
 
52
      state.header = true;
 
53
    } else if (stream.eat('>')) {
 
54
      state.indentation++;
 
55
      state.quote = true;
 
56
    } else if (stream.peek() === '[') {
 
57
      return switchInline(stream, state, footnoteLink);
 
58
    } else if (stream.match(hrRE, true)) {
 
59
      return hr;
 
60
    } else if (match = stream.match(ulRE, true) || stream.match(olRE, true)) {
 
61
      state.indentation += match[0].length;
 
62
      return list;
 
63
    }
 
64
    
 
65
    return switchInline(stream, state, state.inline);
 
66
  }
 
67
 
 
68
  function htmlBlock(stream, state) {
 
69
    var style = htmlMode.token(stream, state.htmlState);
 
70
    if (style === 'tag' && state.htmlState.type !== 'openTag' && !state.htmlState.context) {
 
71
      state.f = inlineNormal;
 
72
      state.block = blockNormal;
 
73
    }
 
74
    return style;
 
75
  }
 
76
 
 
77
 
 
78
  // Inline
 
79
  function getType(state) {
 
80
    var styles = [];
 
81
    
 
82
    if (state.strong) { styles.push(state.em ? emstrong : strong); }
 
83
    else if (state.em) { styles.push(em); }
 
84
    
 
85
    if (state.header) { styles.push(header); }
 
86
    if (state.quote) { styles.push(quote); }
 
87
 
 
88
    return styles.length ? styles.join(' ') : null;
 
89
  }
 
90
 
 
91
  function handleText(stream, state) {
 
92
    if (stream.match(textRE, true)) {
 
93
      return getType(state);
 
94
    }
 
95
    return undefined;        
 
96
  }
 
97
 
 
98
  function inlineNormal(stream, state) {
 
99
    var style = state.text(stream, state)
 
100
    if (typeof style !== 'undefined')
 
101
      return style;
 
102
    
 
103
    var ch = stream.next();
 
104
    
 
105
    if (ch === '\\') {
 
106
      stream.next();
 
107
      return getType(state);
 
108
    }
 
109
    if (ch === '`') {
 
110
      return switchInline(stream, state, inlineElement(code, '`'));
 
111
    }
 
112
    if (ch === '[') {
 
113
      return switchInline(stream, state, linkText);
 
114
    }
 
115
    if (ch === '<' && stream.match(/^\w/, false)) {
 
116
      stream.backUp(1);
 
117
      return switchBlock(stream, state, htmlBlock);
 
118
    }
 
119
 
 
120
    var t = getType(state);
 
121
    if (ch === '*' || ch === '_') {
 
122
      if (stream.eat(ch)) {
 
123
        return (state.strong = !state.strong) ? getType(state) : t;
 
124
      }
 
125
      return (state.em = !state.em) ? getType(state) : t;
 
126
    }
 
127
    
 
128
    return getType(state);
 
129
  }
 
130
 
 
131
  function linkText(stream, state) {
 
132
    while (!stream.eol()) {
 
133
      var ch = stream.next();
 
134
      if (ch === '\\') stream.next();
 
135
      if (ch === ']') {
 
136
        state.inline = state.f = linkHref;
 
137
        return linktext;
 
138
      }
 
139
    }
 
140
    return linktext;
 
141
  }
 
142
 
 
143
  function linkHref(stream, state) {
 
144
    stream.eatSpace();
 
145
    var ch = stream.next();
 
146
    if (ch === '(' || ch === '[') {
 
147
      return switchInline(stream, state, inlineElement(linkhref, ch === '(' ? ')' : ']'));
 
148
    }
 
149
    return 'error';
 
150
  }
 
151
 
 
152
  function footnoteLink(stream, state) {
 
153
    if (stream.match(/^[^\]]*\]:/, true)) {
 
154
      state.f = footnoteUrl;
 
155
      return linktext;
 
156
    }
 
157
    return switchInline(stream, state, inlineNormal);
 
158
  }
 
159
 
 
160
  function footnoteUrl(stream, state) {
 
161
    stream.eatSpace();
 
162
    stream.match(/^[^\s]+/, true);
 
163
    state.f = state.inline = inlineNormal;
 
164
    return linkhref;
 
165
  }
 
166
 
 
167
  function inlineRE(endChar) {
 
168
    if (!inlineRE[endChar]) {
 
169
      // match any not-escaped-non-endChar and any escaped char
 
170
      // then match endChar or eol
 
171
      inlineRE[endChar] = new RegExp('^(?:[^\\\\\\' + endChar + ']|\\\\.)*(?:\\' + endChar + '|$)');
 
172
    }
 
173
    return inlineRE[endChar];
 
174
  }
 
175
 
 
176
  function inlineElement(type, endChar, next) {
 
177
    next = next || inlineNormal;
 
178
    return function(stream, state) {
 
179
      stream.match(inlineRE(endChar));
 
180
      state.inline = state.f = next;
 
181
      return type;
 
182
    };
 
183
  }
 
184
 
 
185
  return {
 
186
    startState: function() {
 
187
      return {
 
188
        f: blockNormal,
 
189
        
 
190
        block: blockNormal,
 
191
        htmlState: htmlMode.startState(),
 
192
        indentation: 0,
 
193
        
 
194
        inline: inlineNormal,
 
195
        text: handleText,
 
196
        em: false,
 
197
        strong: false,
 
198
        header: false,
 
199
        quote: false
 
200
      };
 
201
    },
 
202
 
 
203
    copyState: function(s) {
 
204
      return {
 
205
        f: s.f,
 
206
        
 
207
        block: s.block,
 
208
        htmlState: CodeMirror.copyState(htmlMode, s.htmlState),
 
209
        indentation: s.indentation,
 
210
        
 
211
        inline: s.inline,
 
212
        text: s.text,
 
213
        em: s.em,
 
214
        strong: s.strong,
 
215
        header: s.header,
 
216
        quote: s.quote
 
217
      };
 
218
    },
 
219
 
 
220
    token: function(stream, state) {
 
221
      if (stream.sol()) {
 
222
        if (stream.match(/^\s*$/, true)) { return blankLine(state); }
 
223
 
 
224
        // Reset state.header
 
225
        state.header = false;
 
226
        // Reset state.quote
 
227
        state.quote = false;
 
228
 
 
229
        state.f = state.block;
 
230
        var indentation = stream.match(/^\s*/, true)[0].replace(/\t/g, '    ').length;
 
231
        state.indentationDiff = indentation - state.indentation;
 
232
        state.indentation = indentation;
 
233
        if (indentation > 0) { return null; }
 
234
      }
 
235
      return state.f(stream, state);
 
236
    },
 
237
 
 
238
    blankLine: blankLine,
 
239
 
 
240
    getType: getType
 
241
  };
 
242
 
 
243
});
 
244
 
 
245
CodeMirror.defineMIME("text/x-markdown", "markdown");