caboose-cms 0.2.5 → 0.2.6

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 (39) hide show
  1. data/app/assets/javascripts/caboose/modal.js +4 -0
  2. data/app/assets/javascripts/caboose/modal_integration.js +10 -0
  3. data/app/assets/javascripts/caboose/model/all.js +9 -0
  4. data/app/assets/javascripts/caboose/model/attribute.js +63 -0
  5. data/app/assets/javascripts/caboose/model/bound_checkbox.js +90 -0
  6. data/app/assets/javascripts/caboose/model/bound_checkbox_multiple.js +106 -0
  7. data/app/assets/javascripts/caboose/model/bound_control.js +68 -0
  8. data/app/assets/javascripts/caboose/model/bound_select.js +108 -0
  9. data/app/assets/javascripts/caboose/model/bound_text.js +87 -0
  10. data/app/assets/javascripts/caboose/model/bound_textarea.js +134 -0
  11. data/app/assets/javascripts/caboose/model/class.js +64 -0
  12. data/app/assets/javascripts/caboose/model/model.js +37 -0
  13. data/app/assets/javascripts/caboose/model/model_binder.js +61 -0
  14. data/app/assets/javascripts/caboose/shortcut.js +11 -0
  15. data/app/assets/javascripts/caboose/station.js +7 -5
  16. data/app/assets/stylesheets/caboose/modal.css +106 -1
  17. data/app/controllers/caboose/admin_controller.rb +1 -9
  18. data/app/controllers/caboose/pages_controller.rb +43 -14
  19. data/app/controllers/caboose/station_controller.rb +3 -0
  20. data/app/models/caboose/page_plugin.rb +1 -1
  21. data/app/models/caboose/user_plugin.rb +1 -1
  22. data/app/views/caboose/admin/index.html.erb +0 -4
  23. data/app/views/caboose/pages/edit.html.erb +6 -19
  24. data/app/views/caboose/pages/edit_content.html.erb +48 -0
  25. data/app/views/caboose/pages/edit_css.html.erb +36 -0
  26. data/app/views/caboose/pages/edit_js.html.erb +36 -0
  27. data/app/views/caboose/pages/edit_seo.html.erb +39 -0
  28. data/app/views/caboose/pages/edit_settings.html.erb +35 -24
  29. data/app/views/caboose/pages/edit_title.html.erb +29 -0
  30. data/app/views/caboose/pages/show.html.erb +2 -2
  31. data/app/views/caboose/station/index.html.erb +5 -2
  32. data/app/views/caboose/users/edit.html.erb +1 -1
  33. data/app/views/layouts/caboose/_admin_top_nav.html.erb +1 -3
  34. data/app/views/layouts/caboose/_top_nav.html.erb +1 -1
  35. data/app/views/layouts/caboose/application.html.erb +2 -2
  36. data/config/routes.rb +6 -1
  37. data/lib/caboose/version.rb +1 -1
  38. metadata +19 -3
  39. data/app/assets/javascripts/caboose/bound_input.js +0 -198
