x-editable-rails 1.5.1 → 1.5.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: