liquid_cms 0.3.0.1 → 0.3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG.rdoc +5 -1
- data/Gemfile.lock +1 -1
- data/README.rdoc +5 -1
- data/app/helpers/cms/common_helper.rb +1 -0
- data/app/views/cms/pages/_page.html.erb +2 -1
- data/app/views/layouts/cms.html.erb +2 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +2 -2
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +12 -8
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +123 -29
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/csstest.html +1 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/htmltest.html +1 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/index.html +232 -179
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +211 -65
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +360 -194
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +1 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +11 -7
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +14 -5
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +1 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +140 -87
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +5 -0
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +1 -1
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +7 -7
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/manual.html +148 -52
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/story.html +631 -614
- data/lib/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +7 -7
- data/lib/liquid_cms/version.rb +1 -1
- metadata +4 -26
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/LICENSE +0 -32
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/css/luacolors.css +0 -63
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/index.html +0 -68
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/js/parselua.js +0 -253
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/LICENSE +0 -37
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/css/phpcolors.css +0 -114
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/index.html +0 -292
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/parsephp.js +0 -371
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/parsephphtmlmixed.js +0 -90
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/tokenizephp.js +0 -1006
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/LICENSE +0 -22
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/css/plsqlcolors.css +0 -57
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/index.html +0 -67
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/js/parseplsql.js +0 -233
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/LICENSE +0 -32
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/css/pythoncolors.css +0 -58
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/index.html +0 -141
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/js/parsepython.js +0 -542
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/LICENSE +0 -22
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/css/sqlcolors.css +0 -57
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/index.html +0 -56
- data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/js/parsesql.js +0 -211
@@ -8,6 +8,14 @@ var internetExplorer = document.selection && window.ActiveXObject && /MSIE/.test
|
|
8
8
|
var webkit = /AppleWebKit/.test(navigator.userAgent);
|
9
9
|
var safari = /Apple Computers, Inc/.test(navigator.vendor);
|
10
10
|
var gecko = /gecko\/(\d{8})/i.test(navigator.userAgent);
|
11
|
+
var mac = /Mac/.test(navigator.platform);
|
12
|
+
|
13
|
+
// TODO this is related to the backspace-at-end-of-line bug. Remove
|
14
|
+
// this if Opera gets their act together, make the version check more
|
15
|
+
// broad if they don't.
|
16
|
+
var brokenOpera = window.opera && /Version\/10.[56]/.test(navigator.userAgent);
|
17
|
+
// TODO remove this once WebKit 533 becomes less common.
|
18
|
+
var slowWebkit = /AppleWebKit\/533/.test(navigator.userAgent);
|
11
19
|
|
12
20
|
// Make sure a string does not contain two consecutive 'collapseable'
|
13
21
|
// whitespace characters.
|
@@ -15,7 +23,7 @@ function makeWhiteSpace(n) {
|
|
15
23
|
var buffer = [], nb = true;
|
16
24
|
for (; n > 0; n--) {
|
17
25
|
buffer.push((nb || n == 1) ? nbsp : " ");
|
18
|
-
nb
|
26
|
+
nb ^= true;
|
19
27
|
}
|
20
28
|
return buffer.join("");
|
21
29
|
}
|
@@ -24,22 +32,22 @@ function makeWhiteSpace(n) {
|
|
24
32
|
// by the browser, but will not break text-wrapping either.
|
25
33
|
function fixSpaces(string) {
|
26
34
|
if (string.charAt(0) == " ") string = nbsp + string.slice(1);
|
27
|
-
return string.replace(/\t/g, function(){return makeWhiteSpace(indentUnit);})
|
35
|
+
return string.replace(/\t/g, function() {return makeWhiteSpace(indentUnit);})
|
28
36
|
.replace(/[ \u00a0]{2,}/g, function(s) {return makeWhiteSpace(s.length);});
|
29
37
|
}
|
30
38
|
|
31
39
|
function cleanText(text) {
|
32
|
-
return text.replace(/\u00a0/g, " ")
|
40
|
+
return text.replace(/\u00a0/g, " ");
|
33
41
|
}
|
34
42
|
|
35
43
|
// Create a SPAN node with the expected properties for document part
|
36
44
|
// spans.
|
37
|
-
function makePartSpan(value
|
45
|
+
function makePartSpan(value) {
|
38
46
|
var text = value;
|
39
47
|
if (value.nodeType == 3) text = value.nodeValue;
|
40
|
-
else value =
|
48
|
+
else value = document.createTextNode(text);
|
41
49
|
|
42
|
-
var span =
|
50
|
+
var span = document.createElement("SPAN");
|
43
51
|
span.isPart = true;
|
44
52
|
span.appendChild(value);
|
45
53
|
span.currentText = text;
|
@@ -58,8 +66,11 @@ function makePartSpan(value, doc) {
|
|
58
66
|
var webkitLastLineHack = webkit ?
|
59
67
|
function(container) {
|
60
68
|
var last = container.lastChild;
|
61
|
-
if (!last || !last.
|
62
|
-
|
69
|
+
if (!last || !last.hackBR) {
|
70
|
+
var br = document.createElement("BR");
|
71
|
+
br.hackBR = true;
|
72
|
+
container.appendChild(br);
|
73
|
+
}
|
63
74
|
} : function() {};
|
64
75
|
|
65
76
|
var Editor = (function(){
|
@@ -75,13 +86,12 @@ var Editor = (function(){
|
|
75
86
|
// Helper function for traverseDOM. Flattens an arbitrary DOM node
|
76
87
|
// into an array of textnodes and <br> tags.
|
77
88
|
function simplifyDOM(root, atEnd) {
|
78
|
-
var doc = root.ownerDocument;
|
79
89
|
var result = [];
|
80
90
|
var leaving = true;
|
81
91
|
|
82
92
|
function simplifyNode(node, top) {
|
83
93
|
if (node.nodeType == 3) {
|
84
|
-
var text = node.nodeValue = fixSpaces(node.nodeValue.replace(
|
94
|
+
var text = node.nodeValue = fixSpaces(node.nodeValue.replace(/\r/g, "").replace(/\n/g, " "));
|
85
95
|
if (text.length) leaving = false;
|
86
96
|
result.push(node);
|
87
97
|
}
|
@@ -90,11 +100,11 @@ var Editor = (function(){
|
|
90
100
|
result.push(node);
|
91
101
|
}
|
92
102
|
else {
|
93
|
-
|
103
|
+
for (var n = node.firstChild; n; n = n.nextSibling) simplifyNode(n);
|
94
104
|
if (!leaving && newlineElements.hasOwnProperty(node.nodeName.toUpperCase())) {
|
95
105
|
leaving = true;
|
96
106
|
if (!atEnd || !top)
|
97
|
-
result.push(
|
107
|
+
result.push(document.createElement("BR"));
|
98
108
|
}
|
99
109
|
}
|
100
110
|
}
|
@@ -108,14 +118,7 @@ var Editor = (function(){
|
|
108
118
|
// the nodes. It makes sure that all nodes up to and including the
|
109
119
|
// one whose text is being yielded have been 'normalized' to be just
|
110
120
|
// <span> and <br> elements.
|
111
|
-
// See the story.html file for some short remarks about the use of
|
112
|
-
// continuation-passing style in this iterator.
|
113
121
|
function traverseDOM(start){
|
114
|
-
function _yield(value, c){cc = c; return value;}
|
115
|
-
function push(fun, arg, c){return function(){return fun(arg, c);};}
|
116
|
-
function stop(){cc = stop; throw StopIteration;};
|
117
|
-
var cc = push(scanNode, start, stop);
|
118
|
-
var owner = start.ownerDocument;
|
119
122
|
var nodeQueue = [];
|
120
123
|
|
121
124
|
// Create a function that can be used to insert nodes after the
|
@@ -144,13 +147,13 @@ var Editor = (function(){
|
|
144
147
|
var text = "\n";
|
145
148
|
if (part.nodeType == 3) {
|
146
149
|
select.snapshotChanged();
|
147
|
-
part = makePartSpan(part
|
150
|
+
part = makePartSpan(part);
|
148
151
|
text = part.currentText;
|
149
152
|
afterBR = false;
|
150
153
|
}
|
151
154
|
else {
|
152
155
|
if (afterBR && window.opera)
|
153
|
-
point(makePartSpan(""
|
156
|
+
point(makePartSpan(""));
|
154
157
|
afterBR = true;
|
155
158
|
}
|
156
159
|
part.dirty = true;
|
@@ -160,14 +163,13 @@ var Editor = (function(){
|
|
160
163
|
}
|
161
164
|
|
162
165
|
// Extract the text and newlines from a DOM node, insert them into
|
163
|
-
// the document, and
|
166
|
+
// the document, and return the textual content. Used to replace
|
164
167
|
// non-normalized nodes.
|
165
|
-
function writeNode(node,
|
166
|
-
var
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
return _yield(toYield.join(""), c);
|
168
|
+
function writeNode(node, end) {
|
169
|
+
var simplified = simplifyDOM(node, end);
|
170
|
+
for (var i = 0; i < simplified.length; i++)
|
171
|
+
simplified[i] = insertPart(simplified[i]);
|
172
|
+
return simplified.join("");
|
171
173
|
}
|
172
174
|
|
173
175
|
// Check whether a node is a normalized <span> element.
|
@@ -179,38 +181,36 @@ var Editor = (function(){
|
|
179
181
|
return false;
|
180
182
|
}
|
181
183
|
|
182
|
-
//
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
if (node.nextSibling)
|
188
|
-
c = push(scanNode, node.nextSibling, c);
|
184
|
+
// Advance to next node, return string for current node.
|
185
|
+
function next() {
|
186
|
+
if (!start) throw StopIteration;
|
187
|
+
var node = start;
|
188
|
+
start = node.nextSibling;
|
189
189
|
|
190
190
|
if (partNode(node)){
|
191
191
|
nodeQueue.push(node);
|
192
192
|
afterBR = false;
|
193
|
-
return
|
193
|
+
return node.currentText;
|
194
194
|
}
|
195
195
|
else if (isBR(node)) {
|
196
196
|
if (afterBR && window.opera)
|
197
|
-
node.parentNode.insertBefore(makePartSpan(""
|
197
|
+
node.parentNode.insertBefore(makePartSpan(""), node);
|
198
198
|
nodeQueue.push(node);
|
199
199
|
afterBR = true;
|
200
|
-
return
|
200
|
+
return "\n";
|
201
201
|
}
|
202
202
|
else {
|
203
203
|
var end = !node.nextSibling;
|
204
204
|
point = pointAt(node);
|
205
205
|
removeElement(node);
|
206
|
-
return writeNode(node,
|
206
|
+
return writeNode(node, end);
|
207
207
|
}
|
208
208
|
}
|
209
209
|
|
210
210
|
// MochiKit iterators are objects with a next function that
|
211
211
|
// returns the next value or throws StopIteration when there are
|
212
212
|
// no more values.
|
213
|
-
return {next:
|
213
|
+
return {next: next, nodes: nodeQueue};
|
214
214
|
}
|
215
215
|
|
216
216
|
// Determine the text size of a processed node.
|
@@ -240,128 +240,139 @@ var Editor = (function(){
|
|
240
240
|
// indicating whether anything was found, and can be called again to
|
241
241
|
// skip to the next find. Use the select and replace methods to
|
242
242
|
// actually do something with the found locations.
|
243
|
-
function SearchCursor(editor, string,
|
243
|
+
function SearchCursor(editor, string, from, caseFold) {
|
244
244
|
this.editor = editor;
|
245
|
-
this.caseFold = caseFold;
|
246
|
-
if (caseFold) string = string.toLowerCase();
|
247
245
|
this.history = editor.history;
|
248
246
|
this.history.commit();
|
249
|
-
|
250
|
-
// Are we currently at an occurrence of the search string?
|
247
|
+
this.valid = !!string;
|
251
248
|
this.atOccurrence = false;
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
249
|
+
if (caseFold == undefined) caseFold = string == string.toLowerCase();
|
250
|
+
|
251
|
+
function getText(node){
|
252
|
+
var line = cleanText(editor.history.textAfter(node));
|
253
|
+
return (caseFold ? line.toLowerCase() : line);
|
254
|
+
}
|
255
|
+
|
256
|
+
var topPos = {node: null, offset: 0};
|
257
|
+
if (from && typeof from == "object" && typeof from.character == "number") {
|
258
|
+
editor.checkLine(from.line);
|
259
|
+
var pos = {node: from.line, offset: from.character};
|
260
|
+
this.pos = {from: pos, to: pos};
|
261
|
+
}
|
262
|
+
else if (from) {
|
263
|
+
this.pos = {from: select.cursorPos(editor.container, true) || topPos,
|
264
|
+
to: select.cursorPos(editor.container, false) || topPos};
|
261
265
|
}
|
262
266
|
else {
|
263
|
-
this.
|
264
|
-
this.offset = 0;
|
267
|
+
this.pos = {from: topPos, to: topPos};
|
265
268
|
}
|
266
|
-
this.valid = !!string;
|
267
269
|
|
270
|
+
if (caseFold) string = string.toLowerCase();
|
268
271
|
// Create a matcher function based on the kind of string we have.
|
269
|
-
var target = string.split("\n")
|
272
|
+
var target = string.split("\n");
|
270
273
|
this.matches = (target.length == 1) ?
|
271
274
|
// For one-line strings, searching can be done simply by calling
|
272
|
-
// indexOf on the current line.
|
273
|
-
function() {
|
274
|
-
var line =
|
275
|
-
|
276
|
-
|
277
|
-
return {from: {node:
|
278
|
-
to: {node:
|
275
|
+
// indexOf or lastIndexOf on the current line.
|
276
|
+
function(reverse, node, offset) {
|
277
|
+
var line = getText(node), len = string.length, match;
|
278
|
+
if (reverse ? (offset >= len && (match = line.lastIndexOf(string, offset - len)) != -1)
|
279
|
+
: (match = line.indexOf(string, offset)) != -1)
|
280
|
+
return {from: {node: node, offset: match},
|
281
|
+
to: {node: node, offset: match + len}};
|
279
282
|
} :
|
280
283
|
// Multi-line strings require internal iteration over lines, and
|
281
284
|
// some clunky checks to make sure the first match ends at the
|
282
285
|
// end of the line and the last match starts at the start.
|
283
|
-
function() {
|
284
|
-
var
|
285
|
-
var
|
286
|
-
if (
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
var
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
286
|
+
function(reverse, node, offset) {
|
287
|
+
var idx = (reverse ? target.length - 1 : 0), match = target[idx], line = getText(node);
|
288
|
+
var offsetA = (reverse ? line.indexOf(match) + match.length : line.lastIndexOf(match));
|
289
|
+
if (reverse ? offsetA >= offset || offsetA != match.length
|
290
|
+
: offsetA <= offset || offsetA != line.length - match.length)
|
291
|
+
return;
|
292
|
+
|
293
|
+
var pos = node;
|
294
|
+
while (true) {
|
295
|
+
if (reverse && !pos) return;
|
296
|
+
pos = (reverse ? this.history.nodeBefore(pos) : this.history.nodeAfter(pos) );
|
297
|
+
if (!reverse && !pos) return;
|
298
|
+
|
299
|
+
line = getText(pos);
|
300
|
+
match = target[reverse ? --idx : ++idx];
|
301
|
+
|
302
|
+
if (idx > 0 && idx < target.length - 1) {
|
303
|
+
if (line != match) return;
|
304
|
+
else continue;
|
305
|
+
}
|
306
|
+
var offsetB = (reverse ? line.lastIndexOf(match) : line.indexOf(match) + match.length);
|
307
|
+
if (reverse ? offsetB != line.length - match.length : offsetB != match.length)
|
308
|
+
return;
|
309
|
+
return {from: {node: reverse ? pos : node, offset: reverse ? offsetB : offsetA},
|
310
|
+
to: {node: reverse ? node : pos, offset: reverse ? offsetA : offsetB}};
|
296
311
|
}
|
297
|
-
|
298
|
-
var lastLine = cleanText(self.history.textAfter(line));
|
299
|
-
if ((self.caseFold ? lastLine.toLowerCase() : lastLine).indexOf(target[target.length - 1]) != 0)
|
300
|
-
return false;
|
301
|
-
|
302
|
-
return {from: {node: self.line, offset: startOffset},
|
303
|
-
to: {node: line, offset: target[target.length - 1].length}};
|
304
312
|
};
|
305
313
|
}
|
306
314
|
|
307
315
|
SearchCursor.prototype = {
|
308
|
-
findNext: function() {
|
316
|
+
findNext: function() {return this.find(false);},
|
317
|
+
findPrevious: function() {return this.find(true);},
|
318
|
+
|
319
|
+
find: function(reverse) {
|
309
320
|
if (!this.valid) return false;
|
310
|
-
this.atOccurrence = false;
|
311
|
-
var self = this;
|
312
321
|
|
313
|
-
|
314
|
-
|
315
|
-
if
|
316
|
-
|
317
|
-
|
322
|
+
var self = this, pos = reverse ? this.pos.from : this.pos.to,
|
323
|
+
node = pos.node, offset = pos.offset;
|
324
|
+
// Reset the cursor if the current line is no longer in the DOM tree.
|
325
|
+
if (node && !node.parentNode) {
|
326
|
+
node = null; offset = 0;
|
318
327
|
}
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
self.line = pos.node;
|
325
|
-
self.offset = pos.offset + 1;
|
326
|
-
}
|
327
|
-
else {
|
328
|
-
self.line = self.history.nodeAfter(pos.node);
|
329
|
-
self.offset = 0;
|
330
|
-
}
|
328
|
+
function savePosAndFail() {
|
329
|
+
var pos = {node: node, offset: offset};
|
330
|
+
self.pos = {from: pos, to: pos};
|
331
|
+
self.atOccurrence = false;
|
332
|
+
return false;
|
331
333
|
}
|
332
334
|
|
333
335
|
while (true) {
|
334
|
-
|
335
|
-
|
336
|
-
if (match) {
|
337
|
-
this.atOccurrence = match;
|
338
|
-
saveAfter(match.from);
|
336
|
+
if (this.pos = this.matches(reverse, node, offset)) {
|
337
|
+
this.atOccurrence = true;
|
339
338
|
return true;
|
340
339
|
}
|
341
|
-
|
342
|
-
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
return false;
|
340
|
+
|
341
|
+
if (reverse) {
|
342
|
+
if (!node) return savePosAndFail();
|
343
|
+
node = this.history.nodeBefore(node);
|
344
|
+
offset = this.history.textAfter(node).length;
|
347
345
|
}
|
346
|
+
else {
|
347
|
+
var next = this.history.nodeAfter(node);
|
348
|
+
if (!next) {
|
349
|
+
offset = this.history.textAfter(node).length;
|
350
|
+
return savePosAndFail();
|
351
|
+
}
|
352
|
+
node = next;
|
353
|
+
offset = 0;
|
354
|
+
}
|
348
355
|
}
|
349
356
|
},
|
350
357
|
|
351
358
|
select: function() {
|
352
359
|
if (this.atOccurrence) {
|
353
|
-
select.setCursorPos(this.editor.container, this.
|
360
|
+
select.setCursorPos(this.editor.container, this.pos.from, this.pos.to);
|
354
361
|
select.scrollToCursor(this.editor.container);
|
355
362
|
}
|
356
363
|
},
|
357
364
|
|
358
365
|
replace: function(string) {
|
359
366
|
if (this.atOccurrence) {
|
360
|
-
var end = this.editor.replaceRange(this.
|
361
|
-
this.
|
362
|
-
this.offset = end.offset;
|
367
|
+
var end = this.editor.replaceRange(this.pos.from, this.pos.to, string);
|
368
|
+
this.pos.to = end;
|
363
369
|
this.atOccurrence = false;
|
364
370
|
}
|
371
|
+
},
|
372
|
+
|
373
|
+
position: function() {
|
374
|
+
if (this.atOccurrence)
|
375
|
+
return {line: this.pos.from.node, character: this.pos.from.offset};
|
365
376
|
}
|
366
377
|
};
|
367
378
|
|
@@ -370,10 +381,8 @@ var Editor = (function(){
|
|
370
381
|
this.options = options;
|
371
382
|
window.indentUnit = options.indentUnit;
|
372
383
|
this.parent = parent;
|
373
|
-
this.
|
374
|
-
|
375
|
-
this.win = window;
|
376
|
-
this.history = new History(container, options.undoDepth, options.undoDelay, this);
|
384
|
+
var container = this.container = document.body;
|
385
|
+
this.history = new UndoHistory(container, options.undoDepth, options.undoDelay, this);
|
377
386
|
var self = this;
|
378
387
|
|
379
388
|
if (!Editor.Parser)
|
@@ -395,13 +404,21 @@ var Editor = (function(){
|
|
395
404
|
}
|
396
405
|
|
397
406
|
function setEditable() {
|
398
|
-
//
|
399
|
-
//
|
407
|
+
// Use contentEditable instead of designMode on IE, since designMode frames
|
408
|
+
// can not run any scripts. It would be nice if we could use contentEditable
|
409
|
+
// everywhere, but it is significantly flakier than designMode on every
|
410
|
+
// single non-IE browser.
|
400
411
|
if (document.body.contentEditable != undefined && internetExplorer)
|
401
412
|
document.body.contentEditable = "true";
|
402
413
|
else
|
403
414
|
document.designMode = "on";
|
404
415
|
|
416
|
+
// Work around issue where you have to click on the actual
|
417
|
+
// body of the document to focus it in IE, making focusing
|
418
|
+
// hard when the document is small.
|
419
|
+
if (internetExplorer && options.height != "dynamic")
|
420
|
+
document.body.style.minHeight = (frameElement.clientHeight - 2 * document.body.offsetTop - 5) + "px";
|
421
|
+
|
405
422
|
document.documentElement.style.borderWidth = "0";
|
406
423
|
if (!options.textWrapping)
|
407
424
|
container.style.whiteSpace = "nowrap";
|
@@ -431,7 +448,7 @@ var Editor = (function(){
|
|
431
448
|
// workaround for a gecko bug [?] where going forward and then
|
432
449
|
// back again breaks designmode (no more cursor)
|
433
450
|
if (gecko)
|
434
|
-
addEventHandler(
|
451
|
+
addEventHandler(window, "pagehide", function(){self.unloaded = true;});
|
435
452
|
|
436
453
|
addEventHandler(document.body, "paste", function(event) {
|
437
454
|
cursorActivity();
|
@@ -474,10 +491,13 @@ var Editor = (function(){
|
|
474
491
|
return "";
|
475
492
|
|
476
493
|
var accum = [];
|
477
|
-
select.markSelection(
|
494
|
+
select.markSelection();
|
478
495
|
forEach(traverseDOM(this.container.firstChild), method(accum, "push"));
|
479
496
|
webkitLastLineHack(this.container);
|
480
497
|
select.selectMarked();
|
498
|
+
// On webkit, don't count last (empty) line if the webkitLastLineHack BR is present
|
499
|
+
if (webkit && this.container.lastChild.hackBR)
|
500
|
+
accum.pop();
|
481
501
|
return cleanText(accum.join(""));
|
482
502
|
},
|
483
503
|
|
@@ -514,6 +534,16 @@ var Editor = (function(){
|
|
514
534
|
return startOfLine(line.previousSibling);
|
515
535
|
},
|
516
536
|
|
537
|
+
visibleLineCount: function() {
|
538
|
+
var line = this.container.firstChild;
|
539
|
+
while (line && isBR(line)) line = line.nextSibling; // BR heights are unreliable
|
540
|
+
if (!line) return false;
|
541
|
+
var innerHeight = (window.innerHeight
|
542
|
+
|| document.documentElement.clientHeight
|
543
|
+
|| document.body.clientHeight);
|
544
|
+
return Math.floor(innerHeight / line.offsetHeight);
|
545
|
+
},
|
546
|
+
|
517
547
|
selectLines: function(startLine, startOffset, endLine, endOffset) {
|
518
548
|
this.checkLine(startLine);
|
519
549
|
var start = {node: startLine, offset: startOffset}, end = null;
|
@@ -576,10 +606,10 @@ var Editor = (function(){
|
|
576
606
|
}
|
577
607
|
}
|
578
608
|
|
579
|
-
var lines = asEditorLines(content)
|
609
|
+
var lines = asEditorLines(content);
|
580
610
|
for (var i = 0; i < lines.length; i++) {
|
581
|
-
if (i > 0) this.container.insertBefore(
|
582
|
-
this.container.insertBefore(makePartSpan(lines[i]
|
611
|
+
if (i > 0) this.container.insertBefore(document.createElement("BR"), before);
|
612
|
+
this.container.insertBefore(makePartSpan(lines[i]), before);
|
583
613
|
}
|
584
614
|
this.addDirtyNode(line);
|
585
615
|
this.scheduleHighlight();
|
@@ -617,31 +647,68 @@ var Editor = (function(){
|
|
617
647
|
webkitLastLineHack(this.container);
|
618
648
|
},
|
619
649
|
|
650
|
+
cursorCoords: function(start) {
|
651
|
+
var sel = select.cursorPos(this.container, start);
|
652
|
+
if (!sel) return null;
|
653
|
+
var off = sel.offset, node = sel.node, self = this;
|
654
|
+
function measureFromNode(node, xOffset) {
|
655
|
+
var y = -(document.body.scrollTop || document.documentElement.scrollTop || 0),
|
656
|
+
x = -(document.body.scrollLeft || document.documentElement.scrollLeft || 0) + xOffset;
|
657
|
+
forEach([node, window.frameElement], function(n) {
|
658
|
+
while (n) {x += n.offsetLeft; y += n.offsetTop;n = n.offsetParent;}
|
659
|
+
});
|
660
|
+
return {x: x, y: y, yBot: y + node.offsetHeight};
|
661
|
+
}
|
662
|
+
function withTempNode(text, f) {
|
663
|
+
var node = document.createElement("SPAN");
|
664
|
+
node.appendChild(document.createTextNode(text));
|
665
|
+
try {return f(node);}
|
666
|
+
finally {if (node.parentNode) node.parentNode.removeChild(node);}
|
667
|
+
}
|
668
|
+
|
669
|
+
while (off) {
|
670
|
+
node = node ? node.nextSibling : this.container.firstChild;
|
671
|
+
var txt = nodeText(node);
|
672
|
+
if (off < txt.length)
|
673
|
+
return withTempNode(txt.substr(0, off), function(tmp) {
|
674
|
+
tmp.style.position = "absolute"; tmp.style.visibility = "hidden";
|
675
|
+
tmp.className = node.className;
|
676
|
+
self.container.appendChild(tmp);
|
677
|
+
return measureFromNode(node, tmp.offsetWidth);
|
678
|
+
});
|
679
|
+
off -= txt.length;
|
680
|
+
}
|
681
|
+
if (node && isSpan(node))
|
682
|
+
return measureFromNode(node, node.offsetWidth);
|
683
|
+
else if (node && node.nextSibling && isSpan(node.nextSibling))
|
684
|
+
return measureFromNode(node.nextSibling, 0);
|
685
|
+
else
|
686
|
+
return withTempNode("\u200b", function(tmp) {
|
687
|
+
if (node) node.parentNode.insertBefore(tmp, node.nextSibling);
|
688
|
+
else self.container.insertBefore(tmp, self.container.firstChild);
|
689
|
+
return measureFromNode(tmp, 0);
|
690
|
+
});
|
691
|
+
},
|
692
|
+
|
620
693
|
reroutePasteEvent: function() {
|
621
694
|
if (this.capturingPaste || window.opera) return;
|
622
695
|
this.capturingPaste = true;
|
623
|
-
var te =
|
624
|
-
te.style.position = "absolute";
|
625
|
-
te.style.left = "-10000px";
|
626
|
-
te.style.width = "10px";
|
627
|
-
te.style.top = nodeTop(frameElement) + "px";
|
628
|
-
var wrap = window.frameElement.CodeMirror.wrapping;
|
629
|
-
wrap.parentNode.insertBefore(te, wrap);
|
696
|
+
var te = window.frameElement.CodeMirror.textareaHack;
|
630
697
|
parent.focus();
|
698
|
+
te.value = "";
|
631
699
|
te.focus();
|
632
700
|
|
633
701
|
var self = this;
|
634
702
|
this.parent.setTimeout(function() {
|
635
703
|
self.capturingPaste = false;
|
636
|
-
|
704
|
+
window.focus();
|
637
705
|
if (self.selectionSnapshot) // IE hack
|
638
|
-
|
706
|
+
window.select.setBookmark(self.container, self.selectionSnapshot);
|
639
707
|
var text = te.value;
|
640
708
|
if (text) {
|
641
709
|
self.replaceSelection(text);
|
642
710
|
select.scrollToCursor(self.container);
|
643
711
|
}
|
644
|
-
removeElement(te);
|
645
712
|
}, 10);
|
646
713
|
},
|
647
714
|
|
@@ -667,7 +734,7 @@ var Editor = (function(){
|
|
667
734
|
},
|
668
735
|
|
669
736
|
reindentSelection: function(direction) {
|
670
|
-
if (!select.somethingSelected(
|
737
|
+
if (!select.somethingSelected()) {
|
671
738
|
this.indentAtCursor(direction);
|
672
739
|
}
|
673
740
|
else {
|
@@ -684,11 +751,14 @@ var Editor = (function(){
|
|
684
751
|
},
|
685
752
|
ungrabKeys: function() {
|
686
753
|
this.frozen = "leave";
|
687
|
-
this.keyFilter = null;
|
688
754
|
},
|
689
755
|
|
690
|
-
setParser: function(name) {
|
756
|
+
setParser: function(name, parserConfig) {
|
691
757
|
Editor.Parser = window[name];
|
758
|
+
parserConfig = parserConfig || this.options.parserConfig;
|
759
|
+
if (parserConfig && Editor.Parser.configure)
|
760
|
+
Editor.Parser.configure(parserConfig);
|
761
|
+
|
692
762
|
if (this.container.firstChild) {
|
693
763
|
forEach(this.container.childNodes, function(n) {
|
694
764
|
if (n.nodeType != 3) n.dirty = true;
|
@@ -700,8 +770,8 @@ var Editor = (function(){
|
|
700
770
|
|
701
771
|
// Intercept enter and tab, and assign their new functions.
|
702
772
|
keyDown: function(event) {
|
703
|
-
if (this.frozen == "leave") this.frozen = null;
|
704
|
-
if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) {
|
773
|
+
if (this.frozen == "leave") {this.frozen = null; this.keyFilter = null;}
|
774
|
+
if (this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode, event))) {
|
705
775
|
event.stop();
|
706
776
|
this.frozen(event);
|
707
777
|
return;
|
@@ -722,8 +792,9 @@ var Editor = (function(){
|
|
722
792
|
this.reparseBuffer();
|
723
793
|
}
|
724
794
|
else {
|
725
|
-
select.insertNewlineAtCursor(
|
726
|
-
this.
|
795
|
+
select.insertNewlineAtCursor();
|
796
|
+
var mode = this.options.enterMode;
|
797
|
+
if (mode != "flat") this.indentAtCursor(mode == "keep" ? "keep" : undefined);
|
727
798
|
select.scrollToCursor(this.container);
|
728
799
|
}
|
729
800
|
event.stop();
|
@@ -742,6 +813,13 @@ var Editor = (function(){
|
|
742
813
|
else if (code == 35 && !event.shiftKey && !event.ctrlKey) { // end
|
743
814
|
if (this.end()) event.stop();
|
744
815
|
}
|
816
|
+
// Only in Firefox is the default behavior for PgUp/PgDn correct.
|
817
|
+
else if (code == 33 && !event.shiftKey && !event.ctrlKey && !gecko) { // PgUp
|
818
|
+
if (this.pageUp()) event.stop();
|
819
|
+
}
|
820
|
+
else if (code == 34 && !event.shiftKey && !event.ctrlKey && !gecko) { // PgDn
|
821
|
+
if (this.pageDown()) event.stop();
|
822
|
+
}
|
745
823
|
else if ((code == 219 || code == 221) && event.ctrlKey && !event.altKey) { // [, ]
|
746
824
|
this.highlightParens(event.shiftKey, true);
|
747
825
|
event.stop();
|
@@ -770,7 +848,7 @@ var Editor = (function(){
|
|
770
848
|
this.options.saveFunction();
|
771
849
|
event.stop();
|
772
850
|
}
|
773
|
-
else if (
|
851
|
+
else if (code == 86 && !mac) { // V
|
774
852
|
this.reroutePasteEvent();
|
775
853
|
}
|
776
854
|
}
|
@@ -779,20 +857,63 @@ var Editor = (function(){
|
|
779
857
|
// Check for characters that should re-indent the current line,
|
780
858
|
// and prevent Opera from handling enter and tab anyway.
|
781
859
|
keyPress: function(event) {
|
782
|
-
var electric = Editor.Parser.electricChars, self = this;
|
860
|
+
var electric = this.options.electricChars && Editor.Parser.electricChars, self = this;
|
783
861
|
// Hack for Opera, and Firefox on OS X, in which stopping a
|
784
862
|
// keydown event does not prevent the associated keypress event
|
785
863
|
// from happening, so we have to cancel enter and tab again
|
786
864
|
// here.
|
787
|
-
if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode))) ||
|
865
|
+
if ((this.frozen && (!this.keyFilter || this.keyFilter(event.keyCode || event.code, event))) ||
|
788
866
|
event.code == 13 || (event.code == 9 && this.options.tabMode != "default") ||
|
789
|
-
(event.
|
867
|
+
(event.code == 32 && event.shiftKey && this.options.tabMode == "default"))
|
790
868
|
event.stop();
|
869
|
+
else if (mac && (event.ctrlKey || event.metaKey) && event.character == "v") {
|
870
|
+
this.reroutePasteEvent();
|
871
|
+
}
|
791
872
|
else if (electric && electric.indexOf(event.character) != -1)
|
792
873
|
this.parent.setTimeout(function(){self.indentAtCursor(null);}, 0);
|
793
|
-
|
794
|
-
|
795
|
-
|
874
|
+
// Work around a bug where pressing backspace at the end of a
|
875
|
+
// line, or delete at the start, often causes the cursor to jump
|
876
|
+
// to the start of the line in Opera 10.60.
|
877
|
+
else if (brokenOpera) {
|
878
|
+
if (event.code == 8) { // backspace
|
879
|
+
var sel = select.selectionTopNode(this.container), self = this,
|
880
|
+
next = sel ? sel.nextSibling : this.container.firstChild;
|
881
|
+
if (sel !== false && next && isBR(next))
|
882
|
+
this.parent.setTimeout(function(){
|
883
|
+
if (select.selectionTopNode(self.container) == next)
|
884
|
+
select.focusAfterNode(next.previousSibling, self.container);
|
885
|
+
}, 20);
|
886
|
+
}
|
887
|
+
else if (event.code == 46) { // delete
|
888
|
+
var sel = select.selectionTopNode(this.container), self = this;
|
889
|
+
if (sel && isBR(sel)) {
|
890
|
+
this.parent.setTimeout(function(){
|
891
|
+
if (select.selectionTopNode(self.container) != sel)
|
892
|
+
select.focusAfterNode(sel, self.container);
|
893
|
+
}, 20);
|
894
|
+
}
|
895
|
+
}
|
896
|
+
}
|
897
|
+
// In 533.* WebKit versions, when the document is big, typing
|
898
|
+
// something at the end of a line causes the browser to do some
|
899
|
+
// kind of stupid heavy operation, creating delays of several
|
900
|
+
// seconds before the typed characters appear. This very crude
|
901
|
+
// hack inserts a temporary zero-width space after the cursor to
|
902
|
+
// make it not be at the end of the line.
|
903
|
+
else if (slowWebkit) {
|
904
|
+
var sel = select.selectionTopNode(this.container),
|
905
|
+
next = sel ? sel.nextSibling : this.container.firstChild;
|
906
|
+
// Doesn't work on empty lines, for some reason those always
|
907
|
+
// trigger the delay.
|
908
|
+
if (sel && next && isBR(next) && !isBR(sel)) {
|
909
|
+
var cheat = document.createTextNode("\u200b");
|
910
|
+
this.container.insertBefore(cheat, next);
|
911
|
+
this.parent.setTimeout(function() {
|
912
|
+
if (cheat.nodeValue == "\u200b") removeElement(cheat);
|
913
|
+
else cheat.nodeValue = cheat.nodeValue.replace("\u200b", "");
|
914
|
+
}, 20);
|
915
|
+
}
|
916
|
+
}
|
796
917
|
},
|
797
918
|
|
798
919
|
// Mark the node at the cursor dirty when a non-safe key is
|
@@ -806,32 +927,45 @@ var Editor = (function(){
|
|
806
927
|
// so that it has an indentation method. Returns the whitespace
|
807
928
|
// element that has been modified or created (if any).
|
808
929
|
indentLineAfter: function(start, direction) {
|
930
|
+
function whiteSpaceAfter(node) {
|
931
|
+
var ws = node ? node.nextSibling : self.container.firstChild;
|
932
|
+
if (!ws || !hasClass(ws, "whitespace")) return null;
|
933
|
+
return ws;
|
934
|
+
}
|
935
|
+
|
809
936
|
// whiteSpace is the whitespace span at the start of the line,
|
810
937
|
// or null if there is no such node.
|
811
|
-
var
|
812
|
-
if (whiteSpace && !hasClass(whiteSpace, "whitespace"))
|
813
|
-
whiteSpace = null;
|
814
|
-
|
815
|
-
// Sometimes the start of the line can influence the correct
|
816
|
-
// indentation, so we retrieve it.
|
817
|
-
var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
|
818
|
-
var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
|
819
|
-
|
820
|
-
// Ask the lexical context for the correct indentation, and
|
821
|
-
// compute how much this differs from the current indentation.
|
938
|
+
var self = this, whiteSpace = whiteSpaceAfter(start);
|
822
939
|
var newIndent = 0, curIndent = whiteSpace ? whiteSpace.currentText.length : 0;
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
940
|
+
|
941
|
+
if (direction == "keep") {
|
942
|
+
if (start) {
|
943
|
+
var prevWS = whiteSpaceAfter(startOfLine(start.previousSibling))
|
944
|
+
if (prevWS) newIndent = prevWS.currentText.length;
|
945
|
+
}
|
946
|
+
}
|
947
|
+
else {
|
948
|
+
// Sometimes the start of the line can influence the correct
|
949
|
+
// indentation, so we retrieve it.
|
950
|
+
var firstText = whiteSpace ? whiteSpace.nextSibling : (start ? start.nextSibling : this.container.firstChild);
|
951
|
+
var nextChars = (start && firstText && firstText.currentText) ? firstText.currentText : "";
|
952
|
+
|
953
|
+
// Ask the lexical context for the correct indentation, and
|
954
|
+
// compute how much this differs from the current indentation.
|
955
|
+
if (direction != null && this.options.tabMode == "shift")
|
956
|
+
newIndent = direction ? curIndent + indentUnit : Math.max(0, curIndent - indentUnit)
|
957
|
+
else if (start)
|
958
|
+
newIndent = start.indentation(nextChars, curIndent, direction);
|
959
|
+
else if (Editor.Parser.firstIndentation)
|
960
|
+
newIndent = Editor.Parser.firstIndentation(nextChars, curIndent, direction);
|
961
|
+
}
|
962
|
+
|
829
963
|
var indentDiff = newIndent - curIndent;
|
830
964
|
|
831
965
|
// If there is too much, this is just a matter of shrinking a span.
|
832
966
|
if (indentDiff < 0) {
|
833
967
|
if (newIndent == 0) {
|
834
|
-
if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild, 0);
|
968
|
+
if (firstText) select.snapshotMove(whiteSpace.firstChild, firstText.firstChild || firstText, 0);
|
835
969
|
removeElement(whiteSpace);
|
836
970
|
whiteSpace = null;
|
837
971
|
}
|
@@ -847,18 +981,23 @@ var Editor = (function(){
|
|
847
981
|
if (whiteSpace) {
|
848
982
|
whiteSpace.currentText = makeWhiteSpace(newIndent);
|
849
983
|
whiteSpace.firstChild.nodeValue = whiteSpace.currentText;
|
984
|
+
select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, indentDiff, true);
|
850
985
|
}
|
851
986
|
// Otherwise, we have to add a new whitespace node.
|
852
987
|
else {
|
853
|
-
whiteSpace = makePartSpan(makeWhiteSpace(newIndent)
|
988
|
+
whiteSpace = makePartSpan(makeWhiteSpace(newIndent));
|
854
989
|
whiteSpace.className = "whitespace";
|
855
990
|
if (start) insertAfter(whiteSpace, start);
|
856
991
|
else this.container.insertBefore(whiteSpace, this.container.firstChild);
|
992
|
+
select.snapshotMove(firstText && (firstText.firstChild || firstText),
|
993
|
+
whiteSpace.firstChild, newIndent, false, true);
|
857
994
|
}
|
858
|
-
|
995
|
+
}
|
996
|
+
// Make sure cursor ends up after the whitespace
|
997
|
+
else if (whiteSpace) {
|
998
|
+
select.snapshotMove(whiteSpace.firstChild, whiteSpace.firstChild, newIndent, false);
|
859
999
|
}
|
860
1000
|
if (indentDiff != 0) this.addDirtyNode(start);
|
861
|
-
return whiteSpace;
|
862
1001
|
},
|
863
1002
|
|
864
1003
|
// Re-highlight the selected part of the document.
|
@@ -867,7 +1006,7 @@ var Editor = (function(){
|
|
867
1006
|
var to = select.selectionTopNode(this.container, false);
|
868
1007
|
if (pos === false || to === false) return false;
|
869
1008
|
|
870
|
-
select.markSelection(
|
1009
|
+
select.markSelection();
|
871
1010
|
if (this.highlight(pos, endOfLine(to, this.container), true, 20) === false)
|
872
1011
|
return false;
|
873
1012
|
select.selectMarked();
|
@@ -879,7 +1018,7 @@ var Editor = (function(){
|
|
879
1018
|
// is re-indented.
|
880
1019
|
handleTab: function(direction) {
|
881
1020
|
if (this.options.tabMode == "spaces")
|
882
|
-
select.insertTabAtCursor(
|
1021
|
+
select.insertTabAtCursor();
|
883
1022
|
else
|
884
1023
|
this.reindentSelection(direction);
|
885
1024
|
},
|
@@ -914,6 +1053,37 @@ var Editor = (function(){
|
|
914
1053
|
return true;
|
915
1054
|
},
|
916
1055
|
|
1056
|
+
pageUp: function() {
|
1057
|
+
var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
|
1058
|
+
if (line === false || scrollAmount === false) return false;
|
1059
|
+
// Try to keep one line on the screen.
|
1060
|
+
scrollAmount -= 2;
|
1061
|
+
for (var i = 0; i < scrollAmount; i++) {
|
1062
|
+
line = this.prevLine(line);
|
1063
|
+
if (line === false) break;
|
1064
|
+
}
|
1065
|
+
if (i == 0) return false; // Already at first line
|
1066
|
+
select.setCursorPos(this.container, {node: line, offset: 0});
|
1067
|
+
select.scrollToCursor(this.container);
|
1068
|
+
return true;
|
1069
|
+
},
|
1070
|
+
|
1071
|
+
pageDown: function() {
|
1072
|
+
var line = this.cursorPosition().line, scrollAmount = this.visibleLineCount();
|
1073
|
+
if (line === false || scrollAmount === false) return false;
|
1074
|
+
// Try to move to the last line of the current page.
|
1075
|
+
scrollAmount -= 2;
|
1076
|
+
for (var i = 0; i < scrollAmount; i++) {
|
1077
|
+
var nextLine = this.nextLine(line);
|
1078
|
+
if (nextLine === false) break;
|
1079
|
+
line = nextLine;
|
1080
|
+
}
|
1081
|
+
if (i == 0) return false; // Already at last line
|
1082
|
+
select.setCursorPos(this.container, {node: line, offset: 0});
|
1083
|
+
select.scrollToCursor(this.container);
|
1084
|
+
return true;
|
1085
|
+
},
|
1086
|
+
|
917
1087
|
// Delay (or initiate) the next paren highlight event.
|
918
1088
|
scheduleParenHighlight: function() {
|
919
1089
|
if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
|
@@ -953,7 +1123,7 @@ var Editor = (function(){
|
|
953
1123
|
unhighlight(self.highlighted[1]);
|
954
1124
|
}
|
955
1125
|
|
956
|
-
if (!window.select) return;
|
1126
|
+
if (!window.parent || !window.select) return;
|
957
1127
|
// Clear the event property.
|
958
1128
|
if (this.parenEvent) this.parent.clearTimeout(this.parenEvent);
|
959
1129
|
this.parenEvent = null;
|
@@ -1036,13 +1206,9 @@ var Editor = (function(){
|
|
1036
1206
|
// there's nothing to indent.
|
1037
1207
|
if (cursor === false)
|
1038
1208
|
return;
|
1039
|
-
|
1040
|
-
|
1041
|
-
|
1042
|
-
cursor = whiteSpace;
|
1043
|
-
// This means the indentation has probably messed up the cursor.
|
1044
|
-
if (cursor == whiteSpace)
|
1045
|
-
select.focusAfterNode(cursor, this.container);
|
1209
|
+
select.markSelection();
|
1210
|
+
this.indentLineAfter(startOfLine(cursor), direction);
|
1211
|
+
select.selectMarked();
|
1046
1212
|
},
|
1047
1213
|
|
1048
1214
|
// Indent all lines whose start falls inside of the current
|
@@ -1067,8 +1233,8 @@ var Editor = (function(){
|
|
1067
1233
|
cursorActivity: function(safe) {
|
1068
1234
|
// pagehide event hack above
|
1069
1235
|
if (this.unloaded) {
|
1070
|
-
|
1071
|
-
|
1236
|
+
window.document.designMode = "off";
|
1237
|
+
window.document.designMode = "on";
|
1072
1238
|
this.unloaded = false;
|
1073
1239
|
}
|
1074
1240
|
|
@@ -1153,14 +1319,14 @@ var Editor = (function(){
|
|
1153
1319
|
highlightDirty: function(force) {
|
1154
1320
|
// Prevent FF from raising an error when it is firing timeouts
|
1155
1321
|
// on a page that's no longer loaded.
|
1156
|
-
if (!window.select) return false;
|
1322
|
+
if (!window.parent || !window.select) return false;
|
1157
1323
|
|
1158
|
-
if (!this.options.readOnly) select.markSelection(
|
1324
|
+
if (!this.options.readOnly) select.markSelection();
|
1159
1325
|
var start, endTime = force ? null : time() + this.options.passTime;
|
1160
1326
|
while ((time() < endTime || force) && (start = this.getDirtyNode())) {
|
1161
1327
|
var result = this.highlight(start, endTime);
|
1162
1328
|
if (result && result.node && result.dirty)
|
1163
|
-
this.addDirtyNode(result.node);
|
1329
|
+
this.addDirtyNode(result.node.nextSibling);
|
1164
1330
|
}
|
1165
1331
|
if (!this.options.readOnly) select.selectMarked();
|
1166
1332
|
if (start) this.scheduleHighlight();
|
@@ -1173,12 +1339,12 @@ var Editor = (function(){
|
|
1173
1339
|
var self = this, pos = null;
|
1174
1340
|
return function() {
|
1175
1341
|
// FF timeout weirdness workaround.
|
1176
|
-
if (!window.select) return;
|
1342
|
+
if (!window.parent || !window.select) return;
|
1177
1343
|
// If the current node is no longer in the document... oh
|
1178
1344
|
// well, we start over.
|
1179
1345
|
if (pos && pos.parentNode != self.container)
|
1180
1346
|
pos = null;
|
1181
|
-
select.markSelection(
|
1347
|
+
select.markSelection();
|
1182
1348
|
var result = self.highlight(pos, time() + passTime, true);
|
1183
1349
|
select.selectMarked();
|
1184
1350
|
var newPos = result ? (result.node && result.node.nextSibling) : null;
|
@@ -1244,7 +1410,7 @@ var Editor = (function(){
|
|
1244
1410
|
}
|
1245
1411
|
// Create a part corresponding to a given token.
|
1246
1412
|
function tokenPart(token){
|
1247
|
-
var part = makePartSpan(token.value
|
1413
|
+
var part = makePartSpan(token.value);
|
1248
1414
|
part.className = token.style;
|
1249
1415
|
return part;
|
1250
1416
|
}
|