rails-backbone-forms 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.
@@ -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
+ }