luca 0.8.599 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (149) hide show
  1. data/.gitignore +3 -0
  2. data/.rvmrc +1 -0
  3. data/CHANGELOG +51 -2
  4. data/README.md +10 -247
  5. data/ROADMAP +6 -2
  6. data/app.rb +16 -2
  7. data/assets/javascripts/dependencies/bootstrap.min.js +7 -1
  8. data/assets/javascripts/dependencies/codemirror-coffeescript.js +347 -0
  9. data/assets/javascripts/dependencies/codemirror-css.js +124 -0
  10. data/assets/javascripts/dependencies/codemirror-html.js +410 -0
  11. data/assets/javascripts/dependencies/codemirror-javascript.js +361 -0
  12. data/assets/javascripts/dependencies/codemirror-less.js +232 -0
  13. data/assets/javascripts/dependencies/codemirror-vim.js +500 -0
  14. data/assets/javascripts/dependencies/codemirror.js +3076 -0
  15. data/assets/javascripts/dependencies.coffee +0 -1
  16. data/assets/javascripts/luca-ui-base.coffee +10 -3
  17. data/assets/javascripts/luca-ui-bootstrap.js +1 -0
  18. data/assets/javascripts/luca-ui-development-tools.coffee +9 -0
  19. data/assets/javascripts/luca-ui.coffee +6 -1
  20. data/assets/javascripts/sandbox/application.coffee +51 -0
  21. data/assets/javascripts/sandbox/router.coffee +14 -0
  22. data/assets/javascripts/sandbox/templates/main.luca +33 -0
  23. data/assets/javascripts/sandbox/templates/sandbox/navigation.luca +1 -0
  24. data/assets/javascripts/sandbox/templates/sandbox.luca +1 -0
  25. data/assets/javascripts/sandbox/views/top_navigation.coffee +4 -0
  26. data/assets/javascripts/sandbox.coffee +2 -2
  27. data/assets/stylesheets/bootstrap.min.css +395 -297
  28. data/assets/stylesheets/codemirror-blackboard.css +25 -0
  29. data/assets/stylesheets/codemirror-monokai.css +33 -0
  30. data/assets/stylesheets/codemirror.css +126 -0
  31. data/assets/stylesheets/luca-ui-bootstrap.css +0 -1
  32. data/assets/stylesheets/luca-ui-development-tools.css +5 -0
  33. data/assets/stylesheets/sandbox/sandbox.scss +1 -3
  34. data/assets/stylesheets/themes/amelia-bootstrap.css +826 -0
  35. data/assets/stylesheets/themes/slate-bootstrap.css +797 -0
  36. data/assets/stylesheets/themes/superhero-bootstrap.css +830 -0
  37. data/lib/luca/code_browser.rb +55 -0
  38. data/lib/luca/rails/version.rb +1 -1
  39. data/lib/luca/rails.rb +1 -0
  40. data/spec/components/fields/checkbox_array_spec.coffee +46 -0
  41. data/spec/components/form_view_spec.coffee +10 -4
  42. data/spec/containers/card_view_spec.coffee +7 -0
  43. data/spec/core/collection_spec.coffee +58 -4
  44. data/spec/core/container_spec.coffee +6 -6
  45. data/spec/core/view_spec.coffee +93 -7
  46. data/spec/framework_spec.coffee +15 -12
  47. data/src/components/application.coffee +126 -18
  48. data/src/components/base_toolbar.coffee +2 -2
  49. data/src/components/collection_loader_view.coffee +1 -2
  50. data/src/components/collection_view.coffee +77 -0
  51. data/src/components/controller.coffee +1 -4
  52. data/src/components/fields/button_field.coffee +1 -1
  53. data/src/components/fields/checkbox_array.coffee +2 -2
  54. data/src/components/fields/checkbox_field.coffee +3 -1
  55. data/src/components/fields/file_upload_field.coffee +1 -1
  56. data/src/components/fields/hidden_field.coffee +1 -1
  57. data/src/components/fields/select_field.coffee +1 -1
  58. data/src/components/fields/text_area_field.coffee +1 -1
  59. data/src/components/fields/text_field.coffee +10 -6
  60. data/src/components/fields/type_ahead_field.coffee +18 -5
  61. data/src/components/form_button_toolbar.coffee +1 -2
  62. data/src/components/form_view.coffee +44 -62
  63. data/src/components/grid_view.coffee +27 -20
  64. data/src/components/load_mask.coffee +3 -0
  65. data/src/components/nav_bar.coffee +26 -0
  66. data/src/components/record_manager.coffee +1 -3
  67. data/src/components/router.coffee +1 -1
  68. data/src/components/template.coffee +3 -15
  69. data/src/components/toolbar_dialog.coffee +25 -0
  70. data/src/containers/card_view.coffee +22 -23
  71. data/src/containers/column_view.coffee +1 -6
  72. data/src/containers/modal_view.coffee +20 -71
  73. data/src/containers/panel_toolbar.coffee +156 -0
  74. data/src/containers/panel_view.coffee +1 -1
  75. data/src/containers/split_view.coffee +1 -3
  76. data/src/containers/tab_view.coffee +29 -29
  77. data/src/containers/viewport.coffee +38 -3
  78. data/src/core/collection.coffee +80 -48
  79. data/src/core/container.coffee +153 -72
  80. data/src/core/core.coffee +181 -0
  81. data/src/core/field.coffee +4 -2
  82. data/src/core/model.coffee +1 -1
  83. data/src/core/observer.coffee +3 -3
  84. data/src/core/panel.coffee +143 -0
  85. data/src/core/registry.coffee +104 -0
  86. data/src/core/util.coffee +82 -0
  87. data/src/core/view.coffee +158 -85
  88. data/src/framework.coffee +112 -178
  89. data/src/index.coffee +0 -255
  90. data/src/managers/collection_manager.coffee +1 -0
  91. data/src/samples/definition.coffee +49 -0
  92. data/src/stylesheets/base.scss +0 -78
  93. data/src/stylesheets/components/form_view.scss +8 -3
  94. data/src/stylesheets/components/grid_view.scss +3 -7
  95. data/src/stylesheets/components/load_mask.scss +14 -0
  96. data/src/stylesheets/components/toolbar.scss +0 -15
  97. data/src/stylesheets/containers/container.scss +14 -2
  98. data/src/stylesheets/containers/panels.scss +23 -0
  99. data/src/stylesheets/tools/class_browser.scss +32 -0
  100. data/src/stylesheets/tools/code_editor.scss +24 -0
  101. data/src/stylesheets/tools/component_tester.scss +8 -0
  102. data/src/stylesheets/tools/console.scss +26 -0
  103. data/src/templates/components/collection_loader_view.luca +1 -1
  104. data/src/templates/components/form_view.luca +2 -13
  105. data/src/templates/components/grid_view.luca +0 -2
  106. data/src/templates/components/load_mask.luca +3 -0
  107. data/src/templates/components/nav_bar.luca +2 -0
  108. data/src/templates/containers/tab_view.luca +1 -0
  109. data/src/templates/fields/text_field.luca +4 -1
  110. data/src/tools/class_browser.coffee +39 -0
  111. data/src/tools/code_editor.coffee +258 -0
  112. data/src/tools/code_mirror_field.coffee +57 -0
  113. data/src/tools/coffee_script_editor.coffee +60 -0
  114. data/src/tools/collection_inspector.coffee +4 -0
  115. data/src/tools/component_tester.coffee +472 -0
  116. data/src/tools/components/class_browser_detail.coffee +10 -0
  117. data/src/tools/components/class_browser_list.coffee +74 -0
  118. data/src/tools/console.coffee +147 -0
  119. data/src/tools/development_console.coffee +147 -0
  120. data/src/tools/models/components.coffee +63 -0
  121. data/src/tools/templates/component_tester/help.luca +14 -0
  122. data/vendor/assets/javascripts/luca-ui-base.js +1389 -611
  123. data/vendor/assets/javascripts/luca-ui-bootstrap.js +9 -0
  124. data/vendor/assets/javascripts/luca-ui-development-tools.js +18719 -0
  125. data/vendor/assets/javascripts/luca-ui-spec.js +2065 -878
  126. data/vendor/assets/javascripts/luca-ui.js +1759 -852
  127. data/vendor/assets/javascripts/luca-ui.min.js +3 -3
  128. data/vendor/assets/stylesheets/luca-ui-bootstrap.css +494 -440
  129. data/vendor/assets/stylesheets/luca-ui-development-tools.css +224 -0
  130. data/vendor/assets/stylesheets/luca-ui-spec.css +99 -140
  131. data/vendor/assets/stylesheets/luca-ui.css +99 -140
  132. data/views/index.erb +6 -3
  133. metadata +60 -18
  134. data/assets/javascripts/dependencies/jquery-console.js +0 -649
  135. data/assets/javascripts/development-console.coffee +0 -2
  136. data/assets/javascripts/sandbox/sandbox.coffee +0 -16
  137. data/assets/javascripts/sandbox/templates/features/collection_helpers.luca +0 -33
  138. data/assets/javascripts/sandbox/templates/features/form_demo_code.luca +0 -48
  139. data/assets/javascripts/sandbox/templates/features/grid_demo_code.luca +0 -24
  140. data/assets/javascripts/sandbox/templates/features/introduction.luca +0 -11
  141. data/assets/javascripts/sandbox/templates/features/view_helpers.luca +0 -43
  142. data/assets/javascripts/sandbox/templates/navigation.luca +0 -8
  143. data/assets/javascripts/sandbox/views/form_demo.coffee +0 -47
  144. data/assets/javascripts/sandbox/views/grid_demo.coffee +0 -23
  145. data/assets/javascripts/sandbox/views/pages/collection_events_sample.coffee +0 -1
  146. data/assets/javascripts/sandbox/views/pages/pages_controller.coffee +0 -38
  147. data/src/components/collection_inspector.coffee +0 -2
  148. data/src/components/development_console.coffee +0 -59
  149. data/src/stylesheets/components/development_console.scss +0 -47
