x-editable-rails 1.0.2 → 1.0.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- /*! X-editable - v1.4.5
1
+ /*! X-editable - v1.4.6
2
2
  * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
3
  * http://github.com/vitalets/x-editable
4
4
  * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@@ -107,7 +107,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
107
107
  this.error(false);
108
108
  this.input.$input.removeAttr('disabled');
109
109
  this.$form.find('.editable-submit').removeAttr('disabled');
110
- this.input.value2input(this.value);
110
+ var value = (this.value === null || this.value === undefined || this.value === '') ? this.options.defaultValue : this.value;
111
+ this.input.value2input(value);
111
112
  //attach submit handler
112
113
  this.$form.submit($.proxy(this.submit, this));
113
114
  }
@@ -482,8 +483,17 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
482
483
  **/
483
484
  value: null,
484
485
  /**
485
- Strategy for sending data on server. Can be <code>auto|always|never</code>.
486
- When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element.
486
+ Value that will be displayed in input if original field value is empty (`null|undefined|''`).
487
+
488
+ @property defaultValue
489
+ @type string|object
490
+ @default null
491
+ @since 1.4.6
492
+ **/
493
+ defaultValue: null,
494
+ /**
495
+ Strategy for sending data on server. Can be `auto|always|never`.
496
+ When 'auto' data will be sent on server **only if pk and url defined**, otherwise new value will be stored locally.
487
497
 
488
498
  @property send
489
499
  @type string
@@ -753,7 +763,10 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
753
763
  return [];
754
764
  }
755
765
 
756
- valueProp = valueProp || 'value';
766
+ if (typeof(valueProp) !== "function") {
767
+ var idKey = valueProp || 'value';
768
+ valueProp = function (e) { return e[idKey]; };
769
+ }
757
770
 
758
771
  var isValArray = $.isArray(value),
759
772
  result = [],
@@ -765,11 +778,12 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
765
778
  } else {
766
779
  /*jslint eqeq: true*/
767
780
  if(isValArray) {
768
- if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) {
781
+ if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? valueProp(o) : o); }).length) {
769
782
  result.push(o);
770
783
  }
771
784
  } else {
772
- if(value == (o && typeof o === 'object' ? o[valueProp] : o)) {
785
+ var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o;
786
+ if(value == itemValue) {
773
787
  result.push(o);
774
788
  }
775
789
  }
@@ -880,7 +894,8 @@ Applied as jQuery method.
880
894
 
881
895
  //methods
882
896
  Popup.prototype = {
883
- containerName: null, //tbd in child class
897
+ containerName: null, //method to call container on element
898
+ containerDataName: null, //object name in element's .data()
884
899
  innerCss: null, //tbd in child class
885
900
  containerClass: 'editable-container editable-popup', //css class applied to container element
886
901
  init: function(element, options) {
@@ -981,7 +996,16 @@ Applied as jQuery method.
981
996
 
982
997
  /* returns container object */
983
998
  container: function() {
984
- return this.$element.data(this.containerDataName || this.containerName);
999
+ var container;
1000
+ //first, try get it by `containerDataName`
1001
+ if(this.containerDataName) {
1002
+ if(container = this.$element.data(this.containerDataName)) {
1003
+ return container;
1004
+ }
1005
+ }
1006
+ //second, try `containerName`
1007
+ container = this.$element.data(this.containerName);
1008
+ return container;
985
1009
  },
986
1010
 
987
1011
  /* call native method of underlying container, e.g. this.$element.popover('method') */
@@ -1026,7 +1050,7 @@ Applied as jQuery method.
1026
1050
  /*
1027
1051
  TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events.
1028
1052
  */
1029
- this.$element.triggerHandler('shown', this);
1053
+ this.$element.triggerHandler('shown', $(this.options.scope).data('editable'));
1030
1054
  }, this)
1031
1055
  })
1032
1056
  .editableform('render');
@@ -1480,6 +1504,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1480
1504
  //add 'editable' class to every editable element
1481
1505
  this.$element.addClass('editable');
1482
1506
 
1507
+ //specifically for "textarea" add class .editable-pre-wrapped to keep linebreaks
1508
+ if(this.input.type === 'textarea') {
1509
+ this.$element.addClass('editable-pre-wrapped');
1510
+ }
1511
+
1483
1512
  //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
