liquid_cms 0.2.0.11 → 0.2.0.12

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 (60) hide show
  1. data/CHANGELOG.rdoc +7 -0
  2. data/TODO.rdoc +1 -1
  3. data/app/controllers/cms/main_controller.rb +3 -2
  4. data/app/helpers/cms/common_helper.rb +9 -2
  5. data/app/helpers/cms/components_helper.rb +10 -4
  6. data/app/models/cms/component.rb +4 -0
  7. data/app/views/cms/assets/_list.html.erb +4 -4
  8. data/app/views/cms/components/_list.html.erb +5 -1
  9. data/app/views/cms/pages/_list.html.erb +4 -4
  10. data/app/views/cms/shared/_sidebar.html.erb +32 -8
  11. data/app/views/layouts/cms.html.erb +5 -2
  12. data/generators/liquid_cms/templates/config/locales/cms/en.yml +3 -2
  13. data/generators/liquid_cms/templates/public/cms/codemirror/LICENSE +0 -0
  14. data/generators/liquid_cms/templates/public/cms/codemirror/css/csscolors.css +0 -0
  15. data/generators/liquid_cms/templates/public/cms/codemirror/css/docs.css +17 -3
  16. data/generators/liquid_cms/templates/public/cms/codemirror/css/font.js +15 -0
  17. data/generators/liquid_cms/templates/public/cms/codemirror/css/jscolors.css +0 -0
  18. data/generators/liquid_cms/templates/public/cms/codemirror/css/sparqlcolors.css +0 -0
  19. data/generators/liquid_cms/templates/public/cms/codemirror/css/xmlcolors.css +0 -0
  20. data/generators/liquid_cms/templates/public/cms/codemirror/js/codemirror.js +59 -26
  21. data/generators/liquid_cms/templates/public/cms/codemirror/js/editor.js +149 -71
  22. data/generators/liquid_cms/templates/public/cms/codemirror/js/highlight.js +2 -2
  23. data/generators/liquid_cms/templates/public/cms/codemirror/js/mirrorframe.js +2 -2
  24. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsecss.js +5 -3
  25. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsedummy.js +0 -0
  26. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsehtmlmixed.js +28 -9
  27. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsejavascript.js +0 -0
  28. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsesparql.js +0 -0
  29. data/generators/liquid_cms/templates/public/cms/codemirror/js/parsexml.js +6 -1
  30. data/generators/liquid_cms/templates/public/cms/codemirror/js/select.js +48 -21
  31. data/generators/liquid_cms/templates/public/cms/codemirror/js/stringstream.js +15 -1
  32. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenize.js +0 -0
  33. data/generators/liquid_cms/templates/public/cms/codemirror/js/tokenizejavascript.js +1 -1
  34. data/generators/liquid_cms/templates/public/cms/codemirror/js/undo.js +17 -14
  35. data/generators/liquid_cms/templates/public/cms/codemirror/js/unittests.js +44 -0
  36. data/generators/liquid_cms/templates/public/cms/codemirror/js/util.js +6 -3
  37. data/generators/liquid_cms/templates/public/cms/javascripts/cms.js +15 -1
  38. data/generators/liquid_cms/templates/public/cms/javascripts/livepipe.js +181 -0
  39. data/generators/liquid_cms/templates/public/cms/javascripts/tabs.js +149 -0
  40. data/generators/liquid_cms/templates/public/cms/stylesheets/ie9.css +4 -0
  41. data/generators/liquid_cms/templates/public/cms/stylesheets/sidebar.css +132 -0
  42. data/generators/liquid_cms/templates/public/cms/stylesheets/styles.css +1 -74
  43. data/generators/liquid_cms/templates/public/cms/stylesheets/themes/dark.css +2 -1
  44. data/lib/liquid_cms/context.rb +4 -0
  45. data/lib/liquid_cms/version.rb +1 -1
  46. data/liquid_cms.gemspec +1 -1
  47. data/test/functional/assets_controller_test.rb +3 -3
  48. data/test/rails_app/config/locales/cms/en.yml +8 -0
  49. metadata +11 -16
  50. data/generators/liquid_cms/templates/public/cms/codemirror/bigtest.html +0 -1296
  51. data/generators/liquid_cms/templates/public/cms/codemirror/css/people.jpg +0 -0
  52. data/generators/liquid_cms/templates/public/cms/codemirror/csstest.html +0 -60
  53. data/generators/liquid_cms/templates/public/cms/codemirror/highlight.html +0 -82
  54. data/generators/liquid_cms/templates/public/cms/codemirror/htmltest.html +0 -52
  55. data/generators/liquid_cms/templates/public/cms/codemirror/index.html +0 -245
  56. data/generators/liquid_cms/templates/public/cms/codemirror/jstest.html +0 -56
  57. data/generators/liquid_cms/templates/public/cms/codemirror/manual.html +0 -759
  58. data/generators/liquid_cms/templates/public/cms/codemirror/mixedtest.html +0 -52
  59. data/generators/liquid_cms/templates/public/cms/codemirror/sparqltest.html +0 -41
  60. data/generators/liquid_cms/templates/public/cms/codemirror/story.html +0 -671
