rails-backbone-forms 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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
- })();