feather_cms 0.0.1

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 (42) hide show
  1. data/.gitignore +4 -0
  2. data/Gemfile +4 -0
  3. data/README.md +46 -0
  4. data/Rakefile +1 -0
  5. data/feather_cms.gemspec +24 -0
  6. data/lib/feather_cms/config.rb +54 -0
  7. data/lib/feather_cms/model.rb +59 -0
  8. data/lib/feather_cms/railtie.rb +13 -0
  9. data/lib/feather_cms/template_cache.rb +52 -0
  10. data/lib/feather_cms/version.rb +3 -0
  11. data/lib/feather_cms/view_helper.rb +20 -0
  12. data/lib/feather_cms.rb +19 -0
  13. data/lib/generators/feather_cms/USAGE +10 -0
  14. data/lib/generators/feather_cms/feather_cms_generator.rb +63 -0
  15. data/lib/generators/feather_cms/templates/bootstrap.css +3992 -0
  16. data/lib/generators/feather_cms/templates/codemirror/codemirror.css +117 -0
  17. data/lib/generators/feather_cms/templates/codemirror/codemirror.js +2909 -0
  18. data/lib/generators/feather_cms/templates/codemirror/feather_cms.js +13 -0
  19. data/lib/generators/feather_cms/templates/codemirror/modes/css.js +124 -0
  20. data/lib/generators/feather_cms/templates/codemirror/modes/htmlmixed.js +83 -0
  21. data/lib/generators/feather_cms/templates/codemirror/modes/javascript.js +360 -0
  22. data/lib/generators/feather_cms/templates/codemirror/modes/xml.js +267 -0
  23. data/lib/generators/feather_cms/templates/codemirror/util/dialog.css +23 -0
  24. data/lib/generators/feather_cms/templates/codemirror/util/dialog.js +63 -0
  25. data/lib/generators/feather_cms/templates/codemirror/util/foldcode.js +186 -0
  26. data/lib/generators/feather_cms/templates/codemirror/util/formatting.js +294 -0
  27. data/lib/generators/feather_cms/templates/codemirror/util/javascript-hint.js +134 -0
  28. data/lib/generators/feather_cms/templates/codemirror/util/match-highlighter.js +44 -0
  29. data/lib/generators/feather_cms/templates/codemirror/util/overlay.js +51 -0
  30. data/lib/generators/feather_cms/templates/codemirror/util/runmode.js +49 -0
  31. data/lib/generators/feather_cms/templates/codemirror/util/search.js +114 -0
  32. data/lib/generators/feather_cms/templates/codemirror/util/searchcursor.js +117 -0
  33. data/lib/generators/feather_cms/templates/codemirror/util/simple-hint.css +16 -0
  34. data/lib/generators/feather_cms/templates/codemirror/util/simple-hint.js +68 -0
  35. data/lib/generators/feather_cms/templates/controller.rb +34 -0
  36. data/lib/generators/feather_cms/templates/form.html.erb +40 -0
  37. data/lib/generators/feather_cms/templates/index.html.erb +11 -0
  38. data/lib/generators/feather_cms/templates/initializer.rb +5 -0
  39. data/lib/generators/feather_cms/templates/layout.html.erb +52 -0
  40. data/lib/generators/feather_cms/templates/migration.rb +13 -0
  41. data/lib/generators/feather_cms/templates/model.rb +3 -0
  42. metadata +98 -0
