Browse Source

add ace code editor

adam j hartz 2 months ago
parent
commit
8183c0fa88
100 changed files with 96819 additions and 0 deletions
  1. 2
    0
      CHANGELOG.md
  2. 33
    0
      LICENSE.bundled_software
  3. 21301
    0
      catsoop/__STATIC__/scripts/ace/ace.js
  4. 317
    0
      catsoop/__STATIC__/scripts/ace/ext-beautify.js
  5. 277
    0
      catsoop/__STATIC__/scripts/ace/ext-elastic_tabstops_lite.js
  6. 1226
    0
      catsoop/__STATIC__/scripts/ace/ext-emmet.js
  7. 9
    0
      catsoop/__STATIC__/scripts/ace/ext-error_marker.js
  8. 188
    0
      catsoop/__STATIC__/scripts/ace/ext-keybinding_menu.js
  9. 1993
    0
      catsoop/__STATIC__/scripts/ace/ext-language_tools.js
  10. 64
    0
      catsoop/__STATIC__/scripts/ace/ext-linking.js
  11. 232
    0
      catsoop/__STATIC__/scripts/ace/ext-modelist.js
  12. 753
    0
      catsoop/__STATIC__/scripts/ace/ext-options.js
  13. 109
    0
      catsoop/__STATIC__/scripts/ace/ext-rtl.js
  14. 516
    0
      catsoop/__STATIC__/scripts/ace/ext-searchbox.js
  15. 774
    0
      catsoop/__STATIC__/scripts/ace/ext-settings_menu.js
  16. 74
    0
      catsoop/__STATIC__/scripts/ace/ext-spellcheck.js
  17. 207
    0
      catsoop/__STATIC__/scripts/ace/ext-split.js
  18. 229
    0
      catsoop/__STATIC__/scripts/ace/ext-static_highlight.js
  19. 56
    0
      catsoop/__STATIC__/scripts/ace/ext-statusbar.js
  20. 563
    0
      catsoop/__STATIC__/scripts/ace/ext-textarea.js
  21. 65
    0
      catsoop/__STATIC__/scripts/ace/ext-themelist.js
  22. 209
    0
      catsoop/__STATIC__/scripts/ace/ext-whitespace.js
  23. 1169
    0
      catsoop/__STATIC__/scripts/ace/keybinding-emacs.js
  24. 5661
    0
      catsoop/__STATIC__/scripts/ace/keybinding-vim.js
  25. 221
    0
      catsoop/__STATIC__/scripts/ace/mode-abap.js
  26. 268
    0
      catsoop/__STATIC__/scripts/ace/mode-abc.js
  27. 274
    0
      catsoop/__STATIC__/scripts/ace/mode-actionscript.js
  28. 137
    0
      catsoop/__STATIC__/scripts/ace/mode-ada.js
  29. 363
    0
      catsoop/__STATIC__/scripts/ace/mode-apache_conf.js
  30. 457
    0
      catsoop/__STATIC__/scripts/ace/mode-apex.js
  31. 278
    0
      catsoop/__STATIC__/scripts/ace/mode-applescript.js
  32. 349
    0
      catsoop/__STATIC__/scripts/ace/mode-asciidoc.js
  33. 421
    0
      catsoop/__STATIC__/scripts/ace/mode-asl.js
  34. 193
    0
      catsoop/__STATIC__/scripts/ace/mode-assembly_x86.js
  35. 242
    0
      catsoop/__STATIC__/scripts/ace/mode-autohotkey.js
  36. 230
    0
      catsoop/__STATIC__/scripts/ace/mode-batchfile.js
  37. 334
    0
      catsoop/__STATIC__/scripts/ace/mode-bro.js
  38. 293
    0
      catsoop/__STATIC__/scripts/ace/mode-c9search.js
  39. 502
    0
      catsoop/__STATIC__/scripts/ace/mode-c_cpp.js
  40. 210
    0
      catsoop/__STATIC__/scripts/ace/mode-cirru.js
  41. 314
    0
      catsoop/__STATIC__/scripts/ace/mode-clojure.js
  42. 101
    0
      catsoop/__STATIC__/scripts/ace/mode-cobol.js
  43. 399
    0
      catsoop/__STATIC__/scripts/ace/mode-coffee.js
  44. 2597
    0
      catsoop/__STATIC__/scripts/ace/mode-coldfusion.js
  45. 502
    0
      catsoop/__STATIC__/scripts/ace/mode-csharp.js
  46. 3988
    0
      catsoop/__STATIC__/scripts/ace/mode-csound_document.js
  47. 2924
    0
      catsoop/__STATIC__/scripts/ace/mode-csound_orchestra.js
  48. 453
    0
      catsoop/__STATIC__/scripts/ace/mode-csound_score.js
  49. 59
    0
      catsoop/__STATIC__/scripts/ace/mode-csp.js
  50. 723
    0
      catsoop/__STATIC__/scripts/ace/mode-css.js
  51. 2565
    0
      catsoop/__STATIC__/scripts/ace/mode-curly.js
  52. 520
    0
      catsoop/__STATIC__/scripts/ace/mode-d.js
  53. 713
    0
      catsoop/__STATIC__/scripts/ace/mode-dart.js
  54. 146
    0
      catsoop/__STATIC__/scripts/ace/mode-diff.js
  55. 2595
    0
      catsoop/__STATIC__/scripts/ace/mode-django.js
  56. 499
    0
      catsoop/__STATIC__/scripts/ace/mode-dockerfile.js
  57. 417
    0
      catsoop/__STATIC__/scripts/ace/mode-dot.js
  58. 497
    0
      catsoop/__STATIC__/scripts/ace/mode-drools.js
  59. 165
    0
      catsoop/__STATIC__/scripts/ace/mode-edifact.js
  60. 135
    0
      catsoop/__STATIC__/scripts/ace/mode-eiffel.js
  61. 3029
    0
      catsoop/__STATIC__/scripts/ace/mode-ejs.js
  62. 501
    0
      catsoop/__STATIC__/scripts/ace/mode-elixir.js
  63. 306
    0
      catsoop/__STATIC__/scripts/ace/mode-elm.js
  64. 1009
    0
      catsoop/__STATIC__/scripts/ace/mode-erlang.js
  65. 297
    0
      catsoop/__STATIC__/scripts/ace/mode-forth.js
  66. 430
    0
      catsoop/__STATIC__/scripts/ace/mode-fortran.js
  67. 313
    0
      catsoop/__STATIC__/scripts/ace/mode-fsharp.js
  68. 258
    0
      catsoop/__STATIC__/scripts/ace/mode-fsl.js
  69. 1193
    0
      catsoop/__STATIC__/scripts/ace/mode-ftl.js
  70. 93
    0
      catsoop/__STATIC__/scripts/ace/mode-gcode.js
  71. 170
    0
      catsoop/__STATIC__/scripts/ace/mode-gherkin.js
  72. 59
    0
      catsoop/__STATIC__/scripts/ace/mode-gitignore.js
  73. 579
    0
      catsoop/__STATIC__/scripts/ace/mode-glsl.js
  74. 930
    0
      catsoop/__STATIC__/scripts/ace/mode-gobstones.js
  75. 413
    0
      catsoop/__STATIC__/scripts/ace/mode-golang.js
  76. 213
    0
      catsoop/__STATIC__/scripts/ace/mode-graphqlschema.js
  77. 988
    0
      catsoop/__STATIC__/scripts/ace/mode-groovy.js
  78. 1558
    0
      catsoop/__STATIC__/scripts/ace/mode-haml.js
  79. 2623
    0
      catsoop/__STATIC__/scripts/ace/mode-handlebars.js
  80. 379
    0
      catsoop/__STATIC__/scripts/ace/mode-haskell.js
  81. 141
    0
      catsoop/__STATIC__/scripts/ace/mode-haskell_cabal.js
  82. 386
    0
      catsoop/__STATIC__/scripts/ace/mode-haxe.js
  83. 353
    0
      catsoop/__STATIC__/scripts/ace/mode-hjson.js
  84. 2510
    0
      catsoop/__STATIC__/scripts/ace/mode-html.js
  85. 3089
    0
      catsoop/__STATIC__/scripts/ace/mode-html_elixir.js
  86. 3046
    0
      catsoop/__STATIC__/scripts/ace/mode-html_ruby.js
  87. 162
    0
      catsoop/__STATIC__/scripts/ace/mode-ini.js
  88. 253
    0
      catsoop/__STATIC__/scripts/ace/mode-io.js
  89. 346
    0
      catsoop/__STATIC__/scripts/ace/mode-jack.js
  90. 2025
    0
      catsoop/__STATIC__/scripts/ace/mode-jade.js
  91. 1036
    0
      catsoop/__STATIC__/scripts/ace/mode-java.js
  92. 797
    0
      catsoop/__STATIC__/scripts/ace/mode-javascript.js
  93. 326
    0
      catsoop/__STATIC__/scripts/ace/mode-json.js
  94. 2631
    0
      catsoop/__STATIC__/scripts/ace/mode-jsoniq.js
  95. 1432
    0
      catsoop/__STATIC__/scripts/ace/mode-jsp.js
  96. 325
    0
      catsoop/__STATIC__/scripts/ace/mode-jssm.js
  97. 411
    0
      catsoop/__STATIC__/scripts/ace/mode-jsx.js
  98. 303
    0
      catsoop/__STATIC__/scripts/ace/mode-julia.js
  99. 795
    0
      catsoop/__STATIC__/scripts/ace/mode-kotlin.js
  100. 0
    0
      catsoop/__STATIC__/scripts/ace/mode-latex.js

+ 2
- 0
CHANGELOG.md View File

@@ -26,6 +26,8 @@ _Work toward next release.  Currently under development._
26 26
 
27 27
 * Brought back breadcrumbs in the default theme.
28 28
 
