rails-backbone-forms 0.10.1 → 0.11.0

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.
@@ -55,7 +55,7 @@
55
55
  value = this.value || [];
56
56
 
57
57
  //Create main element
58
- var $el = $(Form.templates[this.schema.listTemplate]({
58
+ var $el = Form.helpers.parseHTML(Form.templates[this.schema.listTemplate]({
59
59
  items: '<b class="bbf-tmp"></b>'
60
60
  }));
61
61
 
@@ -105,10 +105,10 @@
105
105
  self.$list.append(item.el);
106
106
 
107
107
  item.editor.on('all', function(event) {
108
- if (event == 'change') return;
108
+ if (event === 'change') return;
109
109
 
110
110
  // args = ["key:change", itemEditor, fieldEditor]
111
- args = _.toArray(arguments);
111
+ var args = _.toArray(arguments);
112
112
  args[0] = 'item:' + event;
113
113
  args.splice(1, 0, self);
114
114
  // args = ["item:key:change", this=listEditor, itemEditor, fieldEditor]
@@ -156,6 +156,7 @@
156
156
  //Most editors can be added automatically
157
157
  else {
158
158
  _addItem();
159
+ item.editor.focus();
159
160
  }
160
161
 
161
162
  return item;
@@ -206,7 +207,7 @@
206
207
  blur: function() {
207
208
  if (!this.hasFocus) return;
208
209
 
209
- focusedItem = _.find(this.items, function(item) { return item.editor.hasFocus; });
210
+ var focusedItem = _.find(this.items, function(item) { return item.editor.hasFocus; });
210
211
 
211
212
  if (focusedItem) focusedItem.editor.blur();
212
213
  },
@@ -265,7 +266,7 @@
265
266
  this.list.removeItem(this);
266
267
  },
267
268
  'keydown input[type=text]': function(event) {
268
- if(event.keyCode != 13) return;
269
+ if(event.keyCode !== 13) return;
269
270
  event.preventDefault();
270
271
  this.list.addItem();
271
272
  this.list.$list.find("> li:last input").focus();
@@ -291,7 +292,7 @@
291
292
  }).render();
292
293
 
293
294
  //Create main element
294
- var $el = $(Form.templates[this.schema.listItemTemplate]({
295
+ var $el = Form.helpers.parseHTML(Form.templates[this.schema.listItemTemplate]({
295
296
  editor: '<b class="bbf-tmp"></b>'
296
297
  }));
297
298
 
@@ -338,11 +339,15 @@
338
339
  _.every(validators, function(validator) {
339
340
  error = getValidator(validator)(value, formValues);
340
341
 
341
- return continueLoop = error ? false : true;
342
+ return error ? false : true;
342
343
  });
343
344
 
344
345
  //Show/hide error
345
- error ? this.setError(error) : this.clearError();
346
+ if (error){
347
+ this.setError(error);
348
+ } else {
349
+ this.clearError();
350
+ }
346
351
 
347
352
  //Return error to be aggregated by list
348
353
  return error ? error : null;
@@ -367,10 +372,10 @@
367
372
 
368
373
 
369
374
  /**
370
- * Modal object editor for use with the List editor.
371
- * To use it, set the 'itemType' property in a List schema to 'Object' or 'NestedModel'
375
+ * Base modal object editor for use with the List editor; used by Object
376
+ * and NestedModal list types
372
377
  */
373
- editors.List.Modal = editors.List.Object = editors.List.NestedModel = editors.Base.extend({
378
+ editors.List.Modal = editors.Base.extend({
374
379
  events: {
375
380
  'click': 'openEditor'
376
381
  },
@@ -384,26 +389,9 @@
384
389
  */
385
390
  initialize: function(options) {
386
391
  editors.Base.prototype.initialize.call(this, options);
387
-
388
- var schema = this.schema;
389
392
 
390
393
  //Dependencies
391
394
  if (!editors.List.Modal.ModalAdapter) throw 'A ModalAdapter is required';
392
-
393
- //Get nested schema if Object
394
- if (schema.itemType == 'Object') {
395
- if (!schema.subSchema) throw 'Missing required option "schema.subSchema"';
396
-
397
- this.nestedSchema = schema.subSchema;
398
- }
399
-
400
- //Get nested schema if NestedModel
401
- if (schema.itemType == 'NestedModel') {
402
- if (!schema.model) throw 'Missing required option "schema.model"';
403
-
404
- this.nestedSchema = schema.model.prototype.schema;
405
- if (_.isFunction(this.nestedSchema)) this.nestedSchema = this.nestedSchema();
406
- }
407
395
  },
408
396
 
409
397
  /**
@@ -478,11 +466,6 @@
478
466
  //If there's a specified toString use that
479
467
  if (schema.itemToString) return schema.itemToString(value);
480
468
 
481
- //Otherwise check if it's NestedModel with it's own toString() method
482
- if (schema.itemType == 'NestedModel') {
483
- return new (schema.model)(value).toString();
484
- }
485
-
486
469
  //Otherwise use the generic method or custom overridden method
487
470
  return this.itemToString(value);
488
471
  },
@@ -490,50 +473,59 @@
490
473
  openEditor: function() {
491
474
  var self = this;
492
475
 
493
- var form = new Form({
476
+ var form = this.modalForm = new Form({
494
477
  schema: this.nestedSchema,
495
478
  data: this.value
496
479
  });
497
480
 
498
- var modal = this.modal = new Backbone.BootstrapModal({
481
+ var modal = this.modal = new editors.List.Modal.ModalAdapter({
499
482
  content: form,
500
483
  animate: true
501
- }).open();
484
+ });
485
+
486
+ modal.open();
502
487
 
503
488
  this.trigger('open', this);
504
489
  this.trigger('focus', this);
505
490
 
506
- modal.on('cancel', function() {
507
- this.modal = null;
508
-
509
- this.trigger('close', this);
510
- this.trigger('blur', this);
511
- }, this);
491
+ modal.on('cancel', this.onModalClosed, this);
512
492
 
513
- modal.on('ok', _.bind(this.onModalSubmitted, this, form, modal));
493
+ modal.on('ok', _.bind(this.onModalSubmitted, this));
514
494
  },
515
495
 
516
496
  /**
517
497
  * Called when the user clicks 'OK'.
518
498
  * Runs validation and tells the list when ready to add the item
519
499
  */
520
- onModalSubmitted: function(form, modal) {
521
- var isNew = !this.value;
500
+ onModalSubmitted: function() {
501
+ var modal = this.modal,
502
+ form = this.modalForm,
503
+ isNew = !this.value;
522
504
 
523
505
  //Stop if there are validation errors
524
506
  var error = form.validate();
525
507
  if (error) return modal.preventClose();
526
- this.modal = null;
527
508
 
528
- //If OK, render the list item
509
+ //Store form value
529
510
  this.value = form.getValue();
530
511
 
512
+ //Render item
531
513
  this.renderSummary();
532
514
 
533
515
  if (isNew) this.trigger('readyToAdd');
534
516
 
535
517
  this.trigger('change', this);
536
-
518
+
519
+ this.onModalClosed();
520
+ },
521
+
522
+ /**
523
+ * Cleans up references, triggers events. To be called whenever the modal closes
524
+ */
525
+ onModalClosed: function() {
526
+ this.modal = null;
527
+ this.modalForm = null;
528
+
537
529
  this.trigger('close', this);
538
530
  this.trigger('blur', this);
539
531
  },
@@ -557,7 +549,6 @@
557
549
 
558
550
  if (this.modal) {
559
551
  this.modal.trigger('cancel');
560
- this.modal.close();
561
552
  }
562
553
  }
563
554
  }, {
@@ -572,4 +563,48 @@
572
563
  isAsync: true
573
564
  });
574
565
 
566
+
567
+ editors.List.Object = editors.List.Modal.extend({
568
+ initialize: function () {
569
+ editors.List.Modal.prototype.initialize.apply(this, arguments);
570
+
571
+ var schema = this.schema;
572
+
573
+ if (!schema.subSchema) throw 'Missing required option "schema.subSchema"';
574
+
575
+ this.nestedSchema = schema.subSchema;
576
+ }
577
+ });
578
+
579
+
580
+ editors.List.NestedModel = editors.List.Modal.extend({
581
+ initialize: function() {
582
+ editors.List.Modal.prototype.initialize.apply(this, arguments);
583
+
584
+ var schema = this.schema;
585
+
586
+ if (!schema.model) throw 'Missing required option "schema.model"';
587
+
588
+ var nestedSchema = schema.model.prototype.schema;
589
+
590
+ this.nestedSchema = (_.isFunction(nestedSchema)) ? nestedSchema() : nestedSchema;
591
+ },
592
+
593
+ /**
594
+ * Returns the string representation of the object value
595
+ */
596
+ getStringValue: function() {
597
+ var schema = this.schema,
598
+ value = this.getValue();
599
+
600
+ if (_.isEmpty(value)) return null;
601
+
602
+ //If there's a specified toString use that
603
+ if (schema.itemToString) return schema.itemToString(value);
604
+
605
+ //Otherwise use the model
606
+ return new (schema.model)(value).toString();
607
+ },
608
+ });
609
+
575
610
  })();
@@ -0,0 +1 @@
1
+ (function(){var e=Backbone.Form,t=e.editors;t.List=t.Base.extend({events:{'click [data-action="add"]':function(e){e.preventDefault(),this.addItem(null,!0)}},initialize:function(e){t.Base.prototype.initialize.call(this,e);var n=this.schema;if(!n)throw"Missing required option 'schema'";this.schema=_.extend({listTemplate:"list",listItemTemplate:"listItem"},n),this.Editor=function(){var e=n.itemType;return e?t.List[e]?t.List[e]:t[e]:t.Text}(),this.items=[]},render:function(){var t=this,n=this.value||[],r=e.helpers.parseHTML(e.templates[this.schema.listTemplate]({items:'<b class="bbf-tmp"></b>'}));return this.$list=r.find(".bbf-tmp").parent().empty(),n.length?_.each(n,function(e){t.addItem(e)}):this.Editor.isAsync||this.addItem(),this.setElement(r),this.$el.attr("id",this.id),this.$el.attr("name",this.key),this.hasFocus&&this.trigger("blur",this),this},addItem:function(e,n){var r=this,i=(new t.List.Item({list:this,schema:this.schema,value:e,Editor:this.Editor,key:this.key})).render(),s=function(){r.items.push(i),r.$list.append(i.el),i.editor.on("all",function(e){if(e==="change")return;var n=_.toArray(arguments);n[0]="item:"+e,n.splice(1,0,r),t.List.prototype.trigger.apply(this,n)},r),i.editor.on("change",function(){i.addEventTriggered||(i.addEventTriggered=!0,this.trigger("add",this,i.editor)),this.trigger("item:change",this,i.editor),this.trigger("change",this)},r),i.editor.on("focus",function(){if(this.hasFocus)return;this.trigger("focus",this)},r),i.editor.on("blur",function(){if(!this.hasFocus)return;var e=this;setTimeout(function(){if(_.find(e.items,function(e){return e.editor.hasFocus}))return;e.trigger("blur",e)},0)},r);if(n||e)i.addEventTriggered=!0;n&&(r.trigger("add",r,i.editor),r.trigger("change",r))};return this.Editor.isAsync?i.editor.on("readyToAdd",s,this):(s(),i.editor.focus()),i},removeItem:function(e){var t=this.schema.confirmDelete;if(t&&!confirm(t))return;var n=_.indexOf(this.items,e);this.items[n].remove(),this.items.splice(n,1),e.addEventTriggered&&(this.trigger("remove",this,e.editor),this.trigger("change",this)),!this.items.length&&!this.Editor.isAsync&&this.addItem()},getValue:function(){var e=_.map(this.items,function(e){return e.getValue()});return _.without(e,undefined,"")},setValue:function(e){this.value=e,this.render()},focus:function(){if(this.hasFocus)return;this.items[0]&&this.items[0].editor.focus()},blur:function(){if(!this.hasFocus)return;var e=_.find(this.items,function(e){return e.editor.hasFocus});e&&e.editor.blur()},remove:function(){_.invoke(this.items,"remove"),t.Base.prototype.remove.call(this)},validate:function(){if(!this.validators)return null;var e=_.map(this.items,function(e){return e.validate()}),t=_.compact(e).length?!0:!1;if(!t)return null;var n={type:"list",message:"Some of the items in the list failed validation",errors:e};return n}}),t.List.Item=Backbone.View.extend({events:{'click [data-action="remove"]':function(e){e.preventDefault(),this.list.removeItem(this)},"keydown input[type=text]":function(e){if(e.keyCode!==13)return;e.preventDefault(),this.list.addItem(),this.list.$list.find("> li:last input").focus()}},initialize:function(e){this.list=e.list,this.schema=e.schema||this.list.schema,this.value=e.value,this.Editor=e.Editor||t.Text,this.key=e.key},render:function(){this.editor=(new this.Editor({key:this.key,schema:this.schema,value:this.value,list:this.list,item:this})).render();var t=e.helpers.parseHTML(e.templates[this.schema.listItemTemplate]({editor:'<b class="bbf-tmp"></b>'}));return t.find(".bbf-tmp").replaceWith(this.editor.el),this.setElement(t),this},getValue:function(){return this.editor.getValue()},setValue:function(e){this.editor.setValue(e)},focus:function(){this.editor.focus()},blur:function(){this.editor.blur()},remove:function(){this.editor.remove(),Backbone.View.prototype.remove.call(this)},validate:function(){var t=this.getValue(),n=this.list.form?this.list.form.getValue():{},r=this.schema.validators,i=e.helpers.getValidator;if(!r)return null;var s=null;return _.every(r,function(e){return s=i(e)(t,n),s?!1:!0}),s?this.setError(s):this.clearError(),s?s:null},setError:function(t){this.$el.addClass(e.classNames.error),this.$el.attr("title",t.message)},clearError:function(){this.$el.removeClass(e.classNames.error),this.$el.attr("title",null)}}),t.List.Modal=t.Base.extend({events:{click:"openEditor"},initialize:function(e){t.Base.prototype.initialize.call(this,e);if(!t.List.Modal.ModalAdapter)throw"A ModalAdapter is required"},render:function(){var e=this;return _.isEmpty(this.value)?this.openEditor():(this.renderSummary(),setTimeout(function(){e.trigger("readyToAdd")},0)),this.hasFocus&&this.trigger("blur",this),this},renderSummary:function(){var t=e.templates["list.Modal"];this.$el.html(t({summary:this.getStringValue()}))},itemToString:function(t){t=t||{};var n=[];return _.each(this.nestedSchema,function(r,i){var s=r.title?r.title:e.helpers.keyToTitle(i),o=t[i];if(_.isUndefined(o)||_.isNull(o))o="";n.push(s+": "+o)}),n.join("<br />")},getStringValue:function(){var e=this.schema,t=this.getValue();return _.isEmpty(t)?"[Empty]":e.itemToString?e.itemToString(t):this.itemToString(t)},openEditor:function(){var n=this,r=this.modalForm=new e({schema:this.nestedSchema,data:this.value}),i=this.modal=new t.List.Modal.ModalAdapter({content:r,animate:!0});i.open(),this.trigger("open",this),this.trigger("focus",this),i.on("cancel",this.onModalClosed,this),i.on("ok",_.bind(this.onModalSubmitted,this))},onModalSubmitted:function(){var e=this.modal,t=this.modalForm,n=!this.value,r=t.validate();if(r)return e.preventClose();this.value=t.getValue(),this.renderSummary(),n&&this.trigger("readyToAdd"),this.trigger("change",this),this.onModalClosed()},onModalClosed:function(){this.modal=null,this.modalForm=null,this.trigger("close",this),this.trigger("blur",this)},getValue:function(){return this.value},setValue:function(e){this.value=e},focus:function(){if(this.hasFocus)return;this.openEditor()},blur:function(){if(!this.hasFocus)return;this.modal&&this.modal.trigger("cancel")}},{ModalAdapter:Backbone.BootstrapModal,isAsync:!0}),t.List.Object=t.List.Modal.extend({initialize:function(){t.List.Modal.prototype.initialize.apply(this,arguments);var e=this.schema;if(!e.subSchema)throw'Missing required option "schema.subSchema"';this.nestedSchema=e.subSchema}}),t.List.NestedModel=t.List.Modal.extend({initialize:function(){t.List.Modal.prototype.initialize.apply(this,arguments);var e=this.schema;if(!e.model)throw'Missing required option "schema.model"';var n=e.model.prototype.schema;this.nestedSchema=_.isFunction(n)?n():n},getStringValue:function(){var e=this.schema,t=this.getValue();return _.isEmpty(t)?null:e.itemToString?e.itemToString(t):(new e.model(t)).toString()}})})()
@@ -31,7 +31,8 @@
31
31
  <div class="control-group field-{{key}}">\
32
32
  <label class="control-label" for="{{id}}">{{title}}</label>\
33
33
  <div class="controls">\
34
- <div class="input-xlarge">{{editor}}</div>\
34
+ {{editor}}\
35
+ <div class="help-inline">{{error}}</div>\
35
36
  <div class="help-block">{{help}}</div>\
36
37
  </div>\
37
38
  </div>\
@@ -39,7 +40,9 @@
39
40
 
40
41
  nestedField: '\
41
42
  <div class="field-{{key}}">\
42
- <div title="{{title}}" class="input-xlarge">{{editor}}</div>\
43
+ <div title="{{title}}" class="input-xlarge">{{editor}}\
44
+ <div class="help-inline">{{error}}</div>\
45
+ </div>\
43
46
  <div class="help-block">{{help}}</div>\
44
47
  </div>\
45
48
  ',
@@ -47,7 +50,7 @@
47
50
  list: '\
48
51
  <div class="bbf-list">\
49
52
  <ul class="unstyled clearfix">{{items}}</ul>\
50
- <button type="button" class="btn bbf-add" data-action="add">Add</div>\
53
+ <button class="btn bbf-add" data-action="add">Add</button>\
51
54
  </div>\
52
55
  ',
53
56
 
@@ -31,6 +31,7 @@
31
31
  <label for="{{id}}">{{title}}</label>\
32
32
  <div class="bbf-editor">{{editor}}</div>\
33
33
  <div class="bbf-help">{{help}}</div>\
34
+ <div class="bbf-error">{{error}}</div>\
34
35
  </li>\
35
36
  ',
36
37
 
@@ -39,6 +40,7 @@
39
40
  <label for="{{id}}">{{title}}</label>\
40
41
  <div class="bbf-editor">{{editor}}</div>\
41
42
  <div class="bbf-help">{{help}}</div>\
43
+ <div class="bbf-error">{{error}}</div>\
42
44
  </li>\
43
45
  ',
44
46
 
@@ -34,6 +34,12 @@
34
34
  color: #999;
35
35
  }
36
36
 
37
+ .bbf-field .bbf-error {
38
+ margin-left: 25%;
39
+ width: 74%;
40
+ color: red;
41
+ }
42
+
37
43
  .bbf-field.bbf-error .bbf-editor {
38
44
  outline: 1px solid red;
39
45
  }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails-backbone-forms
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.1
4
+ version: 0.11.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-02 00:00:00.000000000 Z
12
+ date: 2013-03-25 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: Rails 3.1 support for the backbone-forms library
15
15
  email:
@@ -29,14 +29,13 @@ files:
29
29
  - vendor/assets/javascripts/backbone-forms-bootstrap.js
30
30
  - vendor/assets/javascripts/backbone-forms.js
31
31
  - vendor/assets/javascripts/backbone-forms/adapters/backbone.bootstrap-modal.js
32
+ - vendor/assets/javascripts/backbone-forms/adapters/backbone.bootstrap-modal.min.js
32
33
  - vendor/assets/javascripts/backbone-forms/backbone-forms.js
33
- - vendor/assets/javascripts/backbone-forms/editors/jquery-ui.js
34
34
  - vendor/assets/javascripts/backbone-forms/editors/list.js
35
+ - vendor/assets/javascripts/backbone-forms/editors/list.min.js
35
36
  - vendor/assets/javascripts/backbone-forms/templates/bootstrap.js
36
37
  - vendor/assets/javascripts/backbone-forms/templates/default.js
37
- - vendor/assets/stylesheets/backbone-forms-bootstrap.css
38
38
  - vendor/assets/stylesheets/backbone-forms.css
39
- - vendor/assets/stylesheets/backbone-forms/editors/jquery-ui.css
40
39
  - vendor/assets/stylesheets/backbone-forms/templates/bootstrap.css
41
40
  - vendor/assets/stylesheets/backbone-forms/templates/default.css
42
41
  homepage: ''
@@ -59,7 +58,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
58
  version: '0'
60
59
  requirements: []
61
60
  rubyforge_project: rails-backbone-forms
62
- rubygems_version: 1.8.24
61
+ rubygems_version: 1.8.25
63
62
  signing_key:
64
63
  specification_version: 3
65
64
  summary: A wrapper for backbone-forms in the Rails asset pipeline
@@ -1,500 +0,0 @@
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
-
13
- //DATE
14
- exports['jqueryui.Date'] = Base.extend({
15
-
16
- className: 'bbf-jui-date',
17
-
18
- initialize: function(options) {
19
- Base.prototype.initialize.call(this, options);
20
-
21
- //Cast to Date
22
- if (this.value && !_.isDate(this.value)) {
23
- this.value = new Date(this.value);
24
- }
25
-
26
- //Set default date
27
- if (!this.value) {
28
- var date = new Date();
29
- date.setSeconds(0);
30
- date.setMilliseconds(0);
31
-
32
- this.value = date;
33
- }
34
- },
35
-
36
- render: function() {
37
- var $el = this.$el;
38
-
39
- $el.html('<input>');
40
-
41
- var input = $('input', $el);
42
-
43
- input.datepicker({
44
- dateFormat: 'dd/mm/yy',
45
- showButtonPanel: true
46
- });
47
-
48
- this._observeDatepickerEvents();
49
-
50
- //Make sure setValue of this object is called, not of any objects extending it (e.g. DateTime)
51
- exports['jqueryui.Date'].prototype.setValue.call(this, this.value);
52
-
53
- return this;
54
- },
55
-
56
- /**
57
- * @return {Date} Selected date
58
- */
59
- getValue: function() {
60
- var input = $('input', this.el),
61
- date = input.datepicker('getDate');
62
-
63
- return date;
64
- },
65
-
66
- setValue: function(value) {
67
- $('input', this.el).datepicker('setDate', value);
68
- },
69
-
70
- focus: function() {
71
- if (this.hasFocus) return;
72
-
73
- this.$('input').datepicker('show');
74
- },
75
-
76
- blur: function() {
77
- if (!this.hasFocus) return;
78
-
79
- this.$('input').datepicker('hide');
80
- },
81
-
82
- _observeDatepickerEvents: function() {
83
- var self = this;
84
- this.$('input').datepicker('option', 'onSelect', function() {
85
- self.trigger('change', self);
86
- })
87
- this.$('input').datepicker('option', 'onClose', function() {
88
- if (!self.hasFocus) return;
89
- self.trigger('blur', self);
90
- });
91
- this.$('input').datepicker('option', 'beforeShow', function() {
92
- if (self.hasFocus) return {};
93
- self.trigger('focus', self);
94
-
95
- return {};
96
- });
97
- }
98
-
99
- });
100
-
101
-
102
- //DATETIME
103
- exports['jqueryui.DateTime'] = exports['jqueryui.Date'].extend({
104
-
105
- className: 'bbf-jui-datetime',
106
-
107
- template: createTemplate('<select>{{hours}}</select> : <select>{{mins}}</select>'),
108
-
109
- render: function() {
110
- function pad(n) {
111
- return n < 10 ? '0' + n : n
112
- }
113
-
114
- //Render the date element first
115
- exports['jqueryui.Date'].prototype.render.call(this);
116
-
117
- //Setup hour options
118
- var hours = _.range(0, 24),
119
- hoursOptions = [];
120
-
121
- _.each(hours, function(hour) {
122
- hoursOptions.push('<option value="'+hour+'">' + pad(hour) + '</option>');
123
- });
124
-
125
- //Setup minute options
126
- var minsInterval = this.schema.minsInterval || 15,
127
- mins = _.range(0, 60, minsInterval),
128
- minsOptions = [];
129
-
130
- _.each(mins, function(min) {
131
- minsOptions.push('<option value="'+min+'">' + pad(min) + '</option>');
132
- });
133
-
134
- //Render time selects
135
- this.$el.append(this.template({
136
- hours: hoursOptions.join(),
137
- mins: minsOptions.join()
138
- }));
139
-
140
- this._observeDatepickerEvents();
141
-
142
- //Store references to selects
143
- this.$hours = $('select:eq(0)', this.el);
144
- this.$mins = $('select:eq(1)', this.el);
145
-
146
- //Set time
147
- this.setValue(this.value);
148
-
149
- return this;
150
- },
151
-
152
- /**
153
- * @return {Date} Selected datetime
154
- */
155
- getValue: function() {
156
- var input = $('input', this.el),
157
- date = input.datepicker('getDate');
158
-
159
- date.setHours(this.$hours.val());
160
- date.setMinutes(this.$mins.val());
161
- date.setMilliseconds(0);
162
-
163
- return date;
164
- },
165
-
166
- setValue: function(date) {
167
- exports['jqueryui.Date'].prototype.setValue.call(this, date);
168
-
169
- this.$hours.val(date.getHours());
170
- this.$mins.val(date.getMinutes());
171
- }
172
-
173
- });
174
-
175
-
176
- //LIST
177
- exports['jqueryui.List'] = Base.extend({
178
-
179
- className: 'bbf-jui-list',
180
-
181
- //Note: The extra div around the <ul> is used to limit the drag area
182
- template: createTemplate('\
183
- <ul></ul>\
184
- <div><button class="bbf-list-add">Add</div>\
185
- '),
186
-
187
- itemTemplate: createTemplate('\
188
- <li rel="{{id}}">\
189
- <span class="bbf-list-text">{{text}}</span>\
190
- <div class="bbf-list-actions">\
191
- <button class="bbf-list-edit">Edit</button>\
192
- <button class="bbf-list-del">Delete</button>\
193
- </div>\
194
- </li>\
195
- '),
196
-
197
- editorTemplate: createTemplate('\
198
- <div class="bbf-field">\
199
- <div class="bbf-list-editor"></div>\
200
- </div>\
201
- '),
202
-
203
- events: {
204
- 'click .bbf-list-add': 'addNewItem',
205
- 'click .bbf-list-edit': 'editItem',
206
- 'click .bbf-list-del': 'deleteItem'
207
- },
208
-
209
- initialize: function(options) {
210
- Base.prototype.initialize.call(this, options);
211
-
212
- if (!this.schema) throw "Missing required option 'schema'";
213
-
214
- this.schema.listType = this.schema.listType || 'Text';
215
-
216
- if (this.schema.listType == 'NestedModel' && !this.schema.model)
217
- throw "Missing required option 'schema.model'";
218
- },
219
-
220
- render: function() {
221
- var $el = this.$el;
222
-
223
- //Main element
224
- $el.html(this.template());
225
-
226
- //Create list
227
- var self = this,
228
- data = this.value || [],
229
- schema = this.schema,
230
- itemToString = this.itemToString,
231
- itemTemplate = this.itemTemplate,
232
- listEl = $('ul', $el);
233
-
234
- _.each(data, function(itemData) {
235
- var text = itemToString.call(self, itemData);
236
-
237
- //Create DOM element
238
- var li = $(itemTemplate({
239
- id: itemData.id || '',
240
- text: text
241
- }));
242
-
243
- //Attach data
244
- $.data(li[0], 'data', itemData);
245
-
246
- listEl.append(li);
247
- });
248
-
249
- //Make sortable
250
- if (schema.sortable !== false) {
251
- listEl.sortable({
252
- axis: 'y',
253
- cursor: 'move',
254
- containment: 'parent'
255
- });
256
-
257
- $el.addClass('bbf-list-sortable');
258
- }
259
-
260
- //jQuery UI buttonize
261
- $('button.bbf-list-add', $el).button({
262
- text: false,
263
- icons: { primary: 'ui-icon-plus' }
264
- });
265
- $('button.bbf-list-edit', $el).button({
266
- text: false,
267
- icons: { primary: 'ui-icon-pencil' }
268
- });
269
- $('button.bbf-list-del', $el).button({
270
- text: false,
271
- icons: { primary: 'ui-icon-trash' }
272
- });
273
-
274
- if (this.hasFocus) this.trigger('blur', this);
275
-
276
- return this;
277
- },
278
-
279
- /**
280
- * Formats an item for display in the list
281
- * For example objects, dates etc. can have a custom
282
- * itemToString method which says how it should be formatted.
283
- */
284
- itemToString: function(data) {
285
- if (!data) return data;
286
-
287
- var schema = this.schema;
288
-
289
- //If there's a specified toString use that
290
- if (schema.itemToString) return schema.itemToString(data);
291
-
292
- //Otherwise check if it's NestedModel with it's own toString() method
293
- if (this.schema.listType == 'NestedModel') {
294
- var model = new (this.schema.model)(data);
295
-
296
- return model.toString();
297
- }
298
-
299
- //Last resort, just return the data as is
300
- return data;
301
- },
302
-
303
- /**
304
- * Add a new item to the list if it is completed in the editor
305
- */
306
- addNewItem: function(event) {
307
- if (event) event.preventDefault();
308
-
309
- var self = this;
310
-
311
- this.openEditor(null, function(value, editor) {
312
- //Fire 'addItem' cancellable event
313
- triggerCancellableEvent(self, 'addItem', [value, editor], function() {
314
- var text = self.itemToString(value);
315
-
316
- //Create DOM element
317
- var li = $(self.itemTemplate({
318
- id: value.id || '',
319
- text: text
320
- }));
321
-
322
- //Store data
323
- $.data(li[0], 'data', value);
324
-
325
- $('ul', self.el).append(li);
326
-
327
- //jQuery UI buttonize
328
- $('button.bbf-list-edit', this.el).button({
329
- text: false,
330
- icons: { primary: 'ui-icon-pencil' }
331
- });
332
- $('button.bbf-list-del', this.el).button({
333
- text: false,
334
- icons: { primary: 'ui-icon-trash' }
335
- });
336
-
337
- self.trigger('add', self, value);
338
- self.trigger('item:change', self, editor);
339
- self.trigger('change', self);
340
- });
341
- });
342
- },
343
-
344
- /**
345
- * Edit an existing item in the list
346
- */
347
- editItem: function(event) {
348
- event.preventDefault();
349
-
350
- var self = this,
351
- li = $(event.target).closest('li'),
352
- originalValue = $.data(li[0], 'data');
353
-
354
- this.openEditor(originalValue, function(newValue, editor) {
355
- //Fire 'editItem' cancellable event
356
- triggerCancellableEvent(self, 'editItem', [newValue, editor], function() {
357
- //Update display
358
- $('.bbf-list-text', li).html(self.itemToString(newValue));
359
-
360
- //Store data
361
- $.data(li[0], 'data', newValue);
362
-
363
- self.trigger('item:change', self, editor);
364
- self.trigger('change', self);
365
- });
366
- });
367
- },
368
-
369
- deleteItem: function(event) {
370
- event.preventDefault();
371
-
372
- var self = this,
373
- li = $(event.target).closest('li'),
374
- data = $.data(li[0], 'data');
375
-
376
- var confirmDelete = (this.schema.confirmDelete) ? this.schema.confirmDelete : false,
377
- confirmMsg = this.schema.confirmDeleteMsg || 'Are you sure?';
378
-
379
- function remove() {
380
- triggerCancellableEvent(self, 'removeItem', [data], function() {
381
- li.remove();
382
-
383
- self.trigger('remove', self, data);
384
- self.trigger('change', self);
385
- });
386
- }
387
-
388
- if (this.schema.confirmDelete) {
389
- if (confirm(confirmMsg)) remove();
390
- } else {
391
- remove();
392
- }
393
- },
394
-
395
- /**
396
- * Opens the sub editor dialog
397
- * @param {Mixed} Data (if editing existing list item, null otherwise)
398
- * @param {Function} Save callback. receives: value
399
- */
400
- openEditor: function(data, callback) {
401
- var self = this,
402
- schema = this.schema,
403
- listType = schema.listType || 'Text';
404
-
405
- var editor = Form.helpers.createEditor(listType, {
406
- key: '',
407
- schema: schema,
408
- value: data
409
- }).render();
410
-
411
- var container = this.editorContainer = $(this.editorTemplate());
412
- $('.bbf-list-editor', container).html(editor.el);
413
-
414
- var saveAndClose = function() {
415
- var errs = editor.validate();
416
- if (errs) return;
417
-
418
- callback(editor.getValue(), editor);
419
- container.dialog('close');
420
- }
421
-
422
- var handleEnterPressed = function(event) {
423
- if (event.keyCode != 13) return;
424
-
425
- saveAndClose();
426
- }
427
-
428
- $(container).dialog({
429
- resizable: false,
430
- modal: true,
431
- width: 500,
432
- title: data ? 'Edit item' : 'New item',
433
- buttons: {
434
- 'OK': saveAndClose,
435
- 'Cancel': function() {
436
- container.dialog('close');
437
- }
438
- },
439
- close: function() {
440
- self.editorContainer = null;
441
-
442
- $(document).unbind('keydown', handleEnterPressed);
443
-
444
- editor.remove();
445
- container.remove();
446
-
447
- self.trigger('item:close', self, editor);
448
- self.trigger('item:blur', self, editor);
449
- self.trigger('blur', self);
450
- }
451
- });
452
-
453
- this.trigger('item:open', this, editor);
454
- this.trigger('item:focus', this, editor);
455
- this.trigger('focus', this);
456
-
457
- //Save and close dialog on Enter keypress
458
- $(document).bind('keydown', handleEnterPressed);
459
- },
460
-
461
- getValue: function() {
462
- var data = [];
463
-
464
- $('li', this.el).each(function(index, li) {
465
- data.push($.data(li, 'data'));
466
- });
467
-
468
- return data;
469
- },
470
-
471
- setValue: function(value) {
472
- this.value = value;
473
- this.render();
474
- },
475
-
476
- focus: function() {
477
- if (this.hasFocus) return;
478
-
479
- var item = this.$('li .bbf-list-edit').first();
480
- if (item.length > 0) {
481
- item.click();
482
- }
483
- else {
484
- this.addNewItem();
485
- }
486
- },
487
-
488
- blur: function() {
489
- if (!this.hasFocus) return;
490
-
491
- if (this.editorContainer) this.editorContainer.dialog('close');
492
- }
493
-
494
- });
495
-
496
-
497
- //Exports
498
- _.extend(Form.editors, exports);
499
-
500
- })();