@@ -0,0 +1,267 @@
1
+ CodeMirror.defineMode("xml", function(config, parserConfig) {
2
+ var indentUnit = config.indentUnit;
3
+ var Kludges = parserConfig.htmlMode ? {
4
+ autoSelfClosers: {"br": true, "img": true, "hr": true, "link": true, "input": true,
5
+ "meta": true, "col": true, "frame": true, "base": true, "area": true},
6
+ doNotIndent: {"pre": true},
7
+ allowUnquoted: true,
8
+ allowMissing: false
9
+ } : {autoSelfClosers: {}, doNotIndent: {}, allowUnquoted: false, allowMissing: false};
10
+ var alignCDATA = parserConfig.alignCDATA;
11
+
12
+ // Return variables for tokenizers
13
+ var tagName, type;
14
+
15
+ function inText(stream, state) {
16
+ function chain(parser) {
17
+ state.tokenize = parser;
18
+ return parser(stream, state);
19
+ }
20
+
21
+ var ch = stream.next();
22
+ if (ch == "<") {
23
+ if (stream.eat("!")) {
24
+ if (stream.eat("[")) {
25
+ if (stream.match("CDATA[")) return chain(inBlock("atom", "]]>"));
26
+ else return null;
27
+ }
28
+ else if (stream.match("--")) return chain(inBlock("comment", "-->"));
29
+ else if (stream.match("DOCTYPE", true, true)) {
30
+ stream.eatWhile(/[\w\._\-]/);
31
+ return chain(doctype(1));
32
+ }
33
+ else return null;
34
+ }
35
+ else if (stream.eat("?")) {
36
+ stream.eatWhile(/[\w\._\-]/);
37
+ state.tokenize = inBlock("meta", "?>");
38
+ return "meta";
39
+ }
40
+ else {
41
+ type = stream.eat("/") ? "closeTag" : "openTag";
42
+ stream.eatSpace();
43
+ tagName = "";
44
+ var c;
45
+ while ((c = stream.eat(/[^\s\u00a0=<>\"\'\/?]/))) tagName += c;
46
+ state.tokenize = inTag;
47
+ return "tag";
48
+ }
49
+ }
50
+ else if (ch == "&") {
51
+ var ok;
52
+ if (stream.eat("#")) {
53
+ if (stream.eat("x")) {
54
+ ok = stream.eatWhile(/[a-fA-F\d]/) && stream.eat(";");
55
+ } else {
56
+ ok = stream.eatWhile(/[\d]/) && stream.eat(";");
57
+ }
58
+ } else {
59
+ ok = stream.eatWhile(/[\w\.\-:]/) && stream.eat(";");
60
+ }
61
+ return ok ? "atom" : "error";
62
+ }
63
+ else {
64
+ stream.eatWhile(/[^&<]/);
65
+ return null;
66
+ }
67
+ }
68
+
69
+ function inTag(stream, state) {
70
+ var ch = stream.next();
71
+ if (ch == ">" || (ch == "/" && stream.eat(">"))) {
72
+ state.tokenize = inText;
73
+ type = ch == ">" ? "endTag" : "selfcloseTag";
74
+ return "tag";
75
+ }
76
+ else if (ch == "=") {
77
+ type = "equals";
78
+ return null;
79
+ }
80
+ else if (/[\'\"]/.test(ch)) {
81
+ state.tokenize = inAttribute(ch);
82
+ return state.tokenize(stream, state);
83
+ }
84
+ else {
85
+ stream.eatWhile(/[^\s\u00a0=<>\"\'\/?]/);
86
+ return "word";
87
+ }
88
+ }
89
+
90
+ function inAttribute(quote) {
91
+ return function(stream, state) {
92
+ while (!stream.eol()) {
93
+ if (stream.next() == quote) {
94
+ state.tokenize = inTag;
95
+ break;
96
+ }
97
+ }
98
+ return "string";
99
+ };
100
+ }
101
+
102
+ function inBlock(style, terminator) {
103
+ return function(stream, state) {
104
+ while (!stream.eol()) {
105
+ if (stream.match(terminator)) {
106
+ state.tokenize = inText;
107
+ break;
108
+ }
109
+ stream.next();
110
+ }
111
+ return style;
112
+ };
113
+ }
114
+ function doctype(depth) {
115
+ return function(stream, state) {
116
+ var ch;
117
+ while ((ch = stream.next()) != null) {
118
+ if (ch == "<") {
119
+ state.tokenize = doctype(depth + 1);
120
+ return state.tokenize(stream, state);
121
+ } else if (ch == ">") {
122
+ if (depth == 1) {
123
+ state.tokenize = inText;
124
+ break;
125
+ } else {
126
+ state.tokenize = doctype(depth - 1);
127
+ return state.tokenize(stream, state);
128
+ }
129
+ }
130
+ }
131
+ return "meta";
132
+ };
133
+ }
134
+
135
+ var curState, setStyle;
136
+ function pass() {
137
+ for (var i = arguments.length - 1; i >= 0; i--) curState.cc.push(arguments[i]);
138
+ }
139
+ function cont() {
140
+ pass.apply(null, arguments);
141
+ return true;
142
+ }
143
+
144
+ function pushContext(tagName, startOfLine) {
145
+ var noIndent = Kludges.doNotIndent.hasOwnProperty(tagName) || (curState.context && curState.context.noIndent);
146
+ curState.context = {
147
+ prev: curState.context,
148
+ tagName: tagName,
149
+ indent: curState.indented,
150
+ startOfLine: startOfLine,
151
+ noIndent: noIndent
152
+ };
153
+ }
154
+ function popContext() {
155
+ if (curState.context) curState.context = curState.context.prev;
156
+ }
157
+
158
+ function element(type) {
159
+ if (type == "openTag") {
160
+ curState.tagName = tagName;
161
+ return cont(attributes, endtag(curState.startOfLine));
162
+ } else if (type == "closeTag") {
163
+ var err = false;
164
+ if (curState.context) {
165
+ err = curState.context.tagName != tagName;
166
+ } else {
167
+ err = true;
168
+ }
169
+ if (err) setStyle = "error";
170
+ return cont(endclosetag(err));
171
+ }
172
+ return cont();
173
+ }
174
+ function endtag(startOfLine) {
175
+ return function(type) {
176
+ if (type == "selfcloseTag" ||
177
+ (type == "endTag" && Kludges.autoSelfClosers.hasOwnProperty(curState.tagName.toLowerCase())))
178
+ return cont();
179
+ if (type == "endTag") {pushContext(curState.tagName, startOfLine); return cont();}
180
+ return cont();
181
+ };
182
+ }
183
+ function endclosetag(err) {
184
+ return function(type) {
185
+ if (err) setStyle = "error";
186
+ if (type == "endTag") { popContext(); return cont(); }
187
+ setStyle = "error";
188
+ return cont(arguments.callee);
189
+ }
190
+ }
191
+
192
+ function attributes(type) {
193
+ if (type == "word") {setStyle = "attribute"; return cont(attribute, attributes);}
194
+ if (type == "endTag" || type == "selfcloseTag") return pass();
195
+ setStyle = "error";
196
+ return cont(attributes);
197
+ }
198
+ function attribute(type) {
199
+ if (type == "equals") return cont(attvalue, attributes);
200
+ if (!Kludges.allowMissing) setStyle = "error";
201
+ return (type == "endTag" || type == "selfcloseTag") ? pass() : cont();
202
+ }
203
+ function attvalue(type) {
204
+ if (type == "string") return cont(attvaluemaybe);
205
+ if (type == "word" && Kludges.allowUnquoted) {setStyle = "string"; return cont();}
206
+ setStyle = "error";
207
+ return (type == "endTag" || type == "selfCloseTag") ? pass() : cont();
208
+ }
209
+ function attvaluemaybe(type) {
210
+ if (type == "string") return cont(attvaluemaybe);
211
+ else return pass();
212
+ }
213
+
214
+ return {
215
+ startState: function() {
216
+ return {tokenize: inText, cc: [], indented: 0, startOfLine: true, tagName: null, context: null};
217
+ },
218
+
219
+ token: function(stream, state) {
220
+ if (stream.sol()) {
221
+ state.startOfLine = true;
222
+ state.indented = stream.indentation();
223
+ }
224
+ if (stream.eatSpace()) return null;
225
+
226
+ setStyle = type = tagName = null;
227
+ var style = state.tokenize(stream, state);
228
+ state.type = type;
229
+ if ((style || type) && style != "comment") {
230
+ curState = state;
231
+ while (true) {
232
+ var comb = state.cc.pop() || element;
233
+ if (comb(type || style)) break;
234
+ }
235
+ }
236
+ state.startOfLine = false;
237
+ return setStyle || style;
238
+ },
239
+
240
+ indent: function(state, textAfter, fullLine) {
241
+ var context = state.context;
242
+ if ((state.tokenize != inTag && state.tokenize != inText) ||
243
+ context && context.noIndent)
244
+ return fullLine ? fullLine.match(/^(\s*)/)[0].length : 0;
245
+ if (alignCDATA && /<!\[CDATA\[/.test(textAfter)) return 0;
246
+ if (context && /^<\//.test(textAfter))
247
+ context = context.prev;
248
+ while (context && !context.startOfLine)
249
+ context = context.prev;
250
+ if (context) return context.indent + indentUnit;
251
+ else return 0;
252
+ },
253
+
254
+ compareStates: function(a, b) {
255
+ if (a.indented != b.indented || a.tokenize != b.tokenize) return false;
256
+ for (var ca = a.context, cb = b.context; ; ca = ca.prev, cb = cb.prev) {
257
+ if (!ca || !cb) return ca == cb;
258
+ if (ca.tagName != cb.tagName) return false;
259
+ }
260
+ },
261
+
262
+ electricChars: "/"
263
+ };
264
+ });
265
+
266
+ CodeMirror.defineMIME("application/xml", "xml");
267
+ CodeMirror.defineMIME("text/html", {name: "xml", htmlMode: true});
@@ -0,0 +1,23 @@
1
+ .CodeMirror-dialog {
2
+ position: relative;
3
+ }
4
+
5
+ .CodeMirror-dialog > div {
6
+ position: absolute;
7
+ top: 0; left: 0; right: 0;
8
+ background: white;
9
+ border-bottom: 1px solid #eee;
10
+ z-index: 15;
11
+ padding: .1em .8em;
12
+ overflow: hidden;
13
+ color: #333;
14
+ }
15
+
16
+ .CodeMirror-dialog input {
17
+ border: none;
18
+ outline: none;
19
+ background: transparent;
20
+ width: 20em;
21
+ color: inherit;
22
+ font-family: monospace;
23
+ }
@@ -0,0 +1,63 @@
1
+ // Open simple dialogs on top of an editor. Relies on dialog.css.
2
+
3
+ (function() {
4
+ function dialogDiv(cm, template) {
5
+ var wrap = cm.getWrapperElement();
6
+ var dialog = wrap.insertBefore(document.createElement("div"), wrap.firstChild);
7
+ dialog.className = "CodeMirror-dialog";
8
+ dialog.innerHTML = '<div>' + template + '</div>';
9
+ return dialog;
10
+ }
11
+
12
+ CodeMirror.defineExtension("openDialog", function(template, callback) {
13
+ var dialog = dialogDiv(this, template);
14
+ var closed = false, me = this;
15
+ function close() {
16
+ if (closed) return;
17
+ closed = true;
18
+ dialog.parentNode.removeChild(dialog);
19
+ }
20
+ var inp = dialog.getElementsByTagName("input")[0];
21
+ if (inp) {
22
+ CodeMirror.connect(inp, "keydown", function(e) {
23
+ if (e.keyCode == 13 || e.keyCode == 27) {
24
+ CodeMirror.e_stop(e);
25
+ close();
26
+ me.focus();
27
+ if (e.keyCode == 13) callback(inp.value);
28
+ }
29
+ });
30
+ inp.focus();
31
+ CodeMirror.connect(inp, "blur", close);
32
+ }
33
+ return close;
34
+ });
35
+
36
+ CodeMirror.defineExtension("openConfirm", function(template, callbacks) {
37
+ var dialog = dialogDiv(this, template);
38
+ var buttons = dialog.getElementsByTagName("button");
39
+ var closed = false, me = this, blurring = 1;
40
+ function close() {
41
+ if (closed) return;
42
+ closed = true;
43
+ dialog.parentNode.removeChild(dialog);
44
+ me.focus();
45
+ }
46
+ buttons[0].focus();
47
+ for (var i = 0; i < buttons.length; ++i) {
48
+ var b = buttons[i];
49
+ (function(callback) {
50
+ CodeMirror.connect(b, "click", function(e) {
51
+ CodeMirror.e_preventDefault(e);
52
+ close();
53
+ if (callback) callback(me);
54
+ });
55
+ })(callbacks[i]);
56
+ CodeMirror.connect(b, "blur", function() {
57
+ --blurring;
58
+ setTimeout(function() { if (blurring <= 0) close(); }, 200);
59
+ });
60
+ CodeMirror.connect(b, "focus", function() { ++blurring; });
61
+ }
62
+ });
63
+ })();
@@ -0,0 +1,186 @@
1
+ // the tagRangeFinder function is
2
+ // Copyright (C) 2011 by Daniel Glazman <daniel@glazman.org>
3
+ // released under the MIT license (../../LICENSE) like the rest of CodeMirror
4
+ CodeMirror.tagRangeFinder = function(cm, line) {
5
+ var nameStartChar = "A-Z_a-z\\u00C0-\\u00D6\\u00D8-\\u00F6\\u00F8-\\u02FF\\u0370-\\u037D\\u037F-\\u1FFF\\u200C-\\u200D\\u2070-\\u218F\\u2C00-\\u2FEF\\u3001-\\uD7FF\\uF900-\\uFDCF\\uFDF0-\\uFFFD";
6
+ var nameChar = nameStartChar + "\-\.0-9\\u00B7\\u0300-\\u036F\\u203F-\\u2040";
7
+ var xmlNAMERegExp = new RegExp("^[" + nameStartChar + "][" + nameChar + "]*");
8
+
9
+ var lineText = cm.getLine(line);
10
+ var found = false;
11
+ var tag = null;
12
+ var pos = 0;
13
+ while (!found) {
14
+ pos = lineText.indexOf("<", pos);
15
+ if (-1 == pos) // no tag on line
16
+ return;
17
+ if (pos + 1 < lineText.length && lineText[pos + 1] == "/") { // closing tag
18
+ pos++;
19
+ continue;
20
+ }
21
+ // ok we weem to have a start tag
22
+ if (!lineText.substr(pos + 1).match(xmlNAMERegExp)) { // not a tag name...
23
+ pos++;
24
+ continue;
25
+ }
26
+ var gtPos = lineText.indexOf(">", pos + 1);
27
+ if (-1 == gtPos) { // end of start tag not in line
28
+ var l = line + 1;
29
+ var foundGt = false;
30
+ var lastLine = cm.lineCount();
31
+ while (l < lastLine && !foundGt) {
32
+ var lt = cm.getLine(l);
33
+ var gt = lt.indexOf(">");
34
+ if (-1 != gt) { // found a >
35
+ foundGt = true;
36
+ var slash = lt.lastIndexOf("/", gt);
37
+ if (-1 != slash && slash < gt) {
38
+ var str = lineText.substr(slash, gt - slash + 1);
39
+ if (!str.match( /\/\s*\>/ )) // yep, that's the end of empty tag
40
+ return l+1;
41
+ }
42
+ }
43
+ l++;
44
+ }
45
+ found = true;
46
+ }
47
+ else {
48
+ var slashPos = lineText.lastIndexOf("/", gtPos);
49
+ if (-1 == slashPos) { // cannot be empty tag
50
+ found = true;
51
+ // don't continue
52
+ }
53
+ else { // empty tag?
54
+ // check if really empty tag
55
+ var str = lineText.substr(slashPos, gtPos - slashPos + 1);
56
+ if (!str.match( /\/\s*\>/ )) { // finally not empty
57
+ found = true;
58
+ // don't continue
59
+ }
60
+ }
61
+ }
62
+ if (found) {
63
+ var subLine = lineText.substr(pos + 1);
64
+ tag = subLine.match(xmlNAMERegExp);
65
+ if (tag) {
66
+ // we have an element name, wooohooo !
67
+ tag = tag[0];
68
+ // do we have the close tag on same line ???
69
+ if (-1 != lineText.indexOf("</" + tag + ">", pos)) // yep
70
+ {
71
+ found = false;
72
+ }
73
+ // we don't, so we have a candidate...
74
+ }
75
+ else
76
+ found = false;
77
+ }
78
+ if (!found)
79
+ pos++;
80
+ }
81
+
82
+ if (found) {
83
+ var startTag = "(\\<\\/" + tag + "\\>)|(\\<" + tag + "\\>)|(\\<" + tag + "\s)|(\\<" + tag + "$)";
84
+ var startTagRegExp = new RegExp(startTag, "g");
85
+ var endTag = "</" + tag + ">";
86
+ var depth = 1;
87
+ var l = line + 1;
88
+ var lastLine = cm.lineCount();
89
+ while (l < lastLine) {
90
+ lineText = cm.getLine(l);
91
+ var match = lineText.match(startTagRegExp);
92
+ if (match) {
93
+ for (var i = 0; i < match.length; i++) {
94
+ if (match[i] == endTag)
95
+ depth--;
96
+ else
97
+ depth++;
98
+ if (!depth)
99
+ return l+1;
100
+ }
101
+ }
102
+ l++;
103
+ }
104
+ return;
105
+ }
106
+ };
107
+
108
+ CodeMirror.braceRangeFinder = function(cm, line) {
109
+ var lineText = cm.getLine(line);
110
+ var startChar = lineText.lastIndexOf("{");
111
+ if (startChar < 0 || lineText.lastIndexOf("}") > startChar) return;
112
+ var tokenType = cm.getTokenAt({line: line, ch: startChar}).className;
113
+ var count = 1, lastLine = cm.lineCount(), end;
114
+ outer: for (var i = line + 1; i < lastLine; ++i) {
115
+ var text = cm.getLine(i), pos = 0;
116
+ for (;;) {
117
+ var nextOpen = text.indexOf("{", pos), nextClose = text.indexOf("}", pos);
118
+ if (nextOpen < 0) nextOpen = text.length;
119
+ if (nextClose < 0) nextClose = text.length;
120
+ pos = Math.min(nextOpen, nextClose);
121
+ if (pos == text.length) break;
122
+ if (cm.getTokenAt({line: i, ch: pos + 1}).className == tokenType) {
123
+ if (pos == nextOpen) ++count;
124
+ else if (!--count) { end = i; break outer; }
125
+ }
126
+ ++pos;
127
+ }
128
+ }
129
+ if (end == null || end == line + 1) return;
130
+ return end;
131
+ };
132
+
133
+ CodeMirror.indentRangeFinder = function(cm, line) {
134
+ var tabSize = cm.getOption("tabSize");
135
+ var myIndent = cm.getLineHandle(line).indentation(tabSize), last;
136
+ for (var i = line + 1, end = cm.lineCount(); i < end; ++i) {
137
+ var handle = cm.getLineHandle(i);
138
+ if (!/^\s*$/.test(handle.text)) {
139
+ if (handle.indentation(tabSize) <= myIndent) break;
140
+ last = i;
141
+ }
142
+ }
143
+ if (!last) return null;
144
+ return last + 1;
145
+ };
146
+
147
+ CodeMirror.newFoldFunction = function(rangeFinder, markText) {
148
+ var folded = [];
149
+ if (markText == null) markText = '<div style="position: absolute; left: 2px; color:#600">&#x25bc;</div>%N%';
150
+
151
+ function isFolded(cm, n) {
152
+ for (var i = 0; i < folded.length; ++i) {
153
+ var start = cm.lineInfo(folded[i].start);
154
+ if (!start) folded.splice(i--, 1);
155
+ else if (start.line == n) return {pos: i, region: folded[i]};
156
+ }
157
+ }
158
+
159
+ function expand(cm, region) {
160
+ cm.clearMarker(region.start);
161
+ for (var i = 0; i < region.hidden.length; ++i)
162
+ cm.showLine(region.hidden[i]);
163
+ }
164
+
165
+ return function(cm, line) {
166
+ cm.operation(function() {
167
+ var known = isFolded(cm, line);
168
+ if (known) {
169
+ folded.splice(known.pos, 1);
170
+ expand(cm, known.region);
171
+ } else {
172
+ var end = rangeFinder(cm, line);
173
+ if (end == null) return;
174
+ var hidden = [];
175
+ for (var i = line + 1; i < end; ++i) {
176
+ var handle = cm.hideLine(i);
177
+ if (handle) hidden.push(handle);
178
+ }
179
+ var first = cm.setMarker(line, markText);
180
+ var region = {start: first, hidden: hidden};
181
+ cm.onDeleteLine(first, function() { expand(cm, region); });
182
+ folded.push(region);
183
+ }
184
+ });
185
+ };
186
+ };