1484
1513
  if(this.options.toggle !== 'manual') {
1485
1514
  this.$element.addClass('editable-click');
@@ -1809,16 +1838,19 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1809
1838
  //highlight when saving
1810
1839
  if(this.options.highlight) {
1811
1840
  var $e = this.$element,
1812
- $bgColor = $e.css('background-color');
1841
+ bgColor = $e.css('background-color');
1813
1842
 
1814
1843
  $e.css('background-color', this.options.highlight);
1815
1844
  setTimeout(function(){
1816
- $e.css('background-color', $bgColor);
1845
+ if(bgColor === 'transparent') {
1846
+ bgColor = '';
1847
+ }
1848
+ $e.css('background-color', bgColor);
1817
1849
  $e.addClass('editable-bg-transition');
1818
1850
  setTimeout(function(){
1819
1851
  $e.removeClass('editable-bg-transition');
1820
1852
  }, 1700);
1821
- }, 0);
1853
+ }, 10);
1822
1854
  }
1823
1855
 
1824
1856
  //set new value
@@ -2032,6 +2064,14 @@ Makes editable any HTML element on the page. Applied as jQuery method.
2032
2064
  data = $this.data(datakey),
2033
2065
  options = typeof option === 'object' && option;
2034
2066
 
2067
+ //for delegated targets do not store `editable` object for element
2068
+ //it's allows several different selectors.
2069
+ //see: https://github.com/vitalets/x-editable/issues/312
2070
+ if(options && options.selector) {
2071
+ data = new Editable(this, options);
2072
+ return;
2073
+ }
2074
+
2035
2075
  if (!data) {
2036
2076
  $this.data(datakey, (data = new Editable(this, options)));
2037
2077
  }
@@ -2371,7 +2411,7 @@ To create your own input you can inherit from this class.
2371
2411
  /**
2372
2412
  Additional actions when destroying element
2373
2413
  **/
2374
- destroy: function() {
2414
+ destroy: function() {
2375
2415
  },
2376
2416
 
2377
2417
  // -------- helper functions --------
@@ -2919,8 +2959,10 @@ $(function(){
2919
2959
  }
2920
2960
  });
2921
2961
  },
2922
-
2923
- value2html: function(value, element) {
2962
+
2963
+ //using `white-space: pre-wrap` solves \n <--> BR conversion very elegant!
2964
+ /*
2965
+ value2html: function(value, element) {
2924
2966
  var html = '', lines;
2925
2967
  if(value) {
2926
2968
  lines = value.split("\n");
@@ -2931,7 +2973,7 @@ $(function(){
2931
2973
  }
2932
2974
  $(element).html(html);
2933
2975
  },
2934
-
2976
+
2935
2977
  html2value: function(html) {
2936
2978
  if(!html) {
2937
2979
  return '';
@@ -2950,7 +2992,7 @@ $(function(){
2950
2992
  }
2951
2993
  return lines.join("\n");
2952
2994
  },
2953
-
2995
+ */
2954
2996
  activate: function() {
2955
2997
  $.fn.editabletypes.text.prototype.activate.call(this);
2956
2998
  }
@@ -3024,12 +3066,19 @@ $(function(){
3024
3066
  this.$input.empty();
3025
3067
 
3026
3068
  var fillItems = function($el, data) {
3069
+ var attr;
3027
3070
  if($.isArray(data)) {
3028
3071
  for(var i=0; i<data.length; i++) {
3072
+ attr = {};
3029
3073
  if(data[i].children) {
3030
- $el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children));
3074
+ attr.label = data[i].text;
3075
+ $el.append(fillItems($('<optgroup>', attr), data[i].children));
3031
3076
  } else {
3032
- $el.append($('<option>', {value: data[i].value}).text(data[i].text));
3077
+ attr.value = data[i].value;
3078
+ if(data[i].disabled) {
3079
+ attr.disabled = true;
3080
+ }
3081
+ $el.append($('<option>', attr).text(data[i].text));
3033
3082
  }
3034
3083
  }
3035
3084
  }
@@ -3241,6 +3290,7 @@ Following types are supported:
3241
3290
  * tel
3242
3291
  * number
3243
3292
  * range
3293
+ * time
3244
3294
 
3245
3295
  Learn more about html5 inputs:
3246
3296
  http://www.w3.org/wiki/HTML5_form_additions
@@ -3425,7 +3475,30 @@ Range (inherit from number)
3425
3475
  inputclass: 'input-medium'
3426
3476
  });
3427
3477
  $.fn.editabletypes.range = Range;
