modeljs 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,121 @@
1
+
2
+ Model.Attribute.Select = Model.Attribute.extend({
3
+
4
+ text: '',
5
+ multiple: false,
6
+
7
+ needs_options: function()
8
+ {
9
+ return true;
10
+ },
11
+
12
+ view: function()
13
+ {
14
+ var html = this.text && this.text.length > 0 ? this.text : this.empty_text;
15
+ //return $('<a/>')
16
+ // .attr('title', 'Click to edit')
17
+ // .attr('href', 'javascript:{};')
18
+ // .addClass('model_attribute_select')
19
+ // .html(html);
20
+ return $('<div/>')
21
+ .html(html);
22
+ },
23
+
24
+ form: function()
25
+ {
26
+ var form = $('<form/>')
27
+ var select = $('<select></select>')
28
+ .attr('id', this.base)
29
+ .attr('name', this.base);
30
+
31
+ if (this.multiple)
32
+ {
33
+ select.attr('multiple', 'multiple');
34
+ select.attr('size', '9');
35
+ }
36
+
37
+ var this2 = this;
38
+ var options = Model.get_attribute_options(this.model.name, this.name);
39
+ $.each(options, function(i, option)
40
+ {
41
+ if (option.children != null) // Option group
42
+ {
43
+ var optgroup = $('<optgroup></optgroup>');
44
+ optgroup.attr('label', option.title);
45
+
46
+ $.each(option.children, function(j, option2)
47
+ {
48
+ var opt = $('<option></option>')
49
+ .attr('text', option2.text)
50
+ .val(option2.value);
51
+
52
+ if (!this2.multiple)
53
+ opt.attr('selected', (option2.value == this2.value));
54
+ else
55
+ {
56
+ opt.attr('selected', false);
57
+ for (var k=0; k<this2.value.length; k++)
58
+ if (option2.value == this2.value[k])
59
+ {
60
+ opt.attr('selected', true);
61
+ break;
62
+ }
63
+ }
64
+ optgroup.append(opt);
65
+ });
66
+
67
+ select.append(optgroup);
68
+ }
69
+ else
70
+ {
71
+ var opt = $('<option></option>')
72
+ .val(option.value)
73
+ .text(option.text);
74
+
75
+ if (!this2.multiple)
76
+ {
77
+ opt.attr('selected', (option.value == this2.value));
78
+ }
79
+ else
80
+ {
81
+ opt.attr('selected', false);
82
+ for (var j=0; j<this2.value.length; j++)
83
+ if (option.value == this2.value[j])
84
+ {
85
+ opt.attr('selected', true);
86
+ break;
87
+ }
88
+ }
89
+ select.append(opt);
90
+ }
91
+ });
92
+ form.append(select);
93
+ if (this.multiple)
94
+ {
95
+ form.append("<br/>");
96
+ form.append(
97
+ $('<input/>')
98
+ .attr('type', 'button')
99
+ .val('Clear')
100
+ .addClass('update_btn')
101
+ .click(function() { $('#' + this.base).selectedIndex = -1; })
102
+ );
103
+ }
104
+
105
+ return form;
106
+ },
107
+
108
+ form_value: function()
109
+ {
110
+ if (!this.multiple)
111
+ return $('#' + this.base + ' option:selected').val();
112
+
113
+ var select = $('#' + this.base);
114
+ val = [];
115
+ $('#' + this.base + ' option:selected').each(function(i, opt) {
116
+ val[val.length] = $(opt).val();
117
+ });
118
+ return val;
119
+ }
120
+
121
+ });
@@ -0,0 +1,42 @@
1
+
2
+ Model.Attribute.Textarea = Model.Attribute.extend({
3
+
4
+ show_controls: true,
5
+ normal_text: false,
6
+ width: 200,
7
+ height: 100,
8
+
9
+ view: function()
10
+ {
11
+ html = this.empty_text;
12
+ if (this.text && this.text.length > 0) html = this.text;
13
+ else if (this.value && this.value.length > 0) html = this.value;
14
+ //return $('<a/>')
15
+ // .attr('title', 'Click to edit')
16
+ // .attr('href', 'javascript:{};')
17
+ // .css({
18
+ // display: 'block',
19
+ // width: this.width
20
+ // })
21
+ // .html(html);
22
+ return $('<div/>')
23
+ .css({
24
+ display: 'block',
25
+ width: this.width
26
+ })
27
+ .html(html);
28
+ },
29
+
30
+ form: function()
31
+ {
32
+ return $('<form/>')
33
+ .append($('<textarea/>')
34
+ .attr('id', this.base)
35
+ .attr('name', this.base)
36
+ .val(this.value)
37
+ .css('width', this.width)
38
+ .css('height', this.height)
39
+ );
40
+ }
41
+
42
+ });
@@ -0,0 +1,63 @@
1
+
2
+ Model.Attribute.Text = Model.Attribute.extend({
3
+
4
+ show_reminder: true,
5
+
6
+ view: function()
7
+ {
8
+ var html = false;
9
+ if (typeof(this.value) == 'string')
10
+ html = this.value && this.value.length > 0 ? this.value : this.empty_text;
11
+ else if (typeof(this.value) == 'number')
12
+ html = this.value != null ? this.value : this.empty_text;
13
+ else
14
+ html = this.value;
15
+
16
+ //return $('<a/>')
17
+ // .attr('title', 'Click to edit')
18
+ // .attr('href', 'javascript:{};')
19
+ // .html(html);
20
+ return $('<div/>')
21
+ .html(html);
22
+ },
23
+
24
+ form: function()
25
+ {
26
+ var this2 = this;
27
+ var input = $('<input/>')
28
+ .attr('type', 'text')
29
+ .attr('id', this.base)
30
+ .attr('name', this.base)
31
+ .val(this.value);
32
+ if (this.show_reminder)
33
+ input.keyup(function (){ this2.flash_reminder(); })
34
+ if (this.css)
35
+ input.css(this.css);
36
+ var form = $('<form/>').append(input);
37
+ if (this.show_reminder)
38
+ form.append($('<span/>').attr('id', this.base + '_reminder').css('font-size', '75%'));
39
+
40
+ return form;
41
+ },
42
+
43
+ flash_reminder: function()
44
+ {
45
+ $('#' + this.base + '_reminder')
46
+ .html(' (Enter to update, Escape to cancel)')
47
+ .delay(1000)
48
+ .fadeOut({ done: function() { $(this).empty().show(); }});
49
+ },
50
+
51
+ post_form_display: function()
52
+ {
53
+ // Put the focus at the end of the string
54
+ $('#' + this.base).focus();
55
+ val = $('#' + this.base).val();
56
+ $('#' + this.base).val('');
57
+ $('#' + this.base).val(val);
58
+
59
+ if (this.show_reminder)
60
+ this.flash_reminder();
61
+ }
62
+
63
+ });
@@ -0,0 +1,31 @@
1
+
2
+ Model.Attribute.Time = Model.Attribute.extend({
3
+
4
+ view: function()
5
+ {
6
+ var html = this.value && this.value.length > 0 ? this.value : this.empty_text;
7
+ return $('<a/>')
8
+ .attr('title', 'Click to edit')
9
+ .attr('href', 'javascript:{};')
10
+ .addClass('model_attribute_text')
11
+ .html(html);
12
+ },
13
+
14
+ form: function()
15
+ {
16
+ return $('<form/>')
17
+ .append($('<input/>')
18
+ .attr('type', 'text')
19
+ .attr('id', this.base)
20
+ .attr('name', this.base)
21
+ .val(this.value)
22
+ .addClass('text_box')
23
+ );
24
+ },
25
+
26
+ post_form_display: function()
27
+ {
28
+ $('#' + this.base).timepicker({ ampm: true });
29
+ }
30
+
31
+ });
@@ -0,0 +1,95 @@
1
+
2
+ Model.Attribute.Video = Model.Attribute.extend({
3
+
4
+ show_reminder: true,
5
+ thumb_width: 320,
6
+ thumb_height: 240,
7
+
8
+ view: function()
9
+ {
10
+ var html = false;
11
+ if (typeof(this.value) == 'string')
12
+ html = this.value && this.value.length > 0 ? this.value : this.empty_text;
13
+ else if (typeof(this.value) == 'number')
14
+ html = this.value != null ? this.value : this.empty_text;
15
+ else
16
+ html = this.value;
17
+ var div = $('<div/>').html(html);
18
+ var iframe = this.video_iframe(this.value);
19
+ if (iframe)
20
+ div.append($('<br/>')).append(iframe);
21
+ return div;
22
+ },
23
+
24
+ form: function()
25
+ {
26
+ var this2 = this;
27
+ var input = $('<input/>')
28
+ .attr('type', 'text')
29
+ .attr('id', this.base)
30
+ .attr('name', this.base)
31
+ .val(this.value);
32
+ if (this.show_reminder)
33
+ input.keyup(function (){ this2.flash_reminder(); })
34
+ if (this.css)
35
+ input.css(this.css);
36
+ var form = $('<form/>').append(input);
37
+ if (this.show_reminder)
38
+ form.append($('<span/>').attr('id', this.base + '_reminder').css('font-size', '75%'));
39
+
40
+ var iframe = this.video_iframe(this.value);
41
+ if (iframe)
42
+ form.append($('<br/>')).append(iframe);
43
+
44
+ return form;
45
+ },
46
+
47
+ flash_reminder: function()
48
+ {
49
+ $('#' + this.base + '_reminder')
50
+ .html(' (Enter to update, Escape to cancel)')
51
+ .delay(1000)
52
+ .fadeOut({ done: function() { $(this).empty().show(); }});
53
+ },
54
+
55
+ post_form_display: function()
56
+ {
57
+ // Put the focus at the end of the string
58
+ $('#' + this.base).focus();
59
+ val = $('#' + this.base).val();
60
+ $('#' + this.base).val('');
61
+ $('#' + this.base).val(val);
62
+
63
+ if (this.show_reminder)
64
+ this.flash_reminder();
65
+ },
66
+
67
+ video_iframe: function()
68
+ {
69
+ if (this.value == null || this.value.length == 0)
70
+ return false;
71
+
72
+ if (this.value.match(/youtube\.com/))
73
+ {
74
+ var v = this.value.replace("youtube.com/watch?v=", '');
75
+ return $('<iframe/>')
76
+ .attr('type', 'text/html')
77
+ .attr('width', this.thumb_width)
78
+ .attr('height', this.thumb_height)
79
+ .attr('frameborder', '0')
80
+ .attr('src', "http://www.youtube.com/embed/" + v + "?autoplay=0");
81
+ }
82
+ if (this.value.match(/vimeo\.com/))
83
+ {
84
+ var v = this.value.replace("vimeo.com/", '');
85
+ return $('<iframe/>')
86
+ .attr('type', 'text/html')
87
+ .attr('width', this.thumb_width)
88
+ .attr('height', this.thumb_height)
89
+ .attr('frameborder', '0')
90
+ .attr('src', "http://player.vimeo.com/video/" + v);
91
+ }
92
+ return false;
93
+ }
94
+
95
+ });
@@ -0,0 +1,69 @@
1
+
2
+ Model.Attribute = Class.extend({
3
+ name: '',
4
+ nice_name: '',
5
+ value: '',
6
+ model: false,
7
+ base: '', // model.name + '_' + model.id + '_' + attribute.name
8
+ type: false,
9
+ container: false,
10
+ controls: false,
11
+ show_controls: false,
12
+ message: false,
13
+ empty_text: '',
14
+ update_url: false, // If this attribute has a special update URL
15
+ custom_form: false, // If the attribute needs a custom form (used in file and image)
16
+ css: false,
17
+
18
+ init: function(params)
19
+ {
20
+ for (var thing in params)
21
+ this[thing] = params[thing];
22
+ this.set_base(this.base);
23
+ if (!this.nice_name) this.nice_name = this.name.replace('_', ' ').capitalize();
24
+ },
25
+
26
+ set_base: function(base)
27
+ {
28
+ this.base = base;
29
+ if (!this.container) this.container = this.base + '_container';
30
+ if (!this.controls) this.controls = this.base + '_controls';
31
+ if (!this.message) this.message = this.base + '_message';
32
+ },
33
+
34
+ // Whether or not the attribute needs to go get options via ajax
35
+ needs_options: function() { return false; },
36
+
37
+ // Returns the viewable representation of the attribute.
38
+ view: function() { return false; },
39
+
40
+ // Returns a form without update or cancel buttons (Assumes options already populated)
41
+ form: function() { return false; },
42
+
43
+ // Returns the current value in the visible form.
44
+ form_value: function() { return $('#' + this.base).val(); },
45
+
46
+ // Returns the values form the form that will be sent to ajax_update
47
+ form_values: function() {
48
+ var vals = {};
49
+ vals[this.name] = this.form_value();
50
+ return vals;
51
+ },
52
+
53
+ // Runs after the view or form is displayed on the screen.
54
+ post_view_display: function() {},
55
+ post_form_display: function() { $('#' + this.base).focus(); },
56
+
57
+ // Notes
58
+ loading: function(str) { this.show_message('loading_small', str); },
59
+ note: function(str) { this.show_message('note_small' , str); },
60
+ success: function(str) { this.show_message('note_small success' , str); },
61
+ error: function(str) { this.show_message('note_small error' , str); },
62
+ show_message: function(class_names, str) {
63
+ $('#' + this.message).empty().removeClass('loading_small');
64
+ $('#' + this.message).addClass(class_names).html(str);
65
+ },
66
+
67
+ // If the attribute needs to handle any finished uploads
68
+ upload_finished: function(e, data) {}
69
+ });
@@ -0,0 +1,13 @@
1
+ /*
2
+ Class: Model.Form
3
+ Author: William Barry (william@nine.is)
4
+ Description: Abstract form class to encapsulate add and edit forms for models.
5
+ */
6
+
7
+ Model.Form.Embedded = Class.extend({
8
+
9
+ add: function() { return false; },
10
+ edit: function() { return false; },
11
+ listing: function() { return false; },
12
+
13
+ });
@@ -0,0 +1,257 @@
1
+ /*
2
+ Class: Model.Form
3
+ Author: William Barry (william@nine.is)
4
+ Description: Abstract form class to encapsulate add and edit forms for models.
5
+ */
6
+
7
+ Model.Form = Class.extend({
8
+
9
+ class_name: 'Model.Form',
10
+ model: false,
11
+ message: false,
12
+ adding_message: null,
13
+ deleting_message: null,
14
+ delete_message: null,
15
+
16
+ // Listing info
17
+ listing_models: false,
18
+
19
+ init: function(model, params)
20
+ {
21
+ this.model = model;
22
+ if (params)
23
+ for (var thing in params)
24
+ this[thing] = params[thing];
25
+
26
+ if (!this.message ) this.message = model.name + '_message';
27
+ if (!this.adding_message ) this.adding_message = 'Adding ' + this.model.name + '...';
28
+ if (!this.deleting_message) this.deleting_message = 'Deleting ' + this.model.name + '...';
29
+ if (!this.delete_message ) this.delete_message = "Are you sure you want to delete the " + this.model.name + "? This can't be undone.";
30
+ },
31
+
32
+ // Returns the form for adding the model or false for an embedded form.
33
+ add: function()
34
+ {
35
+ var m = this.model;
36
+ var tbody = $('<tbody/>');
37
+ $(m.attributes).each(function(i, a) {
38
+ if (a.type == 'hidden')
39
+ return;
40
+ tbody.append($('<tr/>')
41
+ .append($('<td/>').css('vertical-align', 'top').html(a.nice_name + ':'))
42
+ .append($('<td/>').css('vertical-align', 'top').attr('id', m.name + '_' + m.id + '_' + a.name + '_container')
43
+ .append(a.form().children())
44
+ )
45
+ );
46
+ });
47
+ var form = $('<form/>')
48
+ .attr('id', 'new_' + m.name + '_form')
49
+ .submit(function() { return false; })
50
+ .append($('<table/>').append(tbody))
51
+ .append($('<div/>').attr('id', this.message_div))
52
+ .append($('<p/>')
53
+ .append($('<input/>').attr('type', 'submit').val('Add ' + m.name).click(function() { m.ajax_add(); }))
54
+ .append($('<input/>').attr('type', 'button').val('Back').click(function() { window.location = m.listing_url; }))
55
+ );
56
+ return form;
57
+ },
58
+
59
+ // Runs after the add form has been displayed
60
+ post_add_display: function() {},
61
+
62
+ // Returns the form for editing a model or false for an embedded form.
63
+ edit: function()
64
+ {
65
+ var m = this.model;
66
+
67
+ var this2 = this;
68
+ var tbody = $('<tbody/>');
69
+ $(m.attributes).each(function(i, a) {
70
+ if (a.type == 'hidden')
71
+ return;
72
+ tbody.append($('<tr/>')
73
+ .append($('<td/>').css('vertical-align', 'top').html(a.nice_name + ':'))
74
+ .append($('<td/>').css('vertical-align', 'top').attr('id', m.name + '_' + m.id + '_' + a.name + '_container'))
75
+ );
76
+ });
77
+
78
+ var div = $('<div/>')
79
+ .append($('<table/>').append(tbody))
80
+ .append($('<div/>').attr('id', this.message))
81
+ .append($('<p/>')
82
+ .append($('<input/>').attr('type', 'button').val('Back').click(function() { window.location = m.listing_url; }))
83
+ .append(' ')
84
+ .append($('<input/>').attr('type', 'button').val('Delete ' + m.name).click(function() { m.ajax_delete(); }))
85
+ );
86
+ return div;
87
+ },
88
+
89
+ // Runs after the edit form has been displayed
90
+ post_edit_display: function() {},
91
+
92
+ listing_view: function()
93
+ {
94
+ var tr = $('<tr/>');
95
+ var m = this.model;
96
+ $(this.model.attributes).each(function(i, a) {
97
+ if (a.type == 'hidden') return;
98
+ tr.append(
99
+ $('<td/>')
100
+ .attr('id', m.name + '_' + m.id + '_' + a.name + '_container')
101
+ .append(a.view())
102
+ );
103
+ });
104
+ return tr;
105
+ },
106
+
107
+ post_listing_view_display: function() {},
108
+
109
+ // Returns a listing table of models or false for an embedded listing
110
+ listing_form: function()
111
+ {
112
+ var m = this.model;
113
+ var count = m.unhidden_attribute_count();
114
+ var tr = $('<tr/>').attr('id', m.name + '_' + m.id + '_listing_container');
115
+ $(this.model.attributes).each(function(i, a) {
116
+ if (a.type == 'hidden')
117
+ return;
118
+ var c = '';
119
+ if (i == 0) c = 'edit_listing_first_cell';
120
+ else if (i == count-1) c = 'edit_listing_last_cell';
121
+ else c = 'edit_listing_cell';
122
+
123
+ tr.append($('<td/>')
124
+ .css('vertical-align', 'top')
125
+ .addClass(c)
126
+ .attr('id', m.name + '_' + m.id + '_' + a.name + '_container')
127
+ );
128
+ });
129
+ return tr;
130
+ },
131
+
132
+ // Runs after the listing has been displayed
133
+ post_listing_form_display: function() {},
134
+
135
+ listing_view: function()
136
+ {
137
+ var m = this.model;
138
+ var tr = $('<tr/>').attr('id', m.name + '_' + m.id + '_listing_container');
139
+ $(this.model.attributes).each(function(i, a) {
140
+ if (a.type == 'hidden')
141
+ return;
142
+ tr.append($('<td/>').css('vertical-align', 'top').attr('id', m.name + '_' + m.id + '_' + a.name + '_container'));
143
+ });
144
+ return tr;
145
+ },
146
+
147
+ post_listing_view_display: function() {},
148
+
149
+ // Gets the latest rows for the listing
150
+ refresh_listing_models: function(done)
151
+ {
152
+ var this2 = this;
153
+ var obj = Model.parse_url(this.model.listing_url);
154
+ $.ajax({
155
+ url: obj.url,
156
+ type: obj.verb,
157
+ success: function(models) {
158
+ this2.refresh_listing_models_helper(models);
159
+ done();
160
+ },
161
+ error: function() {
162
+ this2.ajax_error('Error');
163
+ }
164
+ });
165
+ },
166
+
167
+ refresh_listing_models_helper: function(models)
168
+ {
169
+ var this2 = this;
170
+ this.listing_models = [];
171
+ $(models).each(function(i, m) {
172
+
173
+ var attribs = [];
174
+ $(m.attributes).each(function(i, a) {
175
+
176
+ // Find the original attribute with the same name
177
+ var attrib = false;
178
+ $(this2.model.attributes_original).each(function(j, a2) {
179
+ if (a2.name == a.name)
180
+ {
181
+ attrib = $.extend(true, {}, a2);
182
+ return false;
183
+ }
184
+ });
185
+
186
+ // Copy new data into the original attribute hash
187
+ for (var thing in a)
188
+ attrib[thing] = a[thing];
189
+ attrib.base = this2.model.name + '_' + m.id + '_' + attrib.name;
190
+
191
+ attribs.push(attrib);
192
+ });
193
+ var params = {
194
+ name: this2.model.name,
195
+ id: m.id,
196
+ form: this2.class_name,
197
+ attributes: attribs,
198
+ create_url: this2.model.create_url,
199
+ update_url: this2.model.update_url,
200
+ delete_url: this2.model.delete_url,
201
+ listing_url: this2.model.listing_url,
202
+ listing_edit: this2.model.listing_edit,
203
+ listing_delete: this2.model.listing_delete,
204
+ listing_empty_text: this2.model.listing_empty_text,
205
+ is_listing: true
206
+ };
207
+ var m2 = new Model(params);
208
+ this2.listing_models.push(m2);
209
+ });
210
+ },
211
+
212
+ // Returns the values of the add form that will be sent to model.ajax_add
213
+ add_values: function()
214
+ {
215
+ var m = this.model;
216
+ var data = {};
217
+ $(m.attributes).each(function(i, a) {
218
+ data[a.name] = a.form_value();
219
+ });
220
+ return data;
221
+ },
222
+
223
+ // Show a confirmation screen
224
+ confirm: function(params)
225
+ {
226
+ var this2 = this;
227
+ $('#' + this.message).empty()
228
+ .append($('<div/>')
229
+ .addClass('note2 error')
230
+ .append($('<p/>').append(params.message))
231
+ .append($('<p/>')
232
+ .append($('<input />').attr('type', 'button').val('Yes').click(params.yes))
233
+ .append(" ")
234
+ .append($('<input />').attr('type', 'button').val('No').click(function() { $('#' + this2.message).empty(); }))
235
+ )
236
+ );
237
+ },
238
+
239
+ ajax_success: function(resp)
240
+ {
241
+ if (resp.text) $('#' + this.message).empty().append($('<p/>').addClass('note' ).html(resp.text));
242
+ else if (resp.success) $('#' + this.message).empty().append($('<p/>').addClass('note success').html(resp.success));
243
+ else if (resp.error) $('#' + this.message).empty().append($('<p/>').addClass('note error' ).html(resp.error));
244
+ else if (resp.flash) $('#' + this.message).empty().append($('<p/>').html(resp.flash).delay(2000).fadeOut().delay(2000).queue(function() { $(this).remove(); }));
245
+ else if (resp.redirect) window.location = resp.redirect;
246
+ else $('#' + this.message).empty();
247
+ },
248
+
249
+ ajax_error: function(str) { this.error(str); },
250
+
251
+ // Notes
252
+ loading: function(str) { this.show_message('loading' , str); },
253
+ note: function(str) { this.show_message('note' , str); },
254
+ success: function(str) { this.show_message('note success' , str); },
255
+ error: function(str) { this.show_message('note error' , str); },
256
+ show_message: function(class_names, str) { $('#' + this.message).empty().append($('<p/>').addClass(class_names).html(str)); }
257
+ });