rails-backbone-forms 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,405 @@
1
+ ;(function() {
2
+
3
+ var Form = Backbone.Form,
4
+ Base = Form.editors.Base,
5
+ createTemplate = Form.helpers.createTemplate,
6
+ triggerCancellableEvent = Form.helpers.triggerCancellableEvent,
7
+ exports = {};
8
+
9
+ /**
10
+ * Additional editors that depend on jQuery UI
11
+ */
12
+ exports.Date = Base.extend({
13
+
14
+ className: 'bbf-date',
15
+
16
+ initialize: function(options) {
17
+ Base.prototype.initialize.call(this, options);
18
+
19
+ if (!this.value) {
20
+ var date = new Date();
21
+ date.setSeconds(0);
22
+ date.setMilliseconds(0);
23
+
24
+ this.value = date;
25
+ }
26
+ },
27
+
28
+ render: function() {
29
+ var el = $(this.el);
30
+
31
+ el.html('<input>');
32
+
33
+ var input = $('input', el);
34
+
35
+ input.datepicker({
36
+ dateFormat: 'dd/mm/yy',
37
+ showButtonPanel: true
38
+ });
39
+
40
+ //Make sure setValue of this object is called, not of any objects extending it (e.g. DateTime)
41
+ exports.Date.prototype.setValue.call(this, this.value);
42
+
43
+ return this;
44
+ },
45
+
46
+ /**
47
+ * @return {Date} Selected date
48
+ */
49
+ getValue: function() {
50
+ var input = $('input', this.el),
51
+ date = input.datepicker('getDate');
52
+
53
+ return date;
54
+ },
55
+
56
+ setValue: function(value) {
57
+ $('input', this.el).datepicker('setDate', value);
58
+ }
59
+
60
+ });
61
+
62
+
63
+
64
+ exports.DateTime = exports.Date.extend({
65
+
66
+ className: 'bbf-datetime',
67
+
68
+ template: createTemplate('<select>{{hours}}</select> : <select>{{mins}}</select>'),
69
+
70
+ render: function() {
71
+ function pad(n) {
72
+ return n < 10 ? '0' + n : n
73
+ }
74
+
75
+ //Render the date element first
76
+ exports.Date.prototype.render.call(this);
77
+
78
+ //Setup hour options
79
+ var hours = _.range(0, 24),
80
+ hoursOptions = [];
81
+
82
+ _.each(hours, function(hour) {
83
+ hoursOptions.push('<option value="'+hour+'">' + pad(hour) + '</option>');
84
+ });
85
+
86
+ //Setup minute options
87
+ var minsInterval = this.schema.minsInterval || 15,
88
+ mins = _.range(0, 60, minsInterval),
89
+ minsOptions = [];
90
+
91
+ _.each(mins, function(min) {
92
+ minsOptions.push('<option value="'+min+'">' + pad(min) + '</option>');
93
+ });
94
+
95
+ //Render time selects
96
+ $(this.el).append(this.template({
97
+ hours: hoursOptions.join(),
98
+ mins: minsOptions.join()
99
+ }));
100
+
101
+ //Store references to selects
102
+ this.$hours = $('select:eq(0)', this.el);
103
+ this.$mins = $('select:eq(1)', this.el);
104
+
105
+ //Set time
106
+ this.setValue(this.value);
107
+
108
+ return this;
109
+ },
110
+
111
+ /**
112
+ * @return {Date} Selected datetime
113
+ */
114
+ getValue: function() {
115
+ var input = $('input', this.el),
116
+ date = input.datepicker('getDate');
117
+
118
+ date.setHours(this.$hours.val());
119
+ date.setMinutes(this.$mins.val());
120
+ date.setMilliseconds(0);
121
+
122
+ return date;
123
+ },
124
+
125
+ setValue: function(date) {
126
+ exports.Date.prototype.setValue.call(this, date);
127
+
128
+ this.$hours.val(date.getHours());
129
+ this.$mins.val(date.getMinutes());
130
+ }
131
+
132
+ });
133
+
134
+
135
+ exports.List = Base.extend({
136
+
137
+ className: 'bbf-list',
138
+
139
+ //Note: The extra div around the <ul> is used to limit the drag area
140
+ template: createTemplate('\
141
+ <ul></ul>\
142
+ <div><button class="bbf-list-add">Add</div>\
143
+ '),
144
+
145
+ itemTemplate: createTemplate('\
146
+ <li rel="{{id}}">\
147
+ <span class="bbf-list-text">{{text}}</span>\
148
+ <div class="bbf-list-actions">\
149
+ <button class="bbf-list-edit">Edit</button>\
150
+ <button class="bbf-list-del">Delete</button>\
151
+ </div>\
152
+ </li>\
153
+ '),
154
+
155
+ editorTemplate: createTemplate('\
156
+ <div class="bbf-field">\
157
+ <div class="bbf-list-editor"></div>\
158
+ </div>\
159
+ '),
160
+
161
+ events: {
162
+ 'click .bbf-list-add': 'addNewItem',
163
+ 'click .bbf-list-edit': 'editItem',
164
+ 'click .bbf-list-del': 'deleteItem'
165
+ },
166
+
167
+ initialize: function(options) {
168
+ Base.prototype.initialize.call(this, options);
169
+
170
+ if (!this.schema) throw "Missing required option 'schema'";
171
+
172
+ this.schema.listType = this.schema.listType || 'Text';
173
+
174
+ if (this.schema.listType == 'NestedModel' && !this.schema.model)
175
+ throw "Missing required option 'schema.model'";
176
+ },
177
+
178
+ render: function() {
179
+ var el = $(this.el);
180
+
181
+ //Main element
182
+ el.html(this.template());
183
+
184
+ //Create list
185
+ var self = this,
186
+ el = $(this.el),
187
+ data = this.value || [],
188
+ schema = this.schema,
189
+ itemToString = this.itemToString,
190
+ itemTemplate = this.itemTemplate,
191
+ listEl = $('ul', el);
192
+
193
+ _.each(data, function(itemData) {
194
+ var text = itemToString.call(self, itemData);
195
+
196
+ //Create DOM element
197
+ var li = $(itemTemplate({
198
+ id: itemData.id || '',
199
+ text: text
200
+ }));
201
+
202
+ //Attach data
203
+ $.data(li[0], 'data', itemData);
204
+
205
+ listEl.append(li);
206
+ });
207
+
208
+ //Make sortable
209
+ if (schema.sortable !== false) {
210
+ listEl.sortable({
211
+ axis: 'y',
212
+ cursor: 'move',
213
+ containment: 'parent'
214
+ });
215
+
216
+ el.addClass('bbf-list-sortable');
217
+ }
218
+
219
+ //jQuery UI buttonize
220
+ $('button.bbf-list-add', el).button({
221
+ text: false,
222
+ icons: { primary: 'ui-icon-plus' }
223
+ });
224
+ $('button.bbf-list-edit', el).button({
225
+ text: false,
226
+ icons: { primary: 'ui-icon-pencil' }
227
+ });
228
+ $('button.bbf-list-del', el).button({
229
+ text: false,
230
+ icons: { primary: 'ui-icon-trash' }
231
+ });
232
+
233
+ return this;
234
+ },
235
+
236
+ /**
237
+ * Formats an item for display in the list
238
+ * For example objects, dates etc. can have a custom
239
+ * itemToString method which says how it should be formatted.
240
+ */
241
+ itemToString: function(data) {
242
+ if (!data) return data;
243
+
244
+ var schema = this.schema;
245
+
246
+ //If there's a specified toString use that
247
+ if (schema.itemToString)
248
+ return schema.itemToString(data);
249
+
250
+ //Otherwise check if it's NestedModel with it's own toString() method
251
+ if (this.schema.listType == 'NestedModel') {
252
+ var model = new (this.schema.model)(data);
253
+
254
+ return model.toString();
255
+ }
256
+
257
+ //Last resort, just return the data as is
258
+ return data;
259
+ },
260
+
261
+ /**
262
+ * Add a new item to the list if it is completed in the editor
263
+ */
264
+ addNewItem: function(event) {
265
+ event.preventDefault();
266
+
267
+ var self = this;
268
+
269
+ this.openEditor(null, function(value) {
270
+ //Fire 'addItem' cancellable event
271
+ triggerCancellableEvent(self, 'addItem', [value], function() {
272
+ var text = self.itemToString(value);
273
+
274
+ //Create DOM element
275
+ var li = $(self.itemTemplate({
276
+ id: value.id || '',
277
+ text: text
278
+ }));
279
+
280
+ //Store data
281
+ $.data(li[0], 'data', value);
282
+
283
+ $('ul', self.el).append(li);
284
+
285
+ //jQuery UI buttonize
286
+ $('button.bbf-list-edit', this.el).button({
287
+ text: false,
288
+ icons: { primary: 'ui-icon-pencil' }
289
+ });
290
+ $('button.bbf-list-del', this.el).button({
291
+ text: false,
292
+ icons: { primary: 'ui-icon-trash' }
293
+ });
294
+ });
295
+ });
296
+ },
297
+
298
+ /**
299
+ * Edit an existing item in the list
300
+ */
301
+ editItem: function(event) {
302
+ event.preventDefault();
303
+
304
+ var self = this,
305
+ li = $(event.target).closest('li'),
306
+ originalValue = $.data(li[0], 'data');
307
+
308
+ this.openEditor(originalValue, function(newValue) {
309
+ //Fire 'editItem' cancellable event
310
+ triggerCancellableEvent(self, 'editItem', [newValue], function() {
311
+ //Update display
312
+ $('.bbf-list-text', li).html(self.itemToString(newValue));
313
+
314
+ //Store data
315
+ $.data(li[0], 'data', newValue);
316
+ });
317
+ });
318
+ },
319
+
320
+ deleteItem: function(event) {
321
+ event.preventDefault();
322
+
323
+ var self = this,
324
+ li = $(event.target).closest('li'),
325
+ data = $.data(li[0], 'data');
326
+
327
+ var confirmDelete = (this.schema.confirmDelete) ? this.schema.confirmDelete : false,
328
+ confirmMsg = this.schema.confirmDeleteMsg || 'Are you sure?';
329
+
330
+ function remove() {
331
+ triggerCancellableEvent(self, 'removeItem', [data], function() {
332
+ li.remove();
333
+ });
334
+ }
335
+
336
+ if (this.schema.confirmDelete) {
337
+ if (confirm(confirmMsg)) remove();
338
+ } else {
339
+ remove();
340
+ }
341
+ },
342
+
343
+ /**
344
+ * Opens the sub editor dialog
345
+ * @param {Mixed} Data (if editing existing list item, null otherwise)
346
+ * @param {Function} Save callback. receives: value
347
+ */
348
+ openEditor: function(data, callback) {
349
+ var self = this,
350
+ schema = this.schema,
351
+ listType = schema.listType || 'Text';
352
+
353
+ var editor = Form.helpers.createEditor(listType, {
354
+ key: '',
355
+ schema: schema,
356
+ value: data
357
+ }).render();
358
+
359
+ var container = $(this.editorTemplate());
360
+ $('.bbf-list-editor', container).html(editor.el);
361
+
362
+ var close = function() {
363
+ container.dialog('close');
364
+
365
+ editor.remove();
366
+ container.remove();
367
+ };
368
+
369
+ $(container).dialog({
370
+ resizable: false,
371
+ modal: true,
372
+ width: 500,
373
+ title: data ? 'Edit item' : 'New item',
374
+ buttons: {
375
+ 'OK': function() {
376
+ callback(editor.getValue());
377
+ close();
378
+ },
379
+ 'Cancel': close
380
+ }
381
+ });
382
+ },
383
+
384
+ getValue: function() {
385
+ var data = [];
386
+
387
+ $('li', this.el).each(function(index, li) {
388
+ data.push($.data(li, 'data'));
389
+ });
390
+
391
+ return data;
392
+ },
393
+
394
+ setValue: function(value) {
395
+ this.value = value;
396
+ this.render();
397
+ }
398
+
399
+ });
400
+
401
+
402
+ //Exports
403
+ _.extend(Form.editors, exports);
404
+
405
+ })();
@@ -0,0 +1,92 @@
1
+ /**
2
+ * Include this file _after_ the main backbone-forms file to override the default templates.
3
+ * You only need to include templates you want to override.
4
+ *
5
+ * Requirements when customising templates:
6
+ * - Each template must have one 'parent' element tag.
7
+ * - "data-type" attributes are required.
8
+ * - The main placeholder tags such as the following are required: fieldsets, fields
9
+ */
10
+ ;(function() {
11
+ var Form = Backbone.Form;
12
+
13
+
14
+ //TWITTER BOOTSTRAP TEMPLATES
15
+ //Requires Bootstrap 2.x
16
+ Form.setTemplates({
17
+
18
+ //HTML
19
+ form: '\
20
+ <form class="form-horizontal">{{fieldsets}}</form>\
21
+ ',
22
+
23
+ fieldset: '\
24
+ <fieldset>\
25
+ <legend>{{legend}}</legend>\
26
+ {{fields}}\
27
+ </fieldset>\
28
+ ',
29
+
30
+ field: '\
31
+ <div class="control-group field-{{key}}">\
32
+ <label class="control-label" for="{{id}}">{{title}}</label>\
33
+ <div class="controls">\
34
+ <div class="input-xlarge">{{editor}}</div>\
35
+ <div class="help-block">{{help}}</div>\
36
+ </div>\
37
+ </div>\
38
+ ',
39
+
40
+ nestedField: '\
41
+ <div class="field-{{key}}">\
42
+ <div title="{{title}}" class="input-xlarge">{{editor}}</div>\
43
+ <div class="help-block">{{help}}</div>\
44
+ </div>\
45
+ ',
46
+
47
+ list: '\
48
+ <div class="bbf-list">\
49
+ <ul class="unstyled clearfix">{{items}}</ul>\
50
+ <button type="button" class="btn bbf-add" data-action="add">Add</div>\
51
+ </div>\
52
+ ',
53
+
54
+ listItem: '\
55
+ <li class="clearfix">\
56
+ <div class="pull-left">{{editor}}</div>\
57
+ <button type="button" class="btn bbf-del" data-action="remove">&times;</button>\
58
+ </li>\
59
+ ',
60
+
61
+ date: '\
62
+ <div class="bbf-date">\
63
+ <select data-type="date" class="bbf-date">{{dates}}</select>\
64
+ <select data-type="month" class="bbf-month">{{months}}</select>\
65
+ <select data-type="year" class="bbf-year">{{years}}</select>\
66
+ </div>\
67
+ ',
68
+
69
+ dateTime: '\
70
+ <div class="bbf-datetime">\
71
+ <p>{{date}}</p>\
72
+ <p>\
73
+ <select data-type="hour" style="width: 4em">{{hours}}</select>\
74
+ :\
75
+ <select data-type="min" style="width: 4em">{{mins}}</select>\
76
+ </p>\
77
+ </div>\
78
+ ',
79
+
80
+ 'list.Modal': '\
81
+ <div class="bbf-list-modal">\
82
+ {{summary}}\
83
+ </div>\
84
+ '
85
+ }, {
86
+
87
+ //CLASSNAMES
88
+ error: 'error' //Set on the field tag when validation fails
89
+ });
90
+
91
+
92
+ })();
@@ -0,0 +1,89 @@
1
+ /**
2
+ * Include this file _after_ the main backbone-forms file to override the default templates.
3
+ * You only need to include templates you want to override.
4
+ *
5
+ * Requirements when customising templates:
6
+ * - Each template must have one 'parent' element tag.
7
+ * - "data-type" attributes are required.
8
+ * - The main placeholder tags such as the following are required: fieldsets, fields
9
+ */
10
+ ;(function() {
11
+ var Form = Backbone.Form;
12
+
13
+
14
+ //DEFAULT TEMPLATES
15
+ Form.setTemplates({
16
+
17
+ //HTML
18
+ form: '\
19
+ <form class="bbf-form">{{fieldsets}}</form>\
20
+ ',
21
+
22
+ fieldset: '\
23
+ <fieldset>\
24
+ <legend>{{legend}}</legend>\
25
+ <ul>{{fields}}</ul>\
26
+ </fieldset>\
27
+ ',
28
+
29
+ field: '\
30
+ <li class="bbf-field field-{{key}}">\
31
+ <label for="{{id}}">{{title}}</label>\
32
+ <div class="bbf-editor">{{editor}}</div>\
33
+ <div class="bbf-help">{{help}}</div>\
34
+ </li>\
35
+ ',
36
+
37
+ nestedField: '\
38
+ <li class="bbf-field bbf-nested-field field-{{key}}" title="{{title}}">\
39
+ <label for="{{id}}">{{title}}</label>\
40
+ <div class="bbf-editor">{{editor}}</div>\
41
+ <div class="bbf-help">{{help}}</div>\
42
+ </li>\
43
+ ',
44
+
45
+ list: '\
46
+ <div class="bbf-list">\
47
+ <ul>{{items}}</ul>\
48
+ <div class="bbf-actions"><button type="button" data-action="add">Add</div>\
49
+ </div>\
50
+ ',
51
+
52
+ listItem: '\
53
+ <li>\
54
+ <button type="button" data-action="remove" class="bbf-remove">&times;</button>\
55
+ <div class="bbf-editor-container">{{editor}}</div>\
56
+ </li>\
57
+ ',
58
+
59
+ date: '\
60
+ <div class="bbf-date">\
61
+ <select data-type="date" class="bbf-date">{{dates}}</select>\
62
+ <select data-type="month" class="bbf-month">{{months}}</select>\
63
+ <select data-type="year" class="bbf-year">{{years}}</select>\
64
+ </div>\
65
+ ',
66
+
67
+ dateTime: '\
68
+ <div class="bbf-datetime">\
69
+ <div class="bbf-date-container">{{date}}</div>\
70
+ <select data-type="hour">{{hours}}</select>\
71
+ :\
72
+ <select data-type="min">{{mins}}</select>\
73
+ </div>\
74
+ ',
75
+
76
+ 'list.Modal': '\
77
+ <div class="bbf-list-modal">\
78
+ {{summary}}\
79
+ </div>\
80
+ '
81
+ }, {
82
+
83
+ //CLASSNAMES
84
+ error: 'bbf-error'
85
+
86
+ });
87
+
88
+
89
+ })();
@@ -0,0 +1,2 @@
1
+ //= require rails-backbone-forms/jquery-ui-editors.js
2
+ //= require rails-backbone-forms/backbone-forms.js
@@ -0,0 +1,65 @@
1
+ .bbf-jui-date input, .bbf-jui-datetime input {
2
+ width: 100px;
3
+ text-align: center;
4
+ }
5
+
6
+ .bbf-jui-datetime input {
7
+ margin-right: 1em;
8
+ }
9
+
10
+ .bbf-jui-datetime select {
11
+ width: 50px;
12
+ text-align: center;
13
+ }
14
+
15
+
16
+
17
+ .bbf-jui-list {
18
+ list-style-type: none;
19
+ }
20
+
21
+ .bbf-jui-list ul {
22
+ border: 1px solid #ccc;
23
+ border-bottom: none;
24
+ max-height: 150px;
25
+ overflow: auto;
26
+ margin: 0;
27
+ padding: 0;
28
+ }
29
+
30
+ .bbf-jui-list li {
31
+ border-top: 1px solid #ccc;
32
+ border-bottom: 1px solid #ccc;
33
+ height: 16px;
34
+ background: #fff;
35
+ padding: 4px;
36
+ margin: 0;
37
+ list-style-type: none;
38
+ margin-top: -1px;
39
+ position: relative;
40
+
41
+ }
42
+ .bbf-jui-list .bbf-list-sortable li {
43
+ cursor: move;
44
+ }
45
+
46
+ .bbf-jui-list .bbf-list-actions {
47
+ position: absolute;
48
+ top: 2px;
49
+ right: 2px;
50
+ }
51
+
52
+ .bbf-jui-list .bbf-list-actions button {
53
+ width: 19px;
54
+ height: 19px;
55
+ }
56
+
57
+ .bbf-jui-list .bbf-list-add {
58
+ width: 100%;
59
+ height: 20px;
60
+ margin-top: -2px;
61
+ }
62
+
63
+ .bbf-jui-list .bbf-list-editor {
64
+ width: 98%;
65
+ }
@@ -0,0 +1,43 @@
1
+ /* Date */
2
+ .bbf-date .bbf-date {
3
+ width: 4em
4
+ }
5
+
6
+ .bbf-date .bbf-month {
7
+ width: 9em;
8
+ }
9
+
10
+ .bbf-date .bbf-year {
11
+ width: 5em;
12
+ }
13
+
14
+
15
+ /* DateTime */
16
+ .bbf-datetime select {
17
+ width: 4em;
18
+ }
19
+
20
+
21
+ /* List */
22
+ .bbf-list .bbf-add {
23
+ margin-top: -10px
24
+ }
25
+
26
+ .bbf-list li {
27
+ margin-bottom: 5px
28
+ }
29
+
30
+ .bbf-list .bbf-del {
31
+ margin-left: 4px
32
+ }
33
+
34
+
35
+ /* List.Modal */
36
+ .bbf-list-modal {
37
+ cursor: pointer;
38
+ border: 1px solid #ccc;
39
+ width: 208px;
40
+ border-radius: 3px;
41
+ padding: 4px;
42
+ color: #555;
43
+ }