3428
- }(window.jQuery));
3478
+ }(window.jQuery));
3479
+
3480
+ /*
3481
+ Time
3482
+ */
3483
+ (function ($) {
3484
+ "use strict";
3485
+
3486
+ var Time = function (options) {
3487
+ this.init('time', options, Time.defaults);
3488
+ };
3489
+ //inherit from abstract, as inheritance from text gives selection error.
3490
+ $.fn.editableutils.inherit(Time, $.fn.editabletypes.abstractinput);
3491
+ $.extend(Time.prototype, {
3492
+ render: function() {
3493
+ this.setClass();
3494
+ }
3495
+ });
3496
+ Time.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
3497
+ tpl: '<input type="time">'
3498
+ });
3499
+ $.fn.editabletypes.time = Time;
3500
+ }(window.jQuery));
3501
+
3429
3502
  /**
3430
3503
  Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
3431
3504
  Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
@@ -3453,6 +3526,7 @@ You need initially put both `data-value` and element's text youself:
3453
3526
  <a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a>
3454
3527
  <script>
3455
3528
  $(function(){
3529
+ //local source
3456
3530
  $('#country').editable({
3457
3531
  source: [
3458
3532
  {id: 'gb', text: 'Great Britain'},
@@ -3463,6 +3537,42 @@ $(function(){
3463
3537
  multiple: true
3464
3538
  }
3465
3539
  });
3540
+ //remote source (simple)
3541
+ $('#country').editable({
3542
+ source: '/getCountries'
3543
+ });
3544
+ //remote source (advanced)
3545
+ $('#country').editable({
3546
+ select2: {
3547
+ placeholder: 'Select Country',
3548
+ allowClear: true,
3549
+ minimumInputLength: 3,
3550
+ id: function (item) {
3551
+ return item.CountryId;
3552
+ },
3553
+ ajax: {
3554
+ url: '/getCountries',
3555
+ dataType: 'json',
3556
+ data: function (term, page) {
3557
+ return { query: term };
3558
+ },
3559
+ results: function (data, page) {
3560
+ return { results: data };
3561
+ }
3562
+ },
3563
+ formatResult: function (item) {
3564
+ return item.CountryName;
3565
+ },
3566
+ formatSelection: function (item) {
3567
+ return item.CountryName;
3568
+ },
3569
+ initSelection: function (element, callback) {
3570
+ return $.get('/getCountryById', { query: element.val() }, function (data) {
3571
+ callback(data);
3572
+ });
3573
+ }
3574
+ }
3575
+ });
3466
3576
  });
3467
3577
  </script>
3468
3578
  **/
@@ -3511,7 +3621,21 @@ $(function(){
3511
3621
 
3512
3622
  //detect whether it is multi-valued
3513
3623
  this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
3514
- this.isRemote = ('ajax' in this.options.select2);
3624
+ this.isRemote = ('ajax' in this.options.select2);
3625
+
3626
+ //store function returning ID of item
3627
+ //should be here as used inautotext for local source
3628
+ this.idFunc = this.options.select2.id;
3629
+ if (typeof(this.idFunc) !== "function") {
3630
+ var idKey = this.idFunc || 'id';
3631
+ this.idFunc = function (e) { return e[idKey]; };
3632
+ }
3633
+
3634
+ //store function that renders text in select2
3635
+ this.formatSelection = this.options.select2.formatSelection;
3636
+ if (typeof(this.formatSelection) !== "function") {
3637
+ this.formatSelection = function (e) { return e.text; };
3638
+ }
3515
3639
  };
3516
3640
 
3517
3641
  $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
@@ -3536,16 +3660,18 @@ $(function(){
3536
3660
  this.$input.on('change', function() {
3537
3661
  $(this).closest('form').parent().triggerHandler('resize');
3538
3662
  });
3539
- }
3663
+ }
3540
3664
  },
3541
3665
 
