rspec_api_documentation 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (30) hide show
  1. data/lib/rspec_api_documentation.rb +1 -1
  2. data/lib/rspec_api_documentation/api_documentation.rb +1 -0
  3. data/lib/rspec_api_documentation/client_base.rb +9 -14
  4. data/lib/rspec_api_documentation/curl.rb +4 -4
  5. data/lib/rspec_api_documentation/html_writer.rb +15 -0
  6. data/lib/rspec_api_documentation/oauth2_mac_client.rb +5 -1
  7. data/lib/rspec_api_documentation/rack_test_client.rb +5 -1
  8. data/lib/rspec_api_documentation/test_server.rb +3 -4
  9. data/lib/rspec_api_documentation/wurl_writer.rb +105 -0
  10. data/templates/assets/img/glyphicons-halflings-white.png +0 -0
  11. data/templates/assets/img/glyphicons-halflings.png +0 -0
  12. data/templates/assets/javascripts/application.js +250 -0
  13. data/templates/assets/javascripts/codemirror.js +3636 -0
  14. data/templates/assets/javascripts/jquery-1-7-2.js +9401 -0
  15. data/templates/assets/javascripts/jquery-base64.js +189 -0
  16. data/templates/assets/javascripts/jquery-livequery.js +226 -0
  17. data/templates/assets/javascripts/jquery-ui-1-8-16-min.js +791 -0
  18. data/templates/assets/javascripts/mode/css/css.js +124 -0
  19. data/templates/assets/javascripts/mode/htmlmixed/htmlmixed.js +85 -0
  20. data/templates/assets/javascripts/mode/javascript/javascript.js +361 -0
  21. data/templates/assets/javascripts/mode/xml/xml.js +325 -0
  22. data/templates/assets/stylesheets/application.css +68 -0
  23. data/templates/assets/stylesheets/bootstrap.css +4960 -0
  24. data/templates/assets/stylesheets/codemirror.css +230 -0
  25. data/templates/rspec_api_documentation/html_example.mustache +52 -9
  26. data/templates/rspec_api_documentation/html_index.mustache +22 -21
  27. data/templates/rspec_api_documentation/wurl_example.mustache +241 -0
  28. data/templates/rspec_api_documentation/wurl_index.mustache +26 -0
  29. metadata +47 -111
  30. data/lib/rspec_api_documentation/syntax.rb +0 -33
@@ -0,0 +1,124 @@
1
+ CodeMirror.defineMode("css", function(config) {
2
+ var indentUnit = config.indentUnit, type;
3
+ function ret(style, tp) {type = tp; return style;}
4
+
5
+ function tokenBase(stream, state) {
6
+ var ch = stream.next();
7
+ if (ch == "@") {stream.eatWhile(/[\w\\\-]/); return ret("meta", stream.current());}
8
+ else if (ch == "/" && stream.eat("*")) {
9
+ state.tokenize = tokenCComment;
10
+ return tokenCComment(stream, state);
11
+ }
12
+ else if (ch == "<" && stream.eat("!")) {
13
+ state.tokenize = tokenSGMLComment;
14
+ return tokenSGMLComment(stream, state);
15
+ }
16
+ else if (ch == "=") ret(null, "compare");
17
+ else if ((ch == "~" || ch == "|") && stream.eat("=")) return ret(null, "compare");
18
+ else if (ch == "\"" || ch == "'") {
19
+ state.tokenize = tokenString(ch);
20
+ return state.tokenize(stream, state);
21
+ }
22
+ else if (ch == "#") {
23
+ stream.eatWhile(/[\w\\\-]/);
24
+ return ret("atom", "hash");
25
+ }
26
+ else if (ch == "!") {
27
+ stream.match(/^\s*\w*/);
28
+ return ret("keyword", "important");
29
+ }
30
+ else if (/\d/.test(ch)) {
31
+ stream.eatWhile(/[\w.%]/);
32
+ return ret("number", "unit");
33
+ }
34
+ else if (/[,.+>*\/]/.test(ch)) {
35
+ return ret(null, "select-op");
36
+ }
37
+ else if (/[;{}:\[\]]/.test(ch)) {
38
+ return ret(null, ch);
39
+ }
40
+ else {
41
+ stream.eatWhile(/[\w\\\-]/);
42
+ return ret("variable", "variable");
43
+ }
44
+ }
45
+
46
+ function tokenCComment(stream, state) {
47
+ var maybeEnd = false, ch;
48
+ while ((ch = stream.next()) != null) {
49
+ if (maybeEnd && ch == "/") {
50
+ state.tokenize = tokenBase;
51
+ break;
52
+ }
53
+ maybeEnd = (ch == "*");
54
+ }
55
+ return ret("comment", "comment");
56
+ }
57
+
58
+ function tokenSGMLComment(stream, state) {
59
+ var dashes = 0, ch;
60
+ while ((ch = stream.next()) != null) {
61
+ if (dashes >= 2 && ch == ">") {
62
+ state.tokenize = tokenBase;
63
+ break;
64
+ }
65
+ dashes = (ch == "-") ? dashes + 1 : 0;
66
+ }
67
+ return ret("comment", "comment");
68
+ }
69
+
70
+ function tokenString(quote) {
71
+ return function(stream, state) {
72
+ var escaped = false, ch;
73
+ while ((ch = stream.next()) != null) {
74
+ if (ch == quote && !escaped)
75
+ break;
76
+ escaped = !escaped && ch == "\\";
77
+ }
78
+ if (!escaped) state.tokenize = tokenBase;
79
+ return ret("string", "string");
80
+ };
81
+ }
82
+
83
+ return {
84
+ startState: function(base) {
85
+ return {tokenize: tokenBase,
86
+ baseIndent: base || 0,
87
+ stack: []};
88
+ },
89
+
90
+ token: function(stream, state) {
91
+ if (stream.eatSpace()) return null;
92
+ var style = state.tokenize(stream, state);
93
+
94
+ var context = state.stack[state.stack.length-1];
95
+ if (type == "hash" && context != "rule") style = "string-2";
96
+ else if (style == "variable") {
97
+ if (context == "rule") style = "number";
98
+ else if (!context || context == "@media{") style = "tag";
99
+ }
100
+
101
+ if (context == "rule" && /^[\{\};]$/.test(type))
102
+ state.stack.pop();
103
+ if (type == "{") {
104
+ if (context == "@media") state.stack[state.stack.length-1] = "@media{";
105
+ else state.stack.push("{");
106
+ }
107
+ else if (type == "}") state.stack.pop();
108
+ else if (type == "@media") state.stack.push("@media");
109
+ else if (context == "{" && type != "comment") state.stack.push("rule");
110
+ return style;
111
+ },
112
+
113
+ indent: function(state, textAfter) {
114
+ var n = state.stack.length;
115
+ if (/^\}/.test(textAfter))
116
+ n -= state.stack[state.stack.length-1] == "rule" ? 2 : 1;
117
+ return state.baseIndent + n * indentUnit;
118
+ },
119
+
120
+ electricChars: "}"
121
+ };
122
+ });
123
+
124
+ CodeMirror.defineMIME("text/css", "css");
@@ -0,0 +1,85 @@
1
+ CodeMirror.defineMode("htmlmixed", function(config, parserConfig) {
2
+ var htmlMode = CodeMirror.getMode(config, {name: "xml", htmlMode: true});
3
+ var jsMode = CodeMirror.getMode(config, "javascript");
4
+ var cssMode = CodeMirror.getMode(config, "css");
5
+
6
+ function html(stream, state) {
7
+ var style = htmlMode.token(stream, state.htmlState);
8
+ if (style == "tag" && stream.current() == ">" && state.htmlState.context) {
9
+ if (/^script$/i.test(state.htmlState.context.tagName)) {
10
+ state.token = javascript;
11
+ state.localState = jsMode.startState(htmlMode.indent(state.htmlState, ""));
12
+ state.mode = "javascript";
13
+ }
14
+ else if (/^style$/i.test(state.htmlState.context.tagName)) {
15
+ state.token = css;
16
+ state.localState = cssMode.startState(htmlMode.indent(state.htmlState, ""));
17
+ state.mode = "css";
18
+ }
19
+ }
20
+ return style;
21
+ }
22
+ function maybeBackup(stream, pat, style) {
23
+ var cur = stream.current();
24
+ var close = cur.search(pat);
25
+ if (close > -1) stream.backUp(cur.length - close);
26
+ return style;
27
+ }
28
+ function javascript(stream, state) {
29
+ if (stream.match(/^<\/\s*script\s*>/i, false)) {
30
+ state.token = html;
31
+ state.localState = null;
32
+ state.mode = "html";
33
+ return html(stream, state);
34
+ }
35
+ return maybeBackup(stream, /<\/\s*script\s*>/,
36
+ jsMode.token(stream, state.localState));
37
+ }
38
+ function css(stream, state) {
39
+ if (stream.match(/^<\/\s*style\s*>/i, false)) {
40
+ state.token = html;
41
+ state.localState = null;
42
+ state.mode = "html";
43
+ return html(stream, state);
44
+ }
45
+ return maybeBackup(stream, /<\/\s*style\s*>/,
46
+ cssMode.token(stream, state.localState));
47
+ }
48
+
49
+ return {
50
+ startState: function() {
51
+ var state = htmlMode.startState();
52
+ return {token: html, localState: null, mode: "html", htmlState: state};
53
+ },
54
+
55
+ copyState: function(state) {
56
+ if (state.localState)
57
+ var local = CodeMirror.copyState(state.token == css ? cssMode : jsMode, state.localState);
58
+ return {token: state.token, localState: local, mode: state.mode,
59
+ htmlState: CodeMirror.copyState(htmlMode, state.htmlState)};
60
+ },
61
+
62
+ token: function(stream, state) {
63
+ return state.token(stream, state);
64
+ },
65
+
66
+ indent: function(state, textAfter) {
67
+ if (state.token == html || /^\s*<\//.test(textAfter))
68
+ return htmlMode.indent(state.htmlState, textAfter);
69
+ else if (state.token == javascript)
70
+ return jsMode.indent(state.localState, textAfter);
71
+ else
72
+ return cssMode.indent(state.localState, textAfter);
73
+ },
74
+
75
+ compareStates: function(a, b) {
76
+ if (a.mode != b.mode) return false;
77
+ if (a.localState) return CodeMirror.Pass;
78
+ return htmlMode.compareStates(a.htmlState, b.htmlState);
79
+ },
80
+
81
+ electricChars: "/{}:"
82
+ }
83
+ }, "xml", "javascript", "css");
84
+
85
+ CodeMirror.defineMIME("text/html", "htmlmixed");
@@ -0,0 +1,361 @@
1
+ CodeMirror.defineMode("javascript", function(config, parserConfig) {
2
+ var indentUnit = config.indentUnit;
3
+ var jsonMode = parserConfig.json;
4
+
5
+ // Tokenizer
6
+
7
+ var keywords = function(){
8
+ function kw(type) {return {type: type, style: "keyword"};}
9
+ var A = kw("keyword a"), B = kw("keyword b"), C = kw("keyword c");
10
+ var operator = kw("operator"), atom = {type: "atom", style: "atom"};
11
+ return {
12
+ "if": A, "while": A, "with": A, "else": B, "do": B, "try": B, "finally": B,
13
+ "return": C, "break": C, "continue": C, "new": C, "delete": C, "throw": C,
14
+ "var": kw("var"), "const": kw("var"), "let": kw("var"),
15
+ "function": kw("function"), "catch": kw("catch"),
16
+ "for": kw("for"), "switch": kw("switch"), "case": kw("case"), "default": kw("default"),
17
+ "in": operator, "typeof": operator, "instanceof": operator,
18
+ "true": atom, "false": atom, "null": atom, "undefined": atom, "NaN": atom, "Infinity": atom
19
+ };
20
+ }();
21
+
22
+ var isOperatorChar = /[+\-*&%=<>!?|]/;
23
+
24
+ function chain(stream, state, f) {
25
+ state.tokenize = f;
26
+ return f(stream, state);
27
+ }
28
+
29
+ function nextUntilUnescaped(stream, end) {
30
+ var escaped = false, next;
31
+ while ((next = stream.next()) != null) {
32
+ if (next == end && !escaped)
33
+ return false;
34
+ escaped = !escaped && next == "\\";
35
+ }
36
+ return escaped;
37
+ }
38
+
39
+ // Used as scratch variables to communicate multiple values without
40
+ // consing up tons of objects.
41
+ var type, content;
42
+ function ret(tp, style, cont) {
43
+ type = tp; content = cont;
44
+ return style;
45
+ }
46
+
47
+ function jsTokenBase(stream, state) {
48
+ var ch = stream.next();
49
+ if (ch == '"' || ch == "'")
50
+ return chain(stream, state, jsTokenString(ch));
51
+ else if (/[\[\]{}\(\),;\:\.]/.test(ch))
52
+ return ret(ch);
53
+ else if (ch == "0" && stream.eat(/x/i)) {
54
+ stream.eatWhile(/[\da-f]/i);
55
+ return ret("number", "number");
56
+ }
57
+ else if (/\d/.test(ch)) {
58
+ stream.match(/^\d*(?:\.\d*)?(?:[eE][+\-]?\d+)?/);
59
+ return ret("number", "number");
60
+ }
61
+ else if (ch == "/") {
62
+ if (stream.eat("*")) {
63
+ return chain(stream, state, jsTokenComment);
64
+ }
65
+ else if (stream.eat("/")) {
66
+ stream.skipToEnd();
67
+ return ret("comment", "comment");
68
+ }
69
+ else if (state.reAllowed) {
70
+ nextUntilUnescaped(stream, "/");
71
+ stream.eatWhile(/[gimy]/); // 'y' is "sticky" option in Mozilla
72
+ return ret("regexp", "string-2");
73
+ }
74
+ else {
75
+ stream.eatWhile(isOperatorChar);
76
+ return ret("operator", null, stream.current());
77
+ }
78
+ }
79
+ else if (ch == "#") {
80
+ stream.skipToEnd();
81
+ return ret("error", "error");
82
+ }
83
+ else if (isOperatorChar.test(ch)) {
84
+ stream.eatWhile(isOperatorChar);
85
+ return ret("operator", null, stream.current());
86
+ }
87
+ else {
88
+ stream.eatWhile(/[\w\$_]/);
89
+ var word = stream.current(), known = keywords.propertyIsEnumerable(word) && keywords[word];
90
+ return (known && state.kwAllowed) ? ret(known.type, known.style, word) :
91
+ ret("variable", "variable", word);
92
+ }
93
+ }
94
+
95
+ function jsTokenString(quote) {
96
+ return function(stream, state) {
97
+ if (!nextUntilUnescaped(stream, quote))
98
+ state.tokenize = jsTokenBase;
99
+ return ret("string", "string");
100
+ };
101
+ }
102
+
103
+ function jsTokenComment(stream, state) {
104
+ var maybeEnd = false, ch;
105
+ while (ch = stream.next()) {
106
+ if (ch == "/" && maybeEnd) {
107
+ state.tokenize = jsTokenBase;
108
+ break;
109
+ }
110
+ maybeEnd = (ch == "*");
111
+ }
112
+ return ret("comment", "comment");
113
+ }
114
+
115
+ // Parser
116
+
117
+ var atomicTypes = {"atom": true, "number": true, "variable": true, "string": true, "regexp": true};
118
+
119
+ function JSLexical(indented, column, type, align, prev, info) {
120
+ this.indented = indented;
121
+ this.column = column;
122
+ this.type = type;
123
+ this.prev = prev;
124
+ this.info = info;
125
+ if (align != null) this.align = align;
126
+ }
127
+
128
+ function inScope(state, varname) {
129
+ for (var v = state.localVars; v; v = v.next)
130
+ if (v.name == varname) return true;
131
+ }
132
+
133
+ function parseJS(state, style, type, content, stream) {
134
+ var cc = state.cc;
135
+ // Communicate our context to the combinators.
136
+ // (Less wasteful than consing up a hundred closures on every call.)
137
+ cx.state = state; cx.stream = stream; cx.marked = null, cx.cc = cc;
138
+
139
+ if (!state.lexical.hasOwnProperty("align"))
140
+ state.lexical.align = true;
141
+
142
+ while(true) {
143
+ var combinator = cc.length ? cc.pop() : jsonMode ? expression : statement;
144
+ if (combinator(type, content)) {
145
+ while(cc.length && cc[cc.length - 1].lex)
146
+ cc.pop()();
147
+ if (cx.marked) return cx.marked;
148
+ if (type == "variable" && inScope(state, content)) return "variable-2";
149
+ return style;
150
+ }
151
+ }
152
+ }
153
+
154
+ // Combinator utils
155
+
156
+ var cx = {state: null, column: null, marked: null, cc: null};
157
+ function pass() {
158
+ for (var i = arguments.length - 1; i >= 0; i--) cx.cc.push(arguments[i]);
159
+ }
160
+ function cont() {
161
+ pass.apply(null, arguments);
162
+ return true;
163
+ }
164
+ function register(varname) {
165
+ var state = cx.state;
166
+ if (state.context) {
167
+ cx.marked = "def";
168
+ for (var v = state.localVars; v; v = v.next)
169
+ if (v.name == varname) return;
170
+ state.localVars = {name: varname, next: state.localVars};
171
+ }
172
+ }
173
+
174
+ // Combinators
175
+
176
+ var defaultVars = {name: "this", next: {name: "arguments"}};
177
+ function pushcontext() {
178
+ if (!cx.state.context) cx.state.localVars = defaultVars;
179
+ cx.state.context = {prev: cx.state.context, vars: cx.state.localVars};
180
+ }
181
+ function popcontext() {
182
+ cx.state.localVars = cx.state.context.vars;
183
+ cx.state.context = cx.state.context.prev;
184
+ }
185
+ function pushlex(type, info) {
186
+ var result = function() {
187
+ var state = cx.state;
188
+ state.lexical = new JSLexical(state.indented, cx.stream.column(), type, null, state.lexical, info)
189
+ };
190
+ result.lex = true;
191
+ return result;
192
+ }
193
+ function poplex() {
194
+ var state = cx.state;
195
+ if (state.lexical.prev) {
196
+ if (state.lexical.type == ")")
197
+ state.indented = state.lexical.indented;
198
+ state.lexical = state.lexical.prev;
199
+ }
200
+ }
201
+ poplex.lex = true;
202
+
203
+ function expect(wanted) {
204
+ return function expecting(type) {
205
+ if (type == wanted) return cont();
206
+ else if (wanted == ";") return pass();
207
+ else return cont(arguments.callee);
208
+ };
209
+ }
210
+
211
+ function statement(type) {
212
+ if (type == "var") return cont(pushlex("vardef"), vardef1, expect(";"), poplex);
213
+ if (type == "keyword a") return cont(pushlex("form"), expression, statement, poplex);
214
+ if (type == "keyword b") return cont(pushlex("form"), statement, poplex);
215
+ if (type == "{") return cont(pushlex("}"), block, poplex);
216
+ if (type == ";") return cont();
217
+ if (type == "function") return cont(functiondef);
218
+ if (type == "for") return cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"),
219
+ poplex, statement, poplex);
220
+ if (type == "variable") return cont(pushlex("stat"), maybelabel);
221
+ if (type == "switch") return cont(pushlex("form"), expression, pushlex("}", "switch"), expect("{"),
222
+ block, poplex, poplex);
223
+ if (type == "case") return cont(expression, expect(":"));
224
+ if (type == "default") return cont(expect(":"));
225
+ if (type == "catch") return cont(pushlex("form"), pushcontext, expect("("), funarg, expect(")"),
226
+ statement, poplex, popcontext);
227
+ return pass(pushlex("stat"), expression, expect(";"), poplex);
228
+ }
229
+ function expression(type) {
230
+ if (atomicTypes.hasOwnProperty(type)) return cont(maybeoperator);
231
+ if (type == "function") return cont(functiondef);
232
+ if (type == "keyword c") return cont(maybeexpression);
233
+ if (type == "(") return cont(pushlex(")"), maybeexpression, expect(")"), poplex, maybeoperator);
234
+ if (type == "operator") return cont(expression);
235
+ if (type == "[") return cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
236
+ if (type == "{") return cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
237
+ return cont();
238
+ }
239
+ function maybeexpression(type) {
240
+ if (type.match(/[;\}\)\],]/)) return pass();
241
+ return pass(expression);
242
+ }
243
+
244
+ function maybeoperator(type, value) {
245
+ if (type == "operator" && /\+\+|--/.test(value)) return cont(maybeoperator);
246
+ if (type == "operator" || type == ":") return cont(expression);
247
+ if (type == ";") return;
248
+ if (type == "(") return cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
249
+ if (type == ".") return cont(property, maybeoperator);
250
+ if (type == "[") return cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
251
+ }
252
+ function maybelabel(type) {
253
+ if (type == ":") return cont(poplex, statement);
254
+ return pass(maybeoperator, expect(";"), poplex);
255
+ }
256
+ function property(type) {
257
+ if (type == "variable") {cx.marked = "property"; return cont();}
258
+ }
259
+ function objprop(type) {
260
+ if (type == "variable") cx.marked = "property";
261
+ if (atomicTypes.hasOwnProperty(type)) return cont(expect(":"), expression);
262
+ }
263
+ function commasep(what, end) {
264
+ function proceed(type) {
265
+ if (type == ",") return cont(what, proceed);
266
+ if (type == end) return cont();
267
+ return cont(expect(end));
268
+ }
269
+ return function commaSeparated(type) {
270
+ if (type == end) return cont();
271
+ else return pass(what, proceed);
272
+ };
273
+ }
274
+ function block(type) {
275
+ if (type == "}") return cont();
276
+ return pass(statement, block);
277
+ }
278
+ function vardef1(type, value) {
279
+ if (type == "variable"){register(value); return cont(vardef2);}
280
+ return cont();
281
+ }
282
+ function vardef2(type, value) {
283
+ if (value == "=") return cont(expression, vardef2);
284
+ if (type == ",") return cont(vardef1);
285
+ }
286
+ function forspec1(type) {
287
+ if (type == "var") return cont(vardef1, forspec2);
288
+ if (type == ";") return pass(forspec2);
289
+ if (type == "variable") return cont(formaybein);
290
+ return pass(forspec2);
291
+ }
292
+ function formaybein(type, value) {
293
+ if (value == "in") return cont(expression);
294
+ return cont(maybeoperator, forspec2);
295
+ }
296
+ function forspec2(type, value) {
297
+ if (type == ";") return cont(forspec3);
298
+ if (value == "in") return cont(expression);
299
+ return cont(expression, expect(";"), forspec3);
300
+ }
301
+ function forspec3(type) {
302
+ if (type != ")") cont(expression);
303
+ }
304
+ function functiondef(type, value) {
305
+ if (type == "variable") {register(value); return cont(functiondef);}
306
+ if (type == "(") return cont(pushlex(")"), pushcontext, commasep(funarg, ")"), poplex, statement, popcontext);
307
+ }
308
+ function funarg(type, value) {
309
+ if (type == "variable") {register(value); return cont();}
310
+ }
311
+
312
+ // Interface
313
+
314
+ return {
315
+ startState: function(basecolumn) {
316
+ return {
317
+ tokenize: jsTokenBase,
318
+ reAllowed: true,
319
+ kwAllowed: true,
320
+ cc: [],
321
+ lexical: new JSLexical((basecolumn || 0) - indentUnit, 0, "block", false),
322
+ localVars: parserConfig.localVars,
323
+ context: parserConfig.localVars && {vars: parserConfig.localVars},
324
+ indented: 0
325
+ };
326
+ },
327
+
328
+ token: function(stream, state) {
329
+ if (stream.sol()) {
330
+ if (!state.lexical.hasOwnProperty("align"))
331
+ state.lexical.align = false;
332
+ state.indented = stream.indentation();
333
+ }
334
+ if (stream.eatSpace()) return null;
335
+ var style = state.tokenize(stream, state);
336
+ if (type == "comment") return style;
337
+ state.reAllowed = !!(type == "operator" || type == "keyword c" || type.match(/^[\[{}\(,;:]$/));
338
+ state.kwAllowed = type != '.';
339
+ return parseJS(state, style, type, content, stream);
340
+ },
341
+
342
+ indent: function(state, textAfter) {
343
+ if (state.tokenize != jsTokenBase) return 0;
344
+ var firstChar = textAfter && textAfter.charAt(0), lexical = state.lexical;
345
+ if (lexical.type == "stat" && firstChar == "}") lexical = lexical.prev;
346
+ var type = lexical.type, closing = firstChar == type;
347
+ if (type == "vardef") return lexical.indented + 4;
348
+ else if (type == "form" && firstChar == "{") return lexical.indented;
349
+ else if (type == "stat" || type == "form") return lexical.indented + indentUnit;
350
+ else if (lexical.info == "switch" && !closing)
351
+ return lexical.indented + (/^(?:case|default)\b/.test(textAfter) ? indentUnit : 2 * indentUnit);
352
+ else if (lexical.align) return lexical.column + (closing ? 0 : 1);
353
+ else return lexical.indented + (closing ? 0 : indentUnit);
354
+ },
355
+
356
+ electricChars: ":{}"
357
+ };
358
+ });
359
+
360
+ CodeMirror.defineMIME("text/javascript", "javascript");
361
+ CodeMirror.defineMIME("application/json", {name: "javascript", json: true});