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.
Files changed (49) hide show
  1. data/CHANGELOG.rdoc +5 -1
  2. data/Gemfile.lock +1 -1
  3. data/README.rdoc +5 -1
  4. data/app/helpers/cms/common_helper.rb +1 -0
  5. data/app/views/cms/pages/_page.html.erb +2 -1
  6. data/app/views/layouts/cms.html.erb +2 -1
  7. data/lib/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +2 -2
  8. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +12 -8
  9. data/lib/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +123 -29
  10. data/lib/generators/liquid_cms/templates/public/cms/codemirror/csstest.html +1 -1
  11. data/lib/generators/liquid_cms/templates/public/cms/codemirror/htmltest.html +1 -1
  12. data/lib/generators/liquid_cms/templates/public/cms/codemirror/index.html +232 -179
  13. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +211 -65
  14. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +360 -194
  15. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +1 -1
  16. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +11 -7
  17. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +14 -5
  18. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +1 -1
  19. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +140 -87
  20. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +5 -0
  21. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +1 -1
  22. data/lib/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +7 -7
  23. data/lib/generators/liquid_cms/templates/public/cms/codemirror/manual.html +148 -52
  24. data/lib/generators/liquid_cms/templates/public/cms/codemirror/story.html +631 -614
  25. data/lib/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +7 -7
  26. data/lib/liquid_cms/version.rb +1 -1
  27. metadata +4 -26
  28. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/LICENSE +0 -32
  29. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/css/luacolors.css +0 -63
  30. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/index.html +0 -68
  31. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/lua/js/parselua.js +0 -253
  32. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/LICENSE +0 -37
  33. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/css/phpcolors.css +0 -114
  34. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/index.html +0 -292
  35. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/parsephp.js +0 -371
  36. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/parsephphtmlmixed.js +0 -90
  37. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/php/js/tokenizephp.js +0 -1006
  38. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/LICENSE +0 -22
  39. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/css/plsqlcolors.css +0 -57
  40. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/index.html +0 -67
  41. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/plsql/js/parseplsql.js +0 -233
  42. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/LICENSE +0 -32
  43. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/css/pythoncolors.css +0 -58
  44. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/index.html +0 -141
  45. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/python/js/parsepython.js +0 -542
  46. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/LICENSE +0 -22
  47. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/css/sqlcolors.css +0 -57
  48. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/index.html +0 -56
  49. data/lib/generators/liquid_cms/templates/public/cms/codemirror/contrib/sql/js/parsesql.js +0 -211