3542
3666
  value2html: function(value, element) {
3543
- var text = '', data;
3667
+ var text = '', data,
3668
+ that = this;
3544
3669
 
3545
3670
  if(this.options.select2.tags) { //in tags mode just assign value
3546
3671
  data = value;
3672
+ //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
3547
3673
  } else if(this.sourceData) {
3548
- data = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
3674
+ data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3549
3675
  } else {
3550
3676
  //can not get list of possible values (e.g. autotext for select2 with ajax source)
3551
3677
  }
@@ -3555,10 +3681,10 @@ $(function(){
3555
3681
  //collect selected data and show with separator
3556
3682
  text = [];
3557
3683
  $.each(data, function(k, v){
3558
- text.push(v && typeof v === 'object' ? v.text : v);
3684
+ text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
3559
3685
  });
3560
3686
  } else if(data) {
3561
- text = data.text;
3687
+ text = that.formatSelection(data);
3562
3688
  }
3563
3689
 
3564
3690
  text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
@@ -3571,25 +3697,28 @@ $(function(){
3571
3697
  },
3572
3698
 
3573
3699
  value2input: function(value) {
3574
- //for remote source .val() is not working, need to look in sourceData
3575
- if(this.isRemote) {
3576
- //todo: check value for array
3577
- var item, items;
3578
- //if sourceData loaded, use it to get text for display
3579
- if(this.sourceData) {
3580
- items = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
3581
- if(items.length) {
3582
- item = items[0];
3583
- }
3584
- }
3585
- //if item not found by sourceData, use element text (e.g. for the first show)
3586
- if(!item) {
3587
- item = {id: value, text: $(this.options.scope).text()};
3588
- }
3589
- //select2('data', ...) allows to set both id and text --> usefull for initial show when items are not loaded
3590
- this.$input.select2('data', item).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3591
- } else {
3592
- this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3700
+ //for local source use data directly from source (to allow autotext)
3701
+ /*
3702
+ if(!this.isRemote && !this.isMultiple) {
3703
+ var items = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3704
+ if(items.length) {
3705
+ this.$input.select2('data', items[0]);
3706
+ return;
3707
+ }
3708
+ }
3709
+ */
3710
+
3711
+ //for remote source just set value, text is updated by initSelection
3712
+ this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3713
+
3714
+ //if remote source AND no user's initSelection provided --> try to use element's text
3715
+ if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) {
3716
+ var customId = this.options.select2.id,
3717
+ customText = this.options.select2.formatSelection;
3718
+ if(!customId && !customText) {
3719
+ var data = {id: value, text: $(this.options.scope).text()};
3720
+ this.$input.select2('data', data);
3721
+ }
3593
3722
  }
3594
3723
  },
3595
3724
 
@@ -3639,6 +3768,12 @@ $(function(){
3639
3768
  }
3640
3769
  }
3641
3770
  return source;
3771
+ },
3772
+
3773
+ destroy: function() {
3774
+ if(this.$input.data('select2')) {
3775
+ this.$input.select2('destroy');
3776
+ }
3642
3777
  }
3643
3778
 
3644
3779
  });
@@ -3694,7 +3829,7 @@ $(function(){
3694
3829
  * Dropdown date and time picker.
3695
3830
  * Converts text input into dropdowns to pick day, month, year, hour, minute and second.
3696
3831
  * Uses momentjs as datetime library http://momentjs.com.
3697
- * For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3832
+ * For internalization include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3698
3833
  *
3699
3834
  * Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
3700
3835
  * In combodate:
@@ -1,4 +1,4 @@
1
- /*! X-editable - v1.4.5
1
+ /*! X-editable - v1.4.6
2
2
  * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
3
3
  * http://github.com/vitalets/x-editable
4
4
  * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
@@ -107,7 +107,8 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
107
107
  this.error(false);
108
108
  this.input.$input.removeAttr('disabled');
109
109
  this.$form.find('.editable-submit').removeAttr('disabled');
110
- this.input.value2input(this.value);
110
+ var value = (this.value === null || this.value === undefined || this.value === '') ? this.options.defaultValue : this.value;
111
+ this.input.value2input(value);
111
112
  //attach submit handler
112
113
  this.$form.submit($.proxy(this.submit, this));
113
114
  }
@@ -482,8 +483,17 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
482
483
  **/
483
484
  value: null,
484
485
  /**
485
- Strategy for sending data on server. Can be <code>auto|always|never</code>.
486
- When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element.
486
+ Value that will be displayed in input if original field value is empty (`null|undefined|''`).
487
+
488
+ @property defaultValue
489
+ @type string|object
490
+ @default null
491
+ @since 1.4.6
492
+ **/
493
+ defaultValue: null,
494
+ /**
495
+ Strategy for sending data on server. Can be `auto|always|never`.
496
+ When 'auto' data will be sent on server **only if pk and url defined**, otherwise new value will be stored locally.
487
497
 
488
498
  @property send
489
499
  @type string
@@ -753,7 +763,10 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
753
763
  return [];
754
764
  }
755
765
 
756
- valueProp = valueProp || 'value';
766
+ if (typeof(valueProp) !== "function") {
767
+ var idKey = valueProp || 'value';
768
+ valueProp = function (e) { return e[idKey]; };
769
+ }
757
770
 
758
771
  var isValArray = $.isArray(value),
759
772
  result = [],
@@ -765,11 +778,12 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
765
778
  } else {
766
779
  /*jslint eqeq: true*/
767
780
  if(isValArray) {
768
- if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) {
781
+ if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? valueProp(o) : o); }).length) {
769
782
  result.push(o);
770
783
  }