@@ -0,0 +1,87 @@
1
+
2
+ BoundText = BoundControl.extend({
3
+
4
+ //el: false,
5
+ //model: false,
6
+ //attribute: false,
7
+ //binder: false,
8
+
9
+ width: false,
10
+
11
+ init: function(params) {
12
+ for (var thing in params)
13
+ this[thing] = params[thing];
14
+
15
+ this.el = this.el ? this.el : this.model.name.toLowerCase() + '_' + this.model.id + '_' + this.attribute.name;
16
+
17
+ $('#'+this.el).wrap($('<div/>').attr('id', this.el + '_container').css('position', 'relative'));
18
+ $('#'+this.el+'_container').empty();
19
+ $('#'+this.el+'_container').append($('<input/>').attr('id', this.el).attr('type', 'text').attr('placeholder', 'empty').val(this.attribute.value));
20
+ $('#'+this.el+'_container').append($('<div/>').attr('id', this.el + '_placeholder').addClass('placeholder').append($('<span/>').html(this.attribute.nice_name + ': ')));
21
+ if (this.attribute.width) $('#'+this.el).css('width' , this.attribute.width);
22
+ var w = $('#'+this.el+'_placeholder').outerWidth();
23
+ $('#'+this.el).attr('placeholder', 'empty').css('padding-left', '+=' + w).css('width', '-=' + w);
24
+
25
+ var this2 = this;
26
+ $('#'+this.el).on('keyup', function(e) {
27
+ if (e.keyCode == 27) this2.cancel(); // Escape
28
+ if (e.keyCode == 13) this2.save(); // Enter
29
+
30
+ if ($('#'+this2.el).val() != this2.attribute.value_clean)
31
+ $('#'+this2.el).addClass('dirty');
32
+ else
33
+ $('#'+this2.el).removeClass('dirty');
34
+ });
35
+ $('#'+this.el).on('blur', function() { this2.save(); });
36
+ },
37
+
38
+ save: function() {
39
+ this.attribute.value = $('#'+this.el).val();
40
+ if (this.attribute.value == this.attribute.value_clean)
41
+ return;
42
+
43
+ this.show_loader();
44
+ var this2 = this;
45
+ this.model.save(this.attribute, function(resp) {
46
+ if (resp.error)
47
+ {
48
+ this2.hide_loader();
49
+ this2.error(resp.error);
50
+ }
51
+ else
52
+ {
53
+ this2.show_check(500);
54
+ $('#'+this2.el).val(this2.attribute.value);
55
+ $('#'+this2.el).removeClass('dirty');
56
+
57
+ if (this2.binder.success)
58
+ this2.binder.success(this2);
59
+ }
60
+ });
61
+ },
62
+
63
+ cancel: function() {
64
+ this.attribute.value = this.attribute.value_clean;
65
+ $('#'+this.el).val(this.attribute.value);
66
+ $('#'+this.el).removeClass('dirty');
67
+
68
+ if ($('#'+this.el+'_check').length)
69
+ this.hide_check();
70
+ },
71
+
72
+ error: function(str) {
73
+ if (!$('#'+this.el+'_message').length)
74
+ {
75
+ $('#'+this.el+'_container').append($('<div/>')
76
+ .attr('id', this.el + '_message')
77
+ .css('width', $('#'+this.el).outerWidth())
78
+ );
79
+ }
80
+ $('#'+this.el+'_message').hide();
81
+ $('#'+this.el+'_message').html("<p class='note error'>" + str + "</p>");
82
+ $('#'+this.el+'_message').slideDown();
83
+ var this2 = this;
84
+ setTimeout(function() { $('#'+this2.el+'_message').slideUp(function() { $(this).empty(); }); }, 3000);
85
+ }
86
+
87
+ });
@@ -0,0 +1,134 @@
1
+
2
+ BoundTextarea = BoundControl.extend({
3
+
4
+ //el: false,
5
+ //model: false,
6
+ //attribute: false,
7
+ //binder: false,
8
+
9
+ width: false,
10
+
11
+ init: function(params) {
12
+ for (var thing in params)
13
+ this[thing] = params[thing];
14
+
15
+ this.el = this.el ? this.el : this.model.name.toLowerCase() + '_' + this.model.id + '_' + this.attribute.name;
16
+
17
+ $('#'+this.el).wrap($('<div/>').attr('id', this.el + '_container').css('position', 'relative'));
18
+ $('#'+this.el+'_container').empty();
19
+ $('#'+this.el+'_container').append($('<textarea/>').attr('id', this.el).attr('placeholder', 'empty').val(this.attribute.value));
20
+ $('#'+this.el+'_container').append($('<div/>').attr('id', this.el + '_placeholder').addClass('placeholder').append($('<span/>').html(this.attribute.nice_name + ': ')));
21
+ if (this.attribute.width) $('#'+this.el).css('width' , this.attribute.width);
22
+ if (this.attribute.height) $('#'+this.el).css('height' , this.attribute.height);
23
+ var h = $('#'+this.el+'_placeholder').outerHeight();
24
+ $('#'+this.el).attr('placeholder', 'empty').css('padding-top', '+=' + h).css('height', '-=' + h);
25
+
26
+ var this2 = this;
27
+ $('#'+this.el).on('keyup', function(e) {
28
+ if (e.keyCode == 27) this2.cancel(); // Escape
29
+ //if (e.keyCode == 13) this2.save(); // Enter
30
+
31
+ if ($('#'+this2.el).val() != this2.attribute.value_clean)
32
+ {
33
+ this2.show_controls();
34
+ $('#'+this2.el).addClass('dirty');
35
+ }
36
+ else
37
+ $('#'+this2.el).removeClass('dirty');
38
+ });
39
+ $('#'+this.el).on('blur', function() { this2.save(); });
40
+ },
41
+
42
+ show_controls: function() {
43
+ if ($('#'+this.el+'_controls').length)
44
+ return;
45
+ var w = $('#'+this.el).outerWidth();
46
+ var this2 = this;
47
+ $('#'+this.el+'_container').prepend($('<div/>')
48
+ .attr('id', this.el + '_controls')
49
+ .addClass('bound_textarea_controls')
50
+ .css('position', 'absolute')
51
+ .css('top', 0)
52
+ .css('left', w-148)
53
+ .css('width', 148)
54
+ .css('overflow', 'hidden')
55
+ .append($('<div/>')
56
+ .css('width', 148)
57
+ .css('margin-left', 148)
58
+ .append($('<a/>').html('Save' ).addClass('save' ).css('width', 60).attr('href', '#').click(function(event) { event.preventDefault(); this2.save(); }))
59
+ .append($('<a/>').html('Discard').addClass('discard').css('width', 80).attr('href', '#').click(function(event) { event.preventDefault(); this2.cancel(); }))
60
+ )
61
+ );
62
+ $('#'+this.el+'_controls div').animate({ 'margin-left': 0 }, 300);
63
+ },
64
+
65
+ hide_controls: function() {
66
+ if (!$('#'+this.el+'_controls').length)
67
+ return;
68
+ var this2 = this;
69
+ $('#'+this.el+'_controls div').animate({ 'margin-left': 100 }, 300, function() {
70
+ $('#'+this2.el+'_controls').remove();
71
+ });
72
+ },
73
+
74
+ save: function() {
75
+ this.attribute.value = $('#'+this.el).val();
76
+ if (this.attribute.value == this.attribute.value_clean)
77
+ return;
78
+
79
+ this.hide_controls();
80
+ this.show_loader();
81
+ var this2 = this;
82
+ this.model.save(this.attribute, function(resp) {
83
+ if (resp.error)
84
+ {
85
+ this2.hide_loader();
86
+ this2.error(resp.error);
87
+ }
88
+ else
89
+ {
90
+ this2.show_check(500);
91
+ $('#'+this2.el).val(this2.attribute.value);
92
+ $('#'+this2.el).removeClass('dirty');
93
+
94
+ if (this2.binder.success)
95
+ this2.binder.success(this2);
96
+ }
97
+ });
98
+ },
99
+
100
+ cancel: function() {
101
+
102
+ if ($('#'+this.el).val() != this.attribute.value_clean)
103
+ {
104
+ if (confirm('This box has unsaved changes. Hit OK to save changes, Cancel to discard.'))
105
+ {
106
+ this.attribute.value = $('#'+this.el).val();
107
+ this.attribute.value_clean = $('#'+this.el).val();
108
+ this.save();
109
+ }
110
+ }
111
+ this.attribute.value = this.attribute.value_clean;
112
+ $('#'+this.el).val(this.attribute.value);
113
+ $('#'+this.el).removeClass('dirty');
114
+
115
+ if ($('#'+this.el+'_check').length)
116
+ this.hide_check();
117
+ },
118
+
119
+ error: function(str) {
120
+ if (!$('#'+this.el+'_message').length)
121
+ {
122
+ $('#'+this.el+'_container').append($('<div/>')
123
+ .attr('id', this.el + '_message')
124
+ .css('width', $('#'+this.el).outerWidth())
125
+ );
126
+ }
127
+ $('#'+this.el+'_message').hide();
128
+ $('#'+this.el+'_message').html("<p class='note error'>" + str + "</p>");
129
+ $('#'+this.el+'_message').slideDown();
130
+ var this2 = this;
131
+ setTimeout(function() { $('#'+this2.el+'_message').slideUp(function() { $(this).empty(); }); }, 3000);
132
+ }
133
+
134
+ });
@@ -0,0 +1,64 @@
1
+ /* Simple JavaScript Inheritance
2
+ * By John Resig http://ejohn.org/
3
+ * MIT Licensed.
4
+ */
5
+ // Inspired by base2 and Prototype
6
+ (function(){
7
+ var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
8
+
9
+ // The base Class implementation (does nothing)
10
+ this.Class = function(){};
11
+
12
+ // Create a new Class that inherits from this class
13
+ Class.extend = function(prop) {
14
+ var _super = this.prototype;
15
+
16
+ // Instantiate a base class (but only create the instance,
17
+ // don't run the init constructor)
18
+ initializing = true;
19
+ var prototype = new this();
20
+ initializing = false;
21
+
22
+ // Copy the properties over onto the new prototype
23
+ for (var name in prop) {
24
+ // Check if we're overwriting an existing function
25
+ prototype[name] = typeof prop[name] == "function" &&
26
+ typeof _super[name] == "function" && fnTest.test(prop[name]) ?
27
+ (function(name, fn){
28
+ return function() {
29
+ var tmp = this._super;
30
+
31
+ // Add a new ._super() method that is the same method
32
+ // but on the super-class
33
+ this._super = _super[name];
34
+
35
+ // The method only need to be bound temporarily, so we
36
+ // remove it when we're done executing
37
+ var ret = fn.apply(this, arguments);
38
+ this._super = tmp;
39
+
40
+ return ret;
41
+ };
42
+ })(name, prop[name]) :
43
+ prop[name];
44
+ }
45
+
46
+ // The dummy class constructor
47
+ function Class() {
48
+ // All construction is actually done in the init method
49
+ if ( !initializing && this.init )
50
+ this.init.apply(this, arguments);
51
+ }
52
+
53
+ // Populate our constructed prototype object
54
+ Class.prototype = prototype;
55
+
56
+ // Enforce the constructor to be what we expect
57
+ Class.prototype.constructor = Class;
58
+
59
+ // And make this class extendable
60
+ Class.extend = arguments.callee;
61
+
62
+ return Class;
63
+ };
64
+ })();
@@ -0,0 +1,37 @@
1
+
2
+ var Model = function(params) {
3
+ for (var thing in params)
4
+ this[thing] = params[thing];
5
+
6
+ if (this.options_url)
7
+ {
8
+ for (var attrib in this.options_url)
9
+ this.populate_options(this.options_url[attrib]);
10
+ }
11
+ };
12
+
13
+ Model.prototype = {
14
+ name: false,
15
+ id: false,
16
+ attributes: [],
17
+ attributes_clean: [],
18
+ update_url: false,
19
+ fetch_url: false,
20
+ options_url: false,
21
+ options: false,
22
+
23
+ save: function(attrib, after) {
24
+ if (!attrib.update_url)
25
+ attrib.update_url = this.update_url;
26
+ attrib.save(after);
27
+ },
28
+
29
+ populate_options: function(after, i) {
30
+ if (i == null || i == undefined)
31
+ i = 0;
32
+ if (i >= this.attributes.length)
33
+ after();
34
+ var this2 = this;
35
+ this.attributes[i].populate_options(function() { this2.populate_options(after, i+1); });
36
+ }
37
+ };
@@ -0,0 +1,61 @@
1
+
2
+ var ModelBinder = function(params) { this.init(params); };
3
+
4
+ ModelBinder.prototype = {
5
+ model: false,
6
+ controls: [],
7
+ //active_control: false,
8
+ success: false,
9
+
10
+ init: function(params) {
11
+ this.model = new Model({
12
+ name: params['name'],
13
+ id: params['id'],
14
+ attributes: [],
15
+ attributes_clean: []
16
+ });
17
+ if (params['update_url']) this.model.update_url = params['update_url'];
18
+ if (params['success']) this.success = params['success'];
19
+
20
+ var m = this.model;
21
+ $.each(params['attributes'], function(i, attrib) {
22
+ m.attributes[m.attributes.length] = new Attribute(attrib);
23
+ });
24
+ //this.model.populate_options();
25
+
26
+ var this2 = this;
27
+ $.each(this.model.attributes, function(i, attrib) {
28
+ var opts = {
29
+ model: this2.model,
30
+ attribute: attrib,
31
+ binder: this2
32
+ };
33
+ var control = false;
34
+ if (attrib.type == 'text') control = new BoundText(opts);
35
+ else if (attrib.type == 'select') control = new BoundSelect(opts);
36
+ else if (attrib.type == 'checkbox') control = new BoundCheckbox(opts);
37
+ else if (attrib.type == 'textarea') control = new BoundTextarea(opts);
38
+
39
+ this2.controls.push();
40
+ });
41
+
42
+ //$(document).keyup(function(e) {
43
+ // if (e.keyCode == 27) this2.cancel_active(); // Escape
44
+ // //if (e.keyCode == 13) this2.save_active(); // Enter
45
+ //});
46
+ },
47
+
48
+ //cancel_active: function() {
49
+ // if (!this.active_control)
50
+ // return;
51
+ // this.active_control.cancel();
52
+ // this.active_control = false;
53
+ //},
54
+
55
+ //save_active: function() {
56
+ // if (!this.active_control)
57
+ // return;
58
+ // this.active_control.save();
59
+ // this.active_control = false;
60
+ //},
61
+ };
@@ -0,0 +1,11 @@
1
+
2
+ lastkeys = "";
3
+ $(document).keyup(function(e) {
4
+ if (e.keyCode == 13 && lastkeys == "caboose") // Enter
5
+ {
6
+
7
+ }
8
+ if (lastkeys.length > 7)
9
+ lastkeys = "";
10
+ lastkeys += String.fromCharCode(e.keyCode);
11
+ });
@@ -1,15 +1,15 @@
1
1
 
