x-editable-rails 1.5.1 → 1.5.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,7 +1,7 @@
1
- /*! X-editable - v1.5.0
2
- * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
- * http://github.com/vitalets/x-editable
4
- * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
1
+ /*! X-editable - v1.5.1
2
+ * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
+ * http://github.com/vitalets/x-editable
4
+ * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
5
5
  /**
6
6
  Form with single input element, two buttons and two states: normal/loading.
7
7
  Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
@@ -185,7 +185,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
185
185
  } else {
186
186
  //convert newline to <br> for more pretty error display
187
187
  if(msg) {
188
- lines = msg.split("\n");
188
+ lines = (''+msg).split('\n');
189
189
  for (var i = 0; i < lines.length; i++) {
190
190
  lines[i] = $('<div>').text(lines[i]).html();
191
191
  }
@@ -200,11 +200,21 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
200
200
  e.stopPropagation();
201
201
  e.preventDefault();
202
202
 
203
- var error,
204
- newValue = this.input.input2value(); //get new value from input
205
-
206
- //validation
207
- if (error = this.validate(newValue)) {
203
+ //get new value from input
204
+ var newValue = this.input.input2value();
205
+
206
+ //validation: if validate returns string or truthy value - means error
207
+ //if returns object like {newValue: '...'} => submitted value is reassigned to it
208
+ var error = this.validate(newValue);
209
+ if ($.type(error) === 'object' && error.newValue !== undefined) {
210
+ newValue = error.newValue;
211
+ this.input.value2input(newValue);
212
+ if(typeof error.msg === 'string') {
213
+ this.error(error.msg);
214
+ this.showForm();
215
+ return;
216
+ }
217
+ } else if (error) {
208
218
  this.error(error);
209
219
  this.showForm();
210
220
  return;
@@ -503,6 +513,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
503
513
  send: 'auto',
504
514
  /**
505
515
  Function for client-side validation. If returns string - means validation not passed and string showed as error.
516
+ Since 1.5.1 you can modify submitted value by returning object from `validate`:
517
+ `{newValue: '...'}` or `{newValue: '...', msg: '...'}`
506
518
 
507
519
  @property validate
508
520
  @type function
@@ -625,7 +637,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
625
637
  //engine
626
638
  $.fn.editableform.engine = 'jquery';
627
639
  }(window.jQuery));
628
-
640
+
629
641
  /**
630
642
  * EditableForm utilites
631
643
  */
@@ -875,7 +887,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
875
887
 
876
888
  };
877
889
  }(window.jQuery));
878
-
890
+
879
891
  /**
880
892
  Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
881
893
  This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
@@ -1391,7 +1403,7 @@ Applied as jQuery method.
1391
1403
  };
1392
1404
 
1393
1405
  }(window.jQuery));
1394
-
1406
+
1395
1407
  /**
1396
1408
  * Editable Inline
1397
1409
  * ---------------------
@@ -1445,7 +1457,7 @@ Applied as jQuery method.
1445
1457
  }
1446
1458
  });
1447
1459
 
1448
- }(window.jQuery));
1460
+ }(window.jQuery));
1449
1461
  /**
1450
1462
  Makes editable any HTML element on the page. Applied as jQuery method.
1451
1463
 
@@ -2015,7 +2027,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
2015
2027
  /**
2016
2028
  This method collects values from several editable elements and submit them all to server.
2017
2029
  Internally it runs client-side validation for all fields and submits only in case of success.
2018
- See <a href="#newrecord">creating new records</a> for details.
2030
+ See <a href="#newrecord">creating new records</a> for details.
2031
+ Since 1.5.1 `submit` can be applied to single element to send data programmatically. In that case
2032
+ `url`, `success` and `error` is taken from initial options and you can just call `$('#username').editable('submit')`.
2019
2033
 
2020
2034
  @method submit(options)
2021
2035
  @param {object} options
@@ -2029,31 +2043,76 @@ Makes editable any HTML element on the page. Applied as jQuery method.
2029
2043
  case 'submit': //collects value, validate and submit to server for creating new record
2030
2044
  var config = arguments[1] || {},
2031
2045
  $elems = this,
2032
- errors = this.editable('validate'),
2033
- values;
2046
+ errors = this.editable('validate');
2034
2047
 
2048
+ // validation ok
2035
2049
  if($.isEmptyObject(errors)) {
2036
- values = this.editable('getValue');
2037
- if(config.data) {
2038
- $.extend(values, config.data);
2050
+ var ajaxOptions = {};
2051
+
2052
+ // for single element use url, success etc from options
2053
+ if($elems.length === 1) {
2054
+ var editable = $elems.data('editable');
2055
+ //standard params
2056
+ var params = {
2057
+ name: editable.options.name || '',
2058
+ value: editable.input.value2submit(editable.value),
2059
+ pk: (typeof editable.options.pk === 'function') ?
2060
+ editable.options.pk.call(editable.options.scope) :
2061
+ editable.options.pk
2062
+ };
2063
+
2064
+ //additional params
2065
+ if(typeof editable.options.params === 'function') {
2066
+ params = editable.options.params.call(editable.options.scope, params);
2067
+ } else {
2068
+ //try parse json in single quotes (from data-params attribute)
2069
+ editable.options.params = $.fn.editableutils.tryParseJson(editable.options.params, true);
2070
+ $.extend(params, editable.options.params);
2071
+ }
2072
+
2073
+ ajaxOptions = {
2074
+ url: editable.options.url,
2075
+ data: params,
2076
+ type: 'POST'
2077
+ };
2078
+
2079
+ // use success / error from options
2080
+ config.success = config.success || editable.options.success;
2081
+ config.error = config.error || editable.options.error;
2082
+
2083
+ // multiple elements
2084
+ } else {
2085
+ var values = this.editable('getValue');
2086
+
2087
+ ajaxOptions = {
2088
+ url: config.url,
2089
+ data: values,
2090
+ type: 'POST'
2091
+ };
2039
2092
  }
2040
-
2041
- $.ajax($.extend({
2042
- url: config.url,
2043
- data: values,
2044
- type: 'POST'
2045
- }, config.ajaxOptions))
2046
- .success(function(response) {
2047
- //successful response 200 OK
2048
- if(typeof config.success === 'function') {
2093
+
2094
+ // ajax success callabck (response 200 OK)
2095
+ ajaxOptions.success = typeof config.success === 'function' ? function(response) {
2049
2096
  config.success.call($elems, response, config);
2050
- }
2051
- })
2052
- .error(function(){ //ajax error
2053
- if(typeof config.error === 'function') {
2054
- config.error.apply($elems, arguments);
2055
- }
2056
- });
2097
+ } : $.noop;
2098
+
2099
+ // ajax error callabck
2100
+ ajaxOptions.error = typeof config.error === 'function' ? function() {
2101
+ config.error.apply($elems, arguments);
2102
+ } : $.noop;
2103
+
2104
+ // extend ajaxOptions
2105
+ if(config.ajaxOptions) {
2106
+ $.extend(ajaxOptions, config.ajaxOptions);
2107
+ }
2108
+
2109
+ // extra data
2110
+ if(config.data) {
2111
+ $.extend(ajaxOptions.data, config.data);
2112
+ }
2113
+
2114
+ // perform ajax request
2115
+ $.ajax(ajaxOptions);
2057
2116
  } else { //client-side validation error
2058
2117
  if(typeof config.error === 'function') {
2059
2118
  config.error.call($elems, errors);
@@ -2257,7 +2316,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
2257
2316
  };
2258
2317
 
2259
2318
  }(window.jQuery));
2260
-
2319
+
2261
2320
  /**
2262
2321
  AbstractInput - base class for all editable inputs.
2263
2322
  It defines interface to be implemented by any input type.
@@ -2478,7 +2537,7 @@ To create your own input you can inherit from this class.
2478
2537
  $.extend($.fn.editabletypes, {abstractinput: AbstractInput});
2479
2538
 
2480
2539
  }(window.jQuery));
2481
-
2540
+
2482
2541
  /**
2483
2542
  List - abstract class for inputs that have source option loaded from js array or via ajax
2484
2543
 
@@ -2814,7 +2873,7 @@ List - abstract class for inputs that have source option loaded from js array or
2814
2873
  $.fn.editabletypes.list = List;
2815
2874
 
2816
2875
  }(window.jQuery));
2817
-
2876
+
2818
2877
  /**
2819
2878
  Text input
2820
2879
 
@@ -2949,7 +3008,7 @@ $(function(){
2949
3008
  $.fn.editabletypes.text = Text;
2950
3009
 
2951
3010
  }(window.jQuery));
2952
-
3011
+
2953
3012
  /**
2954
3013
  Textarea input
2955
3014
 
@@ -3061,7 +3120,7 @@ $(function(){
3061
3120
  $.fn.editabletypes.textarea = Textarea;
3062
3121
 
3063
3122
  }(window.jQuery));
3064
-
3123
+
3065
3124
  /**
3066
3125
  Select (dropdown)
3067
3126
 
@@ -3158,7 +3217,7 @@ $(function(){
3158
3217
  $.fn.editabletypes.select = Select;
3159
3218
 
3160
3219
  }(window.jQuery));
3161
-
3220
+
3162
3221
  /**
3163
3222
  List of checkboxes.
3164
3223
  Internally value stored as javascript array of values.
@@ -3315,7 +3374,7 @@ $(function(){
3315
3374
  $.fn.editabletypes.checklist = Checklist;
3316
3375
 
3317
3376
  }(window.jQuery));
3318
-
3377
+
3319
3378
  /**
3320
3379
  HTML5 input types.
3321
3380
  Following types are supported:
@@ -3534,7 +3593,7 @@ Time
3534
3593
  });
3535
3594
  $.fn.editabletypes.time = Time;
3536
3595
  }(window.jQuery));
3537
-
3596
+
3538
3597
  /**
3539
3598
  Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
3540
3599
  Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
@@ -3575,7 +3634,11 @@ $(function(){
3575
3634
  });
3576
3635
  //remote source (simple)
3577
3636
  $('#country').editable({
3578
- source: '/getCountries'
3637
+ source: '/getCountries',
3638
+ select2: {
3639
+ placeholder: 'Select Country',
3640
+ minimumInputLength: 1
3641
+ }
3579
3642
  });
3580
3643
  //remote source (advanced)
3581
3644
  $('#country').editable({
@@ -3651,14 +3714,14 @@ $(function(){
3651
3714
  options.select2.data = this.sourceData;
3652
3715
  }
3653
3716
  }
3654
-
3717
+
3655
3718
  //overriding objects in config (as by default jQuery extend() is not recursive)
3656
3719
  this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2);
3657
-
3720
+
3658
3721
  //detect whether it is multi-valued
3659
3722
  this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
3660
3723
  this.isRemote = ('ajax' in this.options.select2);
3661
-
3724
+
3662
3725
  //store function returning ID of item
3663
3726
  //should be here as used inautotext for local source
3664
3727
  this.idFunc = this.options.select2.id;
@@ -3666,12 +3729,12 @@ $(function(){
3666
3729
  var idKey = this.idFunc || 'id';
3667
3730
  this.idFunc = function (e) { return e[idKey]; };
3668
3731
  }
3669
-
3732
+
3670
3733
  //store function that renders text in select2
3671
- this.formatSelection = this.options.select2.formatSelection;
3734
+ this.formatSelection = this.options.select2.formatSelection;
3672
3735
  if (typeof(this.formatSelection) !== "function") {
3673
3736
  this.formatSelection = function (e) { return e.text; };
3674
- }
3737
+ }
3675
3738
  };
3676
3739
 
3677
3740
  $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
@@ -3693,61 +3756,55 @@ $(function(){
3693
3756
  }, this));
3694
3757
  }
3695
3758
 
3696
- //trigger resize of editableform to re-position container in multi-valued mode
3759
+ //trigger resize of editableform to re-position container in multi-valued mode
3697
3760
  if(this.isMultiple) {
3698
3761
  this.$input.on('change', function() {
3699
3762
  $(this).closest('form').parent().triggerHandler('resize');
3700
- });
3763
+ });
3701
3764
  }
3702
3765
  },
3703
-
3766
+
3704
3767
  value2html: function(value, element) {
3705
3768
  var text = '', data,
3706
3769
  that = this;
3707
-
3770
+
3708
3771
  if(this.options.select2.tags) { //in tags mode just assign value
3709
3772
  data = value;
3710
- //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
3773
+ //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
3711
3774
  } else if(this.sourceData) {
3712
3775
  data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3713
3776
  } else {
3714
3777
  //can not get list of possible values
3715
- //(e.g. autotext for select2 with ajax source)
3778
+ //(e.g. autotext for select2 with ajax source)
3716
3779
  }
3717
-
3718
- //data may be array (when multiple values allowed)
3780
+
3781
+ //data may be array (when multiple values allowed)
3719
3782
  if($.isArray(data)) {
3720
3783
  //collect selected data and show with separator
3721
3784
  text = [];
3722
3785
  $.each(data, function(k, v){
3723
- text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
3724
- });
3786
+ text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
3787
+ });
3725
3788
  } else if(data) {
3726
- text = that.formatSelection(data);
3789
+ text = that.formatSelection(data);
3727
3790
  }
3728
3791
 
3729
3792
  text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
3730
3793
 
3731
3794
  //$(element).text(text);
3732
3795
  Constructor.superclass.value2html.call(this, text, element);
3733
- },
3734
-
3796
+ },
3797
+
3735
3798
  html2value: function(html) {
3736
3799
  return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
3737
- },
3738
-
3800
+ },
3801
+
3739
3802
  value2input: function(value) {
3740
- //for local source use data directly from source (to allow autotext)
3741
- /*
3742
- if(!this.isRemote && !this.isMultiple) {
3743
- var items = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3744
- if(items.length) {
3745
- this.$input.select2('data', items[0]);
3746
- return;
3747
- }
3748
- }
3749
- */
3750
-
3803
+ // if value array => join it anyway
3804
+ if($.isArray(value)) {
3805
+ value = value.join(this.getSeparator());
3806
+ }
3807
+
3751
3808
  //for remote source just set value, text is updated by initSelection