771
784
  } else {
772
- if(value == (o && typeof o === 'object' ? o[valueProp] : o)) {
785
+ var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o;
786
+ if(value == itemValue) {
773
787
  result.push(o);
774
788
  }
775
789
  }
@@ -880,7 +894,8 @@ Applied as jQuery method.
880
894
 
881
895
  //methods
882
896
  Popup.prototype = {
883
- containerName: null, //tbd in child class
897
+ containerName: null, //method to call container on element
898
+ containerDataName: null, //object name in element's .data()
884
899
  innerCss: null, //tbd in child class
885
900
  containerClass: 'editable-container editable-popup', //css class applied to container element
886
901
  init: function(element, options) {
@@ -981,7 +996,16 @@ Applied as jQuery method.
981
996
 
982
997
  /* returns container object */
983
998
  container: function() {
984
- return this.$element.data(this.containerDataName || this.containerName);
999
+ var container;
1000
+ //first, try get it by `containerDataName`
1001
+ if(this.containerDataName) {
1002
+ if(container = this.$element.data(this.containerDataName)) {
1003
+ return container;
1004
+ }
1005
+ }
1006
+ //second, try `containerName`
1007
+ container = this.$element.data(this.containerName);
1008
+ return container;
985
1009
  },
986
1010
 
987
1011
  /* call native method of underlying container, e.g. this.$element.popover('method') */
@@ -1026,7 +1050,7 @@ Applied as jQuery method.
1026
1050
  /*
1027
1051
  TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events.
1028
1052
  */
1029
- this.$element.triggerHandler('shown', this);
1053
+ this.$element.triggerHandler('shown', $(this.options.scope).data('editable'));
1030
1054
  }, this)
1031
1055
  })
1032
1056
  .editableform('render');
@@ -1480,6 +1504,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1480
1504
  //add 'editable' class to every editable element
1481
1505
  this.$element.addClass('editable');
1482
1506
 
1507
+ //specifically for "textarea" add class .editable-pre-wrapped to keep linebreaks
1508
+ if(this.input.type === 'textarea') {
1509
+ this.$element.addClass('editable-pre-wrapped');
1510
+ }
1511
+
1483
1512
  //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
1484
1513
  if(this.options.toggle !== 'manual') {
1485
1514
  this.$element.addClass('editable-click');
@@ -1809,16 +1838,19 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1809
1838
  //highlight when saving
1810
1839
  if(this.options.highlight) {
1811
1840
  var $e = this.$element,
1812
- $bgColor = $e.css('background-color');
1841
+ bgColor = $e.css('background-color');
1813
1842
 
1814
1843
  $e.css('background-color', this.options.highlight);
1815
1844
  setTimeout(function(){
1816
- $e.css('background-color', $bgColor);
1845
+ if(bgColor === 'transparent') {
1846
+ bgColor = '';
1847
+ }
1848
+ $e.css('background-color', bgColor);
1817
1849
  $e.addClass('editable-bg-transition');
1818
1850
  setTimeout(function(){
1819
1851
  $e.removeClass('editable-bg-transition');
1820
1852
  }, 1700);
1821
- }, 0);
1853
+ }, 10);
1822
1854
  }
1823
1855
 
1824
1856
  //set new value
@@ -2032,6 +2064,14 @@ Makes editable any HTML element on the page. Applied as jQuery method.
2032
2064
  data = $this.data(datakey),
2033
2065
  options = typeof option === 'object' && option;
2034
2066
 
2067
+ //for delegated targets do not store `editable` object for element
2068
+ //it's allows several different selectors.
2069
+ //see: https://github.com/vitalets/x-editable/issues/312
2070
+ if(options && options.selector) {
2071
+ data = new Editable(this, options);
2072
+ return;
2073
+ }
2074
+
2035
2075
  if (!data) {
2036
2076
  $this.data(datakey, (data = new Editable(this, options)));
2037
2077
  }