@@ -0,0 +1,347 @@
1
+ /**
2
+ * Link to the project's GitHub page:
3
+ * https://github.com/pickhardt/coffeescript-codemirror-mode
4
+ */
5
+ CodeMirror.defineMode('coffeescript', function(conf) {
6
+ var ERRORCLASS = 'error';
7
+
8
+ function wordRegexp(words) {
9
+ return new RegExp("^((" + words.join(")|(") + "))\\b");
10
+ }
11
+
12
+ var singleOperators = new RegExp("^[\\+\\-\\*/%&|\\^~<>!\?]");
13
+ var singleDelimiters = new RegExp('^[\\(\\)\\[\\]\\{\\}@,:`=;\\.]');
14
+ var doubleOperators = new RegExp("^((\->)|(\=>)|(\\+\\+)|(\\+\\=)|(\\-\\-)|(\\-\\=)|(\\*\\*)|(\\*\\=)|(\\/\\/)|(\\/\\=)|(==)|(!=)|(<=)|(>=)|(<>)|(<<)|(>>)|(//))");
15
+ var doubleDelimiters = new RegExp("^((\\.\\.)|(\\+=)|(\\-=)|(\\*=)|(%=)|(/=)|(&=)|(\\|=)|(\\^=))");
16
+ var tripleDelimiters = new RegExp("^((\\.\\.\\.)|(//=)|(>>=)|(<<=)|(\\*\\*=))");
17
+ var identifiers = new RegExp("^[_A-Za-z$][_A-Za-z$0-9]*");
18
+
19
+ var wordOperators = wordRegexp(['and', 'or', 'not',
20
+ 'is', 'isnt', 'in',
21
+ 'instanceof', 'typeof']);
22
+ var indentKeywords = ['for', 'while', 'loop', 'if', 'unless', 'else',
23
+ 'switch', 'try', 'catch', 'finally', 'class'];
24
+ var commonKeywords = ['break', 'by', 'continue', 'debugger', 'delete',
25
+ 'do', 'in', 'of', 'new', 'return', 'then',
26
+ 'this', 'throw', 'when', 'until'];
27
+
28
+ var keywords = wordRegexp(indentKeywords.concat(commonKeywords));
29
+
30
+ indentKeywords = wordRegexp(indentKeywords);
31
+
32
+
33
+ var stringPrefixes = new RegExp("^('{3}|\"{3}|['\"])");
34
+ var regexPrefixes = new RegExp("^(/{3}|/)");
35
+ var commonConstants = ['Infinity', 'NaN', 'undefined', 'null', 'true', 'false', 'on', 'off', 'yes', 'no'];
36
+ var constants = wordRegexp(commonConstants);
37
+
38
+ // Tokenizers
39
+ function tokenBase(stream, state) {
40
+ // Handle scope changes
41
+ if (stream.sol()) {
42
+ var scopeOffset = state.scopes[0].offset;
43
+ if (stream.eatSpace()) {
44
+ var lineOffset = stream.indentation();
45
+ if (lineOffset > scopeOffset) {
46
+ return 'indent';
47
+ } else if (lineOffset < scopeOffset) {
48
+ return 'dedent';
49
+ }
50
+ return null;
51
+ } else {
52
+ if (scopeOffset > 0) {
53
+ dedent(stream, state);
54
+ }
55
+ }
56
+ }
57
+ if (stream.eatSpace()) {
58
+ return null;
59
+ }
60
+
61
+ var ch = stream.peek();
62
+
63
+ // Handle docco title comment (single line)
64
+ if (stream.match("####")) {
65
+ stream.skipToEnd();
66
+ return 'comment';
67
+ }
68
+
69
+ // Handle multi line comments
70
+ if (stream.match("###")) {
71
+ state.tokenize = longComment;
72
+ return state.tokenize(stream, state);
73
+ }
74
+
75
+ // Single line comment
76
+ if (ch === '#') {
77
+ stream.skipToEnd();
78
+ return 'comment';
79
+ }
80
+
81
+ // Handle number literals
82
+ if (stream.match(/^-?[0-9\.]/, false)) {
83
+ var floatLiteral = false;
84
+ // Floats
85
+ if (stream.match(/^-?\d*\.\d+(e[\+\-]?\d+)?/i)) {
86
+ floatLiteral = true;
87
+ }
88
+ if (stream.match(/^-?\d+\.\d*/)) {
89
+ floatLiteral = true;
90
+ }
91
+ if (stream.match(/^-?\.\d+/)) {
92
+ floatLiteral = true;
93
+ }
94
+
95
+ if (floatLiteral) {
96
+ // prevent from getting extra . on 1..
97
+ if (stream.peek() == "."){
98
+ stream.backUp(1);
99
+ }
100
+ return 'number';
101
+ }
102
+ // Integers
103
+ var intLiteral = false;
104
+ // Hex
105
+ if (stream.match(/^-?0x[0-9a-f]+/i)) {
106
+ intLiteral = true;
107
+ }
108
+ // Decimal
109
+ if (stream.match(/^-?[1-9]\d*(e[\+\-]?\d+)?/)) {
110
+ intLiteral = true;
111
+ }
112
+ // Zero by itself with no other piece of number.
113
+ if (stream.match(/^-?0(?![\dx])/i)) {
114
+ intLiteral = true;
115
+ }
116
+ if (intLiteral) {
117
+ return 'number';
118
+ }
119
+ }
120
+
121
+ // Handle strings
122
+ if (stream.match(stringPrefixes)) {
123
+ state.tokenize = tokenFactory(stream.current(), 'string');
124
+ return state.tokenize(stream, state);
125
+ }
126
+ // Handle regex literals
127
+ if (stream.match(regexPrefixes)) {
128
+ if (stream.current() != '/' || stream.match(/^.*\//, false)) { // prevent highlight of division
129
+ state.tokenize = tokenFactory(stream.current(), 'string-2');
130
+ return state.tokenize(stream, state);
131
+ } else {
132
+ stream.backUp(1);
133
+ }
134
+ }
135
+
136
+ // Handle operators and delimiters
137
+ if (stream.match(tripleDelimiters) || stream.match(doubleDelimiters)) {
138
+ return 'punctuation';
139
+ }
140
+ if (stream.match(doubleOperators)
141
+ || stream.match(singleOperators)
142
+ || stream.match(wordOperators)) {
143
+ return 'operator';
144
+ }
145
+ if (stream.match(singleDelimiters)) {
146
+ return 'punctuation';
147
+ }
148
+
149
+ if (stream.match(constants)) {
150
+ return 'atom';
151
+ }
152
+
153
+ if (stream.match(keywords)) {
154
+ return 'keyword';
155
+ }
156
+
157
+ if (stream.match(identifiers)) {
158
+ return 'variable';
159
+ }
160
+
161
+ // Handle non-detected items
162
+ stream.next();
163
+ return ERRORCLASS;
164
+ }
165
+
166
+ function tokenFactory(delimiter, outclass) {
167
+ var singleline = delimiter.length == 1;
168
+ return function tokenString(stream, state) {
169
+ while (!stream.eol()) {
170
+ stream.eatWhile(/[^'"\/\\]/);
171
+ if (stream.eat('\\')) {
172
+ stream.next();
173
+ if (singleline && stream.eol()) {
174
+ return outclass;
175
+ }
176
+ } else if (stream.match(delimiter)) {
177
+ state.tokenize = tokenBase;
178
+ return outclass;
179
+ } else {
180
+ stream.eat(/['"\/]/);
181
+ }
182
+ }
183
+ if (singleline) {
184
+ if (conf.mode.singleLineStringErrors) {
185
+ outclass = ERRORCLASS
186
+ } else {
187
+ state.tokenize = tokenBase;
188
+ }
189
+ }
190
+ return outclass;
191
+ };
192
+ }
193
+
194
+ function longComment(stream, state) {
195
+ while (!stream.eol()) {
196
+ stream.eatWhile(/[^#]/);
197
+ if (stream.match("###")) {
198
+ state.tokenize = tokenBase;
199
+ break;
200
+ }
201
+ stream.eatWhile("#");
202
+ }
203
+ return "comment"
204
+ }
205
+
206
+ function indent(stream, state, type) {
207
+ type = type || 'coffee';
208
+ var indentUnit = 0;
209
+ if (type === 'coffee') {
210
+ for (var i = 0; i < state.scopes.length; i++) {
211
+ if (state.scopes[i].type === 'coffee') {
212
+ indentUnit = state.scopes[i].offset + conf.indentUnit;
213
+ break;
214
+ }
215
+ }
216
+ } else {
217
+ indentUnit = stream.column() + stream.current().length;
218
+ }
219
+ state.scopes.unshift({
220
+ offset: indentUnit,
221
+ type: type
222
+ });
223
+ }
224
+
225
+ function dedent(stream, state) {
226
+ if (state.scopes.length == 1) return;
227
+ if (state.scopes[0].type === 'coffee') {
228
+ var _indent = stream.indentation();
229
+ var _indent_index = -1;
230
+ for (var i = 0; i < state.scopes.length; ++i) {
231
+ if (_indent === state.scopes[i].offset) {
232
+ _indent_index = i;
233
+ break;
234
+ }
235
+ }
236
+ if (_indent_index === -1) {
237
+ return true;
238
+ }
239
+ while (state.scopes[0].offset !== _indent) {
240
+ state.scopes.shift();
241
+ }
242
+ return false
243
+ } else {
244
+ state.scopes.shift();
245
+ return false;
246
+ }
247
+ }
248
+
249
+ function tokenLexer(stream, state) {
250
+ var style = state.tokenize(stream, state);
251
+ var current = stream.current();
252
+
253
+ // Handle '.' connected identifiers
254
+ if (current === '.') {
255
+ style = state.tokenize(stream, state);
256
+ current = stream.current();
257
+ if (style === 'variable') {
258
+ return 'variable';
259
+ } else {
260
+ return ERRORCLASS;
261
+ }
262
+ }
263
+
264
+ // Handle properties
265
+ if (current === '@') {
266
+ stream.eat('@');
267
+ return 'keyword';
268
+ }
269
+
270
+ // Handle scope changes.
271
+ if (current === 'return') {
272
+ state.dedent += 1;
273
+ }
274
+ if (((current === '->' || current === '=>') &&
275
+ !state.lambda &&
276
+ state.scopes[0].type == 'coffee' &&
277
+ stream.peek() === '')
278
+ || style === 'indent') {
279
+ indent(stream, state);
280
+ }
281
+ var delimiter_index = '[({'.indexOf(current);
282
+ if (delimiter_index !== -1) {
283
+ indent(stream, state, '])}'.slice(delimiter_index, delimiter_index+1));
284
+ }
285
+ if (indentKeywords.exec(current)){
286
+ indent(stream, state);
287
+ }
288
+ if (current == 'then'){
289
+ dedent(stream, state);
290
+ }
291
+
292
+
293
+ if (style === 'dedent') {
294
+ if (dedent(stream, state)) {
295
+ return ERRORCLASS;
296
+ }
297
+ }
298
+ delimiter_index = '])}'.indexOf(current);
299
+ if (delimiter_index !== -1) {
300
+ if (dedent(stream, state)) {
301
+ return ERRORCLASS;
302
+ }
303
+ }
304
+ if (state.dedent > 0 && stream.eol() && state.scopes[0].type == 'coffee') {
305
+ if (state.scopes.length > 1) state.scopes.shift();
306
+ state.dedent -= 1;
307
+ }
308
+
309
+ return style;
310
+ }
311
+
312
+ var external = {
313
+ startState: function(basecolumn) {
314
+ return {
315
+ tokenize: tokenBase,
316
+ scopes: [{offset:basecolumn || 0, type:'coffee'}],
317
+ lastToken: null,
318
+ lambda: false,
319
+ dedent: 0
320
+ };
321
+ },
322
+
323
+ token: function(stream, state) {
324
+ var style = tokenLexer(stream, state);
325
+
326
+ state.lastToken = {style:style, content: stream.current()};
327
+
328
+ if (stream.eol() && stream.lambda) {
329
+ state.lambda = false;
330
+ }
331
+
332
+ return style;
333
+ },
334
+
335
+ indent: function(state, textAfter) {
336
+ if (state.tokenize != tokenBase) {
337
+ return 0;
338
+ }
339
+
340
+ return state.scopes[0].offset;
341
+ }
342
+
343
+ };
344
+ return external;
345
+ });
346
+
347
+ CodeMirror.defineMIME('text/x-coffeescript', 'coffeescript');
@@ -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");