2
- var CabooseStation = function(m) {
3
- this.modal = m;
4
- this.init();
2
+ var CabooseStation = function(m, initial_tab) {
3
+ this.init(m, initial_tab);
5
4
  };
6
5
 
7
6
  CabooseStation.prototype = {
8
-
7
+
9
8
  modal: false,
10
9
 
11
- init: function()
10
+ init: function(m, initial_tab)
12
11
  {
12
+ this.modal = m;
13
13
  var this2 = this;
14
14
  // Handle main nav items with subnav
15
15
  $('#station > ul > li > a').click(function(event) {
@@ -34,6 +34,8 @@ CabooseStation.prototype = {
34
34
  parent.window.location = $(this).attr('href');
35
35
  }
36
36
  });
37
+ if (initial_tab)
38
+ $('#station > ul > li#nav_item_' + initial_tab + ' > a').trigger('click');
37
39
  },
38
40
 
39
41
  subnav: function(id, href)
@@ -56,11 +56,42 @@ body {
56
56
  padding: 10px 10px 10px 40px;
57
57
  }
58
58
 
59
+ #modal_content div.yesnobox {
60
+ position: absolute;
61
+ top: 0;
62
+ left: 0;
63
+ z-index: 22;
64
+ background-color: rgba(0, 0, 0, 0.9);
65
+ font-size: 20px;
66
+ text-align: center;
67
+ }
68
+
69
+ #modal_content div.yesnobox div {
70
+ padding: 20px;
71
+ }
72
+
73
+ #modal_content div.yesnobox input {
74
+ width: auto;
75
+ }
76
+
59
77
  div.model_attribute_text {
60
78
  color: #000;
61
79
  }