@@ -2371,7 +2411,7 @@ To create your own input you can inherit from this class.
2371
2411
  /**
2372
2412
  Additional actions when destroying element
2373
2413
  **/
2374
- destroy: function() {
2414
+ destroy: function() {
2375
2415
  },
2376
2416
 
2377
2417
  // -------- helper functions --------
@@ -2919,8 +2959,10 @@ $(function(){
2919
2959
  }
2920
2960
  });
2921
2961
  },
2922
-
2923
- value2html: function(value, element) {
2962
+
2963
+ //using `white-space: pre-wrap` solves \n <--> BR conversion very elegant!
2964
+ /*
2965
+ value2html: function(value, element) {
2924
2966
  var html = '', lines;
2925
2967
  if(value) {
2926
2968
  lines = value.split("\n");
@@ -2931,7 +2973,7 @@ $(function(){
2931
2973
  }
2932
2974
  $(element).html(html);
2933
2975
  },
2934
-
2976
+
2935
2977
  html2value: function(html) {
2936
2978
  if(!html) {
2937
2979
  return '';
@@ -2950,7 +2992,7 @@ $(function(){
2950
2992
  }
2951
2993
  return lines.join("\n");
2952
2994
  },
2953
-
2995
+ */
2954
2996
  activate: function() {
2955
2997
  $.fn.editabletypes.text.prototype.activate.call(this);
2956
2998
  }
@@ -3024,12 +3066,19 @@ $(function(){
3024
3066
  this.$input.empty();
3025
3067
 
3026
3068
  var fillItems = function($el, data) {
3069
+ var attr;
3027
3070
  if($.isArray(data)) {
3028
3071
  for(var i=0; i<data.length; i++) {
3072
+ attr = {};
3029
3073
  if(data[i].children) {
3030
- $el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children));
3074
+ attr.label = data[i].text;
3075
+ $el.append(fillItems($('<optgroup>', attr), data[i].children));
3031
3076
  } else {
3032
- $el.append($('<option>', {value: data[i].value}).text(data[i].text));
3077
+ attr.value = data[i].value;
3078
+ if(data[i].disabled) {
3079
+ attr.disabled = true;
3080
+ }
3081
+ $el.append($('<option>', attr).text(data[i].text));
3033
3082
  }
3034
3083
  }
3035
3084
  }
@@ -3241,6 +3290,7 @@ Following types are supported:
3241
3290
  * tel
3242
3291
  * number
3243
3292
  * range
3293
+ * time
3244
3294
 
3245
3295
  Learn more about html5 inputs:
3246
3296
  http://www.w3.org/wiki/HTML5_form_additions
@@ -3425,7 +3475,30 @@ Range (inherit from number)
3425
3475
  inputclass: 'input-medium'
3426
3476
  });
3427
3477
  $.fn.editabletypes.range = Range;
3428
- }(window.jQuery));
3478
+ }(window.jQuery));
3479
+
3480
+ /*
3481
+ Time
3482
+ */
3483
+ (function ($) {
3484
+ "use strict";
3485
+
3486
+ var Time = function (options) {
3487
+ this.init('time', options, Time.defaults);
3488
+ };
3489
+ //inherit from abstract, as inheritance from text gives selection error.
3490
+ $.fn.editableutils.inherit(Time, $.fn.editabletypes.abstractinput);
3491
+ $.extend(Time.prototype, {
3492
+ render: function() {
3493
+ this.setClass();
3494
+ }
3495
+ });
3496
+ Time.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
3497
+ tpl: '<input type="time">'
3498
+ });
3499
+ $.fn.editabletypes.time = Time;
3500
+ }(window.jQuery));
3501
+
3429
3502
  /**
3430
3503
  Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
3431
3504
  Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options.
@@ -3453,6 +3526,7 @@ You need initially put both `data-value` and element's text youself:
3453
3526
  <a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a>
3454
3527
  <script>
3455
3528
  $(function(){
3529
+ //local source
3456
3530
  $('#country').editable({
3457
3531
  source: [
3458
3532
  {id: 'gb', text: 'Great Britain'},
@@ -3463,6 +3537,42 @@ $(function(){
3463
3537
  multiple: true
3464
3538
  }
3465
3539
  });
3540
+ //remote source (simple)
3541
+ $('#country').editable({
3542
+ source: '/getCountries'
3543
+ });
3544
+ //remote source (advanced)
3545
+ $('#country').editable({
3546
+ select2: {
3547
+ placeholder: 'Select Country',
3548
+ allowClear: true,
3549
+ minimumInputLength: 3,
3550
+ id: function (item) {
3551
+ return item.CountryId;
3552
+ },
3553
+ ajax: {
3554
+ url: '/getCountries',
3555
+ dataType: 'json',
3556
+ data: function (term, page) {
3557
+ return { query: term };
3558
+ },
3559
+ results: function (data, page) {
3560
+ return { results: data };
3561
+ }
3562
+ },
3563
+ formatResult: function (item) {
3564
+ return item.CountryName;
3565
+ },
3566
+ formatSelection: function (item) {
3567
+ return item.CountryName;
3568
+ },
3569
+ initSelection: function (element, callback) {
3570
+ return $.get('/getCountryById', { query: element.val() }, function (data) {
3571
+ callback(data);
3572
+ });
3573
+ }
3574
+ }
3575
+ });
3466
3576
  });
3467
3577
  </script>
3468
3578
  **/