3752
3809
  if(!this.$input.data('select2')) {
3753
3810
  this.$input.val(value);
@@ -3755,15 +3812,27 @@ $(function(){
3755
3812
  } else {
3756
3813
  //second argument needed to separate initial change from user's click (for autosubmit)
3757
3814
  this.$input.val(value).trigger('change', true);
3815
+
3816
+ //Uncaught Error: cannot call val() if initSelection() is not defined
3817
+ //this.$input.select2('val', value);
3758
3818
  }
3759
-
3760
- //if remote source AND no user's initSelection provided --> try to use element's text
3819
+
3820
+ // if defined remote source AND no multiple mode AND no user's initSelection provided -->
3821
+ // we should somehow get text for provided id.
3822
+ // The solution is to use element's text as text for that id (exclude empty)
3761
3823
  if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) {
3824
+ // customId and customText are methods to extract `id` and `text` from data object
3825
+ // we can use this workaround only if user did not define these methods
3826
+ // otherwise we cant construct data object
3762
3827
  var customId = this.options.select2.id,
3763
3828
  customText = this.options.select2.formatSelection;
3764
- if(!customId && !customText) {
3765
- var data = {id: value, text: $(this.options.scope).text()};
3766
- this.$input.select2('data', data);
3829
+
3830
+ if(!customId && !customText) {
3831
+ var $el = $(this.options.scope);
3832
+ if (!$el.data('editable').isEmpty) {
3833
+ var data = {id: value, text: $el.text()};
3834
+ this.$input.select2('data', data);
3835
+ }
3767
3836
  }
3768
3837
  }
3769
3838
  },
@@ -3776,11 +3845,11 @@ $(function(){
3776
3845
  if(typeof str !== 'string' || !this.isMultiple) {
3777
3846
  return str;
3778
3847
  }
3779
-
3780
- separator = separator || this.options.select2.separator || $.fn.select2.defaults.separator;
3781
-
3848
+
3849
+ separator = separator || this.getSeparator();
3850
+
3782
3851
  var val, i, l;
3783
-
3852
+
3784
3853
  if (str === null || str.length < 1) {
3785
3854
  return null;
3786
3855
  }
@@ -3788,10 +3857,10 @@ $(function(){
3788
3857
  for (i = 0, l = val.length; i < l; i = i + 1) {
3789
3858
  val[i] = $.trim(val[i]);
3790
3859
  }
3791
-
3860
+
3792
3861
  return val;
3793
3862
  },
3794
-
3863
+
3795
3864
  autosubmit: function() {
3796
3865
  this.$input.on('change', function(e, isInitial){
3797
3866
  if(!isInitial) {
@@ -3799,7 +3868,11 @@ $(function(){
3799
3868
  }
3800
3869
  });
3801
3870
  },
3802
-
3871
+
3872
+ getSeparator: function() {
3873
+ return this.options.select2.separator || $.fn.select2.defaults.separator;
3874
+ },
3875
+
3803
3876
  /*
3804
3877
  Converts source from x-editable format: {value: 1, text: "1"} to
3805
3878
  select2 format: {id: 1, text: "1"}
@@ -3813,26 +3886,26 @@ $(function(){
3813
3886
  }
3814
3887
  }
3815
3888
  }
3816
- return source;
3889
+ return source;
3817
3890
  },
3818
3891
 
3819
3892
  destroy: function() {
3820
3893
  if(this.$input.data('select2')) {
3821
3894
  this.$input.select2('destroy');
3822
3895
  }
3823
- }
3896
+ }
3824
3897
 
3825
- });
3898
+ });
3826
3899
 
3827
3900
  Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
3828
3901
  /**
3829
3902
  @property tpl
3830
3903
  @default <input type="hidden">
3831
- **/
3904
+ **/
3832
3905
  tpl:'<input type="hidden">',
3833
3906
  /**
3834
3907
  Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
3835
-
3908
+
3836
3909
  @property select2
3837
3910
  @type object
3838
3911
  @default null
@@ -3844,21 +3917,21 @@ $(function(){
3844
3917
  @property placeholder
3845
3918
  @type string
3846
3919
  @default null
3847
- **/
3920
+ **/
3848
3921
  placeholder: null,
3849
3922
  /**
3850
3923
  Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
3851
3924
  Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
3852
- E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
3853
-
3925
+ E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
3926
+
3854
3927
  @property source
3855
3928
  @type array|string|function
3856
3929
  @default null
3857
3930
  **/
3858
3931
  source: null,
3859
3932
  /**
3860
- Separator used to display tags.
3861
-
3933
+ Separator used to display tags.
3934
+
3862
3935
  @property viewseparator
3863
3936
  @type string
3864
3937
  @default ', '
@@ -3866,16 +3939,16 @@ $(function(){
3866
3939
  viewseparator: ', '
3867
3940
  });
3868
3941
 
3869
- $.fn.editabletypes.select2 = Constructor;
3870
-
3942
+ $.fn.editabletypes.select2 = Constructor;
3943
+
3871
3944
  }(window.jQuery));
3872
-
3945
+
3873
3946
  /**
3874
- * Combodate - 1.0.4
3947
+ * Combodate - 1.0.5
3875
3948
  * Dropdown date and time picker.
3876
3949
  * Converts text input into dropdowns to pick day, month, year, hour, minute and second.
3877
3950
  * Uses momentjs as datetime library http://momentjs.com.
3878
- * For internalization include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3951
+ * For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3879
3952
  *
3880
3953
  * Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
3881
3954
  * In combodate:
@@ -3922,16 +3995,22 @@ $(function(){
3922
3995
  this.initCombos();
3923
3996
 
3924
3997
  //update original input on change
3925
- this.$widget.on('change', 'select', $.proxy(function(){
3926
- this.$element.val(this.getValue());
3998
+ this.$widget.on('change', 'select', $.proxy(function(e) {
3999
+ this.$element.val(this.getValue()).change();
4000
+ // update days count if month or year changes
4001
+ if (this.options.smartDays) {
4002
+ if ($(e.target).is('.month') || $(e.target).is('.year')) {
4003
+ this.fillCombo('day');
4004
+ }
4005
+ }
3927
4006
  }, this));
3928
4007
 
3929
4008
  this.$widget.find('select').css('width', 'auto');
3930
4009
 
3931
- //hide original input and insert widget
4010
+ // hide original input and insert widget
3932
4011
  this.$element.hide().after(this.$widget);
3933
4012
 
3934
- //set initial value
4013
+ // set initial value
3935
4014
  this.setValue(this.$element.val() || this.options.value);
3936
4015
  },
3937
4016
 
@@ -3968,22 +4047,41 @@ $(function(){
3968
4047
  Initialize combos that presents in template
3969
4048
  */
3970
4049
  initCombos: function() {
3971
- var that = this;
3972
- $.each(this.map, function(k, v) {
3973
- var $c = that.$widget.find('.'+k), f, items;
3974
- if($c.length) {
3975
- that['$'+k] = $c; //set properties like this.$day, this.$month etc.
3976
- f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); //define method name to fill items, e.g `fillDays`
3977
- items = that[f]();
3978
- that['$'+k].html(that.renderItems(items));
3979
- }
3980
- });
4050
+ for (var k in this.map) {
4051
+ var $c = this.$widget.find('.'+k);
4052
+ // set properties like this.$day, this.$month etc.
4053
+ this['$'+k] = $c.length ? $c : null;
4054
+ // fill with items
4055
+ this.fillCombo(k);
4056
+ }
3981
4057
  },
3982
-
4058
+
4059
+ /*
4060
+ Fill combo with items
4061
+ */
4062
+ fillCombo: function(k) {
4063
+ var $combo = this['$'+k];
4064
+ if (!$combo) {
4065
+ return;
4066
+ }
4067
+
4068
+ // define method name to fill items, e.g `fillDays`
4069
+ var f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1);
4070
+ var items = this[f]();
4071
+ var value = $combo.val();
4072
+
4073
+ $combo.empty();
4074
+ for(var i=0; i<items.length; i++) {
4075
+ $combo.append('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
4076
+ }
4077
+
4078
+ $combo.val(value);
4079
+ },
4080
+
3983
4081
  /*
3984
4082
  Initialize items of combos. Handles `firstItem` option
3985
4083
  */
3986
- initItems: function(key) {
4084
+ fillCommon: function(key) {
3987
4085
  var values = [],
3988
4086
  relTime;
3989
4087
 
@@ -3998,27 +4096,29 @@ $(function(){
3998
4096
  values.push(['', '']);
3999
4097
  }
4000
4098
  return values;
4001
- },
4002
-
4003
- /*
4004
- render items to string of <option> tags
4005
- */
4006
- renderItems: function(items) {
4007
- var str = [];
4008
- for(var i=0; i<items.length; i++) {
4009
- str.push('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
4010
- }
4011
- return str.join("\n");
4012
- },
4099
+ },
4100
+
4013
4101
 
4014
4102
  /*
4015
4103
  fill day
4016
4104
  */
4017
4105
  fillDay: function() {
4018
- var items = this.initItems('d'), name, i,
4019
- twoDigit = this.options.template.indexOf('DD') !== -1;
4020
-
4021
- for(i=1; i<=31; i++) {
4106
+ var items = this.fillCommon('d'), name, i,
4107
+ twoDigit = this.options.template.indexOf('DD') !== -1,
4108
+ daysCount = 31;
4109
+
4110
+ // detect days count (depends on month and year)
4111
+ // originally https://github.com/vitalets/combodate/pull/7
4112
+ if (this.options.smartDays && this.$month && this.$year) {
4113
+ var month = parseInt(this.$month.val(), 10);
4114
+ var year = parseInt(this.$year.val(), 10);
4115
+
4116
+ if (!isNaN(month) && !isNaN(year)) {
4117
+ daysCount = moment([year, month]).daysInMonth();
4118
+ }
4119
+ }
4120
+
4121
+ for (i = 1; i <= daysCount; i++) {
4022
4122
  name = twoDigit ? this.leadZero(i) : i;
4023
4123
  items.push([i, name]);
4024
4124
  }
@@ -4029,7 +4129,7 @@ $(function(){
4029
4129
  fill month
4030
4130
  */
4031
4131
  fillMonth: function() {
4032
- var items = this.initItems('M'), name, i,
4132
+ var items = this.fillCommon('M'), name, i,
4033
4133
  longNames = this.options.template.indexOf('MMMM') !== -1,
4034
4134
  shortNames = this.options.template.indexOf('MMM') !== -1,
4035
4135
  twoDigit = this.options.template.indexOf('MM') !== -1;
@@ -4062,7 +4162,7 @@ $(function(){
4062
4162
  items[this.options.yearDescending ? 'push' : 'unshift']([i, name]);
4063
4163
  }
4064
4164
 
4065
- items = this.initItems('y').concat(items);
4165
+ items = this.fillCommon('y').concat(items);
4066
4166
 
4067
4167
  return items;
4068
4168
  },
@@ -4071,7 +4171,7 @@ $(function(){
4071
4171
  fill hour
4072
4172
  */
4073
4173
  fillHour: function() {
4074
- var items = this.initItems('h'), name, i,
4174
+ var items = this.fillCommon('h'), name, i,
4075
4175
  h12 = this.options.template.indexOf('h') !== -1,
4076
4176
  h24 = this.options.template.indexOf('H') !== -1,
4077
4177
  twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
@@ -4089,7 +4189,7 @@ $(function(){
4089
4189
  fill minute
4090
4190
  */
4091
4191
  fillMinute: function() {
4092
- var items = this.initItems('m'), name, i,
4192
+ var items = this.fillCommon('m'), name, i,
4093
4193
  twoDigit = this.options.template.indexOf('mm') !== -1;
4094
4194
 
4095
4195
  for(i=0; i<=59; i+= this.options.minuteStep) {
@@ -4103,7 +4203,7 @@ $(function(){
4103
4203
  fill second
4104
4204
  */
4105
4205
  fillSecond: function() {
4106
- var items = this.initItems('s'), name, i,
4206
+ var items = this.fillCommon('s'), name, i,
4107
4207
  twoDigit = this.options.template.indexOf('ss') !== -1;
4108
4208
 
4109
4209
  for(i=0; i<=59; i+= this.options.secondStep) {
@@ -4125,7 +4225,7 @@ $(function(){
4125
4225
  ];
4126
4226
  return items;
4127
4227
  },
4128
-
4228
+
4129
4229
  /*
4130
4230
  Returns current date value from combos.
4131
4231
  If format not specified - `options.format` used.
@@ -4188,63 +4288,68 @@ $(function(){
4188
4288
  that = this,
4189
4289
  values = {};
4190
4290
 
4191
- //function to find nearest value in select options
4192
- function getNearest($select, value) {
4193
- var delta = {};
4194
- $select.children('option').each(function(i, opt){
4195
- var optValue = $(opt).attr('value'),
4196
- distance;
4197
-
4198
- if(optValue === '') return;
4199
- distance = Math.abs(optValue - value);
4200
- if(typeof delta.distance === 'undefined' || distance < delta.distance) {
4201
- delta = {value: optValue, distance: distance};
4202
- }
4203
- });
4204
- return delta.value;
4205
- }
4291
+ //function to find nearest value in select options
4292
+ function getNearest($select, value) {
4293
+ var delta = {};
4294
+ $select.children('option').each(function(i, opt){
4295
+ var optValue = $(opt).attr('value'),
4296
+ distance;
4297
+
4298
+ if(optValue === '') return;
4299
+ distance = Math.abs(optValue - value);
4300
+ if(typeof delta.distance === 'undefined' || distance < delta.distance) {
4301
+ delta = {value: optValue, distance: distance};
4302
+ }
4303
+ });
4304
+ return delta.value;
4305
+ }
4206
4306
 
4207
4307
  if(dt.isValid()) {
4208
- //read values from date object
4209
- $.each(this.map, function(k, v) {
4210
- if(k === 'ampm') {
4211
- return;
4212
- }
4213
- values[k] = dt[v[1]]();
4214
- });
4308
+ //read values from date object
4309
+ $.each(this.map, function(k, v) {
4310
+ if(k === 'ampm') {
4311
+ return;
4312
+ }
4313
+ values[k] = dt[v[1]]();
4314
+ });
4215
4315
 
4216
- if(this.$ampm) {
4217
- //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
4218
- if(values.hour >= 12) {
4219
- values.ampm = 'pm';
4220
- if(values.hour > 12) {
4221
- values.hour -= 12;
4222
- }
4223
- } else {
4224
- values.ampm = 'am';
4225
- if(values.hour === 0) {
4226
- values.hour = 12;
4227
- }
4228
- }
4229
- }
4316
+ if(this.$ampm) {
4317
+ //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
4318
+ if(values.hour >= 12) {
4319
+ values.ampm = 'pm';
4320
+ if(values.hour > 12) {
4321
+ values.hour -= 12;
4322
+ }
4323
+ } else {
4324
+ values.ampm = 'am';
4325
+ if(values.hour === 0) {
4326
+ values.hour = 12;
4327
+ }
4328
+ }
4329
+ }
4230
4330
 
4231
- $.each(values, function(k, v) {
4232
- //call val() for each existing combo, e.g. this.$hour.val()
4233
- if(that['$'+k]) {
4331
+ $.each(values, function(k, v) {
4332
+ //call val() for each existing combo, e.g. this.$hour.val()
4333
+ if(that['$'+k]) {
4234
4334
 
4235
- if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) {
4236
- v = getNearest(that['$'+k], v);
4237
- }
4335
+ if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) {
4336
+ v = getNearest(that['$'+k], v);
4337
+ }
4238
4338
 
4239
- if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) {
4240
- v = getNearest(that['$'+k], v);
4241
- }
4339
+ if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) {
4340
+ v = getNearest(that['$'+k], v);
4341
+ }
4242
4342
 
4243
- that['$'+k].val(v);
4244
- }
4245
- });
4343
+ that['$'+k].val(v);
4344
+ }
4345
+ });
4346
+
4347
+ // update days count
4348
+ if (this.options.smartDays) {
4349
+ this.fillCombo('day');
4350
+ }
4246
4351
 
4247
- this.$element.val(dt.format(this.options.format));
4352
+ this.$element.val(dt.format(this.options.format)).change();
4248
4353
  }
4249
4354
  },
4250
4355
 
@@ -4319,10 +4424,11 @@ $(function(){
4319
4424
  secondStep: 1,
4320
4425
  firstItem: 'empty', //'name', 'empty', 'none'
4321
4426
  errorClass: null,
4322
- roundTime: true //whether to round minutes and seconds if step > 1
4427
+ roundTime: true, // whether to round minutes and seconds if step > 1
4428
+ smartDays: false // whether days in combo depend on selected month: 31, 30, 28
4323
4429
  };
4324
4430
 
4325
- }(window.jQuery));
4431
+ }(window.jQuery));
4326
4432
  /**
4327
4433
  Combodate input - dropdown date and time picker.
4328
4434
  Based on [combodate](http://vitalets.github.com/combodate) plugin (included). To use it you should manually include [momentjs](http://momentjs.com).
@@ -4520,7 +4626,7 @@ $(function(){
4520
4626
  $.fn.editabletypes.combodate = Constructor;
4521
4627
 
4522
4628
  }(window.jQuery));
4523
-
4629
+
4524
4630
  /*
4525
4631
  Editableform based on Twitter Bootstrap 2
4526
4632
  */
@@ -4562,7 +4668,7 @@ Editableform based on Twitter Bootstrap 2
4562
4668
  //engine
4563
4669
  $.fn.editableform.engine = 'bs2';
4564
4670
 
4565
- }(window.jQuery));
4671
+ }(window.jQuery));
4566
4672
  /**
4567
4673
  * Editable Popover
4568
4674
  * ---------------------
@@ -4743,7 +4849,7 @@ Editableform based on Twitter Bootstrap 2
4743
4849
  });
4744
4850
 
4745
4851
  }(window.jQuery));
4746
-
4852
+
4747
4853
  /* =========================================================
4748
4854
  * bootstrap-datepicker.js
4749
4855
  * http://www.eyecon.ro/bootstrap-datepicker
@@ -5998,7 +6104,7 @@ Editableform based on Twitter Bootstrap 2
5998
6104
  });
5999
6105
 
6000
6106
  }( window.jQuery ));
6001
-
6107
+
6002
6108
  /**
6003
6109
  Bootstrap-datepicker.
6004
6110
  Description and examples: https://github.com/eternicode/bootstrap-datepicker.
@@ -6197,7 +6303,7 @@ $(function(){
6197
6303
  viewformat: null,
6198
6304
  /**
6199
6305
  Configuration of datepicker.
6200
- Full list of options: http://vitalets.github.com/bootstrap-datepicker
6306
+ Full list of options: http://bootstrap-datepicker.readthedocs.org/en/latest/options.html
6201
6307
 
6202
6308
  @property datepicker
6203
6309
  @type object
@@ -6228,7 +6334,7 @@ $(function(){
6228
6334
  $.fn.editabletypes.date = Date;
6229
6335
 
6230
6336
  }(window.jQuery));
6231
-
6337
+
6232
6338
  /**
6233
6339
  Bootstrap datefield input - modification for inline mode.
6234
6340
  Shows normal <input type="text"> and binds popup datepicker.
@@ -6309,7 +6415,7 @@ Automatically shown in inline mode.
6309
6415
 
6310
6416
  $.fn.editabletypes.datefield = DateField;
6311
6417
 
6312
- }(window.jQuery));
6418
+ }(window.jQuery));
6313
6419
  /**
6314
6420
  Bootstrap-datetimepicker.
6315
6421
  Based on [smalot bootstrap-datetimepicker plugin](https://github.com/smalot/bootstrap-datetimepicker).
@@ -6553,7 +6659,7 @@ $(function(){
6553
6659
 
6554
6660
  $.fn.editabletypes.datetime = DateTime;
6555
6661
 
6556
- }(window.jQuery));
6662
+ }(window.jQuery));
6557
6663
  /**
6558
6664
  Bootstrap datetimefield input - datetime input for inline mode.
6559
6665
  Shows normal <input type="text"> and binds popup datetimepicker.
@@ -6630,7 +6736,7 @@ Automatically shown in inline mode.
6630
6736
 
6631
6737
  $.fn.editabletypes.datetimefield = DateTimeField;
6632
6738
 
6633
- }(window.jQuery));
6739
+ }(window.jQuery));
6634
6740
  /**
6635
6741
  Typeahead input (bootstrap 2 only). Based on Twitter Bootstrap 2 [typeahead](http://getbootstrap.com/2.3.2/javascript.html#typeahead).
6636
6742
  Depending on `source` format typeahead operates in two modes: