liquid_cms 0.2.1.1 → 0.2.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (130) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/README.rdoc +15 -1
  3. data/TODO.rdoc +0 -2
  4. data/app/controllers/cms/assets_controller.rb +20 -6
  5. data/app/controllers/cms/components_controller.rb +11 -3
  6. data/app/controllers/cms/pages_controller.rb +15 -4
  7. data/app/helpers/cms/common_helper.rb +31 -31
  8. data/app/liquid/cms_paperclip_extension.rb +75 -3
  9. data/app/models/cms/asset.rb +1 -1
  10. data/app/models/cms/component.rb +3 -3
  11. data/app/views/cms/assets/_form.html.erb +1 -1
  12. data/app/views/cms/assets/update.js.rjs +1 -0
  13. data/app/views/cms/components/update.js.rjs +1 -0
  14. data/app/views/cms/pages/_form.html.erb +1 -1
  15. data/app/views/cms/pages/update.js.rjs +1 -0
  16. data/app/views/layouts/cms.html.erb +4 -3
  17. data/generators/liquid_cms/liquid_cms_generator.rb +1 -0
  18. data/generators/liquid_cms/templates/config/locales/cms/en.yml +4 -0
  19. data/generators/liquid_cms/templates/migration_rev2.rb +13 -0
  20. data/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +16 -20
  21. data/generators/liquid_cms/templates/public/cms/codemirror/lib/codemirror.css +68 -0
  22. data/generators/liquid_cms/templates/public/cms/codemirror/lib/codemirror.js +2197 -0
  23. data/generators/liquid_cms/templates/public/cms/codemirror/lib/overlay.js +51 -0
  24. data/generators/liquid_cms/templates/public/cms/codemirror/lib/runmode.js +27 -0
  25. data/generators/liquid_cms/templates/public/cms/codemirror/mode/clike/clike.js +247 -0
  26. data/generators/liquid_cms/templates/public/cms/codemirror/mode/clike/index.html +102 -0
  27. data/generators/liquid_cms/templates/public/cms/codemirror/mode/clojure/clojure.js +207 -0
  28. data/generators/liquid_cms/templates/public/cms/codemirror/mode/clojure/index.html +85 -0
  29. data/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/LICENSE +22 -0
  30. data/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/coffeescript.js +325 -0
  31. data/generators/liquid_cms/templates/public/cms/codemirror/mode/coffeescript/index.html +722 -0
  32. data/generators/liquid_cms/templates/public/cms/codemirror/mode/css/css.js +124 -0
  33. data/generators/liquid_cms/templates/public/cms/codemirror/mode/css/index.html +56 -0
  34. data/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/diff.css +3 -0
  35. data/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/diff.js +13 -0
  36. data/generators/liquid_cms/templates/public/cms/codemirror/mode/diff/index.html +99 -0
  37. data/generators/liquid_cms/templates/public/cms/codemirror/mode/haskell/haskell.js +242 -0
  38. data/generators/liquid_cms/templates/public/cms/codemirror/mode/haskell/index.html +60 -0
  39. data/generators/liquid_cms/templates/public/cms/codemirror/mode/htmlmixed/htmlmixed.js +79 -0
  40. data/generators/liquid_cms/templates/public/cms/codemirror/mode/htmlmixed/index.html +52 -0
  41. data/generators/liquid_cms/templates/public/cms/codemirror/mode/javascript/index.html +78 -0
  42. data/generators/liquid_cms/templates/public/cms/codemirror/mode/javascript/javascript.js +352 -0
  43. data/generators/liquid_cms/templates/public/cms/codemirror/mode/jinja2/index.html +38 -0
  44. data/generators/liquid_cms/templates/public/cms/codemirror/mode/jinja2/jinja2.js +42 -0
  45. data/generators/liquid_cms/templates/public/cms/codemirror/mode/lua/index.html +72 -0
  46. data/generators/liquid_cms/templates/public/cms/codemirror/mode/lua/lua.js +140 -0
  47. data/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/index.html +340 -0
  48. data/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/markdown.css +10 -0
  49. data/generators/liquid_cms/templates/public/cms/codemirror/mode/markdown/markdown.js +230 -0
  50. data/generators/liquid_cms/templates/public/cms/codemirror/mode/ntriples/index.html +33 -0
  51. data/generators/liquid_cms/templates/public/cms/codemirror/mode/ntriples/ntriples.js +172 -0
  52. data/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/LICENSE +7 -0
  53. data/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/index.html +49 -0
  54. data/generators/liquid_cms/templates/public/cms/codemirror/mode/pascal/pascal.js +138 -0
  55. data/generators/liquid_cms/templates/public/cms/codemirror/mode/php/index.html +49 -0
  56. data/generators/liquid_cms/templates/public/cms/codemirror/mode/php/php.js +116 -0
  57. data/generators/liquid_cms/templates/public/cms/codemirror/mode/plsql/index.html +63 -0
  58. data/generators/liquid_cms/templates/public/cms/codemirror/mode/plsql/plsql.js +217 -0
  59. data/generators/liquid_cms/templates/public/cms/codemirror/mode/python/LICENSE.txt +21 -0
  60. data/generators/liquid_cms/templates/public/cms/codemirror/mode/python/index.html +123 -0
  61. data/generators/liquid_cms/templates/public/cms/codemirror/mode/python/python.js +320 -0
  62. data/generators/liquid_cms/templates/public/cms/codemirror/mode/r/LICENSE +24 -0
  63. data/generators/liquid_cms/templates/public/cms/codemirror/mode/r/index.html +74 -0
  64. data/generators/liquid_cms/templates/public/cms/codemirror/mode/r/r.js +141 -0
  65. data/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/index.html +526 -0
  66. data/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/rst.css +75 -0
  67. data/generators/liquid_cms/templates/public/cms/codemirror/mode/rst/rst.js +333 -0
  68. data/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/LICENSE +24 -0
  69. data/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/index.html +172 -0
  70. data/generators/liquid_cms/templates/public/cms/codemirror/mode/ruby/ruby.js +195 -0
  71. data/generators/liquid_cms/templates/public/cms/codemirror/mode/scheme/index.html +65 -0
  72. data/generators/liquid_cms/templates/public/cms/codemirror/mode/scheme/scheme.js +202 -0
  73. data/generators/liquid_cms/templates/public/cms/codemirror/mode/smalltalk/index.html +56 -0
  74. data/generators/liquid_cms/templates/public/cms/codemirror/mode/smalltalk/smalltalk.js +122 -0
  75. data/generators/liquid_cms/templates/public/cms/codemirror/mode/sparql/index.html +41 -0
  76. data/generators/liquid_cms/templates/public/cms/codemirror/mode/sparql/sparql.js +143 -0
  77. data/generators/liquid_cms/templates/public/cms/codemirror/mode/stex/index.html +96 -0
  78. data/generators/liquid_cms/templates/public/cms/codemirror/mode/stex/stex.js +167 -0
  79. data/generators/liquid_cms/templates/public/cms/codemirror/mode/velocity/index.html +103 -0
  80. data/generators/liquid_cms/templates/public/cms/codemirror/mode/velocity/velocity.js +146 -0
  81. data/generators/liquid_cms/templates/public/cms/codemirror/mode/xml/index.html +42 -0
  82. data/generators/liquid_cms/templates/public/cms/codemirror/mode/xml/xml.js +231 -0
  83. data/generators/liquid_cms/templates/public/cms/codemirror/mode/xmlpure/index.html +60 -0
  84. data/generators/liquid_cms/templates/public/cms/codemirror/mode/xmlpure/xmlpure.js +481 -0
  85. data/generators/liquid_cms/templates/public/cms/codemirror/mode/yaml/index.html +68 -0
  86. data/generators/liquid_cms/templates/public/cms/codemirror/mode/yaml/yaml.js +95 -0
  87. data/generators/liquid_cms/templates/public/cms/codemirror/theme/cobalt.css +17 -0
  88. data/generators/liquid_cms/templates/public/cms/codemirror/theme/default.css +19 -0
  89. data/generators/liquid_cms/templates/public/cms/codemirror/theme/eclipse.css +24 -0
  90. data/generators/liquid_cms/templates/public/cms/codemirror/theme/elegant.css +9 -0
  91. data/generators/liquid_cms/templates/public/cms/codemirror/theme/neat.css +8 -0
  92. data/generators/liquid_cms/templates/public/cms/codemirror/theme/night.css +20 -0
  93. data/generators/liquid_cms/templates/public/cms/javascripts/cms.js +1 -1
  94. data/generators/liquid_cms/templates/public/cms/javascripts/codemirror_custom.js +96 -0
  95. data/generators/liquid_cms/templates/public/cms/stylesheets/codemirror_changes.css +26 -0
  96. data/generators/liquid_cms/templates/public/cms/stylesheets/liquid.css +7 -0
  97. data/generators/liquid_cms/templates/public/cms/stylesheets/simple_form.css +0 -8
  98. data/lib/liquid_cms/version.rb +1 -1
  99. data/liquid_cms.gemspec +0 -1
  100. data/test/functional/assets_controller_test.rb +35 -20
  101. data/test/functional/components_controller_test.rb +15 -5
  102. data/test/functional/pages_controller_test.rb +19 -6
  103. data/test/rails_app/config/environment.rb +1 -1
  104. data/test/rails_app/config/locales/cms/en.yml +19 -4
  105. data/test/rails_app/db/migrate/20110511161859_create_liquid_cms_upgrade_rev2.rb +13 -0
  106. data/test/unit/asset_test.rb +1 -1
  107. metadata +100 -58
  108. data/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +0 -55
  109. data/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +0 -158
  110. data/generators/liquid_cms/templates/public/cms/codemirror/css/font.js +0 -15
  111. data/generators/liquid_cms/templates/public/cms/codemirror/css/jscolors.css +0 -59
  112. data/generators/liquid_cms/templates/public/cms/codemirror/css/sparqlcolors.css +0 -43
  113. data/generators/liquid_cms/templates/public/cms/codemirror/css/xmlcolors.css +0 -55
  114. data/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +0 -582
  115. data/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +0 -1671
  116. data/generators/liquid_cms/templates/public/cms/codemirror/js/highlight.js +0 -68
  117. data/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +0 -81
  118. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +0 -161
  119. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsedummy.js +0 -32
  120. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsehtmlmixed.js +0 -93
  121. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +0 -359
  122. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +0 -162
  123. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsexml.js +0 -291
  124. data/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +0 -699
  125. data/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +0 -159
  126. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenize.js +0 -57
  127. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +0 -174
  128. data/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +0 -413
  129. data/generators/liquid_cms/templates/public/cms/codemirror/js/unittests.js +0 -44
  130. data/generators/liquid_cms/templates/public/cms/codemirror/js/util.js +0 -133