@@ -3511,7 +3621,21 @@ $(function(){
3511
3621
 
3512
3622
  //detect whether it is multi-valued
3513
3623
  this.isMultiple = this.options.select2.tags || this.options.select2.multiple;
3514
- this.isRemote = ('ajax' in this.options.select2);
3624
+ this.isRemote = ('ajax' in this.options.select2);
3625
+
3626
+ //store function returning ID of item
3627
+ //should be here as used inautotext for local source
3628
+ this.idFunc = this.options.select2.id;
3629
+ if (typeof(this.idFunc) !== "function") {
3630
+ var idKey = this.idFunc || 'id';
3631
+ this.idFunc = function (e) { return e[idKey]; };
3632
+ }
3633
+
3634
+ //store function that renders text in select2
3635
+ this.formatSelection = this.options.select2.formatSelection;
3636
+ if (typeof(this.formatSelection) !== "function") {
3637
+ this.formatSelection = function (e) { return e.text; };
3638
+ }
3515
3639
  };
3516
3640
 
3517
3641
  $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
@@ -3536,16 +3660,18 @@ $(function(){
3536
3660
  this.$input.on('change', function() {
3537
3661
  $(this).closest('form').parent().triggerHandler('resize');
3538
3662
  });
3539
- }
3663
+ }
3540
3664
  },
3541
3665
 
3542
3666
  value2html: function(value, element) {
3543
- var text = '', data;
3667
+ var text = '', data,
3668
+ that = this;
3544
3669
 
3545
3670
  if(this.options.select2.tags) { //in tags mode just assign value
3546
3671
  data = value;
3672
+ //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc);
3547
3673
  } else if(this.sourceData) {
3548
- data = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
3674
+ data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3549
3675
  } else {
3550
3676
  //can not get list of possible values (e.g. autotext for select2 with ajax source)
3551
3677
  }
@@ -3555,10 +3681,10 @@ $(function(){
3555
3681
  //collect selected data and show with separator
3556
3682
  text = [];
3557
3683
  $.each(data, function(k, v){
3558
- text.push(v && typeof v === 'object' ? v.text : v);
3684
+ text.push(v && typeof v === 'object' ? that.formatSelection(v) : v);
3559
3685
  });
3560
3686
  } else if(data) {
3561
- text = data.text;
3687
+ text = that.formatSelection(data);
3562
3688
  }
3563
3689
 
3564
3690
  text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
@@ -3571,25 +3697,28 @@ $(function(){
3571
3697
  },
3572
3698
 
3573
3699
  value2input: function(value) {
3574
- //for remote source .val() is not working, need to look in sourceData
3575
- if(this.isRemote) {
3576
- //todo: check value for array
3577
- var item, items;
3578
- //if sourceData loaded, use it to get text for display
3579
- if(this.sourceData) {
3580
- items = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id');
3581
- if(items.length) {
3582
- item = items[0];
3583
- }
3584
- }
3585
- //if item not found by sourceData, use element text (e.g. for the first show)
3586
- if(!item) {
3587
- item = {id: value, text: $(this.options.scope).text()};
3588
- }
3589
- //select2('data', ...) allows to set both id and text --> usefull for initial show when items are not loaded
3590
- this.$input.select2('data', item).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3591
- } else {
3592
- this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3700
+ //for local source use data directly from source (to allow autotext)
3701
+ /*
3702
+ if(!this.isRemote && !this.isMultiple) {
3703
+ var items = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc);
3704
+ if(items.length) {
3705
+ this.$input.select2('data', items[0]);
3706
+ return;
3707
+ }
3708
+ }
3709
+ */
3710
+
3711
+ //for remote source just set value, text is updated by initSelection
3712
+ this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit)
3713
+
3714
+ //if remote source AND no user's initSelection provided --> try to use element's text
3715
+ if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) {
3716
+ var customId = this.options.select2.id,
3717
+ customText = this.options.select2.formatSelection;
3718
+ if(!customId && !customText) {
3719
+ var data = {id: value, text: $(this.options.scope).text()};
3720
+ this.$input.select2('data', data);
3721
+ }
3593
3722
  }
3594
3723
  },
3595
3724
 
@@ -3639,6 +3768,12 @@ $(function(){
3639
3768
  }
3640
3769
  }
3641
3770
  return source;
3771
+ },
3772
+
3773
+ destroy: function() {
3774
+ if(this.$input.data('select2')) {
3775
+ this.$input.select2('destroy');
3776
+ }
3642
3777
  }
3643
3778
 
3644
3779
  });