@@ -36,7 +36,7 @@ MirrorFrame.prototype = {
36
36
 
37
37
  var first = true;
38
38
  do {
39
- var cursor = this.mirror.getSearchCursor(text, first, true);
39
+ var cursor = this.mirror.getSearchCursor(text, first);
40
40
  first = false;
41
41
  while (cursor.findNext()) {
42
42
  cursor.select();
@@ -112,16 +112,18 @@ var CSSParser = Editor.Parser = (function() {
112
112
  function parseCSS(source, basecolumn) {
113
113
  basecolumn = basecolumn || 0;
114
114
  var tokens = tokenizeCSS(source);
115
- var inBraces = false, inRule = false;
115
+ var inBraces = false, inRule = false, inDecl = false;;
116
116
 
117
117
  var iter = {
118
118
  next: function() {
119
119
  var token = tokens.next(), style = token.style, content = token.content;
120
120
 
121
- if (style == "css-identifier" && inRule)
122
- token.style = "css-value";
123
121
  if (style == "css-hash")
124
- token.style = inRule ? "css-colorcode" : "css-identifier";
122
+ style = token.style = inRule ? "css-colorcode" : "css-identifier";
123
+ if (style == "css-identifier") {
124
+ if (inRule) token.style = "css-value";
125
+ else if (!inBraces && !inDecl) token.style = "css-selector";
126
+ }
125
127
 
126
128
  if (content == "\n")
127
129
  token.indentation = indentCSS(inBraces, inRule, basecolumn);
@@ -129,11 +131,13 @@ var CSSParser = Editor.Parser = (function() {
129
131
  if (content == "{")
130
132
  inBraces = true;
131
133
  else if (content == "}")
132
- inBraces = inRule = false;
133
- else if (inBraces && content == ";")
134
- inRule = false;
134
+ inBraces = inRule = inDecl = false;
135
+ else if (content == ";")
136
+ inRule = inDecl = false;
135
137
  else if (inBraces && style != "css-comment" && style != "whitespace")
136
138
  inRule = true;
139
+ else if (!inBraces && style == "css-at" && content != "@media")
140
+ inDecl = true;
137
141
 
138
142
  return token;
139
143
  },
@@ -60,7 +60,7 @@ var JSParser = Editor.Parser = (function() {
60
60
  // semicolon. Actions at the end of the stack go first. It is
61
61
  // initialized with an infinitely looping action that consumes
62
62
  // whole statements.
63
- var cc = [statements];
63
+ var cc = [json ? expressions : statements];
64
64
  // Context contains information about the current local scope, the
65
65
  // variables defined in that, and the scopes above it.
66
66
  var context = null;
@@ -206,6 +206,8 @@ var JSParser = Editor.Parser = (function() {
206
206
  }
207
207
  // Pop off the current lexical context.
208
208
  function poplex(){
209
+ if (lexical.type == ")")
210
+ indented = lexical.indented;
209
211
  lexical = lexical.prev;
210
212
  }
211
213
  poplex.lex = true;
@@ -218,6 +220,7 @@ var JSParser = Editor.Parser = (function() {
218
220
  function expect(wanted){
219
221
  return function expecting(type){
220
222
  if (type == wanted) cont();
223
+ else if (wanted == ";") pass();
221
224
  else cont(arguments.callee);
222
225
  };
223
226
  }
@@ -226,14 +229,17 @@ var JSParser = Editor.Parser = (function() {
226
229
  function statements(type){
227
230
  return pass(statement, statements);
228
231
  }
232
+ function expressions(type){
233
+ return pass(expression, expressions);
234
+ }
229
235
  // Dispatches various types of statements based on the type of the
230
236
  // current token.
231
237
  function statement(type){
232
238
  if (type == "var") cont(pushlex("vardef"), vardef1, expect(";"), poplex);
233
239
  else if (type == "keyword a") cont(pushlex("form"), expression, statement, poplex);
234
240
  else if (type == "keyword b") cont(pushlex("form"), statement, poplex);
235
- else if (type == "{" && json) cont(pushlex("}"), commasep(objprop, "}"), poplex);
236
241
  else if (type == "{") cont(pushlex("}"), block, poplex);
242
+ else if (type == ";") cont();
237
243
  else if (type == "function") cont(functiondef);
238
244
  else if (type == "for") cont(pushlex("form"), expect("("), pushlex(")"), forspec1, expect(")"), poplex, statement, poplex);
239
245
  else if (type == "variable") cont(pushlex("stat"), maybelabel);
@@ -252,13 +258,16 @@ var JSParser = Editor.Parser = (function() {
252
258
  else if (type == "operator") cont(expression);
253
259
  else if (type == "[") cont(pushlex("]"), commasep(expression, "]"), poplex, maybeoperator);
254
260
  else if (type == "{") cont(pushlex("}"), commasep(objprop, "}"), poplex, maybeoperator);
261
+ else cont();
255
262
  }
256
263
  // Called for places where operators, function calls, or
257
264
  // subscripts are valid. Will skip on to the next action if none
258
265
  // is found.
259
- function maybeoperator(type){
260
- if (type == "operator") cont(expression);
261
- else if (type == "(") cont(pushlex(")"), expression, commasep(expression, ")"), poplex, maybeoperator);
266
+ function maybeoperator(type, value){
267
+ if (type == "operator" && /\+\+|--/.test(value)) cont(maybeoperator);
268
+ else if (type == "operator") cont(expression);
269
+ else if (type == ";") pass();
270
+ else if (type == "(") cont(pushlex(")"), commasep(expression, ")"), poplex, maybeoperator);
262
271
  else if (type == ".") cont(property, maybeoperator);
263
272
  else if (type == "[") cont(pushlex("]"), expression, expect("]"), poplex, maybeoperator);
264
273
  }
@@ -6,7 +6,7 @@ var SparqlParser = Editor.Parser = (function() {
6
6
  "isblank", "isliteral", "union", "a"]);
7
7
  var keywords = wordRegexp(["base", "prefix", "select", "distinct", "reduced", "construct", "describe",
8
8
  "ask", "from", "named", "where", "order", "limit", "offset", "filter", "optional",
9
- "graph", "by", "asc", "desc", ]);
9
+ "graph", "by", "asc", "desc"]);
10
10
  var operatorChars = /[*+\-<>=&|]/;
11
11
 
12
12
  var tokenizeSparql = (function() {
@@ -29,11 +29,10 @@ var select = {};
29
29
 
30
30
  var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
31
31
 
32
- select.scrollToNode = function(element) {
33
- if (!element) return;
34
- var doc = element.ownerDocument, body = doc.body,
35
- win = (doc.defaultView || doc.parentWindow),
36
- html = doc.documentElement,
32
+ select.scrollToNode = function(node, cursor) {
33
+ if (!node) return;
34
+ var element = node, body = document.body,
35
+ html = document.documentElement,
37
36
  atEnd = !element.nextSibling || !element.nextSibling.nextSibling
38
37
  || !element.nextSibling.nextSibling.nextSibling;
39
38
  // In Opera (and recent Webkit versions), BR elements *always*
@@ -48,7 +47,14 @@ var select = {};
48
47
  // offset, just scroll to the end.
49
48
  if (compensateHack == 0) atEnd = false;
50
49
 
51
- var y = compensateHack * (element ? element.offsetHeight : 0), x = 0, pos = element;
50
+ // WebKit has a bad habit of (sometimes) happily returning bogus
51
+ // offsets when the document has just been changed. This seems to
52
+ // always be 5/5, so we don't use those.
53
+ if (webkit && element && element.offsetTop == 5 && element.offsetLeft == 5)
54
+ return;
55
+
56
+ var y = compensateHack * (element ? element.offsetHeight : 0), x = 0,
57
+ width = (node ? node.offsetWidth : 0), pos = element;
52
58
  while (pos && pos.offsetParent) {
53
59
  y += pos.offsetTop;
54
60
  // Don't count X offset for <br> nodes
@@ -59,21 +65,29 @@ var select = {};
59
65
 
60
66
  var scroll_x = body.scrollLeft || html.scrollLeft || 0,
61
67
  scroll_y = body.scrollTop || html.scrollTop || 0,
62
- screen_x = x - scroll_x, screen_y = y - scroll_y, scroll = false;
68
+ scroll = false, screen_width = window.innerWidth || html.clientWidth || 0;
63
69
 
64
- if (screen_x < 0 || screen_x > (win.innerWidth || html.clientWidth || 0)) {
65
- scroll_x = x;
66
- scroll = true;
70
+ if (cursor || width < screen_width) {
71
+ if (cursor) {
72
+ var off = select.offsetInNode(node), size = nodeText(node).length;
73
+ if (size) x += width * (off / size);
74
+ }
75
+ var screen_x = x - scroll_x;
76
+ if (screen_x < 0 || screen_x > screen_width) {
77
+ scroll_x = x;
78
+ scroll = true;
79
+ }
67
80
  }
68
- if (screen_y < 0 || atEnd || screen_y > (win.innerHeight || html.clientHeight || 0) - 50) {
81
+ var screen_y = y - scroll_y;
82
+ if (screen_y < 0 || atEnd || screen_y > (window.innerHeight || html.clientHeight || 0) - 50) {
69
83
  scroll_y = atEnd ? 1e6 : y;
70
84
  scroll = true;
71
85
  }
72
- if (scroll) win.scrollTo(scroll_x, scroll_y);
86
+ if (scroll) window.scrollTo(scroll_x, scroll_y);
73
87
  };
74
88
 
75
89
  select.scrollToCursor = function(container) {
76
- select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild);
90
+ select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild, true);
77
91
  };
78
92
 
79
93
  // Used to prevent restoring a selection when we do not need to.
@@ -130,8 +144,8 @@ var select = {};
130
144
  // Most functions are defined in two ways, one for the IE selection
131
145
  // model, one for the W3C one.
132
146
  if (select.ie_selection) {
133
- function selectionNode(win, start) {
134
- var range = win.document.selection.createRange();
147
+ function selectionNode(start) {
148
+ var range = document.selection.createRange();
135
149
  range.collapse(start);
136
150
 
137
151
  function nodeAfter(node) {
@@ -149,7 +163,7 @@ var select = {};
149
163
  }
150
164
 
151
165
  var containing = range.parentElement();
152
- if (!isAncestor(win.document.body, containing)) return null;
166
+ if (!isAncestor(document.body, containing)) return null;
153
167
  if (!containing.firstChild) return nodeAtStartOf(containing);
154
168
 
155
169
  var working = range.duplicate();
@@ -176,25 +190,24 @@ var select = {};
176
190
  return nodeAfter(containing);
177
191
  }
178
192
 
179
- select.markSelection = function(win) {
193
+ select.markSelection = function() {
180
194
  currentSelection = null;
181
- var sel = win.document.selection;
195
+ var sel = document.selection;
182
196
  if (!sel) return;
183
- var start = selectionNode(win, true),
184
- end = selectionNode(win, false);
197
+ var start = selectionNode(true),
198
+ end = selectionNode(false);
185
199
  if (!start || !end) return;
186
- currentSelection = {start: start, end: end, window: win, changed: false};
200
+ currentSelection = {start: start, end: end, changed: false};
187
201
  };
188
202
 
189
203
  select.selectMarked = function() {
190
204
  if (!currentSelection || !currentSelection.changed) return;
191
- var win = currentSelection.window, doc = win.document;
192
205
 
193
206
  function makeRange(point) {
194
- var range = doc.body.createTextRange(),
207
+ var range = document.body.createTextRange(),
195
208
  node = point.node;
196
209
  if (!node) {
197
- range.moveToElementText(currentSelection.window.document.body);
210
+ range.moveToElementText(document.body);
198
211
  range.collapse(false);
199
212
  }
200
213
  else if (node.nodeType == 3) {
@@ -218,11 +231,20 @@ var select = {};
218
231
  start.select();
219
232
  };
220
233
 
234
+ select.offsetInNode = function(node) {
235
+ var sel = document.selection;
236
+ if (!sel) return 0;
237
+ var range = sel.createRange(), range2 = range.duplicate();
238
+ try {range2.moveToElementText(node);} catch(e){return 0;}
239
+ range.setEndPoint("StartToStart", range2);
240
+ return range.text.length;
241
+ };
242
+
221
243
  // Get the top-level node that one end of the cursor is inside or
222
244
  // after. Note that this returns false for 'no cursor', and null
223
245
  // for 'start of document'.
224
246
  select.selectionTopNode = function(container, start) {
225
- var selection = container.ownerDocument.selection;
247
+ var selection = document.selection;
226
248
  if (!selection) return false;
227
249
 
228
250
  var range = selection.createRange(), range2 = range.duplicate();
@@ -272,6 +294,13 @@ var select = {};
272
294
  else
273
295
  end = middle - 1;
274
296
  }
297
+
298
+ if (start == 0) {
299
+ var test1 = selection.createRange(), test2 = test1.duplicate();
300
+ test2.moveToElementText(container);
301
+ if (test1.compareEndPoints("StartToStart", test2) == 0)
302
+ return null;
303
+ }
275
304
  return container.childNodes[start] || null;
276
305
  };
277
306
 
@@ -279,19 +308,19 @@ var select = {};
279
308
  // manually moving the cursor instead of restoring it to its old
280
309
  // position.
281
310
  select.focusAfterNode = function(node, container) {
282
- var range = container.ownerDocument.body.createTextRange();
311
+ var range = document.body.createTextRange();
283
312
  range.moveToElementText(node || container);
284
313
  range.collapse(!node);
285
314
  range.select();
286
315
  };
287
316
 
288
- select.somethingSelected = function(win) {
289
- var sel = win.document.selection;
317
+ select.somethingSelected = function() {
318
+ var sel = document.selection;
290
319
  return sel && (sel.createRange().text != "");
291
320
  };
292
321
 
293
- function insertAtCursor(window, html) {
294
- var selection = window.document.selection;
322
+ function insertAtCursor(html) {
323
+ var selection = document.selection;
295
324
  if (selection) {
296
325
  var range = selection.createRange();
297
326
  range.pasteHTML(html);
@@ -302,19 +331,19 @@ var select = {};
302
331
 
303
332
  // Used to normalize the effect of the enter key, since browsers
304
333
  // do widely different things when pressing enter in designMode.
305
- select.insertNewlineAtCursor = function(window) {
306
- insertAtCursor(window, "<br>");
334
+ select.insertNewlineAtCursor = function() {
335
+ insertAtCursor("<br>");
307
336
  };
308
337
 
309
- select.insertTabAtCursor = function(window) {
310
- insertAtCursor(window, fourSpaces);
338
+ select.insertTabAtCursor = function() {
339
+ insertAtCursor(fourSpaces);
311
340
  };
312
341
 
313
342
  // Get the BR node at the start of the line on which the cursor
314
343
  // currently is, and the offset into the line. Returns null as
315
344
  // node if cursor is on first line.
316
345
  select.cursorPos = function(container, start) {
317
- var selection = container.ownerDocument.selection;
346
+ var selection = document.selection;
318
347
  if (!selection) return null;
319
348
 
320
349
  var topNode = select.selectionTopNode(container, start);
@@ -340,7 +369,7 @@ var select = {};
340
369
 
341
370
  select.setCursorPos = function(container, from, to) {
342
371
  function rangeAt(pos) {
343
- var range = container.ownerDocument.body.createTextRange();
372
+ var range = document.body.createTextRange();
344
373
  if (!pos.node) {
345
374
  range.moveToElementText(container);
346
375
  range.collapse(true);
@@ -373,42 +402,38 @@ var select = {};
373
402
  }
374
403
  // W3C model
375
404
  else {
405
+ // Find the node right at the cursor, not one of its
406
+ // ancestors with a suitable offset. This goes down the DOM tree
407
+ // until a 'leaf' is reached (or is it *up* the DOM tree?).
408
+ function innerNode(node, offset) {
409
+ while (node.nodeType != 3 && !isBR(node)) {
410
+ var newNode = node.childNodes[offset] || node.nextSibling;
411
+ offset = 0;
412
+ while (!newNode && node.parentNode) {
413
+ node = node.parentNode;
414
+ newNode = node.nextSibling;
415
+ }
416
+ node = newNode;
417
+ if (!newNode) break;
418
+ }
419
+ return {node: node, offset: offset};
420
+ }
421
+
376
422
  // Store start and end nodes, and offsets within these, and refer
377
423
  // back to the selection object from those nodes, so that this
378
424
  // object can be updated when the nodes are replaced before the
379
425
  // selection is restored.
380
- select.markSelection = function (win) {
381
- var selection = win.getSelection();
426
+ select.markSelection = function () {
427
+ var selection = window.getSelection();
382
428
  if (!selection || selection.rangeCount == 0)
383
429
  return (currentSelection = null);
384
430
  var range = selection.getRangeAt(0);
385
431
 
386
432
  currentSelection = {
387
- start: {node: range.startContainer, offset: range.startOffset},
388
- end: {node: range.endContainer, offset: range.endOffset},
389
- window: win,
433
+ start: innerNode(range.startContainer, range.startOffset),
434
+ end: innerNode(range.endContainer, range.endOffset),
390
435
  changed: false
391
436
  };
392
-
393
- // We want the nodes right at the cursor, not one of their
394
- // ancestors with a suitable offset. This goes down the DOM tree
395
- // until a 'leaf' is reached (or is it *up* the DOM tree?).
396
- function normalize(point){
397
- while (point.node.nodeType != 3 && !isBR(point.node)) {
398
- var newNode = point.node.childNodes[point.offset] || point.node.nextSibling;
399
- point.offset = 0;
400
- while (!newNode && point.node.parentNode) {
401
- point.node = point.node.parentNode;
402
- newNode = point.node.nextSibling;
403
- }
404
- point.node = newNode;
405
- if (!newNode)
406
- break;
407
- }
408
- }
409
-
410
- normalize(currentSelection.start);
411
- normalize(currentSelection.end);
412
437
  };
413
438
 
414
439
  select.selectMarked = function () {
@@ -419,10 +444,15 @@ var select = {};
419
444
  // this occurs is when a selection is deleted or overwitten. we
420
445
  // check for that here.
421
446
  function focusIssue() {
422
- return cs.start.node == cs.end.node && cs.start.offset == 0 && cs.end.offset == 0;
447
+ if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
448
+ var selection = window.getSelection();
449
+ if (!selection || selection.rangeCount == 0) return true;
450
+ var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
451
+ return cs.start.node != point.node || cs.start.offset != point.offset;
452
+ }
423
453
  }
424
454
  if (!cs || !(cs.changed || (webkit && focusIssue()))) return;
425
- var win = cs.window, range = win.document.createRange();
455
+ var range = document.createRange();
426
456
 
427
457
  function setPoint(point, which) {
428
458
  if (point.node) {
@@ -434,22 +464,23 @@ var select = {};
434
464
  range["set" + which](point.node, point.offset);
435
465
  }
436
466
  else {
437
- range.setStartAfter(win.document.body.lastChild || win.document.body);
467
+ range.setStartAfter(document.body.lastChild || document.body);
438
468
  }
439
469
  }
440
470
 
441
471
  setPoint(cs.end, "End");
442
472
  setPoint(cs.start, "Start");
443
- selectRange(range, win);
473
+ selectRange(range);
444
474
  };
445
475
 
446
476
  // Helper for selecting a range object.
447
- function selectRange(range, window) {
477
+ function selectRange(range) {
448
478
  var selection = window.getSelection();
479
+ if (!selection) return;
449
480
  selection.removeAllRanges();
450
481
  selection.addRange(range);
451
482
  }
452
- function selectionRange(window) {
483
+ function selectionRange() {
453
484
  var selection = window.getSelection();
454
485
  if (!selection || selection.rangeCount == 0)
455
486
  return false;
@@ -460,7 +491,7 @@ var select = {};
460
491
  // Finding the top-level node at the cursor in the W3C is, as you
461
492
  // can see, quite an involved process.
462
493
  select.selectionTopNode = function(container, start) {
463
- var range = selectionRange(container.ownerDocument.defaultView);
494
+ var range = selectionRange();
464
495
  if (!range) return false;
465
496
 
466
497
  var node = start ? range.startContainer : range.endContainer;
@@ -505,8 +536,7 @@ var select = {};
505
536
  };
506
537
 
507
538
  select.focusAfterNode = function(node, container) {
508
- var win = container.ownerDocument.defaultView,
509
- range = win.document.createRange();
539
+ var range = document.createRange();
510
540
  range.setStartBefore(container.firstChild || container);
511
541
  // In Opera, setting the end of a range at the end of a line
512
542
  // (before a BR) will cause the cursor to appear on the next
@@ -519,37 +549,59 @@ var select = {};
519
549
  else
520
550
  range.setEndBefore(container.firstChild || container);
521
551
  range.collapse(false);
522
- selectRange(range, win);
552
+ selectRange(range);
523
553
  };
524
554
 
525
- select.somethingSelected = function(win) {
526
- var range = selectionRange(win);
555
+ select.somethingSelected = function() {
556
+ var range = selectionRange();
527
557
  return range && !range.collapsed;
528
558
  };
529
559
 
530
- function insertNodeAtCursor(window, node) {
531
- var range = selectionRange(window);
560
+ select.offsetInNode = function(node) {
561
+ var range = selectionRange();
562
+ if (!range) return 0;
563
+ range = range.cloneRange();
564
+ range.setStartBefore(node);
565
+ return range.toString().length;
566
+ };
567
+
568
+ function insertNodeAtCursor(node) {
569
+ var range = selectionRange();
532
570
  if (!range) return;
533
571
 
534
572
  range.deleteContents();
535
573
  range.insertNode(node);
536
- webkitLastLineHack(window.document.body);
537
- range = window.document.createRange();
574
+ webkitLastLineHack(document.body);
575
+
576
+ // work around weirdness where Opera will magically insert a new
577
+ // BR node when a BR node inside a span is moved around. makes
578
+ // sure the BR ends up outside of spans.
579
+ if (window.opera && isBR(node) && isSpan(node.parentNode)) {
580
+ var next = node.nextSibling, p = node.parentNode, outer = p.parentNode;
581
+ outer.insertBefore(node, p.nextSibling);
582
+ var textAfter = "";
583
+ for (; next && next.nodeType == 3; next = next.nextSibling) {
584
+ textAfter += next.nodeValue;
585
+ removeElement(next);
586
+ }
587
+ outer.insertBefore(makePartSpan(textAfter, document), node.nextSibling);
588
+ }
589
+ range = document.createRange();
538
590
  range.selectNode(node);
539
591
  range.collapse(false);
540
- selectRange(range, window);
592
+ selectRange(range);
541
593
  }
542
594
 
543
- select.insertNewlineAtCursor = function(window) {
544
- insertNodeAtCursor(window, window.document.createElement("BR"));
595
+ select.insertNewlineAtCursor = function() {
596
+ insertNodeAtCursor(document.createElement("BR"));
545
597
  };
546
598
 
547
- select.insertTabAtCursor = function(window) {
548
- insertNodeAtCursor(window, window.document.createTextNode(fourSpaces));
599
+ select.insertTabAtCursor = function() {
600
+ insertNodeAtCursor(document.createTextNode(fourSpaces));
549
601
  };
550
602
 
551
603
  select.cursorPos = function(container, start) {
552
- var range = selectionRange(window);
604
+ var range = selectionRange();
553
605
  if (!range) return;
554
606
 
555
607
  var topNode = select.selectionTopNode(container, start);
@@ -562,12 +614,13 @@ var select = {};
562
614
  range.setStartAfter(topNode);
563
615
  else
564
616
  range.setStartBefore(container);
565
- return {node: topNode, offset: range.toString().length};
617
+
618
+ var text = range.toString();
619
+ return {node: topNode, offset: text.length};
566
620
  };
567
621
 
568
622
  select.setCursorPos = function(container, from, to) {
569
- var win = container.ownerDocument.defaultView,
570
- range = win.document.createRange();
623
+ var range = document.createRange();
571
624
 
572
625
  function setPoint(node, offset, side) {
573
626
  if (offset == 0 && node && !node.nextSibling) {
@@ -613,7 +666,7 @@ var select = {};
613
666
 
614
667
  to = to || from;
615
668
  if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
616
- selectRange(range, win);
669
+ selectRange(range);
617
670
  };
618
671
  }
619
672
  })();
@@ -39,11 +39,13 @@ var stringStream = function(source){
39
39
  }
40
40
 
41
41
  return {
42
+ // peek: -> character
42
43
  // Return the next character in the stream.
43
44
  peek: function() {
44
45
  if (!ensureChars()) return null;
45
46
  return current.charAt(pos);
46
47
  },
48
+ // next: -> character
47
49
  // Get the next character, throw StopIteration if at end, check
48
50
  // for unused content.
49
51
  next: function() {
@@ -55,6 +57,7 @@ var stringStream = function(source){
55
57
  }
56
58
  return current.charAt(pos++);
57
59
  },
60
+ // get(): -> string
58
61
  // Return the characters iterated over since the last call to
59
62
  // .get().
60
63
  get: function() {
@@ -108,6 +111,8 @@ var stringStream = function(source){
108
111
  },
109
112
 
110
113
  // Utils built on top of the above
114
+ // more: -> boolean
115
+ // Produce true if the stream isn't empty.
111
116
  more: function() {
112
117
  return this.peek() !== null;
113
118
  },
@@ -65,7 +65,7 @@ var tokenizeJavaScript = (function() {
65
65
  };
66
66
  }
67
67
 
68
- // The token reader, inteded to be used by the tokenizer from
68
+ // The token reader, intended to be used by the tokenizer from
69
69
  // tokenize.js (through jsTokenState). Advances the source stream
70
70
  // over a token, and returns an object containing the type and style
71
71
  // of that token.
@@ -9,7 +9,7 @@
9
9
  * complexity and hackery.
10
10
  *
11
11
  * In short, the editor 'touches' BR elements as it parses them, and
12
- * the History stores these. When nothing is touched in commitDelay
12
+ * the UndoHistory stores these. When nothing is touched in commitDelay
13
13
  * milliseconds, the changes are committed: It goes over all touched
14
14
  * nodes, throws out the ones that did not change since last commit or
15
15
  * are no longer in the document, and assembles the rest into zero or
@@ -26,7 +26,7 @@
26
26
  // delay (of no input) after which it commits a set of changes, and,
27
27
  // unfortunately, the 'parent' window -- a window that is not in
28
28
  // designMode, and on which setTimeout works in every browser.
29
- function History(container, maxDepth, commitDelay, editor) {
29
+ function UndoHistory(container, maxDepth, commitDelay, editor) {
30
30
  this.container = container;
31
31
  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
32
32
  this.editor = editor; this.parent = editor.parent;
@@ -47,7 +47,7 @@ function History(container, maxDepth, commitDelay, editor) {
47
47
  this.history = []; this.redoHistory = []; this.touched = [];
48
48
  }
49
49
 
50
- History.prototype = {
50
+ UndoHistory.prototype = {
51
51
  // Schedule a commit (if no other touches come in for commitDelay
52
52
  // milliseconds).
53
53
  scheduleCommit: function() {
@@ -145,7 +145,7 @@ History.prototype = {
145
145
 
146
146
  // Commit unless there are pending dirty nodes.
147
147
  tryCommit: function() {
148
- if (!window.History) return; // Stop when frame has been unloaded
148
+ if (!window.parent || !window.UndoHistory) return; // Stop when frame has been unloaded
149
149
  if (this.editor.highlightDirty()) this.commit(true);
150
150
  else this.scheduleCommit();
151
151
  },
@@ -192,10 +192,10 @@ History.prototype = {
192
192
  },
193
193
 
194
194
  notifyEnvironment: function() {
195
+ if (this.onChange) this.onChange();
195
196
  // Used by the line-wrapping line-numbering code.
196
197
  if (window.frameElement && window.frameElement.CodeMirror.updateNumbers)
197
198
  window.frameElement.CodeMirror.updateNumbers();
198
- if (this.onChange) this.onChange();
199
199
  },
200
200
 
201
201
  // Link a chain into the DOM nodes (or the first/last links for null
@@ -380,7 +380,7 @@ History.prototype = {
380
380
  self.container.insertBefore(line.from, end);
381
381
 
382
382
  // Add the text.
383
- var node = makePartSpan(fixSpaces(line.text), this.container.ownerDocument);
383
+ var node = makePartSpan(fixSpaces(line.text));
384
384
  self.container.insertBefore(node, end);
385
385
  // See if the cursor was on this line. Put it back, adjusting
386
386
  // for changed line length, if it was.
@@ -391,7 +391,7 @@ History.prototype = {
391
391
  // Only adjust if the cursor is after the unchanged part of
392
392
  // the line.
393
393
  for (var match = 0; match < cursor.offset &&
394
- line.text.charAt(match) == prev.text.charAt(match); match++);
394
+ line.text.charAt(match) == prev.text.charAt(match); match++){}
395
395
  if (cursor.offset > match)
396
396
  cursordiff = line.text.length - prev.text.length;
397
397
  }