@@ -1,699 +0,0 @@
1
- /* Functionality for finding, storing, and restoring selections
2
- *
3
- * This does not provide a generic API, just the minimal functionality
4
- * required by the CodeMirror system.
5
- */
6
-
7
- // Namespace object.
8
- var select = {};
9
-
10
- (function() {
11
- select.ie_selection = document.selection && document.selection.createRangeCollection;
12
-
13
- // Find the 'top-level' (defined as 'a direct child of the node
14
- // passed as the top argument') node that the given node is
15
- // contained in. Return null if the given node is not inside the top
16
- // node.
17
- function topLevelNodeAt(node, top) {
18
- while (node && node.parentNode != top)
19
- node = node.parentNode;
20
- return node;
21
- }
22
-
23
- // Find the top-level node that contains the node before this one.
24
- function topLevelNodeBefore(node, top) {
25
- while (!node.previousSibling && node.parentNode != top)
26
- node = node.parentNode;
27
- return topLevelNodeAt(node.previousSibling, top);
28
- }
29
-
30
- var fourSpaces = "\u00a0\u00a0\u00a0\u00a0";
31
-
32
- select.scrollToNode = function(node, cursor) {
33
- if (!node) return;
34
- var element = node, body = document.body,
35
- html = document.documentElement,
36
- atEnd = !element.nextSibling || !element.nextSibling.nextSibling
37
- || !element.nextSibling.nextSibling.nextSibling;
38
- // In Opera (and recent Webkit versions), BR elements *always*
39
- // have a offsetTop property of zero.
40
- var compensateHack = 0;
41
- while (element && !element.offsetTop) {
42
- compensateHack++;
43
- element = element.previousSibling;
44
- }
45
- // atEnd is another kludge for these browsers -- if the cursor is
46
- // at the end of the document, and the node doesn't have an
47
- // offset, just scroll to the end.
48
- if (compensateHack == 0) atEnd = false;
49
-
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;
58
- while (pos && pos.offsetParent) {
59
- y += pos.offsetTop;
60
- // Don't count X offset for <br> nodes
61
- if (!isBR(pos))
62
- x += pos.offsetLeft;
63
- pos = pos.offsetParent;
64
- }
65
-
66
- var scroll_x = body.scrollLeft || html.scrollLeft || 0,
67
- scroll_y = body.scrollTop || html.scrollTop || 0,
68
- scroll = false, screen_width = window.innerWidth || html.clientWidth || 0;
69
-
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
- }
80
- }
81
- var screen_y = y - scroll_y;
82
- if (screen_y < 0 || atEnd || screen_y > (window.innerHeight || html.clientHeight || 0) - 50) {
83
- scroll_y = atEnd ? 1e6 : y;
84
- scroll = true;
85
- }
86
- if (scroll) window.scrollTo(scroll_x, scroll_y);
87
- };
88
-
89
- select.scrollToCursor = function(container) {
90
- select.scrollToNode(select.selectionTopNode(container, true) || container.firstChild, true);
91
- };
92
-
93
- // Used to prevent restoring a selection when we do not need to.
94
- var currentSelection = null;
95
-
96
- select.snapshotChanged = function() {
97
- if (currentSelection) currentSelection.changed = true;
98
- };
99
-
100
- // Find the 'leaf' node (BR or text) after the given one.
101
- function baseNodeAfter(node) {
102
- var next = node.nextSibling;
103
- if (next) {
104
- while (next.firstChild) next = next.firstChild;
105
- if (next.nodeType == 3 || isBR(next)) return next;
106
- else return baseNodeAfter(next);
107
- }
108
- else {
109
- var parent = node.parentNode;
110
- while (parent && !parent.nextSibling) parent = parent.parentNode;
111
- return parent && baseNodeAfter(parent);
112
- }
113
- }
114
-
115
- // This is called by the code in editor.js whenever it is replacing
116
- // a text node. The function sees whether the given oldNode is part
117
- // of the current selection, and updates this selection if it is.
118
- // Because nodes are often only partially replaced, the length of
119
- // the part that gets replaced has to be taken into account -- the
120
- // selection might stay in the oldNode if the newNode is smaller
121
- // than the selection's offset. The offset argument is needed in
122
- // case the selection does move to the new object, and the given
123
- // length is not the whole length of the new node (part of it might
124
- // have been used to replace another node).
125
- select.snapshotReplaceNode = function(from, to, length, offset) {
126
- if (!currentSelection) return;
127
-
128
- function replace(point) {
129
- if (from == point.node) {
130
- currentSelection.changed = true;
131
- if (length && point.offset > length) {
132
- point.offset -= length;
133
- }
134
- else {
135
- point.node = to;
136
- point.offset += (offset || 0);
137
- }
138
- }
139
- else if (select.ie_selection && point.offset == 0 && point.node == baseNodeAfter(from)) {
140
- currentSelection.changed = true;
141
- }
142
- }
143
- replace(currentSelection.start);
144
- replace(currentSelection.end);
145
- };
146
-
147
- select.snapshotMove = function(from, to, distance, relative, ifAtStart) {
148
- if (!currentSelection) return;
149
-
150
- function move(point) {
151
- if (from == point.node && (!ifAtStart || point.offset == 0)) {
152
- currentSelection.changed = true;
153
- point.node = to;
154
- if (relative) point.offset = Math.max(0, point.offset + distance);
155
- else point.offset = distance;
156
- }
157
- }
158
- move(currentSelection.start);
159
- move(currentSelection.end);
160
- };
161
-
162
- // Most functions are defined in two ways, one for the IE selection
163
- // model, one for the W3C one.
164
- if (select.ie_selection) {
165
- function selRange() {
166
- var sel = document.selection;
167
- if (!sel) return null;
168
- if (sel.createRange) return sel.createRange();
169
- else return sel.createTextRange();
170
- }
171
-
172
- function selectionNode(start) {
173
- var range = selRange();
174
- range.collapse(start);
175
-
176
- function nodeAfter(node) {
177
- var found = null;
178
- while (!found && node) {
179
- found = node.nextSibling;
180
- node = node.parentNode;
181
- }
182
- return nodeAtStartOf(found);
183
- }
184
-
185
- function nodeAtStartOf(node) {
186
- while (node && node.firstChild) node = node.firstChild;
187
- return {node: node, offset: 0};
188
- }
189
-
190
- var containing = range.parentElement();
191
- if (!isAncestor(document.body, containing)) return null;
192
- if (!containing.firstChild) return nodeAtStartOf(containing);
193
-
194
- var working = range.duplicate();
195
- working.moveToElementText(containing);
196
- working.collapse(true);
197
- for (var cur = containing.firstChild; cur; cur = cur.nextSibling) {
198
- if (cur.nodeType == 3) {
199
- var size = cur.nodeValue.length;
200
- working.move("character", size);
201
- }
202
- else {
203
- working.moveToElementText(cur);
204
- working.collapse(false);
205
- }
206
-
207
- var dir = range.compareEndPoints("StartToStart", working);
208
- if (dir == 0) return nodeAfter(cur);
209
- if (dir == 1) continue;
210
- if (cur.nodeType != 3) return nodeAtStartOf(cur);
211
-
212
- working.setEndPoint("StartToEnd", range);
213
- return {node: cur, offset: size - working.text.length};
214
- }
215
- return nodeAfter(containing);
216
- }
217
-
218
- select.markSelection = function() {
219
- currentSelection = null;
220
- var sel = document.selection;
221
- if (!sel) return;
222
- var start = selectionNode(true),
223
- end = selectionNode(false);
224
- if (!start || !end) return;
225
- currentSelection = {start: start, end: end, changed: false};
226
- };
227
-
228
- select.selectMarked = function() {
229
- if (!currentSelection || !currentSelection.changed) return;
230
-
231
- function makeRange(point) {
232
- var range = document.body.createTextRange(),
233
- node = point.node;
234
- if (!node) {
235
- range.moveToElementText(document.body);
236
- range.collapse(false);
237
- }
238
- else if (node.nodeType == 3) {
239
- range.moveToElementText(node.parentNode);
240
- var offset = point.offset;
241
- while (node.previousSibling) {
242
- node = node.previousSibling;
243
- offset += (node.innerText || "").length;
244
- }
245
- range.move("character", offset);
246
- }
247
- else {
248
- range.moveToElementText(node);
249
- range.collapse(true);
250
- }
251
- return range;
252
- }
253
-
254
- var start = makeRange(currentSelection.start), end = makeRange(currentSelection.end);
255
- start.setEndPoint("StartToEnd", end);
256
- start.select();
257
- };
258
-
259
- select.offsetInNode = function(node) {
260
- var range = selRange();
261
- if (!range) return 0;
262
- var range2 = range.duplicate();
263
- try {range2.moveToElementText(node);} catch(e){return 0;}
264
- range.setEndPoint("StartToStart", range2);
265
- return range.text.length;
266
- };
267
-
268
- // Get the top-level node that one end of the cursor is inside or
269
- // after. Note that this returns false for 'no cursor', and null
270
- // for 'start of document'.
271
- select.selectionTopNode = function(container, start) {
272
- var range = selRange();
273
- if (!range) return false;
274
- var range2 = range.duplicate();
275
- range.collapse(start);
276
- var around = range.parentElement();
277
- if (around && isAncestor(container, around)) {
278
- // Only use this node if the selection is not at its start.
279
- range2.moveToElementText(around);
280
- if (range.compareEndPoints("StartToStart", range2) == 1)
281
- return topLevelNodeAt(around, container);
282
- }
283
-
284
- // Move the start of a range to the start of a node,
285
- // compensating for the fact that you can't call
286
- // moveToElementText with text nodes.
287
- function moveToNodeStart(range, node) {
288
- if (node.nodeType == 3) {
289
- var count = 0, cur = node.previousSibling;
290
- while (cur && cur.nodeType == 3) {
291
- count += cur.nodeValue.length;
292
- cur = cur.previousSibling;
293
- }
294
- if (cur) {
295
- try{range.moveToElementText(cur);}
296
- catch(e){return false;}
297
- range.collapse(false);
298
- }
299
- else range.moveToElementText(node.parentNode);
300
- if (count) range.move("character", count);
301
- }
302
- else {
303
- try{range.moveToElementText(node);}
304
- catch(e){return false;}
305
- }
306
- return true;
307
- }
308
-
309
- // Do a binary search through the container object, comparing
310
- // the start of each node to the selection
311
- var start = 0, end = container.childNodes.length - 1;
312
- while (start < end) {
313
- var middle = Math.ceil((end + start) / 2), node = container.childNodes[middle];
314
- if (!node) return false; // Don't ask. IE6 manages this sometimes.
315
- if (!moveToNodeStart(range2, node)) return false;
316
- if (range.compareEndPoints("StartToStart", range2) == 1)
317
- start = middle;
318
- else
319
- end = middle - 1;
320
- }
321
-
322
- if (start == 0) {
323
- var test1 = selRange(), test2 = test1.duplicate();
324
- try {
325
- test2.moveToElementText(container);
326
- } catch(exception) {
327
- return null;
328
- }
329
- if (test1.compareEndPoints("StartToStart", test2) == 0)
330
- return null;
331
- }
332
- return container.childNodes[start] || null;
333
- };
334
-
335
- // Place the cursor after this.start. This is only useful when
336
- // manually moving the cursor instead of restoring it to its old
337
- // position.
338
- select.focusAfterNode = function(node, container) {
339
- var range = document.body.createTextRange();
340
- range.moveToElementText(node || container);
341
- range.collapse(!node);
342
- range.select();
343
- };
344
-
345
- select.somethingSelected = function() {
346
- var range = selRange();
347
- return range && (range.text != "");
348
- };
349
-
350
- function insertAtCursor(html) {
351
- var range = selRange();
352
- if (range) {
353
- range.pasteHTML(html);
354
- range.collapse(false);
355
- range.select();
356
- }
357
- }
358
-
359
- // Used to normalize the effect of the enter key, since browsers
360
- // do widely different things when pressing enter in designMode.
361
- select.insertNewlineAtCursor = function() {
362
- insertAtCursor("<br>");
363
- };
364
-
365
- select.insertTabAtCursor = function() {
366
- insertAtCursor(fourSpaces);
367
- };
368
-
369
- // Get the BR node at the start of the line on which the cursor
370
- // currently is, and the offset into the line. Returns null as
371
- // node if cursor is on first line.
372
- select.cursorPos = function(container, start) {
373
- var range = selRange();
374
- if (!range) return null;
375
-
376
- var topNode = select.selectionTopNode(container, start);
377
- while (topNode && !isBR(topNode))
378
- topNode = topNode.previousSibling;
379
-
380
- var range2 = range.duplicate();
381
- range.collapse(start);
382
- if (topNode) {
383
- range2.moveToElementText(topNode);
384
- range2.collapse(false);
385
- }
386
- else {
387
- // When nothing is selected, we can get all kinds of funky errors here.
388
- try { range2.moveToElementText(container); }
389
- catch (e) { return null; }
390
- range2.collapse(true);
391
- }
392
- range.setEndPoint("StartToStart", range2);
393
-
394
- return {node: topNode, offset: range.text.length};
395
- };
396
-
397
- select.setCursorPos = function(container, from, to) {
398
- function rangeAt(pos) {
399
- var range = document.body.createTextRange();
400
- if (!pos.node) {
401
- range.moveToElementText(container);
402
- range.collapse(true);
403
- }
404
- else {
405
- range.moveToElementText(pos.node);
406
- range.collapse(false);
407
- }
408
- range.move("character", pos.offset);
409
- return range;
410
- }
411
-
412
- var range = rangeAt(from);
413
- if (to && to != from)
414
- range.setEndPoint("EndToEnd", rangeAt(to));
415
- range.select();
416
- }
417
-
418
- // Some hacks for storing and re-storing the selection when the editor loses and regains focus.
419
- select.getBookmark = function (container) {
420
- var from = select.cursorPos(container, true), to = select.cursorPos(container, false);
421
- if (from && to) return {from: from, to: to};
422
- };
423
-
424
- // Restore a stored selection.
425
- select.setBookmark = function(container, mark) {
426
- if (!mark) return;
427
- select.setCursorPos(container, mark.from, mark.to);
428
- };
429
- }
430
- // W3C model
431
- else {
432
- // Find the node right at the cursor, not one of its
433
- // ancestors with a suitable offset. This goes down the DOM tree
434
- // until a 'leaf' is reached (or is it *up* the DOM tree?).
435
- function innerNode(node, offset) {
436
- while (node.nodeType != 3 && !isBR(node)) {
437
- var newNode = node.childNodes[offset] || node.nextSibling;
438
- offset = 0;
439
- while (!newNode && node.parentNode) {
440
- node = node.parentNode;
441
- newNode = node.nextSibling;
442
- }
443
- node = newNode;
444
- if (!newNode) break;
445
- }
446
- return {node: node, offset: offset};
447
- }
448
-
449
- // Store start and end nodes, and offsets within these, and refer
450
- // back to the selection object from those nodes, so that this
451
- // object can be updated when the nodes are replaced before the
452
- // selection is restored.
453
- select.markSelection = function () {
454
- var selection = window.getSelection();
455
- if (!selection || selection.rangeCount == 0)
456
- return (currentSelection = null);
457
- var range = selection.getRangeAt(0);
458
-
459
- currentSelection = {
460
- start: innerNode(range.startContainer, range.startOffset),
461
- end: innerNode(range.endContainer, range.endOffset),
462
- changed: false
463
- };
464
- };
465
-
466
- select.selectMarked = function () {
467
- var cs = currentSelection;
468
- // on webkit-based browsers, it is apparently possible that the
469
- // selection gets reset even when a node that is not one of the
470
- // endpoints get messed with. the most common situation where
471
- // this occurs is when a selection is deleted or overwitten. we
472
- // check for that here.
473
- function focusIssue() {
474
- if (cs.start.node == cs.end.node && cs.start.offset == cs.end.offset) {
475
- var selection = window.getSelection();
476
- if (!selection || selection.rangeCount == 0) return true;
477
- var range = selection.getRangeAt(0), point = innerNode(range.startContainer, range.startOffset);
478
- return cs.start.node != point.node || cs.start.offset != point.offset;
479
- }
480
- }
481
- if (!cs || !(cs.changed || (webkit && focusIssue()))) return;
482
- var range = document.createRange();
483
-
484
- function setPoint(point, which) {
485
- if (point.node) {
486
- // Some magic to generalize the setting of the start and end
487
- // of a range.
488
- if (point.offset == 0)
489
- range["set" + which + "Before"](point.node);
490
- else
491
- range["set" + which](point.node, point.offset);
492
- }
493
- else {
494
- range.setStartAfter(document.body.lastChild || document.body);
495
- }
496
- }
497
-
498
- setPoint(cs.end, "End");
499
- setPoint(cs.start, "Start");
500
- selectRange(range);
501
- };
502
-
503
- // Helper for selecting a range object.
504
- function selectRange(range) {
505
- var selection = window.getSelection();
506
- if (!selection) return;
507
- selection.removeAllRanges();
508
- selection.addRange(range);
509
- }
510
- function selectionRange() {
511
- var selection = window.getSelection();
512
- if (!selection || selection.rangeCount == 0)
513
- return false;
514
- else
515
- return selection.getRangeAt(0);
516
- }
517
-
518
- // Finding the top-level node at the cursor in the W3C is, as you
519
- // can see, quite an involved process.
520
- select.selectionTopNode = function(container, start) {
521
- var range = selectionRange();
522
- if (!range) return false;
523
-
524
- var node = start ? range.startContainer : range.endContainer;
525
- var offset = start ? range.startOffset : range.endOffset;
526
- // Work around (yet another) bug in Opera's selection model.
527
- if (window.opera && !start && range.endContainer == container && range.endOffset == range.startOffset + 1 &&
528
- container.childNodes[range.startOffset] && isBR(container.childNodes[range.startOffset]))
529
- offset--;
530
-
531
- // For text nodes, we look at the node itself if the cursor is
532
- // inside, or at the node before it if the cursor is at the
533
- // start.
534
- if (node.nodeType == 3){
535
- if (offset > 0)
536
- return topLevelNodeAt(node, container);
537
- else
538
- return topLevelNodeBefore(node, container);
539
- }
540
- // Occasionally, browsers will return the HTML node as
541
- // selection. If the offset is 0, we take the start of the frame
542
- // ('after null'), otherwise, we take the last node.
543
- else if (node.nodeName.toUpperCase() == "HTML") {
544
- return (offset == 1 ? null : container.lastChild);
545
- }
546
- // If the given node is our 'container', we just look up the
547
- // correct node by using the offset.
548
- else if (node == container) {
549
- return (offset == 0) ? null : node.childNodes[offset - 1];
550
- }
551
- // In any other case, we have a regular node. If the cursor is
552
- // at the end of the node, we use the node itself, if it is at
553
- // the start, we use the node before it, and in any other
554
- // case, we look up the child before the cursor and use that.
555
- else {
556
- if (offset == node.childNodes.length)
557
- return topLevelNodeAt(node, container);
558
- else if (offset == 0)
559
- return topLevelNodeBefore(node, container);
560
- else
561
- return topLevelNodeAt(node.childNodes[offset - 1], container);
562
- }
563
- };
564
-
565
- select.focusAfterNode = function(node, container) {
566
- var range = document.createRange();
567
- range.setStartBefore(container.firstChild || container);
568
- // In Opera, setting the end of a range at the end of a line
569
- // (before a BR) will cause the cursor to appear on the next
570
- // line, so we set the end inside of the start node when
571
- // possible.
572
- if (node && !node.firstChild)
573
- range.setEndAfter(node);
574
- else if (node)
575
- range.setEnd(node, node.childNodes.length);
576
- else
577
- range.setEndBefore(container.firstChild || container);
578
- range.collapse(false);
579
- selectRange(range);
580
- };
581
-
582
- select.somethingSelected = function() {
583
- var range = selectionRange();
584
- return range && !range.collapsed;
585
- };
586
-
587
- select.offsetInNode = function(node) {
588
- var range = selectionRange();
589
- if (!range) return 0;
590
- range = range.cloneRange();
591
- range.setStartBefore(node);
592
- return range.toString().length;
593
- };
594
-
595
- select.insertNodeAtCursor = function(node) {
596
- var range = selectionRange();
597
- if (!range) return;
598
-
599
- range.deleteContents();
600
- range.insertNode(node);
601
- webkitLastLineHack(document.body);
602
-
603
- // work around weirdness where Opera will magically insert a new
604
- // BR node when a BR node inside a span is moved around. makes
605
- // sure the BR ends up outside of spans.
606
- if (window.opera && isBR(node) && isSpan(node.parentNode)) {
607
- var next = node.nextSibling, p = node.parentNode, outer = p.parentNode;
608
- outer.insertBefore(node, p.nextSibling);
609
- var textAfter = "";
610
- for (; next && next.nodeType == 3; next = next.nextSibling) {
611
- textAfter += next.nodeValue;
612
- removeElement(next);
613
- }
614
- outer.insertBefore(makePartSpan(textAfter, document), node.nextSibling);
615
- }
616
- range = document.createRange();
617
- range.selectNode(node);
618
- range.collapse(false);
619
- selectRange(range);
620
- }
621
-
622
- select.insertNewlineAtCursor = function() {
623
- select.insertNodeAtCursor(document.createElement("BR"));
624
- };
625
-
626
- select.insertTabAtCursor = function() {
627
- select.insertNodeAtCursor(document.createTextNode(fourSpaces));
628
- };
629
-
630
- select.cursorPos = function(container, start) {
631
- var range = selectionRange();
632
- if (!range) return;
633
-
634
- var topNode = select.selectionTopNode(container, start);
635
- while (topNode && !isBR(topNode))
636
- topNode = topNode.previousSibling;
637
-
638
- range = range.cloneRange();
639
- range.collapse(start);
640
- if (topNode)
641
- range.setStartAfter(topNode);
642
- else
643
- range.setStartBefore(container);
644
-
645
- var text = range.toString();
646
- return {node: topNode, offset: text.length};
647
- };
648
-
649
- select.setCursorPos = function(container, from, to) {
650
- var range = document.createRange();
651
-
652
- function setPoint(node, offset, side) {
653
- if (offset == 0 && node && !node.nextSibling) {
654
- range["set" + side + "After"](node);
655
- return true;
656
- }
657
-
658
- if (!node)
659
- node = container.firstChild;
660
- else
661
- node = node.nextSibling;
662
-
663
- if (!node) return;
664
-
665
- if (offset == 0) {
666
- range["set" + side + "Before"](node);
667
- return true;
668
- }
669
-
670
- var backlog = []
671
- function decompose(node) {
672
- if (node.nodeType == 3)
673
- backlog.push(node);
674
- else
675
- forEach(node.childNodes, decompose);
676
- }
677
- while (true) {
678
- while (node && !backlog.length) {
679
- decompose(node);
680
- node = node.nextSibling;
681
- }
682
- var cur = backlog.shift();
683
- if (!cur) return false;
684
-
685
- var length = cur.nodeValue.length;
686
- if (length >= offset) {
687
- range["set" + side](cur, offset);
688
- return true;
689
- }
690
- offset -= length;
691
- }
692
- }
693
-
694
- to = to || from;
695
- if (setPoint(to.node, to.offset, "End") && setPoint(from.node, from.offset, "Start"))
696
- selectRange(range);
697
- };
698
- }
699
- })();