62
80
 
63
- input {
81
+ #modal_content div.top_right_controls {
82
+ position: absolute;
83
+ top: 0;
84
+ right: 0;
85
+ }
86
+
87
+ #modal_content div.top_right_controls a {
88
+ display: inline-block;
89
+ margin: 0;
90
+ padding: 4px 8px;
91
+ }
92
+
93
+ input, select, textarea {
94
+ font-family: Helvetica, arial;
64
95
  border: 1px solid #ccc;
65
96
  -moz-border-radius: 2px;
66
97
  -webkit-border-radius: 2px;
@@ -71,6 +102,33 @@ input {
71
102
  z-index: 20;
72
103
  }
73
104
 
105
+ input.dirty,
106
+ textarea.dirty {
107
+ background: #fff799;
108
+ }
109
+
110
+ select {
111
+ padding: 0;
112
+ height: 44px;
113
+ }
114
+
115
+ option {
116
+
117
+ }
118
+
119
+ select.fake {
120
+ background: transparent;
121
+ position: absolute;
122
+ top: 0;
123
+ left: 0;
124
+ color: transparent;
125
+ }
126
+
127
+ select.fake option {
128
+ background: #fff;
129
+ color: #000;
130
+ }
131
+
74
132
  #modal_content .bound_input_check a,
75
133
  #modal_content .bound_input_cancel a {
76
134
  border: 1px solid #ccc;
@@ -96,6 +154,53 @@ input {
96
154
  color: rgba(255, 255, 255, 0.0);
97
155
  }
98
156
 
157
+ #modal_content .bound_textarea_controls a {
158
+ display: inline-block;
159
+ margin: 3px 0 0 1px;
160
+ padding: 8px 0 6px 0;
161
+ background: #ccc;
162
+ color: #000;
163
+ text-align: center;
164
+ border: #666 1px solid;
165
+ }
166
+
167
+ #modal_content .bound_textarea_controls a.save { width: 60px; }
168
+ #modal_content .bound_textarea_controls a.discard { width: 80px; }
169
+
170
+ #modal_content .note,
171
+ #modal_content .error {
172
+ margin: 0;
173
+ }
174
+
175
+ #modal_content .placeholder {
176
+ display: block;
177
+ margin: 0;
178
+ padding: 0 5px 0 0;
179
+ height: 34px;
180
+
181
+ position: absolute;
182
+ top: 12px;
183
+ left: 10px;
184
+ z-index: 19;
185
+ }
186
+
187
+ #modal_content .placeholder span {
188
+ display: block;
189
+ font-size: 20px;
190
+ color: #757575;
191
+ }
192
+
193
+ #modal_content input[type='checkbox'] {
194
+ position: absolute;
195
+ top: 4px;
196
+ left: 0;
197
+ z-index: 19;
198
+
199
+ margin: 0;
200
+ padding: 0;
201
+ width: 20px;
202
+ }
203
+
99
204
  #modal_content .search_form {
100
205
  position: absolute;
101
206
  top: 0;