29
+* The CAT-SOOP source distribution now includes the [Ace code editor](https://ace.c9.io/).
30
+
29 31
 #### CHANGED:
30 32
 
31 33
 * Updated KaTeX to version 0.10.0.

+ 33
- 0
LICENSE.bundled_software View File

@@ -6,6 +6,39 @@ those pieces of software, separated by the string ######
6 6
 ########
7 7
 
8 8
 
9
+The CAT-SOOP distribution contains a verbatim copy of Ace, a web-based code
10
+editor (https://ace.c9.io/) in the __STATIC__/scripts/ace directory.  Ace is
11
+licensed according to the following terms:
12
+
13
+Copyright (c) 2010, Ajax.org B.V.
14
+All rights reserved.
15
+
16
+Redistribution and use in source and binary forms, with or without
17
+modification, are permitted provided that the following conditions are met:
18
+    * Redistributions of source code must retain the above copyright notice,
19
+      this list of conditions and the following disclaimer.
20
+    * Redistributions in binary form must reproduce the above copyright notice,
21
+      this list of conditions and the following disclaimer in the documentation
22
+      and/or other materials provided with the distribution.
23
+    * Neither the name of Ajax.org B.V. nor the names of its contributors may
24
+      be used to endorse or promote products derived from this software without
25
+      specific prior written permission.
26
+
27
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
28
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
29
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30
+DISCLAIMED. IN NO EVENT SHALL AJAX.ORG B.V. BE LIABLE FOR ANY DIRECT, INDIRECT,
31
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
32
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
33
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
34
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
35
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
36
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
37
+
38
+
39
+########
40
+
41
+
9 42
 The CAT-SOOP distribution contains a verbatim copy of KaTeX, a Javascript
10 43
 library for fast math rendering in the browser (https://khan.github.io/KaTeX/),
11 44
 in the __STATIC__/scripts/katex directory.  Some fonts included with KaTeX are

+ 21301
- 0
catsoop/__STATIC__/scripts/ace/ace.js
File diff suppressed because it is too large
View File


+ 317
- 0
catsoop/__STATIC__/scripts/ace/ext-beautify.js View File

@@ -0,0 +1,317 @@
1
+ace.define("ace/ext/beautify",["require","exports","module","ace/token_iterator"], function(require, exports, module) {
2
+"use strict";
3
+var TokenIterator = require("../token_iterator").TokenIterator;
4
+
5
+function is(token, type) {
6
+    return token.type.lastIndexOf(type + ".xml") > -1;
7
+}
8
+exports.singletonTags = ["area", "base", "br", "col", "command", "embed", "hr", "html", "img", "input", "keygen", "link", "meta", "param", "source", "track", "wbr"];
9
+exports.blockTags = ["article", "aside", "blockquote", "body", "div", "dl", "fieldset", "footer", "form", "head", "header", "html", "nav", "ol", "p", "script", "section", "style", "table", "tbody", "tfoot", "thead", "ul"];
10
+
11
+exports.beautify = function(session) {
12
+    var iterator = new TokenIterator(session, 0, 0);
13
+    var token = iterator.getCurrentToken();
14
+    var tabString = session.getTabString();
15
+    var singletonTags = exports.singletonTags;
16
+    var blockTags = exports.blockTags;
17
+    var nextToken;
18
+    var breakBefore = false;
19
+    var spaceBefore = false;
20
+    var spaceAfter = false;
21
+    var code = "";
22
+    var value = "";
23
+    var tagName = "";
24
+    var depth = 0;
25
+    var lastDepth = 0;
26
+    var lastIndent = 0;
27
+    var indent = 0;
28
+    var unindent = 0;
29
+    var roundDepth = 0;
30
+    var onCaseLine = false;
31
+    var row;
32
+    var curRow = 0;
33
+    var rowsToAdd = 0;
34
+    var rowTokens = [];
35
+    var abort = false;
36
+    var i;
37
+    var indentNextLine = false;
38
+    var inTag = false;
39
+    var inCSS = false;
40
+    var inBlock = false;
41
+    var levels = {0: 0};
42
+    var parents = {};
43
+
44
+    var trimNext = function() {
45
+        if (nextToken && nextToken.value && nextToken.type !== 'string.regexp')
46
+            nextToken.value = nextToken.value.trim();
47
+    };
48
+
49
+    var trimLine = function() {
50
+        code = code.replace(/ +$/, "");
51
+    };
52
+
53
+    var trimCode = function() {
54
+        code = code.trimRight();
55
+        breakBefore = false;
56
+    };
57
+
58
+    while (token !== null) {
59
+        curRow = iterator.getCurrentTokenRow();
60
+        rowTokens = iterator.$rowTokens;
61
+        nextToken = iterator.stepForward();
62
+
63
+        if (typeof token !== "undefined") {
64
+            value = token.value;
65
+            unindent = 0;
66
+            inCSS = (tagName === "style" || session.$modeId === "ace/mode/css");
67
+            if (is(token, "tag-open")) {
68
+                inTag = true;
69
+                if (nextToken)
70
+                    inBlock = (blockTags.indexOf(nextToken.value) !== -1);
71
+                if (value === "</") {
72
+                    if (inBlock && !breakBefore && rowsToAdd < 1)
73
+                        rowsToAdd++;
74
+
75
+                    if (inCSS)
76
+                        rowsToAdd = 1;
77
+
78
+                    unindent = 1;
79
+                    inBlock = false;
80
+                }
81
+            } else if (is(token, "tag-close")) {
82
+                inTag = false;
83
+            } else if (is(token, "comment.start")) {
84
+                inBlock = true;
85
+            } else if (is(token, "comment.end")) {
86
+                inBlock = false;
87
+            }
88
+            if (!inTag && !rowsToAdd && token.type === "paren.rparen" && token.value.substr(0, 1) === "}") {
89
+                rowsToAdd++;
90
+            }
91
+            if (curRow !== row) {
92
+                rowsToAdd = curRow;
93
+
94
+                if (row)
95
+                    rowsToAdd -= row;
96
+            }
97
+
98
+            if (rowsToAdd) {
99
+                trimCode();
100
+                for (; rowsToAdd > 0; rowsToAdd--)
101
+                    code += "\n";
102
+
103
+                breakBefore = true;
104
+                if (!is(token, "comment") && !token.type.match(/^(comment|string)$/))
105
+                   value = value.trimLeft();
106
+            }
107
+
108
+            if (value) {
109
+                if (token.type === "keyword" && value.match(/^(if|else|elseif|for|foreach|while|switch)$/)) {
110
+                    parents[depth] = value;
111
+
112
+                    trimNext();
113
+                    spaceAfter = true;
114
+                    if (value.match(/^(else|elseif)$/)) {
115
+                        if (code.match(/\}[\s]*$/)) {
116
+                            trimCode();
117
+                            spaceBefore = true;
118
+                        }
119
+                    }
120
+                } else if (token.type === "paren.lparen") {
121
+                    trimNext();
122
+                    if (value.substr(-1) === "{") {
123
+                        spaceAfter = true;
124
+                        indentNextLine = false;
125
+
126
+                        if(!inTag)
127
+                            rowsToAdd = 1;
128
+                    }
129
+                    if (value.substr(0, 1) === "{") {
130
+                        spaceBefore = true;
131
+                        if (code.substr(-1) !== '[' && code.trimRight().substr(-1) === '[') {
132
+                            trimCode();
133
+                            spaceBefore = false;
134
+                        } else if (code.trimRight().substr(-1) === ')') {
135
+                            trimCode();
136
+                        } else {
137
+                            trimLine();
138
+                        }
139
+                    }
140
+                } else if (token.type === "paren.rparen") {
141
+                    unindent = 1;
142
+                    if (value.substr(0, 1) === "}") {
143
+                        if (parents[depth-1] === 'case')
144
+                            unindent++;
145
+
146
+                        if (code.trimRight().substr(-1) === '{') {
147
+                            trimCode();
148
+                        } else {
149
+                            spaceBefore = true;
150
+
151
+                            if (inCSS)
152
+                                rowsToAdd+=2;
153
+                        }
154
+                    }
155
+                    if (value.substr(0, 1) === "]") {
156
+                        if (code.substr(-1) !== '}' && code.trimRight().substr(-1) === '}') {
157
+                            spaceBefore = false;
158
+                            indent++;
159
+                            trimCode();
160
+                        }
161
+                    }
162
+                    if (value.substr(0, 1) === ")") {
163
+                        if (code.substr(-1) !== '(' && code.trimRight().substr(-1) === '(') {
164
+                            spaceBefore = false;
165
+                            indent++;
166
+                            trimCode();
167
+                        }
168
+                    }
169
+
170
+                    trimLine();
171
+                } else if ((token.type === "keyword.operator" || token.type === "keyword") && value.match(/^(=|==|===|!=|!==|&&|\|\||and|or|xor|\+=|.=|>|>=|<|<=|=>)$/)) {
172
+                    trimCode();
173
+                    trimNext();
174
+                    spaceBefore = true;
175
+                    spaceAfter = true;
176
+                } else if (token.type === "punctuation.operator" && value === ';') {
177
+                    trimCode();
178
+                    trimNext();
179
+                    spaceAfter = true;
180
+
181
+                    if (inCSS)
182
+                        rowsToAdd++;
183
+                } else if (token.type === "punctuation.operator" && value.match(/^(:|,)$/)) {
184
+                    trimCode();
185
+                    trimNext();
186
+                    spaceAfter = true;
187
+                    breakBefore = false;
188
+                } else if (token.type === "support.php_tag" && value === "?>" && !breakBefore) {
189
+                    trimCode();
190
+                    spaceBefore = true;
191
+                } else if (is(token, "attribute-name") && code.substr(-1).match(/^\s$/)) {
192
+                    spaceBefore = true;
193
+                } else if (is(token, "attribute-equals")) {
194
+                    trimLine();
195
+                    trimNext();
196
+                } else if (is(token, "tag-close")) {
197
+                    trimLine();
198
+                    if(value === "/>")
199
+                        spaceBefore = true;
200
+                }
201
+                if (breakBefore && !(token.type.match(/^(comment)$/) && !value.substr(0, 1).match(/^[/#]$/)) && !(token.type.match(/^(string)$/) && !value.substr(0, 1).match(/^['"]$/))) {
202
+
203
+                    indent = lastIndent;
204
+
205
+                    if(depth > lastDepth) {
206
+                        indent++;
207
+
208
+                        for (i=depth; i > lastDepth; i--)
209
+                            levels[i] = indent;
210
+                    } else if(depth < lastDepth)
211
+                        indent = levels[depth];
212
+
213
+                    lastDepth = depth;
214
+                    lastIndent = indent;
215
+
216
+                    if(unindent)
217
+                        indent -= unindent;
218
+
219
+                    if (indentNextLine && !roundDepth) {
220
+                        indent++;
221
+                        indentNextLine = false;
222
+                    }
223
+
224
+                    for (i = 0; i < indent; i++)
225
+                        code += tabString;
226
+                }
227
+
228
+
229
+                if (token.type === "keyword" && value.match(/^(case|default)$/)) {
230
+                    parents[depth] = value;
231
+                    depth++;
232
+                }
233
+
234
+
235
+                if (token.type === "keyword" && value.match(/^(break)$/)) {
236
+                    if(parents[depth-1] && parents[depth-1].match(/^(case|default)$/)) {
237
+                        depth--;
238
+                    }
239
+                }
240
+                if (token.type === "paren.lparen") {
241
+                    roundDepth += (value.match(/\(/g) || []).length;
242
+                    depth += value.length;
243
+                }
244
+
245
+                if (token.type === "keyword" && value.match(/^(if|else|elseif|for|while)$/)) {
246
+                    indentNextLine = true;
247
+                    roundDepth = 0;
248
+                } else if (!roundDepth && value.trim() && token.type !== "comment")
249
+                    indentNextLine = false;
250
+
251
+                if (token.type === "paren.rparen") {
252
+                    roundDepth -= (value.match(/\)/g) || []).length;
253
+
254
+                    for (i = 0; i < value.length; i++) {
255
+                        depth--;
256
+                        if(value.substr(i, 1)==='}' && parents[depth]==='case') {
257
+                            depth--;
258
+                        }
259
+                    }
260
+                }
261
+                if (spaceBefore && !breakBefore) {
262
+                    trimLine();
263
+                    if (code.substr(-1) !== "\n")
264
+                        code += " ";
265
+                }
266
+
267
+                code += value;
268
+
269
+                if (spaceAfter)
270
+                    code += " ";
271
+
272
+                breakBefore = false;
273
+                spaceBefore = false;
274
+                spaceAfter = false;
275
+                if ((is(token, "tag-close") && (inBlock || blockTags.indexOf(tagName) !== -1)) || (is(token, "doctype") && value === ">")) {
276
+                    if (inBlock && nextToken && nextToken.value === "</")
277
+                        rowsToAdd = -1;
278
+                    else
279
+                        rowsToAdd = 1;
280
+                }
281
+                if (is(token, "tag-open") && value === "</") {
282
+                    depth--;
283
+                } else if (is(token, "tag-open") && value === "<" && singletonTags.indexOf(nextToken.value) === -1) {
284
+                    depth++;
285
+                } else if (is(token, "tag-name")) {
286
+                    tagName = value;
287
+                } else if (is(token, "tag-close") && value === "/>" && singletonTags.indexOf(tagName) === -1){
288
+                    depth--;
289
+                }
290
+
291
+                row = curRow;
292
+            }
293
+        }
294
+
295
+        token = nextToken;
296
+    }
297
+
298
+    code = code.trim();
299
+    session.doc.setValue(code);
300
+};
301
+
302
+exports.commands = [{
303
+    name: "beautify",
304
+    exec: function(editor) {
305
+        exports.beautify(editor.session);
306
+    },
307
+    bindKey: "Ctrl-Shift-B"
308
+}];
309
+
310
+});                (function() {
311
+                    ace.require(["ace/ext/beautify"], function(m) {
312
+                        if (typeof module == "object" && typeof exports == "object" && module) {
313
+                            module.exports = m;
314
+                        }
315
+                    });
316
+                })();
317
+            

+ 277
- 0
catsoop/__STATIC__/scripts/ace/ext-elastic_tabstops_lite.js View File

@@ -0,0 +1,277 @@
1
+ace.define("ace/ext/elastic_tabstops_lite",["require","exports","module","ace/editor","ace/config"], function(require, exports, module) {
2
+"use strict";
3
+
4
+var ElasticTabstopsLite = function(editor) {
5
+    this.$editor = editor;
6
+    var self = this;
7
+    var changedRows = [];
8
+    var recordChanges = false;
9
+    this.onAfterExec = function() {
10
+        recordChanges = false;
11
+        self.processRows(changedRows);
12
+        changedRows = [];
13
+    };
14
+    this.onExec = function() {
15
+        recordChanges = true;
16
+    };
17
+    this.onChange = function(delta) {
18
+        if (recordChanges) {
19
+            if (changedRows.indexOf(delta.start.row) == -1)
20
+                changedRows.push(delta.start.row);
21
+            if (delta.end.row != delta.start.row)
22
+                changedRows.push(delta.end.row);
23
+        }
24
+    };
25
+};
26
+
27
+(function() {
28
+    this.processRows = function(rows) {
29
+        this.$inChange = true;
30
+        var checkedRows = [];
31
+
32
+        for (var r = 0, rowCount = rows.length; r < rowCount; r++) {
33
+            var row = rows[r];
34
+
35
+            if (checkedRows.indexOf(row) > -1)
36
+                continue;
37
+
38
+            var cellWidthObj = this.$findCellWidthsForBlock(row);
39
+            var cellWidths = this.$setBlockCellWidthsToMax(cellWidthObj.cellWidths);
40
+            var rowIndex = cellWidthObj.firstRow;
41
+
42
+            for (var w = 0, l = cellWidths.length; w < l; w++) {
43
+                var widths = cellWidths[w];
44
+                checkedRows.push(rowIndex);
45
+                this.$adjustRow(rowIndex, widths);
46
+                rowIndex++;
47
+            }
48
+        }
49
+        this.$inChange = false;
50
+    };
51
+
52
+    this.$findCellWidthsForBlock = function(row) {
53
+        var cellWidths = [], widths;
54
+        var rowIter = row;
55
+        while (rowIter >= 0) {
56
+            widths = this.$cellWidthsForRow(rowIter);
57
+            if (widths.length == 0)
58
+                break;
59
+
60
+            cellWidths.unshift(widths);
61
+            rowIter--;
62
+        }
63
+        var firstRow = rowIter + 1;
64
+        rowIter = row;
65
+        var numRows = this.$editor.session.getLength();
66
+
67
+        while (rowIter < numRows - 1) {
68
+            rowIter++;
69
+
70
+            widths = this.$cellWidthsForRow(rowIter);
71
+            if (widths.length == 0)
72
+                break;
73
+
74
+            cellWidths.push(widths);
75
+        }
76
+
77
+        return { cellWidths: cellWidths, firstRow: firstRow };
78
+    };
79
+
80
+    this.$cellWidthsForRow = function(row) {
81
+        var selectionColumns = this.$selectionColumnsForRow(row);
82
+
83
+        var tabs = [-1].concat(this.$tabsForRow(row));
84
+        var widths = tabs.map(function(el) { return 0; } ).slice(1);
85
+        var line = this.$editor.session.getLine(row);
86
+
87
+        for (var i = 0, len = tabs.length - 1; i < len; i++) {
88
+            var leftEdge = tabs[i]+1;
89
+            var rightEdge = tabs[i+1];
90
+
91
+            var rightmostSelection = this.$rightmostSelectionInCell(selectionColumns, rightEdge);
92
+            var cell = line.substring(leftEdge, rightEdge);
93
+            widths[i] = Math.max(cell.replace(/\s+$/g,'').length, rightmostSelection - leftEdge);
94
+        }
95
+
96
+        return widths;
97
+    };
98
+
99
+    this.$selectionColumnsForRow = function(row) {
100
+        var selections = [], cursor = this.$editor.getCursorPosition();
101
+        if (this.$editor.session.getSelection().isEmpty()) {
102
+            if (row == cursor.row)
103
+                selections.push(cursor.column);
104
+        }
105
+
106
+        return selections;
107
+    };
108
+
109
+    this.$setBlockCellWidthsToMax = function(cellWidths) {
110
+        var startingNewBlock = true, blockStartRow, blockEndRow, maxWidth;
111
+        var columnInfo = this.$izip_longest(cellWidths);
112
+
113
+        for (var c = 0, l = columnInfo.length; c < l; c++) {
114
+            var column = columnInfo[c];
115
+            if (!column.push) {
116
+                console.error(column);
117
+                continue;
118
+            }
119
+            column.push(NaN);
120
+
121
+            for (var r = 0, s = column.length; r < s; r++) {
122
+                var width = column[r];
123
+                if (startingNewBlock) {
124
+                    blockStartRow = r;
125
+                    maxWidth = 0;
126
+                    startingNewBlock = false;
127
+                }
128
+                if (isNaN(width)) {
129
+                    blockEndRow = r;
130
+
131
+                    for (var j = blockStartRow; j < blockEndRow; j++) {
132
+                        cellWidths[j][c] = maxWidth;
133
+                    }
134
+                    startingNewBlock = true;
135
+                }
136
+
137
+                maxWidth = Math.max(maxWidth, width);
138
+            }
139
+        }
140
+
141
+        return cellWidths;
142
+    };
143
+
144
+    this.$rightmostSelectionInCell = function(selectionColumns, cellRightEdge) {
145
+        var rightmost = 0;
146
+
147
+        if (selectionColumns.length) {
148
+            var lengths = [];
149
+            for (var s = 0, length = selectionColumns.length; s < length; s++) {
150
+                if (selectionColumns[s] <= cellRightEdge)
151
+                    lengths.push(s);
152
+                else
153
+                    lengths.push(0);
154
+            }
155
+            rightmost = Math.max.apply(Math, lengths);
156
+        }
157
+
158
+        return rightmost;
159
+    };
160
+
161
+    this.$tabsForRow = function(row) {
162
+        var rowTabs = [], line = this.$editor.session.getLine(row),
163
+            re = /\t/g, match;
164
+
165
+        while ((match = re.exec(line)) != null) {
166
+            rowTabs.push(match.index);
167
+        }
168
+
169
+        return rowTabs;
170
+    };
171
+
172
+    this.$adjustRow = function(row, widths) {
173
+        var rowTabs = this.$tabsForRow(row);
174
+
175
+        if (rowTabs.length == 0)
176
+            return;
177
+
178
+        var bias = 0, location = -1;
179
+        var expandedSet = this.$izip(widths, rowTabs);
180
+
181
+        for (var i = 0, l = expandedSet.length; i < l; i++) {
182
+            var w = expandedSet[i][0], it = expandedSet[i][1];
183
+            location += 1 + w;
184
+            it += bias;
185
+            var difference = location - it;
186
+
187
+            if (difference == 0)
188
+                continue;
189
+
190
+            var partialLine = this.$editor.session.getLine(row).substr(0, it );
191
+            var strippedPartialLine = partialLine.replace(/\s*$/g, "");
192
+            var ispaces = partialLine.length - strippedPartialLine.length;
193
+
194
+            if (difference > 0) {
195
+                this.$editor.session.getDocument().insertInLine({row: row, column: it + 1}, Array(difference + 1).join(" ") + "\t");
196
+                this.$editor.session.getDocument().removeInLine(row, it, it + 1);
197
+
198
+                bias += difference;
199
+            }
200
+
201
+            if (difference < 0 && ispaces >= -difference) {
202
+                this.$editor.session.getDocument().removeInLine(row, it + difference, it);
203
+                bias += difference;
204
+            }
205
+        }
206
+    };
207
+    this.$izip_longest = function(iterables) {
208
+        if (!iterables[0])
209
+            return [];
210
+        var longest = iterables[0].length;
211
+        var iterablesLength = iterables.length;
212
+
213
+        for (var i = 1; i < iterablesLength; i++) {
214
+            var iLength = iterables[i].length;
215
+            if (iLength > longest)
216
+                longest = iLength;
217
+        }
218
+
219
+        var expandedSet = [];
220
+
221
+        for (var l = 0; l < longest; l++) {
222
+            var set = [];
223
+            for (var i = 0; i < iterablesLength; i++) {
224
+                if (iterables[i][l] === "")
225
+                    set.push(NaN);
226
+                else
227
+                    set.push(iterables[i][l]);
228
+            }
229
+
230
+            expandedSet.push(set);
231
+        }
232
+
233
+
234
+        return expandedSet;
235
+    };
236
+    this.$izip = function(widths, tabs) {
237
+        var size = widths.length >= tabs.length ? tabs.length : widths.length;
238
+
239
+        var expandedSet = [];
240
+        for (var i = 0; i < size; i++) {
241
+            var set = [ widths[i], tabs[i] ];
242
+            expandedSet.push(set);
243
+        }
244
+        return expandedSet;
245
+    };
246
+
247
+}).call(ElasticTabstopsLite.prototype);
248
+
249
+exports.ElasticTabstopsLite = ElasticTabstopsLite;
250
+
251
+var Editor = require("../editor").Editor;
252
+require("../config").defineOptions(Editor.prototype, "editor", {
253
+    useElasticTabstops: {
254
+        set: function(val) {
255
+            if (val) {
256
+                if (!this.elasticTabstops)
257
+                    this.elasticTabstops = new ElasticTabstopsLite(this);
258
+                this.commands.on("afterExec", this.elasticTabstops.onAfterExec);
259
+                this.commands.on("exec", this.elasticTabstops.onExec);
260
+                this.on("change", this.elasticTabstops.onChange);
261
+            } else if (this.elasticTabstops) {
262
+                this.commands.removeListener("afterExec", this.elasticTabstops.onAfterExec);
263
+                this.commands.removeListener("exec", this.elasticTabstops.onExec);
264
+                this.removeListener("change", this.elasticTabstops.onChange);
265
+            }
266
+        }
267
+    }
268
+});
269
+
270
+});                (function() {
271
+                    ace.require(["ace/ext/elastic_tabstops_lite"], function(m) {
272
+                        if (typeof module == "object" && typeof exports == "object" && module) {
273
+                            module.exports = m;
274
+                        }
275
+                    });
276
+                })();
277
+            

+ 1226
- 0
catsoop/__STATIC__/scripts/ace/ext-emmet.js View File

@@ -0,0 +1,1226 @@
1
+ace.define("ace/snippets",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/anchor","ace/keyboard/hash_handler","ace/tokenizer","ace/lib/dom","ace/editor"], function(require, exports, module) {
2
+"use strict";
3
+var oop = require("./lib/oop");
4
+var EventEmitter = require("./lib/event_emitter").EventEmitter;
5
+var lang = require("./lib/lang");
6
+var Range = require("./range").Range;
7
+var Anchor = require("./anchor").Anchor;
8
+var HashHandler = require("./keyboard/hash_handler").HashHandler;
9
+var Tokenizer = require("./tokenizer").Tokenizer;
10
+var comparePoints = Range.comparePoints;
11
+
12
+var SnippetManager = function() {
13
+    this.snippetMap = {};
14
+    this.snippetNameMap = {};
15
+};
16
+
17
+(function() {
18
+    oop.implement(this, EventEmitter);
19
+    
20
+    this.getTokenizer = function() {
21
+        function TabstopToken(str, _, stack) {
22
+            str = str.substr(1);
23
+            if (/^\d+$/.test(str) && !stack.inFormatString)
24
+                return [{tabstopId: parseInt(str, 10)}];
25
+            return [{text: str}];
26
+        }
27
+        function escape(ch) {
28
+            return "(?:[^\\\\" + ch + "]|\\\\.)";
29
+        }
30
+        SnippetManager.$tokenizer = new Tokenizer({
31
+            start: [
32
+                {regex: /:/, onMatch: function(val, state, stack) {
33
+                    if (stack.length && stack[0].expectIf) {
34
+                        stack[0].expectIf = false;
35
+                        stack[0].elseBranch = stack[0];
36
+                        return [stack[0]];
37
+                    }
38
+                    return ":";
39
+                }},
40
+                {regex: /\\./, onMatch: function(val, state, stack) {
41
+                    var ch = val[1];
42
+                    if (ch == "}" && stack.length) {
43
+                        val = ch;
44
+                    }else if ("`$\\".indexOf(ch) != -1) {
45
+                        val = ch;
46
+                    } else if (stack.inFormatString) {
47
+                        if (ch == "n")
48
+                            val = "\n";
49
+                        else if (ch == "t")
50
+                            val = "\n";
51
+                        else if ("ulULE".indexOf(ch) != -1) {
52
+                            val = {changeCase: ch, local: ch > "a"};
53
+                        }
54
+                    }
55
+
56
+                    return [val];
57
+                }},
58
+                {regex: /}/, onMatch: function(val, state, stack) {
59
+                    return [stack.length ? stack.shift() : val];
60
+                }},
61
+                {regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken},
62
+                {regex: /\$\{[\dA-Z_a-z]+/, onMatch: function(str, state, stack) {
63
+                    var t = TabstopToken(str.substr(1), state, stack);
64
+                    stack.unshift(t[0]);
65
+                    return t;
66
+                }, next: "snippetVar"},
67
+                {regex: /\n/, token: "newline", merge: false}
68
+            ],
69
+            snippetVar: [
70
+                {regex: "\\|" + escape("\\|") + "*\\|", onMatch: function(val, state, stack) {
71
+                    stack[0].choices = val.slice(1, -1).split(",");
72
+                }, next: "start"},
73
+                {regex: "/(" + escape("/") + "+)/(?:(" + escape("/") + "*)/)(\\w*):?",
74
+                 onMatch: function(val, state, stack) {
75
+                    var ts = stack[0];
76
+                    ts.fmtString = val;
77
+
78
+                    val = this.splitRegex.exec(val);
79
+                    ts.guard = val[1];
80
+                    ts.fmt = val[2];
81
+                    ts.flag = val[3];
82
+                    return "";
83
+                }, next: "start"},
84
+                {regex: "`" + escape("`") + "*`", onMatch: function(val, state, stack) {
85
+                    stack[0].code = val.splice(1, -1);
86
+                    return "";
87
+                }, next: "start"},
88
+                {regex: "\\?", onMatch: function(val, state, stack) {
89
+                    if (stack[0])
90
+                        stack[0].expectIf = true;
91
+                }, next: "start"},
92
+                {regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "start"}
93
+            ],
94
+            formatString: [
95
+                {regex: "/(" + escape("/") + "+)/", token: "regex"},
96
+                {regex: "", onMatch: function(val, state, stack) {
97
+                    stack.inFormatString = true;
98
+                }, next: "start"}
99
+            ]
100
+        });
101
+        SnippetManager.prototype.getTokenizer = function() {
102
+            return SnippetManager.$tokenizer;
103
+        };
104
+        return SnippetManager.$tokenizer;
105
+    };
106
+
107
+    this.tokenizeTmSnippet = function(str, startState) {
108
+        return this.getTokenizer().getLineTokens(str, startState).tokens.map(function(x) {
109
+            return x.value || x;
110
+        });
111
+    };
112
+
113
+    this.$getDefaultValue = function(editor, name) {
114
+        if (/^[A-Z]\d+$/.test(name)) {
115
+            var i = name.substr(1);
116
+            return (this.variables[name[0] + "__"] || {})[i];
117
+        }
118
+        if (/^\d+$/.test(name)) {
119
+            return (this.variables.__ || {})[name];
120
+        }
121
+        name = name.replace(/^TM_/, "");
122
+
123
+        if (!editor)
124
+            return;
125
+        var s = editor.session;
126
+        switch(name) {
127
+            case "CURRENT_WORD":
128
+                var r = s.getWordRange();
129
+            case "SELECTION":
130
+            case "SELECTED_TEXT":
131
+                return s.getTextRange(r);
132
+            case "CURRENT_LINE":
133
+                return s.getLine(editor.getCursorPosition().row);
134
+            case "PREV_LINE": // not possible in textmate
135
+                return s.getLine(editor.getCursorPosition().row - 1);
136
+            case "LINE_INDEX":
137
+                return editor.getCursorPosition().column;
138
+            case "LINE_NUMBER":
139
+                return editor.getCursorPosition().row + 1;
140
+            case "SOFT_TABS":
141
+                return s.getUseSoftTabs() ? "YES" : "NO";
142
+            case "TAB_SIZE":
143
+                return s.getTabSize();
144
+            case "FILENAME":
145
+            case "FILEPATH":
146
+                return "";
147
+            case "FULLNAME":
148
+                return "Ace";
149
+        }
150
+    };
151
+    this.variables = {};
152
+    this.getVariableValue = function(editor, varName) {
153
+        if (this.variables.hasOwnProperty(varName))
154
+            return this.variables[varName](editor, varName) || "";
155
+        return this.$getDefaultValue(editor, varName) || "";
156
+    };
157
+    this.tmStrFormat = function(str, ch, editor) {
158
+        var flag = ch.flag || "";
159
+        var re = ch.guard;
160
+        re = new RegExp(re, flag.replace(/[^gi]/, ""));
161
+        var fmtTokens = this.tokenizeTmSnippet(ch.fmt, "formatString");
162
+        var _self = this;
163
+        var formatted = str.replace(re, function() {
164
+            _self.variables.__ = arguments;
165
+            var fmtParts = _self.resolveVariables(fmtTokens, editor);
166
+            var gChangeCase = "E";
167
+            for (var i  = 0; i < fmtParts.length; i++) {
168
+                var ch = fmtParts[i];
169
+                if (typeof ch == "object") {
170
+                    fmtParts[i] = "";
171
+                    if (ch.changeCase && ch.local) {
172
+                        var next = fmtParts[i + 1];
173
+                        if (next && typeof next == "string") {
174
+                            if (ch.changeCase == "u")
175
+                                fmtParts[i] = next[0].toUpperCase();
176
+                            else
177
+                                fmtParts[i] = next[0].toLowerCase();
178
+                            fmtParts[i + 1] = next.substr(1);
179
+                        }
180
+                    } else if (ch.changeCase) {
181
+                        gChangeCase = ch.changeCase;
182
+                    }
183
+                } else if (gChangeCase == "U") {
184
+                    fmtParts[i] = ch.toUpperCase();
185
+                } else if (gChangeCase == "L") {
186
+                    fmtParts[i] = ch.toLowerCase();
187
+                }
188
+            }
189
+            return fmtParts.join("");
190
+        });
191
+        this.variables.__ = null;
192
+        return formatted;
193
+    };
194
+
195
+    this.resolveVariables = function(snippet, editor) {
196
+        var result = [];
197
+        for (var i = 0; i < snippet.length; i++) {
198
+            var ch = snippet[i];
199
+            if (typeof ch == "string") {
200
+                result.push(ch);
201
+            } else if (typeof ch != "object") {
202
+                continue;
203
+            } else if (ch.skip) {
204
+                gotoNext(ch);
205
+            } else if (ch.processed < i) {
206
+                continue;
207
+            } else if (ch.text) {
208
+                var value = this.getVariableValue(editor, ch.text);
209
+                if (value && ch.fmtString)
210
+                    value = this.tmStrFormat(value, ch);
211
+                ch.processed = i;
212
+                if (ch.expectIf == null) {
213
+                    if (value) {
214
+                        result.push(value);
215
+                        gotoNext(ch);
216
+                    }
217
+                } else {
218
+                    if (value) {
219
+                        ch.skip = ch.elseBranch;
220
+                    } else
221
+                        gotoNext(ch);
222
+                }
223
+            } else if (ch.tabstopId != null) {
224
+                result.push(ch);
225
+            } else if (ch.changeCase != null) {
226
+                result.push(ch);
227
+            }
228
+        }
229
+        function gotoNext(ch) {
230
+            var i1 = snippet.indexOf(ch, i + 1);
231
+            if (i1 != -1)
232
+                i = i1;
233
+        }
234
+        return result;
235
+    };
236
+
237
+    this.insertSnippetForSelection = function(editor, snippetText) {
238
+        var cursor = editor.getCursorPosition();
239
+        var line = editor.session.getLine(cursor.row);
240
+        var tabString = editor.session.getTabString();
241
+        var indentString = line.match(/^\s*/)[0];
242
+        
243
+        if (cursor.column < indentString.length)
244
+            indentString = indentString.slice(0, cursor.column);
245
+
246
+        snippetText = snippetText.replace(/\r/g, "");
247
+        var tokens = this.tokenizeTmSnippet(snippetText);
248
+        tokens = this.resolveVariables(tokens, editor);
249
+        tokens = tokens.map(function(x) {
250
+            if (x == "\n")
251
+                return x + indentString;
252
+            if (typeof x == "string")
253
+                return x.replace(/\t/g, tabString);
254
+            return x;
255
+        });
256
+        var tabstops = [];
257
+        tokens.forEach(function(p, i) {
258
+            if (typeof p != "object")
259
+                return;
260
+            var id = p.tabstopId;
261
+            var ts = tabstops[id];
262
+            if (!ts) {
263
+                ts = tabstops[id] = [];
264
+                ts.index = id;
265
+                ts.value = "";
266
+            }
267
+            if (ts.indexOf(p) !== -1)
268
+                return;
269
+            ts.push(p);
270
+            var i1 = tokens.indexOf(p, i + 1);
271
+            if (i1 === -1)
272
+                return;
273
+
274
+            var value = tokens.slice(i + 1, i1);
275
+            var isNested = value.some(function(t) {return typeof t === "object";});
276
+            if (isNested && !ts.value) {
277
+                ts.value = value;
278
+            } else if (value.length && (!ts.value || typeof ts.value !== "string")) {
279
+                ts.value = value.join("");
280
+            }
281
+        });
282
+        tabstops.forEach(function(ts) {ts.length = 0;});
283
+        var expanding = {};
284
+        function copyValue(val) {
285
+            var copy = [];
286
+            for (var i = 0; i < val.length; i++) {
287
+                var p = val[i];
288
+                if (typeof p == "object") {
289
+                    if (expanding[p.tabstopId])
290
+                        continue;
291
+                    var j = val.lastIndexOf(p, i - 1);
292
+                    p = copy[j] || {tabstopId: p.tabstopId};
293
+                }
294
+                copy[i] = p;
295
+            }
296
+            return copy;
297
+        }
298
+        for (var i = 0; i < tokens.length; i++) {
299
+            var p = tokens[i];
300
+            if (typeof p != "object")
301
+                continue;
302
+            var id = p.tabstopId;
303
+            var i1 = tokens.indexOf(p, i + 1);
304
+            if (expanding[id]) {
305
+                if (expanding[id] === p)
306
+                    expanding[id] = null;
307
+                continue;
308
+            }
309
+            
310
+            var ts = tabstops[id];
311
+            var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value);
312
+            arg.unshift(i + 1, Math.max(0, i1 - i));
313
+            arg.push(p);
314
+            expanding[id] = p;
315
+            tokens.splice.apply(tokens, arg);
316
+
317
+            if (ts.indexOf(p) === -1)
318
+                ts.push(p);
319
+        }
320
+        var row = 0, column = 0;
321
+        var text = "";
322
+        tokens.forEach(function(t) {
323
+            if (typeof t === "string") {
324
+                var lines = t.split("\n");
325
+                if (lines.length > 1){
326
+                    column = lines[lines.length - 1].length;
327
+                    row += lines.length - 1;
328
+                } else
329
+                    column += t.length;
330
+                text += t;
331
+            } else {
332
+                if (!t.start)
333
+                    t.start = {row: row, column: column};
334
+                else
335
+                    t.end = {row: row, column: column};
336
+            }
337
+        });
338
+        var range = editor.getSelectionRange();
339
+        var end = editor.session.replace(range, text);
340
+
341
+        var tabstopManager = new TabstopManager(editor);
342
+        var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
343
+        tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
344
+    };
345
+    
346
+    this.insertSnippet = function(editor, snippetText) {
347
+        var self = this;
348
+        if (editor.inVirtualSelectionMode)
349
+            return self.insertSnippetForSelection(editor, snippetText);
350
+        
351
+        editor.forEachSelection(function() {
352
+            self.insertSnippetForSelection(editor, snippetText);
353
+        }, null, {keepOrder: true});
354
+        
355
+        if (editor.tabstopManager)
356
+            editor.tabstopManager.tabNext();
357
+    };
358
+
359
+    this.$getScope = function(editor) {
360
+        var scope = editor.session.$mode.$id || "";
361
+        scope = scope.split("/").pop();
362
+        if (scope === "html" || scope === "php") {
363
+            if (scope === "php" && !editor.session.$mode.inlinePhp) 
364
+                scope = "html";
365
+            var c = editor.getCursorPosition();
366
+            var state = editor.session.getState(c.row);
367
+            if (typeof state === "object") {
368
+                state = state[0];
369
+            }
370
+            if (state.substring) {
371
+                if (state.substring(0, 3) == "js-")
372
+                    scope = "javascript";
373
+                else if (state.substring(0, 4) == "css-")
374
+                    scope = "css";
375
+                else if (state.substring(0, 4) == "php-")
376
+                    scope = "php";
377
+            }
378
+        }
379
+        
380
+        return scope;
381
+    };
382
+
383
+    this.getActiveScopes = function(editor) {
384
+        var scope = this.$getScope(editor);
385
+        var scopes = [scope];
386
+        var snippetMap = this.snippetMap;
387
+        if (snippetMap[scope] && snippetMap[scope].includeScopes) {
388
+            scopes.push.apply(scopes, snippetMap[scope].includeScopes);
389
+        }
390
+        scopes.push("_");
391
+        return scopes;
392
+    };
393
+
394
+    this.expandWithTab = function(editor, options) {
395
+        var self = this;
396
+        var result = editor.forEachSelection(function() {
397
+            return self.expandSnippetForSelection(editor, options);
398
+        }, null, {keepOrder: true});
399
+        if (result && editor.tabstopManager)
400
+            editor.tabstopManager.tabNext();
401
+        return result;
402
+    };
403
+    
404
+    this.expandSnippetForSelection = function(editor, options) {
405
+        var cursor = editor.getCursorPosition();
406
+        var line = editor.session.getLine(cursor.row);
407
+        var before = line.substring(0, cursor.column);
408
+        var after = line.substr(cursor.column);
409
+
410
+        var snippetMap = this.snippetMap;
411
+        var snippet;
412
+        this.getActiveScopes(editor).some(function(scope) {
413
+            var snippets = snippetMap[scope];
414
+            if (snippets)
415
+                snippet = this.findMatchingSnippet(snippets, before, after);
416
+            return !!snippet;
417
+        }, this);
418
+        if (!snippet)
419
+            return false;
420
+        if (options && options.dryRun)
421
+            return true;
422
+        editor.session.doc.removeInLine(cursor.row,
423
+            cursor.column - snippet.replaceBefore.length,
424
+            cursor.column + snippet.replaceAfter.length
425
+        );
426
+
427
+        this.variables.M__ = snippet.matchBefore;
428
+        this.variables.T__ = snippet.matchAfter;
429
+        this.insertSnippetForSelection(editor, snippet.content);
430
+
431
+        this.variables.M__ = this.variables.T__ = null;
432
+        return true;
433
+    };
434
+
435
+    this.findMatchingSnippet = function(snippetList, before, after) {
436
+        for (var i = snippetList.length; i--;) {
437
+            var s = snippetList[i];
438
+            if (s.startRe && !s.startRe.test(before))
439
+                continue;
440
+            if (s.endRe && !s.endRe.test(after))
441
+                continue;
442
+            if (!s.startRe && !s.endRe)
443
+                continue;
444
+
445
+            s.matchBefore = s.startRe ? s.startRe.exec(before) : [""];
446
+            s.matchAfter = s.endRe ? s.endRe.exec(after) : [""];
447
+            s.replaceBefore = s.triggerRe ? s.triggerRe.exec(before)[0] : "";
448
+            s.replaceAfter = s.endTriggerRe ? s.endTriggerRe.exec(after)[0] : "";
449
+            return s;
450
+        }
451
+    };
452
+
453
+    this.snippetMap = {};
454
+    this.snippetNameMap = {};
455
+    this.register = function(snippets, scope) {
456
+        var snippetMap = this.snippetMap;
457
+        var snippetNameMap = this.snippetNameMap;
458
+        var self = this;
459
+        
460
+        if (!snippets) 
461
+            snippets = [];
462
+        
463
+        function wrapRegexp(src) {
464
+            if (src && !/^\^?\(.*\)\$?$|^\\b$/.test(src))
465
+                src = "(?:" + src + ")";
466
+
467
+            return src || "";
468
+        }
469
+        function guardedRegexp(re, guard, opening) {
470
+            re = wrapRegexp(re);
471
+            guard = wrapRegexp(guard);
472
+            if (opening) {
473
+                re = guard + re;
474
+                if (re && re[re.length - 1] != "$")
475
+                    re = re + "$";
476
+            } else {
477
+                re = re + guard;
478
+                if (re && re[0] != "^")
479
+                    re = "^" + re;
480
+            }
481
+            return new RegExp(re);
482
+        }
483
+
484
+        function addSnippet(s) {
485
+            if (!s.scope)
486
+                s.scope = scope || "_";
487
+            scope = s.scope;
488
+            if (!snippetMap[scope]) {
489
+                snippetMap[scope] = [];
490
+                snippetNameMap[scope] = {};
491
+            }
492
+
493
+            var map = snippetNameMap[scope];
494
+            if (s.name) {
495
+                var old = map[s.name];
496
+                if (old)
497
+                    self.unregister(old);
498
+                map[s.name] = s;
499
+            }
500
+            snippetMap[scope].push(s);
501
+
502
+            if (s.tabTrigger && !s.trigger) {
503
+                if (!s.guard && /^\w/.test(s.tabTrigger))
504
+                    s.guard = "\\b";
505
+                s.trigger = lang.escapeRegExp(s.tabTrigger);
506
+            }
507
+            
508
+            if (!s.trigger && !s.guard && !s.endTrigger && !s.endGuard)
509
+                return;
510
+            
511
+            s.startRe = guardedRegexp(s.trigger, s.guard, true);
512
+            s.triggerRe = new RegExp(s.trigger);
513
+
514
+            s.endRe = guardedRegexp(s.endTrigger, s.endGuard, true);
515
+            s.endTriggerRe = new RegExp(s.endTrigger);
516
+        }
517
+
518
+        if (snippets && snippets.content)
519
+            addSnippet(snippets);
520
+        else if (Array.isArray(snippets))
521
+            snippets.forEach(addSnippet);
522
+        
523
+        this._signal("registerSnippets", {scope: scope});
524
+    };
525
+    this.unregister = function(snippets, scope) {
526
+        var snippetMap = this.snippetMap;
527
+        var snippetNameMap = this.snippetNameMap;
528
+
529
+        function removeSnippet(s) {
530
+            var nameMap = snippetNameMap[s.scope||scope];
531
+            if (nameMap && nameMap[s.name]) {
532
+                delete nameMap[s.name];
533
+                var map = snippetMap[s.scope||scope];
534
+                var i = map && map.indexOf(s);
535
+                if (i >= 0)
536
+                    map.splice(i, 1);
537
+            }
538
+        }
539
+        if (snippets.content)
540
+            removeSnippet(snippets);
541
+        else if (Array.isArray(snippets))
542
+            snippets.forEach(removeSnippet);
543
+    };
544
+    this.parseSnippetFile = function(str) {
545
+        str = str.replace(/\r/g, "");
546
+        var list = [], snippet = {};
547
+        var re = /^#.*|^({[\s\S]*})\s*$|^(\S+) (.*)$|^((?:\n*\t.*)+)/gm;
548
+        var m;
549
+        while (m = re.exec(str)) {
550
+            if (m[1]) {
551
+                try {
552
+                    snippet = JSON.parse(m[1]);
553
+                    list.push(snippet);
554
+                } catch (e) {}
555
+            } if (m[4]) {
556
+                snippet.content = m[4].replace(/^\t/gm, "");
557
+                list.push(snippet);
558
+                snippet = {};
559
+            } else {
560
+                var key = m[2], val = m[3];
561
+                if (key == "regex") {
562
+                    var guardRe = /\/((?:[^\/\\]|\\.)*)|$/g;
563
+                    snippet.guard = guardRe.exec(val)[1];
564
+                    snippet.trigger = guardRe.exec(val)[1];
565
+                    snippet.endTrigger = guardRe.exec(val)[1];
566
+                    snippet.endGuard = guardRe.exec(val)[1];
567
+                } else if (key == "snippet") {
568
+                    snippet.tabTrigger = val.match(/^\S*/)[0];
569
+                    if (!snippet.name)
570
+                        snippet.name = val;
571
+                } else {
572
+                    snippet[key] = val;
573
+                }
574
+            }
575
+        }
576
+        return list;
577
+    };
578
+    this.getSnippetByName = function(name, editor) {
579
+        var snippetMap = this.snippetNameMap;
580
+        var snippet;
581
+        this.getActiveScopes(editor).some(function(scope) {
582
+            var snippets = snippetMap[scope];
583
+            if (snippets)
584
+                snippet = snippets[name];
585
+            return !!snippet;
586
+        }, this);
587
+        return snippet;
588
+    };
589
+
590
+}).call(SnippetManager.prototype);
591
+
592
+
593
+var TabstopManager = function(editor) {
594
+    if (editor.tabstopManager)
595
+        return editor.tabstopManager;
596
+    editor.tabstopManager = this;
597
+    this.$onChange = this.onChange.bind(this);
598
+    this.$onChangeSelection = lang.delayedCall(this.onChangeSelection.bind(this)).schedule;
599
+    this.$onChangeSession = this.onChangeSession.bind(this);
600
+    this.$onAfterExec = this.onAfterExec.bind(this);
601
+    this.attach(editor);
602
+};
603
+(function() {
604
+    this.attach = function(editor) {
605
+        this.index = 0;
606
+        this.ranges = [];
607
+        this.tabstops = [];
608
+        this.$openTabstops = null;
609
+        this.selectedTabstop = null;
610
+
611
+        this.editor = editor;
612
+        this.editor.on("change", this.$onChange);
613
+        this.editor.on("changeSelection", this.$onChangeSelection);
614
+        this.editor.on("changeSession", this.$onChangeSession);
615
+        this.editor.commands.on("afterExec", this.$onAfterExec);
616
+        this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
617
+    };
618
+    this.detach = function() {
619
+        this.tabstops.forEach(this.removeTabstopMarkers, this);
620
+        this.ranges = null;
621
+        this.tabstops = null;
622
+        this.selectedTabstop = null;
623
+        this.editor.removeListener("change", this.$onChange);
624
+        this.editor.removeListener("changeSelection", this.$onChangeSelection);
625
+        this.editor.removeListener("changeSession", this.$onChangeSession);
626
+        this.editor.commands.removeListener("afterExec", this.$onAfterExec);
627
+        this.editor.keyBinding.removeKeyboardHandler(this.keyboardHandler);
628
+        this.editor.tabstopManager = null;
629
+        this.editor = null;
630
+    };
631
+
632
+    this.onChange = function(delta) {
633
+        var changeRange = delta;
634
+        var isRemove = delta.action[0] == "r";
635
+        var start = delta.start;
636
+        var end = delta.end;
637
+        var startRow = start.row;
638
+        var endRow = end.row;
639
+        var lineDif = endRow - startRow;
640
+        var colDiff = end.column - start.column;
641
+
642
+        if (isRemove) {
643
+            lineDif = -lineDif;
644
+            colDiff = -colDiff;
645
+        }
646
+        if (!this.$inChange && isRemove) {
647
+            var ts = this.selectedTabstop;
648
+            var changedOutside = ts && !ts.some(function(r) {
649
+                return comparePoints(r.start, start) <= 0 && comparePoints(r.end, end) >= 0;
650
+            });
651
+            if (changedOutside)
652
+                return this.detach();
653
+        }
654
+        var ranges = this.ranges;
655
+        for (var i = 0; i < ranges.length; i++) {
656
+            var r = ranges[i];
657
+            if (r.end.row < start.row)
658
+                continue;
659
+
660
+            if (isRemove && comparePoints(start, r.start) < 0 && comparePoints(end, r.end) > 0) {
661
+                this.removeRange(r);
662
+                i--;
663
+                continue;
664
+            }
665
+
666
+            if (r.start.row == startRow && r.start.column > start.column)
667
+                r.start.column += colDiff;
668
+            if (r.end.row == startRow && r.end.column >= start.column)
669
+                r.end.column += colDiff;
670
+            if (r.start.row >= startRow)
671
+                r.start.row += lineDif;
672
+            if (r.end.row >= startRow)
673
+                r.end.row += lineDif;
674
+
675
+            if (comparePoints(r.start, r.end) > 0)
676
+                this.removeRange(r);
677
+        }
678
+        if (!ranges.length)
679
+            this.detach();
680
+    };
681
+    this.updateLinkedFields = function() {
682
+        var ts = this.selectedTabstop;
683
+        if (!ts || !ts.hasLinkedRanges)
684
+            return;
685
+        this.$inChange = true;
686
+        var session = this.editor.session;
687
+        var text = session.getTextRange(ts.firstNonLinked);
688
+        for (var i = ts.length; i--;) {
689
+            var range = ts[i];
690
+            if (!range.linked)
691
+                continue;
692
+            var fmt = exports.snippetManager.tmStrFormat(text, range.original);
693
+            session.replace(range, fmt);
694
+        }
695
+        this.$inChange = false;
696
+    };
697
+    this.onAfterExec = function(e) {
698
+        if (e.command && !e.command.readOnly)
699
+            this.updateLinkedFields();
700
+    };
701
+    this.onChangeSelection = function() {
702
+        if (!this.editor)
703
+            return;
704
+        var lead = this.editor.selection.lead;
705
+        var anchor = this.editor.selection.anchor;
706
+        var isEmpty = this.editor.selection.isEmpty();
707
+        for (var i = this.ranges.length; i--;) {
708
+            if (this.ranges[i].linked)
709
+                continue;
710
+            var containsLead = this.ranges[i].contains(lead.row, lead.column);
711
+            var containsAnchor = isEmpty || this.ranges[i].contains(anchor.row, anchor.column);
712
+            if (containsLead && containsAnchor)
713
+                return;
714
+        }
715
+        this.detach();
716
+    };
717
+    this.onChangeSession = function() {
718
+        this.detach();
719
+    };
720
+    this.tabNext = function(dir) {
721
+        var max = this.tabstops.length;
722
+        var index = this.index + (dir || 1);
723
+        index = Math.min(Math.max(index, 1), max);
724
+        if (index == max)
725
+            index = 0;
726
+        this.selectTabstop(index);
727
+        if (index === 0)
728
+            this.detach();
729
+    };
730
+    this.selectTabstop = function(index) {
731
+        this.$openTabstops = null;
732
+        var ts = this.tabstops[this.index];
733
+        if (ts)
734
+            this.addTabstopMarkers(ts);
735
+        this.index = index;
736
+        ts = this.tabstops[this.index];
737
+        if (!ts || !ts.length)
738
+            return;
739
+        
740
+        this.selectedTabstop = ts;
741
+        if (!this.editor.inVirtualSelectionMode) {        
742
+            var sel = this.editor.multiSelect;
743
+            sel.toSingleRange(ts.firstNonLinked.clone());
744
+            for (var i = ts.length; i--;) {
745
+                if (ts.hasLinkedRanges && ts[i].linked)
746
+                    continue;
747
+                sel.addRange(ts[i].clone(), true);
748
+            }
749
+            if (sel.ranges[0])
750
+                sel.addRange(sel.ranges[0].clone());
751
+        } else {
752
+            this.editor.selection.setRange(ts.firstNonLinked);
753
+        }
754
+        
755
+        this.editor.keyBinding.addKeyboardHandler(this.keyboardHandler);
756
+    };
757
+    this.addTabstops = function(tabstops, start, end) {
758
+        if (!this.$openTabstops)
759
+            this.$openTabstops = [];
760
+        if (!tabstops[0]) {
761
+            var p = Range.fromPoints(end, end);
762
+            moveRelative(p.start, start);
763
+            moveRelative(p.end, start);
764
+            tabstops[0] = [p];
765
+            tabstops[0].index = 0;
766
+        }
767
+
768
+        var i = this.index;
769
+        var arg = [i + 1, 0];
770
+        var ranges = this.ranges;
771
+        tabstops.forEach(function(ts, index) {
772
+            var dest = this.$openTabstops[index] || ts;
773
+                
774
+            for (var i = ts.length; i--;) {
775
+                var p = ts[i];
776
+                var range = Range.fromPoints(p.start, p.end || p.start);
777
+                movePoint(range.start, start);
778
+                movePoint(range.end, start);
779
+                range.original = p;
780
+                range.tabstop = dest;
781
+                ranges.push(range);
782
+                if (dest != ts)
783
+                    dest.unshift(range);
784
+                else
785
+                    dest[i] = range;
786
+                if (p.fmtString) {
787
+                    range.linked = true;
788
+                    dest.hasLinkedRanges = true;
789
+                } else if (!dest.firstNonLinked)
790
+                    dest.firstNonLinked = range;
791
+            }
792
+            if (!dest.firstNonLinked)
793
+                dest.hasLinkedRanges = false;
794
+            if (dest === ts) {
795
+                arg.push(dest);
796
+                this.$openTabstops[index] = dest;
797
+            }
798
+            this.addTabstopMarkers(dest);
799
+        }, this);
800
+        
801
+        if (arg.length > 2) {
802
+            if (this.tabstops.length)
803
+                arg.push(arg.splice(2, 1)[0]);
804
+            this.tabstops.splice.apply(this.tabstops, arg);
805
+        }
806
+    };
807
+
808
+    this.addTabstopMarkers = function(ts) {
809
+        var session = this.editor.session;
810
+        ts.forEach(function(range) {
811
+            if  (!range.markerId)
812
+                range.markerId = session.addMarker(range, "ace_snippet-marker", "text");
813
+        });
814
+    };
815
+    this.removeTabstopMarkers = function(ts) {
816
+        var session = this.editor.session;
817
+        ts.forEach(function(range) {
818
+            session.removeMarker(range.markerId);
819
+            range.markerId = null;
820
+        });
821
+    };
822
+    this.removeRange = function(range) {
823
+        var i = range.tabstop.indexOf(range);
824
+        range.tabstop.splice(i, 1);
825
+        i = this.ranges.indexOf(range);
826
+        this.ranges.splice(i, 1);
827
+        this.editor.session.removeMarker(range.markerId);
828
+        if (!range.tabstop.length) {
829
+            i = this.tabstops.indexOf(range.tabstop);
830
+            if (i != -1)
831
+                this.tabstops.splice(i, 1);
832
+            if (!this.tabstops.length)
833
+                this.detach();
834
+        }
835
+    };
836
+
837
+    this.keyboardHandler = new HashHandler();
838
+    this.keyboardHandler.bindKeys({
839
+        "Tab": function(ed) {
840
+            if (exports.snippetManager && exports.snippetManager.expandWithTab(ed)) {
841
+                return;
842
+            }
843
+
844
+            ed.tabstopManager.tabNext(1);
845
+        },
846
+        "Shift-Tab": function(ed) {
847
+            ed.tabstopManager.tabNext(-1);
848
+        },
849
+        "Esc": function(ed) {
850
+            ed.tabstopManager.detach();
851
+        },
852
+        "Return": function(ed) {
853
+            return false;
854
+        }
855
+    });
856
+}).call(TabstopManager.prototype);
857
+
858
+
859
+
860
+var changeTracker = {};
861
+changeTracker.onChange = Anchor.prototype.onChange;
862
+changeTracker.setPosition = function(row, column) {
863
+    this.pos.row = row;
864
+    this.pos.column = column;
865
+};
866
+changeTracker.update = function(pos, delta, $insertRight) {
867
+    this.$insertRight = $insertRight;
868
+    this.pos = pos; 
869
+    this.onChange(delta);
870
+};
871
+
872
+var movePoint = function(point, diff) {
873
+    if (point.row == 0)
874
+        point.column += diff.column;
875
+    point.row += diff.row;
876
+};
877
+
878
+var moveRelative = function(point, start) {
879
+    if (point.row == start.row)
880
+        point.column -= start.column;
881
+    point.row -= start.row;
882
+};
883
+
884
+
885
+require("./lib/dom").importCssString("\
886
+.ace_snippet-marker {\
887
+    -moz-box-sizing: border-box;\
888
+    box-sizing: border-box;\
889
+    background: rgba(194, 193, 208, 0.09);\
890
+    border: 1px dotted rgba(211, 208, 235, 0.62);\
891
+    position: absolute;\
892
+}");
893
+
894
+exports.snippetManager = new SnippetManager();
895
+
896
+
897
+var Editor = require("./editor").Editor;
898
+(function() {
899
+    this.insertSnippet = function(content, options) {
900
+        return exports.snippetManager.insertSnippet(this, content, options);
901
+    };
902
+    this.expandSnippet = function(options) {
903
+        return exports.snippetManager.expandWithTab(this, options);
904
+    };
905
+}).call(Editor.prototype);
906
+
907
+});
908
+
909
+ace.define("ace/ext/emmet",["require","exports","module","ace/keyboard/hash_handler","ace/editor","ace/snippets","ace/range","resources","resources","tabStops","resources","utils","actions","ace/config","ace/config"], function(require, exports, module) {
910
+"use strict";
911
+var HashHandler = require("ace/keyboard/hash_handler").HashHandler;
912
+var Editor = require("ace/editor").Editor;
913
+var snippetManager = require("ace/snippets").snippetManager;
914
+var Range = require("ace/range").Range;
915
+var emmet, emmetPath;
916
+function AceEmmetEditor() {}
917
+
918
+AceEmmetEditor.prototype = {
919
+    setupContext: function(editor) {
920
+        this.ace = editor;
921
+        this.indentation = editor.session.getTabString();
922
+        if (!emmet)
923
+            emmet = window.emmet;
924
+        var resources = emmet.resources || emmet.require("resources");
925
+        resources.setVariable("indentation", this.indentation);
926
+        this.$syntax = null;
927
+        this.$syntax = this.getSyntax();
928
+    },
929
+    getSelectionRange: function() {
930
+        var range = this.ace.getSelectionRange();
931
+        var doc = this.ace.session.doc;
932
+        return {
933
+            start: doc.positionToIndex(range.start),
934
+            end: doc.positionToIndex(range.end)
935
+        };
936
+    },
937
+    createSelection: function(start, end) {
938
+        var doc = this.ace.session.doc;
939
+        this.ace.selection.setRange({
940
+            start: doc.indexToPosition(start),
941
+            end: doc.indexToPosition(end)
942
+        });
943
+    },
944
+    getCurrentLineRange: function() {
945
+        var ace = this.ace;
946
+        var row = ace.getCursorPosition().row;
947
+        var lineLength = ace.session.getLine(row).length;
948
+        var index = ace.session.doc.positionToIndex({row: row, column: 0});
949
+        return {
950
+            start: index,
951
+            end: index + lineLength
952
+        };
953
+    },
954
+    getCaretPos: function(){
955
+        var pos = this.ace.getCursorPosition();
956
+        return this.ace.session.doc.positionToIndex(pos);
957
+    },
958
+    setCaretPos: function(index){
959
+        var pos = this.ace.session.doc.indexToPosition(index);
960
+        this.ace.selection.moveToPosition(pos);
961
+    },
962
+    getCurrentLine: function() {
963
+        var row = this.ace.getCursorPosition().row;
964
+        return this.ace.session.getLine(row);
965
+    },
966
+    replaceContent: function(value, start, end, noIndent) {
967
+        if (end == null)
968
+            end = start == null ? this.getContent().length : start;
969
+        if (start == null)
970
+            start = 0;        
971
+        
972
+        var editor = this.ace;
973
+        var doc = editor.session.doc;
974
+        var range = Range.fromPoints(doc.indexToPosition(start), doc.indexToPosition(end));
975
+        editor.session.remove(range);
976
+        
977
+        range.end = range.start;
978
+        
979
+        value = this.$updateTabstops(value);
980
+        snippetManager.insertSnippet(editor, value);
981
+    },
982
+    getContent: function(){
983
+        return this.ace.getValue();
984
+    },
985
+    getSyntax: function() {
986
+        if (this.$syntax)
987
+            return this.$syntax;
988
+        var syntax = this.ace.session.$modeId.split("/").pop();
989
+        if (syntax == "html" || syntax == "php") {
990
+            var cursor = this.ace.getCursorPosition();
991
+            var state = this.ace.session.getState(cursor.row);
992
+            if (typeof state != "string")
993
+                state = state[0];
994
+            if (state) {
995
+                state = state.split("-");
996
+                if (state.length > 1)
997
+                    syntax = state[0];
998
+                else if (syntax == "php")
999
+                    syntax = "html";
1000
+            }
1001
+        }
1002
+        return syntax;
1003
+    },
1004
+    getProfileName: function() {
1005
+        var resources = emmet.resources || emmet.require("resources");
1006
+        switch (this.getSyntax()) {
1007
+          case "css": return "css";
1008
+          case "xml":
1009
+          case "xsl":
1010
+            return "xml";
1011
+          case "html":
1012
+            var profile = resources.getVariable("profile");
1013
+            if (!profile)
1014
+                profile = this.ace.session.getLines(0,2).join("").search(/<!DOCTYPE[^>]+XHTML/i) != -1 ? "xhtml": "html";
1015
+            return profile;
1016
+          default:
1017
+            var mode = this.ace.session.$mode;
1018
+            return mode.emmetConfig && mode.emmetConfig.profile || "xhtml";
1019
+        }
1020
+    },
1021
+    prompt: function(title) {
1022
+        return prompt(title);
1023
+    },
1024
+    getSelection: function() {
1025
+        return this.ace.session.getTextRange();
1026
+    },
1027
+    getFilePath: function() {
1028
+        return "";
1029
+    },
1030
+    $updateTabstops: function(value) {
1031
+        var base = 1000;
1032
+        var zeroBase = 0;
1033
+        var lastZero = null;
1034
+        var ts = emmet.tabStops || emmet.require('tabStops');
1035
+        var resources = emmet.resources || emmet.require("resources");
1036
+        var settings = resources.getVocabulary("user");
1037
+        var tabstopOptions = {
1038
+            tabstop: function(data) {
1039
+                var group = parseInt(data.group, 10);
1040
+                var isZero = group === 0;
1041
+                if (isZero)
1042
+                    group = ++zeroBase;
1043
+                else
1044
+                    group += base;
1045
+
1046
+                var placeholder = data.placeholder;
1047
+                if (placeholder) {
1048
+                    placeholder = ts.processText(placeholder, tabstopOptions);
1049
+                }
1050
+
1051
+                var result = '${' + group + (placeholder ? ':' + placeholder : '') + '}';
1052
+
1053
+                if (isZero) {
1054
+                    lastZero = [data.start, result];
1055
+                }
1056
+
1057
+                return result;
1058
+            },
1059
+            escape: function(ch) {
1060
+                if (ch == '$') return '\\$';
1061
+                if (ch == '\\') return '\\\\';
1062
+                return ch;
1063
+            }
1064
+        };
1065
+
1066
+        value = ts.processText(value, tabstopOptions);
1067
+
1068
+        if (settings.variables['insert_final_tabstop'] && !/\$\{0\}$/.test(value)) {
1069
+            value += '${0}';
1070
+        } else if (lastZero) {
1071
+            var common = emmet.utils ? emmet.utils.common : emmet.require('utils');
1072
+            value = common.replaceSubstring(value, '${0}', lastZero[0], lastZero[1]);
1073
+        }
1074
+        
1075
+        return value;
1076
+    }
1077
+};
1078
+
1079
+
1080
+var keymap = {
1081
+    expand_abbreviation: {"mac": "ctrl+alt+e", "win": "alt+e"},
1082
+    match_pair_outward: {"mac": "ctrl+d", "win": "ctrl+,"},
1083
+    match_pair_inward: {"mac": "ctrl+j", "win": "ctrl+shift+0"},
1084
+    matching_pair: {"mac": "ctrl+alt+j", "win": "alt+j"},
1085
+    next_edit_point: "alt+right",
1086
+    prev_edit_point: "alt+left",
1087
+    toggle_comment: {"mac": "command+/", "win": "ctrl+/"},
1088
+    split_join_tag: {"mac": "shift+command+'", "win": "shift+ctrl+`"},
1089
+    remove_tag: {"mac": "command+'", "win": "shift+ctrl+;"},
1090
+    evaluate_math_expression: {"mac": "shift+command+y", "win": "shift+ctrl+y"},
1091
+    increment_number_by_1: "ctrl+up",
1092
+    decrement_number_by_1: "ctrl+down",
1093
+    increment_number_by_01: "alt+up",
1094
+    decrement_number_by_01: "alt+down",
1095
+    increment_number_by_10: {"mac": "alt+command+up", "win": "shift+alt+up"},
1096
+    decrement_number_by_10: {"mac": "alt+command+down", "win": "shift+alt+down"},
1097
+    select_next_item: {"mac": "shift+command+.", "win": "shift+ctrl+."},
1098
+    select_previous_item: {"mac": "shift+command+,", "win": "shift+ctrl+,"},
1099
+    reflect_css_value: {"mac": "shift+command+r", "win": "shift+ctrl+r"},
1100
+
1101
+    encode_decode_data_url: {"mac": "shift+ctrl+d", "win": "ctrl+'"},
1102
+    expand_abbreviation_with_tab: "Tab",
1103
+    wrap_with_abbreviation: {"mac": "shift+ctrl+a", "win": "shift+ctrl+a"}
1104
+};
1105
+
1106
+var editorProxy = new AceEmmetEditor();
1107
+exports.commands = new HashHandler();
1108
+exports.runEmmetCommand = function runEmmetCommand(editor) {
1109
+    try {
1110
+        editorProxy.setupContext(editor);
1111
+        var actions = emmet.actions || emmet.require("actions");
1112
+    
1113
+        if (this.action == "expand_abbreviation_with_tab") {
1114
+            if (!editor.selection.isEmpty())
1115
+                return false;
1116
+            var pos = editor.selection.lead;
1117
+            var token = editor.session.getTokenAt(pos.row, pos.column);
1118
+            if (token && /\btag\b/.test(token.type))
1119
+                return false;
1120
+        }
1121
+        
1122
+        if (this.action == "wrap_with_abbreviation") {
1123
+            return setTimeout(function() {
1124
+                actions.run("wrap_with_abbreviation", editorProxy);
1125
+            }, 0);
1126
+        }
1127
+        
1128
+        var result = actions.run(this.action, editorProxy);
1129
+    } catch(e) {
1130
+        if (!emmet) {
1131
+            exports.load(runEmmetCommand.bind(this, editor));
1132
+            return true;
1133
+        }
1134
+        editor._signal("changeStatus", typeof e == "string" ? e : e.message);
1135
+        console.log(e);
1136
+        result = false;
1137
+    }
1138
+    return result;
1139
+};
1140
+
1141
+for (var command in keymap) {
1142
+    exports.commands.addCommand({
1143
+        name: "emmet:" + command,
1144
+        action: command,
1145
+        bindKey: keymap[command],
1146
+        exec: exports.runEmmetCommand,
1147
+        multiSelectAction: "forEach"
1148
+    });
1149
+}
1150
+
1151
+exports.updateCommands = function(editor, enabled) {
1152
+    if (enabled) {
1153
+        editor.keyBinding.addKeyboardHandler(exports.commands);
1154
+    } else {
1155
+        editor.keyBinding.removeKeyboardHandler(exports.commands);
1156
+    }
1157
+};
1158
+
1159
+exports.isSupportedMode = function(mode) {
1160
+    if (!mode) return false;
1161
+    if (mode.emmetConfig) return true;
1162
+    var id = mode.$id || mode;
1163
+    return /css|less|scss|sass|stylus|html|php|twig|ejs|handlebars/.test(id);
1164
+};
1165
+
1166
+exports.isAvailable = function(editor, command) {
1167
+    if (/(evaluate_math_expression|expand_abbreviation)$/.test(command))
1168
+        return true;
1169
+    var mode = editor.session.$mode;
1170
+    var isSupported = exports.isSupportedMode(mode);
1171
+    if (isSupported && mode.$modes) {
1172
+        try {
1173
+            editorProxy.setupContext(editor);
1174
+            if (/js|php/.test(editorProxy.getSyntax()))
1175
+                isSupported = false;
1176
+        } catch(e) {}
1177
+    }
1178
+    return isSupported;
1179
+};
1180
+
1181
+var onChangeMode = function(e, target) {
1182
+    var editor = target;
1183
+    if (!editor)
1184
+        return;
1185
+    var enabled = exports.isSupportedMode(editor.session.$mode);
1186
+    if (e.enableEmmet === false)
1187
+        enabled = false;
1188
+    if (enabled)
1189
+        exports.load();
1190
+    exports.updateCommands(editor, enabled);
1191
+};
1192
+
1193
+exports.load = function(cb) {
1194
+    if (typeof emmetPath == "string") {
1195
+        require("ace/config").loadModule(emmetPath, function() {
1196
+            emmetPath = null;
1197
+            cb && cb();
1198
+        });
1199
+    }
1200
+};
1201
+
1202
+exports.AceEmmetEditor = AceEmmetEditor;
1203
+require("ace/config").defineOptions(Editor.prototype, "editor", {
1204
+    enableEmmet: {
1205
+        set: function(val) {
1206
+            this[val ? "on" : "removeListener"]("changeMode", onChangeMode);
1207
+            onChangeMode({enableEmmet: !!val}, this);
1208
+        },
1209
+        value: true
1210
+    }
1211
+});
1212
+
1213
+exports.setCore = function(e) {
1214
+    if (typeof e == "string")
1215
+       emmetPath = e;
1216
+    else
1217
+       emmet = e;
1218
+};
1219
+});                (function() {
1220
+                    ace.require(["ace/ext/emmet"], function(m) {
1221
+                        if (typeof module == "object" && typeof exports == "object" && module) {
1222
+                            module.exports = m;
1223
+                        }
1224
+                    });
1225
+                })();
1226
+            

+ 9
- 0
catsoop/__STATIC__/scripts/ace/ext-error_marker.js View File

@@ -0,0 +1,9 @@
1
+
2
+;                (function() {
3
+                    ace.require(["ace/ext/error_marker"], function(m) {
4
+                        if (typeof module == "object" && typeof exports == "object" && module) {
5
+                            module.exports = m;
6
+                        }
7
+                    });
8
+                })();
9
+            

+ 188
- 0
catsoop/__STATIC__/scripts/ace/ext-keybinding_menu.js View File

@@ -0,0 +1,188 @@
1
+ace.define("ace/ext/menu_tools/overlay_page",["require","exports","module","ace/lib/dom"], function(require, exports, module) {
2
+'use strict';
3
+var dom = require("../../lib/dom");
4
+var cssText = "#ace_settingsmenu, #kbshortcutmenu {\
5
+background-color: #F7F7F7;\
6
+color: black;\
7
+box-shadow: -5px 4px 5px rgba(126, 126, 126, 0.55);\
8
+padding: 1em 0.5em 2em 1em;\
9
+overflow: auto;\
10
+position: absolute;\
11
+margin: 0;\
12
+bottom: 0;\
13
+right: 0;\
14
+top: 0;\
15
+z-index: 9991;\
16
+cursor: default;\
17
+}\
18
+.ace_dark #ace_settingsmenu, .ace_dark #kbshortcutmenu {\
19
+box-shadow: -20px 10px 25px rgba(126, 126, 126, 0.25);\
20
+background-color: rgba(255, 255, 255, 0.6);\
21
+color: black;\
22
+}\
23
+.ace_optionsMenuEntry:hover {\
24
+background-color: rgba(100, 100, 100, 0.1);\
25
+transition: all 0.3s\
26
+}\
27
+.ace_closeButton {\
28
+background: rgba(245, 146, 146, 0.5);\
29
+border: 1px solid #F48A8A;\
30
+border-radius: 50%;\
31
+padding: 7px;\
32
+position: absolute;\
33
+right: -8px;\
34
+top: -8px;\
35
+z-index: 100000;\
36
+}\
37
+.ace_closeButton{\
38
+background: rgba(245, 146, 146, 0.9);\
39
+}\
40
+.ace_optionsMenuKey {\
41
+color: darkslateblue;\
42
+font-weight: bold;\
43
+}\
44
+.ace_optionsMenuCommand {\
45
+color: darkcyan;\
46
+font-weight: normal;\
47
+}\
48
+.ace_optionsMenuEntry input, .ace_optionsMenuEntry button {\
49
+vertical-align: middle;\
50
+}\
51
+.ace_optionsMenuEntry button[ace_selected_button=true] {\
52
+background: #e7e7e7;\
53
+box-shadow: 1px 0px 2px 0px #adadad inset;\
54
+border-color: #adadad;\
55
+}\
56
+.ace_optionsMenuEntry button {\
57
+background: white;\
58
+border: 1px solid lightgray;\
59
+margin: 0px;\
60
+}\
61
+.ace_optionsMenuEntry button:hover{\
62
+background: #f0f0f0;\
63
+}";
64
+dom.importCssString(cssText);
65
+module.exports.overlayPage = function overlayPage(editor, contentElement, top, right, bottom, left) {
66
+    top = top ? 'top: ' + top + ';' : '';
67
+    bottom = bottom ? 'bottom: ' + bottom + ';' : '';
68
+    right = right ? 'right: ' + right + ';' : '';
69
+    left = left ? 'left: ' + left + ';' : '';
70
+
71
+    var closer = document.createElement('div');
72
+    var contentContainer = document.createElement('div');
73
+
74
+    function documentEscListener(e) {
75
+        if (e.keyCode === 27) {
76
+            closer.click();
77
+        }
78
+    }
79
+
80
+    closer.style.cssText = 'margin: 0; padding: 0; ' +
81
+        'position: fixed; top:0; bottom:0; left:0; right:0;' +
82
+        'z-index: 9990; ' +
83
+        'background-color: rgba(0, 0, 0, 0.3);';
84
+    closer.addEventListener('click', function() {
85
+        document.removeEventListener('keydown', documentEscListener);
86
+        closer.parentNode.removeChild(closer);
87
+        editor.focus();
88
+        closer = null;
89
+    });
90
+    document.addEventListener('keydown', documentEscListener);
91
+
92
+    contentContainer.style.cssText = top + right + bottom + left;
93
+    contentContainer.addEventListener('click', function(e) {
94
+        e.stopPropagation();
95
+    });
96
+
97
+    var wrapper = dom.createElement("div");
98
+    wrapper.style.position = "relative";
99
+    
100
+    var closeButton = dom.createElement("div");
101
+    closeButton.className = "ace_closeButton";
102
+    closeButton.addEventListener('click', function() {
103
+        closer.click();
104
+    });
105
+    
106
+    wrapper.appendChild(closeButton);
107
+    contentContainer.appendChild(wrapper);
108
+    
109
+    contentContainer.appendChild(contentElement);
110
+    closer.appendChild(contentContainer);
111
+    document.body.appendChild(closer);
112
+    editor.blur();
113
+};
114
+
115
+});
116
+
117
+ace.define("ace/ext/menu_tools/get_editor_keyboard_shortcuts",["require","exports","module","ace/lib/keys"], function(require, exports, module) {
118
+"use strict";
119
+var keys = require("../../lib/keys");
120
+module.exports.getEditorKeybordShortcuts = function(editor) {
121
+    var KEY_MODS = keys.KEY_MODS;
122
+    var keybindings = [];
123
+    var commandMap = {};
124
+    editor.keyBinding.$handlers.forEach(function(handler) {
125
+        var ckb = handler.commandKeyBinding;
126
+        for (var i in ckb) {
127
+            var key = i.replace(/(^|-)\w/g, function(x) { return x.toUpperCase(); });
128
+            var commands = ckb[i];
129
+            if (!Array.isArray(commands))
130
+                commands = [commands];
131
+            commands.forEach(function(command) {
132
+                if (typeof command != "string")
133
+                    command  = command.name;
134
+                if (commandMap[command]) {
135
+                    commandMap[command].key += "|" + key;
136
+                } else {
137
+                    commandMap[command] = {key: key, command: command};
138
+                    keybindings.push(commandMap[command]);
139
+                }         
140
+            });
141
+        }
142
+    });
143
+    return keybindings;
144
+};
145
+
146
+});
147
+
148
+ace.define("ace/ext/keybinding_menu",["require","exports","module","ace/editor","ace/ext/menu_tools/overlay_page","ace/ext/menu_tools/get_editor_keyboard_shortcuts"], function(require, exports, module) {
149
+    "use strict";
150
+    var Editor = require("ace/editor").Editor;
151
+    function showKeyboardShortcuts (editor) {
152
+        if(!document.getElementById('kbshortcutmenu')) {
153
+            var overlayPage = require('./menu_tools/overlay_page').overlayPage;
154
+            var getEditorKeybordShortcuts = require('./menu_tools/get_editor_keyboard_shortcuts').getEditorKeybordShortcuts;
155
+            var kb = getEditorKeybordShortcuts(editor);
156
+            var el = document.createElement('div');
157
+            var commands = kb.reduce(function(previous, current) {
158
+                return previous + '<div class="ace_optionsMenuEntry"><span class="ace_optionsMenuCommand">' 
159
+                    + current.command + '</span> : '
160
+                    + '<span class="ace_optionsMenuKey">' + current.key + '</span></div>';
161
+            }, '');
162
+
163
+            el.id = 'kbshortcutmenu';
164
+            el.innerHTML = '<h1>Keyboard Shortcuts</h1>' + commands + '</div>';
165
+            overlayPage(editor, el, '0', '0', '0', null);
166
+        }
167
+    }
168
+    module.exports.init = function(editor) {
169
+        Editor.prototype.showKeyboardShortcuts = function() {
170
+            showKeyboardShortcuts(this);
171
+        };
172
+        editor.commands.addCommands([{
173
+            name: "showKeyboardShortcuts",
174
+            bindKey: {win: "Ctrl-Alt-h", mac: "Command-Alt-h"},
175
+            exec: function(editor, line) {
176
+                editor.showKeyboardShortcuts();
177
+            }
178
+        }]);
179
+    };
180
+
181
+});                (function() {
182
+                    ace.require(["ace/ext/keybinding_menu"], function(m) {
183
+                        if (typeof module == "object" && typeof exports == "object" && module) {
184
+                            module.exports = m;
185
+                        }
186
+                    });
187
+                })();
188
+            

+ 1993
- 0
catsoop/__STATIC__/scripts/ace/ext-language_tools.js View File

@@ -0,0 +1,1993 @@
1
+ace.define("ace/snippets",["require","exports","module","ace/lib/oop","ace/lib/event_emitter","ace/lib/lang","ace/range","ace/anchor","ace/keyboard/hash_handler","ace/tokenizer","ace/lib/dom","ace/editor"], function(require, exports, module) {
2
+"use strict";
3
+var oop = require("./lib/oop");
4
+var EventEmitter = require("./lib/event_emitter").EventEmitter;
5
+var lang = require("./lib/lang");
6
+var Range = require("./range").Range;
7
+var Anchor = require("./anchor").Anchor;
8
+var HashHandler = require("./keyboard/hash_handler").HashHandler;
9
+var Tokenizer = require("./tokenizer").Tokenizer;
10
+var comparePoints = Range.comparePoints;
11
+
12
+var SnippetManager = function() {
13
+    this.snippetMap = {};
14
+    this.snippetNameMap = {};
15
+};
16
+
17
+(function() {
18
+    oop.implement(this, EventEmitter);
19
+    
20
+    this.getTokenizer = function() {
21
+        function TabstopToken(str, _, stack) {
22
+            str = str.substr(1);
23
+            if (/^\d+$/.test(str) && !stack.inFormatString)
24
+                return [{tabstopId: parseInt(str, 10)}];
25
+            return [{text: str}];
26
+        }
27
+        function escape(ch) {
28
+            return "(?:[^\\\\" + ch + "]|\\\\.)";
29
+        }
30
+        SnippetManager.$tokenizer = new Tokenizer({
31
+            start: [
32
+                {regex: /:/, onMatch: function(val, state, stack) {
33
+                    if (stack.length && stack[0].expectIf) {
34
+                        stack[0].expectIf = false;
35
+                        stack[0].elseBranch = stack[0];
36
+                        return [stack[0]];
37
+                    }
38
+                    return ":";
39
+                }},
40
+                {regex: /\\./, onMatch: function(val, state, stack) {
41
+                    var ch = val[1];
42
+                    if (ch == "}" && stack.length) {
43
+                        val = ch;
44
+                    }else if ("`$\\".indexOf(ch) != -1) {
45
+                        val = ch;
46
+                    } else if (stack.inFormatString) {
47
+                        if (ch == "n")
48
+                            val = "\n";
49
+                        else if (ch == "t")
50
+                            val = "\n";
51
+                        else if ("ulULE".indexOf(ch) != -1) {
52
+                            val = {changeCase: ch, local: ch > "a"};
53
+                        }
54
+                    }
55
+
56
+                    return [val];
57
+                }},
58
+                {regex: /}/, onMatch: function(val, state, stack) {
59
+                    return [stack.length ? stack.shift() : val];
60
+                }},
61
+                {regex: /\$(?:\d+|\w+)/, onMatch: TabstopToken},
62
+                {regex: /\$\{[\dA-Z_a-z]+/, onMatch: function(str, state, stack) {
63
+                    var t = TabstopToken(str.substr(1), state, stack);
64
+                    stack.unshift(t[0]);
65
+                    return t;
66
+                }, next: "snippetVar"},
67
+                {regex: /\n/, token: "newline", merge: false}
68
+            ],
69
+            snippetVar: [
70
+                {regex: "\\|" + escape("\\|") + "*\\|", onMatch: function(val, state, stack) {
71
+                    stack[0].choices = val.slice(1, -1).split(",");
72
+                }, next: "start"},
73
+                {regex: "/(" + escape("/") + "+)/(?:(" + escape("/") + "*)/)(\\w*):?",
74
+                 onMatch: function(val, state, stack) {
75
+                    var ts = stack[0];
76
+                    ts.fmtString = val;
77
+
78
+                    val = this.splitRegex.exec(val);
79
+                    ts.guard = val[1];
80
+                    ts.fmt = val[2];
81
+                    ts.flag = val[3];
82
+                    return "";
83
+                }, next: "start"},
84
+                {regex: "`" + escape("`") + "*`", onMatch: function(val, state, stack) {
85
+                    stack[0].code = val.splice(1, -1);
86
+                    return "";
87
+                }, next: "start"},
88
+                {regex: "\\?", onMatch: function(val, state, stack) {
89
+                    if (stack[0])
90
+                        stack[0].expectIf = true;
91
+                }, next: "start"},
92
+                {regex: "([^:}\\\\]|\\\\.)*:?", token: "", next: "start"}
93
+            ],
94
+            formatString: [
95
+                {regex: "/(" + escape("/") + "+)/", token: "regex"},
96
+                {regex: "", onMatch: function(val, state, stack) {
97
+                    stack.inFormatString = true;
98
+                }, next: "start"}
99
+            ]
100
+        });
101
+        SnippetManager.prototype.getTokenizer = function() {
102
+            return SnippetManager.$tokenizer;
103
+        };
104
+        return SnippetManager.$tokenizer;
105
+    };
106
+
107
+    this.tokenizeTmSnippet = function(str, startState) {
108
+        return this.getTokenizer().getLineTokens(str, startState).tokens.map(function(x) {
109
+            return x.value || x;
110
+        });
111
+    };
112
+
113
+    this.$getDefaultValue = function(editor, name) {
114
+        if (/^[A-Z]\d+$/.test(name)) {
115
+            var i = name.substr(1);
116
+            return (this.variables[name[0] + "__"] || {})[i];
117
+        }
118
+        if (/^\d+$/.test(name)) {
119
+            return (this.variables.__ || {})[name];
120
+        }
121
+        name = name.replace(/^TM_/, "");
122
+
123
+        if (!editor)
124
+            return;
125
+        var s = editor.session;
126
+        switch(name) {
127
+            case "CURRENT_WORD":
128
+                var r = s.getWordRange();
129
+            case "SELECTION":
130
+            case "SELECTED_TEXT":
131
+                return s.getTextRange(r);
132
+            case "CURRENT_LINE":
133
+                return s.getLine(editor.getCursorPosition().row);
134
+            case "PREV_LINE": // not possible in textmate
135
+                return s.getLine(editor.getCursorPosition().row - 1);
136
+            case "LINE_INDEX":
137
+                return editor.getCursorPosition().column;
138
+            case "LINE_NUMBER":
139
+                return editor.getCursorPosition().row + 1;
140
+            case "SOFT_TABS":
141
+                return s.getUseSoftTabs() ? "YES" : "NO";
142
+            case "TAB_SIZE":
143
+                return s.getTabSize();
144
+            case "FILENAME":
145
+            case "FILEPATH":
146
+                return "";
147
+            case "FULLNAME":
148
+                return "Ace";
149
+        }
150
+    };
151
+    this.variables = {};
152
+    this.getVariableValue = function(editor, varName) {
153
+        if (this.variables.hasOwnProperty(varName))
154
+            return this.variables[varName](editor, varName) || "";
155
+        return this.$getDefaultValue(editor, varName) || "";
156
+    };
157
+    this.tmStrFormat = function(str, ch, editor) {
158
+        var flag = ch.flag || "";
159
+        var re = ch.guard;
160
+        re = new RegExp(re, flag.replace(/[^gi]/, ""));
161
+        var fmtTokens = this.tokenizeTmSnippet(ch.fmt, "formatString");
162
+        var _self = this;
163
+        var formatted = str.replace(re, function() {
164
+            _self.variables.__ = arguments;
165
+            var fmtParts = _self.resolveVariables(fmtTokens, editor);
166
+            var gChangeCase = "E";
167
+            for (var i  = 0; i < fmtParts.length; i++) {
168
+                var ch = fmtParts[i];
169
+                if (typeof ch == "object") {
170
+                    fmtParts[i] = "";
171
+                    if (ch.changeCase && ch.local) {
172
+                        var next = fmtParts[i + 1];
173
+                        if (next && typeof next == "string") {
174
+                            if (ch.changeCase == "u")
175
+                                fmtParts[i] = next[0].toUpperCase();
176
+                            else
177
+                                fmtParts[i] = next[0].toLowerCase();
178
+                            fmtParts[i + 1] = next.substr(1);
179
+                        }
180
+                    } else if (ch.changeCase) {
181
+                        gChangeCase = ch.changeCase;
182
+                    }
183
+                } else if (gChangeCase == "U") {
184
+                    fmtParts[i] = ch.toUpperCase();
185
+                } else if (gChangeCase == "L") {
186
+                    fmtParts[i] = ch.toLowerCase();
187
+                }
188
+            }
189
+            return fmtParts.join("");
190
+        });
191
+        this.variables.__ = null;
192
+        return formatted;
193
+    };
194
+
195
+    this.resolveVariables = function(snippet, editor) {
196
+        var result = [];
197
+        for (var i = 0; i < snippet.length; i++) {
198
+            var ch = snippet[i];
199
+            if (typeof ch == "string") {
200
+                result.push(ch);
201
+            } else if (typeof ch != "object") {
202
+                continue;
203
+            } else if (ch.skip) {
204
+                gotoNext(ch);
205
+            } else if (ch.processed < i) {
206
+                continue;
207
+            } else if (ch.text) {
208
+                var value = this.getVariableValue(editor, ch.text);
209
+                if (value && ch.fmtString)
210
+                    value = this.tmStrFormat(value, ch);
211
+                ch.processed = i;
212
+                if (ch.expectIf == null) {
213
+                    if (value) {
214
+                        result.push(value);
215
+                        gotoNext(ch);
216
+                    }
217
+                } else {
218
+                    if (value) {
219
+                        ch.skip = ch.elseBranch;
220
+                    } else
221
+                        gotoNext(ch);
222
+                }
223
+            } else if (ch.tabstopId != null) {
224
+                result.push(ch);
225
+            } else if (ch.changeCase != null) {
226
+                result.push(ch);
227
+            }
228
+        }
229
+        function gotoNext(ch) {
230
+            var i1 = snippet.indexOf(ch, i + 1);
231
+            if (i1 != -1)
232
+                i = i1;
233
+        }
234
+        return result;
235
+    };
236
+
237
+    this.insertSnippetForSelection = function(editor, snippetText) {
238
+        var cursor = editor.getCursorPosition();
239
+        var line = editor.session.getLine(cursor.row);
240
+        var tabString = editor.session.getTabString();
241
+        var indentString = line.match(/^\s*/)[0];
242
+        
243
+        if (cursor.column < indentString.length)
244
+            indentString = indentString.slice(0, cursor.column);
245
+
246
+        snippetText = snippetText.replace(/\r/g, "");
247
+        var tokens = this.tokenizeTmSnippet(snippetText);
248
+        tokens = this.resolveVariables(tokens, editor);
249
+        tokens = tokens.map(function(x) {
250
+            if (x == "\n")
251
+                return x + indentString;
252
+            if (typeof x == "string")
253
+                return x.replace(/\t/g, tabString);
254
+            return x;
255
+        });
256
+        var tabstops = [];
257
+        tokens.forEach(function(p, i) {
258
+            if (typeof p != "object")
259
+                return;
260
+            var id = p.tabstopId;
261
+            var ts = tabstops[id];
262
+            if (!ts) {
263
+                ts = tabstops[id] = [];
264
+                ts.index = id;
265
+                ts.value = "";
266
+            }
267
+            if (ts.indexOf(p) !== -1)
268
+                return;
269
+            ts.push(p);
270
+            var i1 = tokens.indexOf(p, i + 1);
271
+            if (i1 === -1)
272
+                return;
273
+
274
+            var value = tokens.slice(i + 1, i1);
275
+            var isNested = value.some(function(t) {return typeof t === "object";});
276
+            if (isNested && !ts.value) {
277
+                ts.value = value;
278
+            } else if (value.length && (!ts.value || typeof ts.value !== "string")) {
279
+                ts.value = value.join("");
280
+            }
281
+        });
282
+        tabstops.forEach(function(ts) {ts.length = 0;});
283
+        var expanding = {};
284
+        function copyValue(val) {
285
+            var copy = [];
286
+            for (var i = 0; i < val.length; i++) {
287
+                var p = val[i];
288
+                if (typeof p == "object") {
289
+                    if (expanding[p.tabstopId])
290
+                        continue;
291
+                    var j = val.lastIndexOf(p, i - 1);
292
+                    p = copy[j] || {tabstopId: p.tabstopId};
293
+                }
294
+                copy[i] = p;
295
+            }
296
+            return copy;
297
+        }
298
+        for (var i = 0; i < tokens.length; i++) {
299
+            var p = tokens[i];
300
+            if (typeof p != "object")
301
+                continue;
302
+            var id = p.tabstopId;
303
+            var i1 = tokens.indexOf(p, i + 1);
304
+            if (expanding[id]) {
305
+                if (expanding[id] === p)
306
+                    expanding[id] = null;
307
+                continue;
308
+            }
309
+            
310
+            var ts = tabstops[id];
311
+            var arg = typeof ts.value == "string" ? [ts.value] : copyValue(ts.value);
312
+            arg.unshift(i + 1, Math.max(0, i1 - i));
313
+            arg.push(p);
314
+            expanding[id] = p;
315
+            tokens.splice.apply(tokens, arg);
316
+
317
+            if (ts.indexOf(p) === -1)
318
+                ts.push(p);
319
+        }
320
+        var row = 0, column = 0;
321
+        var text = "";
322
+        tokens.forEach(function(t) {
323
+            if (typeof t === "string") {
324
+                var lines = t.split("\n");
325
+                if (lines.length > 1){
326
+                    column = lines[lines.length - 1].length;
327
+                    row += lines.length - 1;
328
+                } else
329
+                    column += t.length;
330
+                text += t;
331
+            } else {
332
+                if (!t.start)
333
+                    t.start = {row: row, column: column};
334
+                else
335
+                    t.end = {row: row, column: column};
336
+            }
337
+        });
338
+        var range = editor.getSelectionRange();
339
+        var end = editor.session.replace(range, text);
340
+
341
+        var tabstopManager = new TabstopManager(editor);
342
+        var selectionId = editor.inVirtualSelectionMode && editor.selection.index;
343
+        tabstopManager.addTabstops(tabstops, range.start, end, selectionId);
344
+    };
345
+    
346
+    this.insertSnippet = function(editor, snippetText) {
347
+        var self = this;
348
+        if (editor.inVirtualSelectionMode)
349
+            return self.insertSnippetForSelection(editor, snippetText);
350
+        
351
+        editor.forEachSelection(function() {
352
+            self.insertSnippetForSelection(editor, snippetText);
353
+        }, null, {keepOrder: true});
354
+        
355
+        if (editor.tabstopManager)
356
+            editor.tabstopManager.tabNext();
357
+    };
358
+
359
+    this.$getScope = function(editor) {
360
+        var scope = editor.session.$mode.$id || "";
361
+        scope = scope.split("/").pop();
362
+        if (scope === "html" || scope === "php") {
363
+            if (scope === "php" && !editor.session.$mode.inlinePhp) 
364
+                scope = "html";
365
+            var c = editor.getCursorPosition();
366
+            var state = editor.session.getState(c.row);
367
+            if (typeof state === "object") {
368
+                state = state[0];
369
+            }
370
+            if (state.substring) {
371
+                if (state.substring(0, 3) == "js-")
372
+                    scope = "javascript";
373
+                else if (state.substring(0, 4) == "css-")
374
+                    scope = "css";
375
+                else if (state.substring(0, 4) == "php-")
376
+                    scope = "php";
377
+            }
378
+        }
379
+        
380
+        return scope;
381
+    };
382
+
383
+    this.getActiveScopes = function(editor) {
384
+        var scope = this.$getScope(editor);
385
+        var scopes = [scope];
386
+        var snippetMap = this.snippetMap;
387
+        if (snippetMap[scope] && snippetMap[scope].includeScopes) {
388
+            scopes.push.apply(scopes, snippetMap[scope].includeScopes);
389
+        }
390
+        scopes.push("_");
391
+        return scopes;
392
+    };
393
+
394
+    this.expandWithTab = function(editor, options) {
395
+        var self = this;
396
+        var result = editor.forEachSelection(function() {
397
+            return self.expandSnippetForSelection(editor, options);
398
+        }, null, {keepOrder: true});
399
+        if (result && editor.tabstopManager)
400
+            editor.tabstopManager.tabNext();
401
+        return result;
402
+    };
403
+    
404
+    this.expandSnippetForSelection = function(editor, options) {
405
+        var cursor = editor.getCursorPosition();
406
+        var line = editor.session.getLine(cursor.row);
407
+        var before = line.substring(0, cursor.column);
408
+        var after = line.substr(cursor.column);
409
+
410
+        var snippetMap = this.snippetMap;
411
+        var snippet;
412