@@ -41,7 +41,7 @@ var indentUnit = 2;
41
41
  callback = function(line) {
42
42
  for (var i = 0; i < line.length; i++)
43
43
  node.appendChild(line[i]);
44
- node.appendChild(document.createElement("BR"));
44
+ node.appendChild(document.createElement("br"));
45
45
  };
46
46
  }
47
47
 
@@ -53,7 +53,7 @@ var indentUnit = 2;
53
53
  line = [];
54
54
  }
55
55
  else {
56
- var span = document.createElement("SPAN");
56
+ var span = document.createElement("span");
57
57
  span.className = token.style;
58
58
  span.appendChild(document.createTextNode(token.value));
59
59
  line.push(span);
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  function MirrorFrame(place, options) {
7
- this.home = document.createElement("DIV");
7
+ this.home = document.createElement("div");
8
8
  if (place.appendChild)
9
9
  place.appendChild(this.home);
10
10
  else
@@ -12,7 +12,7 @@ function MirrorFrame(place, options) {
12
12
 
13
13
  var self = this;
14
14
  function makeButton(name, action) {
15
- var button = document.createElement("INPUT");
15
+ var button = document.createElement("input");
16
16
  button.type = "button";
17
17
  button.value = name;
18
18
  self.home.appendChild(button);
@@ -128,7 +128,9 @@ var CSSParser = Editor.Parser = (function() {
128
128
  if (content == "\n")
129
129
  token.indentation = indentCSS(inBraces, inRule, basecolumn);
130
130
 
131
- if (content == "{")
131
+ if (content == "{" && inDecl == "@media")
132
+ inDecl = false;
133
+ else if (content == "{")
132
134
  inBraces = true;
133
135
  else if (content == "}")
134
136
  inBraces = inRule = inDecl = false;
@@ -136,8 +138,8 @@ var CSSParser = Editor.Parser = (function() {
136
138
  inRule = inDecl = false;
137
139
  else if (inBraces && style != "css-comment" && style != "whitespace")
138
140
  inRule = true;
139
- else if (!inBraces && style == "css-at" && content != "@media")
140
- inDecl = true;
141
+ else if (!inBraces && style == "css-at")
142
+ inDecl = content;
141
143
 
142
144
  return token;
143
145
  },
@@ -1,9 +1,22 @@
1
1
  var HTMLMixedParser = Editor.Parser = (function() {
2
- if (!(CSSParser && JSParser && XMLParser))
3
- throw new Error("CSS, JS, and XML parsers must be loaded for HTML mixed mode to work.");
4
- XMLParser.configure({useHTMLKludges: true});
2
+
3
+ // tags that trigger seperate parsers
4
+ var triggers = {
5
+ "script": "JSParser",
6
+ "style": "CSSParser"
7
+ };
8
+
9
+ function checkDependencies() {
10
+ var parsers = ['XMLParser'];
11
+ for (var p in triggers) parsers.push(triggers[p]);
12
+ for (var i in parsers) {
13
+ if (!window[parsers[i]]) throw new Error(parsers[i] + " parser must be loaded for HTML mixed mode to work.");
14
+ }
15
+ XMLParser.configure({useHTMLKludges: true});
16
+ }
5
17
 
6
18
  function parseMixed(stream) {
19
+ checkDependencies();
7
20
  var htmlParser = XMLParser.make(stream), localParser = null, inTag = false;
8
21
  var iter = {next: top, copy: copy};
9
22
 
@@ -14,10 +27,10 @@ var HTMLMixedParser = Editor.Parser = (function() {
14
27
  else if (token.style == "xml-tagname" && inTag === true)
15
28
  inTag = token.content.toLowerCase();
16
29
  else if (token.content == ">") {
17
- if (inTag == "script")
18
- iter.next = local(JSParser, "</script");
19
- else if (inTag == "style")
20
- iter.next = local(CSSParser, "</style");
30
+ if (triggers[inTag]) {
31
+ var parser = window[triggers[inTag]];
32
+ iter.next = local(parser, "</" + inTag);
33
+ }
21
34
  inTag = false;
22
35
  }
23
36
  return token;
@@ -47,7 +60,7 @@ var HTMLMixedParser = Editor.Parser = (function() {
47
60
  return baseIndent;
48
61
  else
49
62
  return oldIndent(chars);
50
- }
63
+ };
51
64
  }
52
65
 
53
66
  return token;
@@ -69,6 +82,12 @@ var HTMLMixedParser = Editor.Parser = (function() {
69
82
  return iter;
70
83
  }
71
84
 
72
- return {make: parseMixed, electricChars: "{}/:"};
85
+ return {
86
+ make: parseMixed,
87
+ electricChars: "{}/:",
88
+ configure: function(obj) {
89
+ if (obj.triggers) triggers = obj.triggers;
90
+ }
91
+ };
73
92
 
74
93
  })();
@@ -38,6 +38,11 @@ var XMLParser = Editor.Parser = (function() {
38
38
  setState(inBlock("xml-comment", "-->"));
39
39
  return null;
40
40
  }
41
+ else if (source.lookAhead("DOCTYPE", true)) {
42
+ source.nextWhileMatches(/[\w\._\-]/);
43
+ setState(inBlock("xml-doctype", ">"));
44
+ return "xml-doctype";
45
+ }
41
46
  else {
42
47
  return "xml-text";
43
48
  }
@@ -182,7 +187,7 @@ var XMLParser = Editor.Parser = (function() {
182
187
  function base() {
183
188
  return pass(element, base);
184
189
  }
185
- var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true};
190
+ var harmlessTokens = {"xml-text": true, "xml-entity": true, "xml-comment": true, "xml-processing": true, "xml-doctype": true};
186
191
  function element(style, content) {
187
192
  if (content == "<") cont(tagname, attributes, endtag(tokenNr == 1));
188
193
  else if (content == "</") cont(closetagname, expect(">"));
@@ -97,6 +97,21 @@ var select = {};
97
97
  if (currentSelection) currentSelection.changed = true;
98
98
  };
99
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
+
100
115
  // This is called by the code in editor.js whenever it is replacing
101
116
  // a text node. The function sees whether the given oldNode is part
102
117
  // of the current selection, and updates this selection if it is.
@@ -121,6 +136,9 @@ var select = {};
121
136
  point.offset += (offset || 0);
122
137
  }
123
138
  }
139
+ else if (select.ie_selection && point.offset == 0 && point.node == baseNodeAfter(from)) {
140
+ currentSelection.changed = true;
141
+ }
124
142
  }
125
143
  replace(currentSelection.start);
126
144
  replace(currentSelection.end);
@@ -144,8 +162,15 @@ var select = {};
144
162
  // Most functions are defined in two ways, one for the IE selection
145
163
  // model, one for the W3C one.
146
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
+
147
172
  function selectionNode(start) {
148
- var range = document.selection.createRange();
173
+ var range = selRange();
149
174
  range.collapse(start);
150
175
 
151
176
  function nodeAfter(node) {
@@ -232,9 +257,9 @@ var select = {};
232
257
  };
233
258
 
234
259
  select.offsetInNode = function(node) {
235
- var sel = document.selection;
236
- if (!sel) return 0;
237
- var range = sel.createRange(), range2 = range.duplicate();
260
+ var range = selRange();
261
+ if (!range) return 0;
262
+ var range2 = range.duplicate();
238
263
  try {range2.moveToElementText(node);} catch(e){return 0;}
239
264
  range.setEndPoint("StartToStart", range2);
240
265
  return range.text.length;
@@ -244,10 +269,9 @@ var select = {};
244
269
  // after. Note that this returns false for 'no cursor', and null
245
270
  // for 'start of document'.
246
271
  select.selectionTopNode = function(container, start) {
247
- var selection = document.selection;
248
- if (!selection) return false;
249
-
250
- var range = selection.createRange(), range2 = range.duplicate();
272
+ var range = selRange();
273
+ if (!range) return false;
274
+ var range2 = range.duplicate();
251
275
  range.collapse(start);
252
276
  var around = range.parentElement();
253
277
  if (around && isAncestor(container, around)) {
@@ -296,8 +320,12 @@ var select = {};
296
320
  }
297
321
 
298
322
  if (start == 0) {
299
- var test1 = selection.createRange(), test2 = test1.duplicate();
300
- test2.moveToElementText(container);
323
+ var test1 = selRange(), test2 = test1.duplicate();
324
+ try {
325
+ test2.moveToElementText(container);
326
+ } catch(exception) {
327
+ return null;
328
+ }
301
329
  if (test1.compareEndPoints("StartToStart", test2) == 0)
302
330
  return null;
303
331
  }
@@ -315,14 +343,13 @@ var select = {};
315
343
  };
316
344
 
317
345
  select.somethingSelected = function() {
318
- var sel = document.selection;
319
- return sel && (sel.createRange().text != "");
346
+ var range = selRange();
347
+ return range && (range.text != "");
320
348
  };
321
349
 
322
350
  function insertAtCursor(html) {
323
- var selection = document.selection;
324
- if (selection) {
325
- var range = selection.createRange();
351
+ var range = selRange();
352
+ if (range) {
326
353
  range.pasteHTML(html);
327
354
  range.collapse(false);
328
355
  range.select();
@@ -343,14 +370,14 @@ var select = {};
343
370
  // currently is, and the offset into the line. Returns null as
344
371
  // node if cursor is on first line.
345
372
  select.cursorPos = function(container, start) {
346
- var selection = document.selection;
347
- if (!selection) return null;
373
+ var range = selRange();
374
+ if (!range) return null;
348
375
 
349
376
  var topNode = select.selectionTopNode(container, start);
350
377
  while (topNode && !isBR(topNode))
351
378
  topNode = topNode.previousSibling;
352
379
 
353
- var range = selection.createRange(), range2 = range.duplicate();
380
+ var range2 = range.duplicate();
354
381
  range.collapse(start);
355
382
  if (topNode) {
356
383
  range2.moveToElementText(topNode);
@@ -565,7 +592,7 @@ var select = {};
565
592
  return range.toString().length;
566
593
  };
567
594
 
568
- function insertNodeAtCursor(node) {
595
+ select.insertNodeAtCursor = function(node) {
569
596
  var range = selectionRange();
570
597
  if (!range) return;
571
598
 
@@ -593,11 +620,11 @@ var select = {};
593
620
  }
594
621
 
595
622
  select.insertNewlineAtCursor = function() {
596
- insertNodeAtCursor(document.createElement("BR"));
623
+ select.insertNodeAtCursor(document.createElement("BR"));
597
624
  };
598
625
 
599
626
  select.insertTabAtCursor = function() {
600
- insertNodeAtCursor(document.createTextNode(fourSpaces));
627
+ select.insertNodeAtCursor(document.createTextNode(fourSpaces));
601
628
  };
602
629
 
603
630
  select.cursorPos = function(container, start) {
@@ -92,7 +92,7 @@ var stringStream = function(source){
92
92
  else if (str.slice(0, left) == cased(current.slice(pos))) {
93
93
  accum += current; current = "";
94
94
  try {current = source.next();}
95
- catch (e) {break;}
95
+ catch (e) {if (e != StopIteration) throw e; break;}
96
96
  pos = 0;
97
97
  str = str.slice(left);
98
98
  }
@@ -109,6 +109,20 @@ var stringStream = function(source){
109
109
 
110
110
  return found;
111
111
  },
112
+ // Wont't match past end of line.
113
+ lookAheadRegex: function(regex, consume) {
114
+ if (regex.source.charAt(0) != "^")
115
+ throw new Error("Regexps passed to lookAheadRegex must start with ^");
116
+
117
+ // Fetch the rest of the line
118
+ while (current.indexOf("\n", pos) == -1) {
119
+ try {current += source.next();}
120
+ catch (e) {if (e != StopIteration) throw e; break;}
121
+ }
122
+ var matched = current.slice(pos).match(regex);
123
+ if (matched && consume) pos += matched[0].length;
124
+ return matched;
125
+ },
112
126
 
113
127
  // Utils built on top of the above
114
128
  // more: -> boolean
@@ -101,7 +101,7 @@ var tokenizeJavaScript = (function() {
101
101
  }
102
102
  function readRegexp() {
103
103
  nextUntilUnescaped(source, "/");
104
- source.nextWhileMatches(/[gi]/);
104
+ source.nextWhileMatches(/[gimy]/); // 'y' is "sticky" option in Mozilla
105
105
  return {type: "regexp", style: "js-string"};
106
106
  }
107
107
  // Mutli-line comments are tricky. We want to return the newlines
@@ -29,7 +29,7 @@
29
29
  function UndoHistory(container, maxDepth, commitDelay, editor) {
30
30
  this.container = container;
31
31
  this.maxDepth = maxDepth; this.commitDelay = commitDelay;
32
- this.editor = editor; this.parent = editor.parent;
32
+ this.editor = editor;
33
33
  // This line object represents the initial, empty editor.
34
34
  var initial = {text: "", from: null, to: null};
35
35
  // As the borders between lines are represented by BR elements, the
@@ -44,7 +44,7 @@ function UndoHistory(container, maxDepth, commitDelay, editor) {
44
44
  this.firstTouched = false;
45
45
  // History is the set of committed changes, touched is the set of
46
46
  // nodes touched since the last commit.
47
- this.history = []; this.redoHistory = []; this.touched = [];
47
+ this.history = []; this.redoHistory = []; this.touched = []; this.lostundo = 0;
48
48
  }
49
49
 
50
50
  UndoHistory.prototype = {
@@ -52,8 +52,8 @@ UndoHistory.prototype = {
52
52
  // milliseconds).
53
53
  scheduleCommit: function() {
54
54
  var self = this;
55
- this.parent.clearTimeout(this.commitTimeout);
56
- this.commitTimeout = this.parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
55
+ parent.clearTimeout(this.commitTimeout);
56
+ this.commitTimeout = parent.setTimeout(function(){self.tryCommit();}, this.commitDelay);
57
57
  },
58
58
 
59
59
  // Mark a node as touched. Null is a valid argument.
@@ -92,18 +92,19 @@ UndoHistory.prototype = {
92
92
  clear: function() {
93
93
  this.history = [];
94
94
  this.redoHistory = [];
95
+ this.lostundo = 0;
95
96
  },
96
97
 
97
98
  // Ask for the size of the un/redo histories.
98
99
  historySize: function() {
99
- return {undo: this.history.length, redo: this.redoHistory.length};
100
+ return {undo: this.history.length, redo: this.redoHistory.length, lostundo: this.lostundo};
100
101
  },
101
102
 
102
103
  // Push a changeset into the document.
103
104
  push: function(from, to, lines) {
104
105
  var chain = [];
105
106
  for (var i = 0; i < lines.length; i++) {
106
- var end = (i == lines.length - 1) ? to : this.container.ownerDocument.createElement("BR");
107
+ var end = (i == lines.length - 1) ? to : document.createElement("br");
107
108
  chain.push({from: from, to: end, text: cleanText(lines[i])});
108
109
  from = end;
109
110
  }
@@ -128,7 +129,7 @@ UndoHistory.prototype = {
128
129
  // Clear the undo history, make the current document the start
129
130
  // position.
130
131
  reset: function() {
131
- this.history = []; this.redoHistory = [];
132
+ this.history = []; this.redoHistory = []; this.lostundo = 0;
132
133
  },
133
134
 
134
135
  textAfter: function(br) {
@@ -145,7 +146,7 @@ UndoHistory.prototype = {
145
146
 
146
147
  // Commit unless there are pending dirty nodes.
147
148
  tryCommit: function() {
148
- if (!window.parent || !window.UndoHistory) return; // Stop when frame has been unloaded
149
+ if (!window || !window.parent || !window.UndoHistory) return; // Stop when frame has been unloaded
149
150
  if (this.editor.highlightDirty()) this.commit(true);
150
151
  else this.scheduleCommit();
151
152
  },
@@ -153,7 +154,7 @@ UndoHistory.prototype = {
153
154
  // Check whether the touched nodes hold any changes, if so, commit
154
155
  // them.
155
156
  commit: function(doNotHighlight) {
156
- this.parent.clearTimeout(this.commitTimeout);
157
+ parent.clearTimeout(this.commitTimeout);
157
158
  // Make sure there are no pending dirty nodes.
158
159
  if (!doNotHighlight) this.editor.highlightDirty(true);
159
160
  // Build set of chains.
@@ -192,7 +193,7 @@ UndoHistory.prototype = {
192
193
  },
193
194
 
194
195
  notifyEnvironment: function() {
195
- if (this.onChange) this.onChange();
196
+ if (this.onChange) this.onChange(this.editor);
196
197
  // Used by the line-wrapping line-numbering code.
197
198
  if (window.frameElement && window.frameElement.CodeMirror.updateNumbers)
198
199
  window.frameElement.CodeMirror.updateNumbers();
@@ -235,8 +236,10 @@ UndoHistory.prototype = {
235
236
  // it than allowed.
236
237
  addUndoLevel: function(diffs) {
237
238
  this.history.push(diffs);
238
- if (this.history.length > this.maxDepth)
239
+ if (this.history.length > this.maxDepth) {
239
240
  this.history.shift();
241
+ lostundo += 1;
242
+ }
240
243
  },
241
244
 
242
245
  // Build chains from a set of touched nodes.
@@ -257,8 +260,8 @@ UndoHistory.prototype = {
257
260
  function buildLine(node) {
258
261
  var text = [];
259
262
  for (var cur = node ? node.nextSibling : self.container.firstChild;
260
- cur && !isBR(cur); cur = cur.nextSibling)
261
- if (cur.currentText) text.push(cur.currentText);
263
+ cur && (!isBR(cur) || cur.hackBR); cur = cur.nextSibling)
264
+ if (!cur.hackBR && cur.currentText) text.push(cur.currentText);
262
265
  return {from: node, to: cur, text: cleanText(text.join(""))};
263
266
  }
264
267
 
@@ -267,7 +270,7 @@ UndoHistory.prototype = {
267
270
  var lines = [];
268
271
  if (self.firstTouched) self.touched.push(null);
269
272
  forEach(self.touched, function(node) {
270
- if (node && node.parentNode != self.container) return;
273
+ if (node && (node.parentNode != self.container || node.hackBR)) return;
271
274
 
272
275
  if (node) node.historyTouched = false;
273
276
  else self.firstTouched = false;