@@ -3694,7 +3829,7 @@ $(function(){
3694
3829
  * Dropdown date and time picker.
3695
3830
  * Converts text input into dropdowns to pick day, month, year, hour, minute and second.
3696
3831
  * Uses momentjs as datetime library http://momentjs.com.
3697
- * For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3832
+ * For internalization include corresponding file from https://github.com/timrwood/moment/tree/master/lang
3698
3833
  *
3699
3834
  * Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight
3700
3835
  * In combodate:
@@ -4373,13 +4508,20 @@ Editableform based on jQuery UI
4373
4508
  //extend methods
4374
4509
  $.extend($.fn.editableContainer.Popup.prototype, {
4375
4510
  containerName: 'tooltip', //jQuery method, aplying the widget
4376
- containerDataName: 'uiTooltip', //object name in elements .data() (e.g. uiTooltip for tooltip)
4511
+ //object name in element's .data()
4512
+ containerDataName: 'ui-tooltip',
4377
4513
  innerCss: '.ui-tooltip-content',
4378
4514
 
4379
4515
  //split options on containerOptions and formOptions
4380
4516
  splitOptions: function() {
4381
4517
  this.containerOptions = {};
4382
4518
  this.formOptions = {};
4519
+
4520
+ //check that jQueryUI build contains tooltip widget
4521
+ if(!$.ui[this.containerName]) {
4522
+ $.error('Please use jQueryUI with "tooltip" widget! http://jqueryui.com/download');
4523
+ return;
4524
+ }
4383
4525
  //defaults for tooltip
4384
4526
  var cDef = $.ui[this.containerName].prototype.options;
4385
4527
  for(var k in this.options) {
@@ -4408,7 +4550,7 @@ Editableform based on jQuery UI
4408
4550
 
4409
4551
  this.call(this.containerOptions);
4410
4552
 
4411
- //disable standart triggering tooltip event
4553
+ //disable standart triggering tooltip events
4412
4554
  this.container()._off(this.container().element, 'mouseover focusin');
4413
4555
  },
4414
4556
 
@@ -4442,25 +4584,29 @@ Editableform based on jQuery UI
4442
4584
  case 'top':
4443
4585
  pos = {
4444
4586
  my: "center bottom-5",
4445
- at: "center top"
4587
+ at: "center top",
4588
+ collision: 'flipfit'
4446
4589
  };
4447
4590
  break;
4448
4591
  case 'right':
4449
4592
  pos = {
4450
4593
  my: "left+5 center",
4451
- at: "right center"
4594
+ at: "right center",
4595
+ collision: 'flipfit'
4452
4596
  };
4453
4597
  break;
4454
4598
  case 'bottom':
4455
4599
  pos = {
4456
4600
  my: "center top+5",
4457
- at: "center bottom"
4601
+ at: "center bottom",
4602
+ collision: 'flipfit'
4458
4603
  };
4459
4604
  break;
4460
4605
  case 'left':
4461
4606
  pos = {
4462
4607
  my: "right-5 center",
4463
- at: "left center"
4608
+ at: "left center",
4609
+ collision: 'flipfit'
4464
4610
  };
4465
4611
  break;
4466
4612
  }