bootstrap-editable-rails 0.0.1 → 0.0.2

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.
data/README.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  In-place editing with Twitter Bootstrap for Rails
4
4
 
5
- This gem is based on X-editable (v1.1.1) which is the new version of Bootstrap Editable.
5
+ This gem is based on X-editable (v1.3.0) which is the new version of Bootstrap Editable.
6
6
 
7
7
  https://github.com/vitalets/x-editable
8
8
 
@@ -22,6 +22,8 @@ And then execute:
22
22
 
23
23
  ## Usage
24
24
 
25
+ ### JavaScript & Stylesheet
26
+
25
27
  Write the top of `app/assets/javascripts/application.js` like this:
26
28
 
27
29
  ```javascript
@@ -33,8 +35,55 @@ Write the top of `app/assets/javascripts/application.js` like this:
33
35
  //= require_tree .
34
36
  ```
35
37
 
38
+ (You can choose `bootstrap-editable-inline`)
39
+
36
40
  and need to load `bootstrap-editable.css` at the place where you like.
37
41
 
42
+ ### HTML
43
+
44
+ Follow the documents above.
45
+
46
+ Additional required attribute(option) is `resource`.
47
+
48
+ ```html
49
+ <a href="#" id="username" data-type="text" data-resource="post" data-name="username" data-url="/posts/1" data-original-title="Enter username">superuser</a>
50
+ ```
51
+
52
+ then, sends `PUT /posts/1` request with the body:
53
+
54
+ ```
55
+ post[username]=superuser
56
+ ```
57
+
58
+ ### Controller
59
+
60
+ PostsController receives the parameters
61
+
62
+ ```
63
+ { "id" => "1", "post" => { "username" => "superuser" } }
64
+ ```
65
+
66
+ and must respond with 2xx (means _success_) status code if successful.
67
+
68
+ For example, scaffold goes well because default dataType is json.
69
+
70
+ ```ruby
71
+ def update
72
+ @post = Post.find(params[:id])
73
+
74
+ respond_to do |format|
75
+ if @post.update_attributes(params[:post])
76
+ format.html { redirect_to @post, notice: 'Post was successfully updated.' }
77
+ format.json { head :no_content } # 204 No Content
78
+ else
79
+ format.html { render action: "edit" }
80
+ format.json { render json: @post.errors, status: :unprocessable_entity }
81
+ end
82
+ end
83
+ end
84
+ ```
85
+
86
+
38
87
  ## Contributing
39
88
 
40
89
  1. Fork it
@@ -1,7 +1,7 @@
1
1
  module Bootstrap
2
2
  module Editable
3
3
  module Rails
4
- VERSION = "0.0.1"
4
+ VERSION = "0.0.2"
5
5
  end
6
6
  end
7
7
  end
@@ -1,12 +1,12 @@
1
- /*! X-editable - v1.1.1
1
+ /*! X-editable - v1.3.0
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) 2012 Vitaliy Potapov; Licensed MIT */
5
5
 
6
6
  /**
7
7
  Form with single input element, two buttons and two states: normal/loading.
8
- Applied as jQuery method to DIV tag (not to form tag!)
9
- Editableform is linked with one of input types, e.g. 'text' or 'select'.
8
+ Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
9
+ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
10
10
 
11
11
  @class editableform
12
12
  @uses text
@@ -14,9 +14,12 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
14
14
  **/
15
15
  (function ($) {
16
16
 
17
- var EditableForm = function (element, options) {
17
+ var EditableForm = function (div, options) {
18
18
  this.options = $.extend({}, $.fn.editableform.defaults, options);
19
- this.$element = $(element); //div, containing form. Not form tag! Not editable-element.
19
+ this.$div = $(div); //div, containing form. Not form tag! Not editable-element.
20
+ if(!this.options.scope) {
21
+ this.options.scope = this;
22
+ }
20
23
  this.initInput();
21
24
  };
22
25
 
@@ -26,9 +29,9 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
26
29
  var TypeConstructor, typeOptions;
27
30
 
28
31
  //create input of specified type
29
- if(typeof $.fn.editableform.types[this.options.type] === 'function') {
30
- TypeConstructor = $.fn.editableform.types[this.options.type];
31
- typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
32
+ if(typeof $.fn.editabletypes[this.options.type] === 'function') {
33
+ TypeConstructor = $.fn.editabletypes[this.options.type];
34
+ typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
32
35
  this.input = new TypeConstructor(typeOptions);
33
36
  } else {
34
37
  $.error('Unknown type: '+ this.options.type);
@@ -50,7 +53,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
50
53
  **/
51
54
  render: function() {
52
55
  this.$loading = $($.fn.editableform.loading);
53
- this.$element.empty().append(this.$loading);
56
+ this.$div.empty().append(this.$loading);
54
57
  this.showLoading();
55
58
 
56
59
  //init form template and buttons
@@ -66,7 +69,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
66
69
  @event rendering
67
70
  @param {Object} event event object
68
71
  **/
69
- this.$element.triggerHandler('rendering');
72
+ this.$div.triggerHandler('rendering');
70
73
 
71
74
  //render input
72
75
  $.when(this.input.render())
@@ -85,21 +88,23 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
85
88
  }
86
89
 
87
90
  //append form to container
88
- this.$element.append(this.$form);
89
-
91
+ this.$div.append(this.$form);
92
+
90
93
  //attach 'cancel' handler
91
94
  this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
92
- // this.$form.find('.editable-buttons button').eq(1).click($.proxy(this.cancel, this));
93
95
 
94
96
  if(this.input.error) {
95
97
  this.error(this.input.error);
96
98
  this.$form.find('.editable-submit').attr('disabled', true);
97
99
  this.input.$input.attr('disabled', true);
100
+ //prevent form from submitting
101
+ this.$form.submit(function(e){ e.preventDefault(); });
98
102
  } else {
99
103
  this.error(false);
100
104
  this.input.$input.removeAttr('disabled');
101
105
  this.$form.find('.editable-submit').removeAttr('disabled');
102
106
  this.input.value2input(this.value);
107
+ //attach submit handler
103
108
  this.$form.submit($.proxy(this.submit, this));
104
109
  }
105
110
 
@@ -108,7 +113,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
108
113
  @event rendered
109
114
  @param {Object} event event object
110
115
  **/
111
- this.$element.triggerHandler('rendered');
116
+ this.$div.triggerHandler('rendered');
112
117
 
113
118
  this.showForm();
114
119
  }, this));
@@ -119,7 +124,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
119
124
  @event cancel
120
125
  @param {Object} event event object
121
126
  **/
122
- this.$element.triggerHandler('cancel');
127
+ this.$div.triggerHandler('cancel');
123
128
  },
124
129
  showLoading: function() {
125
130
  var w;
@@ -138,16 +143,18 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
138
143
  this.$loading.show();
139
144
  },
140
145
 
141
- showForm: function() {
146
+ showForm: function(activate) {
142
147
  this.$loading.hide();
143
148
  this.$form.show();
144
- this.input.activate();
149
+ if(activate !== false) {
150
+ this.input.activate();
151
+ }
145
152
  /**
146
153
  Fired when form is shown
147
154
  @event show
148
155
  @param {Object} event event object
149
156
  **/
150
- this.$element.triggerHandler('show');
157
+ this.$div.triggerHandler('show');
151
158
  },
152
159
 
153
160
  error: function(msg) {
@@ -168,8 +175,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
168
175
  e.preventDefault();
169
176
 
170
177
  var error,
171
- newValue = this.input.input2value(), //get new value from input
172
- newValueStr;
178
+ newValue = this.input.input2value(); //get new value from input
173
179
 
174
180
  //validation
175
181
  if (error = this.validate(newValue)) {
@@ -178,25 +184,34 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
178
184
  return;
179
185
  }
180
186
 
181
- //value as string
182
- newValueStr = this.input.value2str(newValue);
183
-
184
- //if value not changed --> cancel
187
+ //if value not changed --> trigger 'nochange' event and return
185
188
  /*jslint eqeq: true*/
186
- if (newValueStr == this.input.value2str(this.value)) {
189
+ if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) {
187
190
  /*jslint eqeq: false*/
188
- this.cancel();
191
+ /**
192
+ Fired when value not changed but form is submitted. Requires savenochange = false.
193
+ @event nochange
194
+ @param {Object} event event object
195
+ **/
196
+ this.$div.triggerHandler('nochange');
189
197
  return;
190
198
  }
191
199
 
192
200
  //sending data to server
193
- $.when(this.save(newValueStr))
201
+ $.when(this.save(newValue))
194
202
  .done($.proxy(function(response) {
195
203
  //run success callback
196
- var res = typeof this.options.success === 'function' ? this.options.success.call(this, response, newValue) : null;
204
+ var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
197
205
 
198
- //if success callback returns string --> show error
199
- if(res && typeof res === 'string') {
206
+ //if success callback returns false --> keep form open and do not activate input
207
+ if(res === false) {
208
+ this.error(false);
209
+ this.showForm(false);
210
+ return;
211
+ }
212
+
213
+ //if success callback returns string --> keep form open, show error and activate input
214
+ if(typeof res === 'string') {
200
215
  this.error(res);
201
216
  this.showForm();
202
217
  return;
@@ -223,7 +238,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
223
238
  if(params.newValue === 'username') {...}
224
239
  });
225
240
  **/
226
- this.$element.triggerHandler('save', {newValue: newValue, response: response});
241
+ this.$div.triggerHandler('save', {newValue: newValue, response: response});
227
242
  }, this))
228
243
  .fail($.proxy(function(xhr) {
229
244
  this.error(typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!');
@@ -231,10 +246,16 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
231
246
  }, this));
232
247
  },
233
248
 
234
- save: function(value) {
235
- var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this) : this.options.pk,
249
+ save: function(newValue) {
250
+ //convert value for submitting to server
251
+ var submitValue = this.input.value2submit(newValue);
252
+
253
+ //try parse composite pk defined as json string in data-pk
254
+ this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true);
255
+
256
+ var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk,
236
257
  send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk)))),
237
- params, ajaxOptions;
258
+ params;
238
259
 
239
260
  if (send) { //send to server
240
261
  this.showLoading();
@@ -242,30 +263,28 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
242
263
  //standard params
243
264
  params = {
244
265
  name: this.options.name || '',
245
- value: value,
266
+ value: submitValue,
246
267
  pk: pk
247
268
  };
248
269
 
249
270
  //additional params
250
271
  if(typeof this.options.params === 'function') {
251
- $.extend(params, this.options.params.call(this, params));
272
+ params = this.options.params.call(this.options.scope, params);
252
273
  } else {
253
274
  //try parse json in single quotes (from data-params attribute)
254
- this.options.params = $.fn.editableform.utils.tryParseJson(this.options.params, true);
275
+ this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true);
255
276
  $.extend(params, this.options.params);
256
277
  }
257
278
 
258
279
  if(typeof this.options.url === 'function') { //user's function
259
- return this.options.url.call(this, params);
260
- } else { //send ajax to server and return deferred object
261
- ajaxOptions = $.extend({
280
+ return this.options.url.call(this.options.scope, params);
281
+ } else {
282
+ //send ajax to server and return deferred object
283
+ return $.ajax($.extend({
262
284
  url : this.options.url,
263
285
  data : params,
264
- type : 'post',
265
- dataType: 'json'
266
- }, this.options.ajaxOptions);
267
-
268
- return $.ajax(ajaxOptions);
286
+ type : 'POST'
287
+ }, this.options.ajaxOptions));
269
288
  }
270
289
  }
271
290
  },
@@ -275,7 +294,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
275
294
  value = this.value;
276
295
  }
277
296
  if (typeof this.options.validate === 'function') {
278
- return this.options.validate.call(this, value);
297
+ return this.options.validate.call(this.options.scope, value);
279
298
  }
280
299
  },
281
300
 
@@ -361,10 +380,13 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
361
380
  **/
362
381
  url:null,
363
382
  /**
364
- Additional params for submit. Function can be used to calculate params dynamically
383
+ Additional params for submit. If defined as <code>object</code> - it is **appended** to original ajax data (pk, name and value).
384
+ If defined as <code>function</code> - returned object **overwrites** original ajax data.
365
385
  @example
366
386
  params: function(params) {
367
- return { a: 1 };
387
+ //originally params contain pk, name and value
388
+ params.a = 1;
389
+ return params;
368
390
  }
369
391
 
370
392
  @property params
@@ -382,7 +404,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
382
404
  name: null,
383
405
  /**
384
406
  Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. <code>{id: 1, lang: 'en'}</code>.
385
- Can be calculated dinamically via function.
407
+ Can be calculated dynamically via function.
386
408
 
387
409
  @property pk
388
410
  @type string|object|function
@@ -423,7 +445,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
423
445
  validate: null,
424
446
  /**
425
447
  Success callback. Called when value successfully sent on server and **response status = 200**.
426
- Usefull to work with json response. For example, if your backend response can be <code>{success: true}</code>
448
+ Useful to work with json response. For example, if your backend response can be <code>{success: true}</code>
427
449
  or <code>{success: false, msg: "server error"}</code> you can check it inside this callback.
428
450
  If it returns **string** - means error occured and string is shown as error message.
429
451
  If it returns **object like** <code>{newValue: &lt;something&gt;}</code> - it overwrites value, submitted by user.
@@ -437,7 +459,7 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
437
459
  if(!response.success) return response.msg;
438
460
  }
439
461
  **/
440
- success: function(response, newValue) {},
462
+ success: null,
441
463
  /**
442
464
  Additional options for ajax request.
443
465
  List of values: http://api.jquery.com/jQuery.ajax
@@ -445,28 +467,44 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
445
467
  @property ajaxOptions
446
468
  @type object
447
469
  @default null
470
+ @since 1.1.1
448
471
  **/
449
472
  ajaxOptions: null,
450
473
  /**
451
- Wether to show buttons or not.
474
+ Whether to show buttons or not.
452
475
  Form without buttons can be auto-submitted by input or by onblur = 'submit'.
476
+ @example
477
+ ajaxOptions: {
478
+ method: 'PUT',
479
+ dataType: 'xml'
480
+ }
453
481
 
454
482
  @property showbuttons
455
483
  @type boolean
456
484
  @default true
485
+ @since 1.1.1
457
486
  **/
458
- showbuttons: true
459
-
460
- /*todo:
461
- Submit strategy. Can be <code>normal|never</code>
462
- <code>submitmode='never'</code> usefull for turning into classic form several inputs and submitting them together manually.
463
- Works pretty with <code>showbuttons=false</code>
487
+ showbuttons: true,
488
+ /**
489
+ Scope for callback methods (success, validate).
490
+ If <code>null</code> means editableform instance itself.
464
491
 
465
- @property submitmode
466
- @type string
467
- @default normal
468
- */
469
- // submitmode: 'normal'
492
+ @property scope
493
+ @type DOMElement|object
494
+ @default null
495
+ @since 1.2.0
496
+ @private
497
+ **/
498
+ scope: null,
499
+ /**
500
+ Whether to save or cancel value when it was not changed but form was submitted
501
+
502
+ @property savenochange
503
+ @type boolean
504
+ @default false
505
+ @since 1.2.0
506
+ **/
507
+ savenochange: false
470
508
  };
471
509
 
472
510
  /*
@@ -487,26 +525,21 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
487
525
  $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+
488
526
  '<button type="button" class="editable-cancel">cancel</button>';
489
527
 
490
- //error class attahced to control-group
528
+ //error class attached to control-group
491
529
  $.fn.editableform.errorGroupClass = null;
492
530
 
493
- //error class attahced to editable-error-block
531
+ //error class attached to editable-error-block
494
532
  $.fn.editableform.errorBlockClass = 'editable-error';
495
-
496
- //input types
497
- $.fn.editableform.types = {};
498
- //utils
499
- $.fn.editableform.utils = {};
500
-
501
533
  }(window.jQuery));
502
534
  /**
503
535
  * EditableForm utilites
504
536
  */
505
537
  (function ($) {
506
- $.fn.editableform.utils = {
538
+ //utils
539
+ $.fn.editableutils = {
507
540
  /**
508
541
  * classic JS inheritance function
509
- */
542
+ */
510
543
  inherit: function (Child, Parent) {
511
544
  var F = function() { };
512
545
  F.prototype = Parent.prototype;
@@ -617,7 +650,14 @@ Editableform is linked with one of input types, e.g. 'text' or 'select'.
617
650
  return k;
618
651
  }
619
652
 
620
- }
653
+ },
654
+
655
+ /**
656
+ method to escape html.
657
+ **/
658
+ escape: function(str) {
659
+ return $('<div>').text(str).html();
660
+ }
621
661
  };
622
662
  }(window.jQuery));
623
663
  /**
@@ -642,7 +682,7 @@ Applied as jQuery method.
642
682
  init: function(element, options) {
643
683
  this.$element = $(element);
644
684
  //todo: what is in priority: data or js?
645
- this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
685
+ this.options = $.extend({}, $.fn.editableContainer.defaults, $.fn.editableutils.getConfigData(this.$element), options);
646
686
  this.splitOptions();
647
687
  this.initContainer();
648
688
 
@@ -697,12 +737,14 @@ Applied as jQuery method.
697
737
  },
698
738
 
699
739
  initForm: function() {
740
+ this.formOptions.scope = this.$element[0]; //set scope of form callbacks to element
700
741
  this.$form = $('<div>')
701
742
  .editableform(this.formOptions)
702
743
  .on({
703
744
  save: $.proxy(this.save, this),
704
- cancel: $.proxy(this.cancel, this),
705
- show: $.proxy(this.setPosition, this), //re-position container every time form is shown (after loading state)
745
+ cancel: $.proxy(function(){ this.hide('cancel'); }, this),
746
+ nochange: $.proxy(function(){ this.hide('nochange'); }, this),
747
+ show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
706
748
  rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
707
749
  rendered: $.proxy(function(){
708
750
  /**
@@ -741,7 +783,7 @@ Applied as jQuery method.
741
783
  /**
742
784
  Shows container with form
743
785
  @method show()
744
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
786
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
745
787
  **/
746
788
  show: function (closeAll) {
747
789
  this.$element.addClass('editable-open');
@@ -765,8 +807,9 @@ Applied as jQuery method.
765
807
  /**
766
808
  Hides container with form
767
809
  @method hide()
810
+ @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
768
811
  **/
769
- hide: function() {
812
+ hide: function(reason) {
770
813
  if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
771
814
  return;
772
815
  }
@@ -776,9 +819,17 @@ Applied as jQuery method.
776
819
  Fired when container was hidden. It occurs on both save or cancel.
777
820
 
778
821
  @event hidden
779
- @param {Object} event event object
822
+ @param {object} event event object
823
+ @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
824
+ @example
825
+ $('#username').on('hidden', function(e, reason) {
826
+ if(reason === 'save' || reason === 'cancel') {
827
+ //auto-open next editable
828
+ $(this).closest('tr').next().find('.editable').editable('show');
829
+ }
830
+ });
780
831
  **/
781
- this.$element.triggerHandler('hidden');
832
+ this.$element.triggerHandler('hidden', reason);
782
833
  },
783
834
 
784
835
  /* internal hide method. To be overwritten in child classes */
@@ -789,7 +840,7 @@ Applied as jQuery method.
789
840
  /**
790
841
  Toggles container visibility (show / hide)
791
842
  @method toggle()
792
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
843
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
793
844
  **/
794
845
  toggle: function(closeAll) {
795
846
  if(this.tip && this.tip().is(':visible')) {
@@ -807,23 +858,8 @@ Applied as jQuery method.
807
858
  //tbd in child class
808
859
  },
809
860
 
810
- cancel: function() {
811
- if(this.options.autohide) {
812
- this.hide();
813
- }
814
- /**
815
- Fired when form was cancelled by user
816
-
817
- @event cancel
818
- @param {Object} event event object
819
- **/
820
- this.$element.triggerHandler('cancel');
821
- },
822
-
823
861
  save: function(e, params) {
824
- if(this.options.autohide) {
825
- this.hide();
826
- }
862
+ this.hide('save');
827
863
  /**
828
864
  Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
829
865
 
@@ -884,8 +920,8 @@ Applied as jQuery method.
884
920
  */
885
921
  closeOthers: function(element) {
886
922
  $('.editable-open').each(function(i, el){
887
- //do nothing with passed element
888
- if(el === element) {
923
+ //do nothing with passed element and it's children
924
+ if(el === element || $(el).find(element).length) {
889
925
  return;
890
926
  }
891
927
 
@@ -898,7 +934,7 @@ Applied as jQuery method.
898
934
  }
899
935
 
900
936
  if(ec.options.onblur === 'cancel') {
901
- $el.data('editableContainer').hide();
937
+ $el.data('editableContainer').hide('onblur');
902
938
  } else if(ec.options.onblur === 'submit') {
903
939
  $el.data('editableContainer').tip().find('form').submit();
904
940
  }
@@ -972,7 +1008,7 @@ Applied as jQuery method.
972
1008
  **/
973
1009
  placement: 'top',
974
1010
  /**
975
- Wether to hide container on save/cancel.
1011
+ Whether to hide container on save/cancel.
976
1012
 
977
1013
  @property autohide
978
1014
  @type boolean
@@ -987,6 +1023,7 @@ Applied as jQuery method.
987
1023
  @property onblur
988
1024
  @type string
989
1025
  @default 'cancel'
1026
+ @since 1.1.1
990
1027
  **/
991
1028
  onblur: 'cancel'
992
1029
  };
@@ -1015,7 +1052,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1015
1052
 
1016
1053
  var Editable = function (element, options) {
1017
1054
  this.$element = $(element);
1018
- this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableform.utils.getConfigData(this.$element), options);
1055
+ this.options = $.extend({}, $.fn.editable.defaults, $.fn.editableutils.getConfigData(this.$element), options);
1019
1056
  this.init();
1020
1057
  };
1021
1058
 
@@ -1027,9 +1064,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1027
1064
  doAutotext,
1028
1065
  finalize;
1029
1066
 
1030
- //initialization flag
1031
- this.isInit = true;
1032
-
1033
1067
  //editableContainer must be defined
1034
1068
  if(!$.fn.editableContainer) {
1035
1069
  $.error('You must define $.fn.editableContainer via including corresponding file (e.g. editable-popover.js)');
@@ -1040,9 +1074,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1040
1074
  this.options.name = this.options.name || this.$element.attr('id');
1041
1075
 
1042
1076
  //create input of specified type. Input will be used for converting value, not in form
1043
- if(typeof $.fn.editableform.types[this.options.type] === 'function') {
1044
- TypeConstructor = $.fn.editableform.types[this.options.type];
1045
- this.typeOptions = $.fn.editableform.utils.sliceObj(this.options, $.fn.editableform.utils.objectKeys(TypeConstructor.defaults));
1077
+ if(typeof $.fn.editabletypes[this.options.type] === 'function') {
1078
+ TypeConstructor = $.fn.editabletypes[this.options.type];
1079
+ this.typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
1046
1080
  this.input = new TypeConstructor(this.typeOptions);
1047
1081
  } else {
1048
1082
  $.error('Unknown type: '+ this.options.type);
@@ -1054,13 +1088,20 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1054
1088
  this.value = this.input.html2value($.trim(this.$element.html()));
1055
1089
  isValueByText = true;
1056
1090
  } else {
1091
+ /*
1092
+ value can be string when received from 'data-value' attribute
1093
+ for complext objects value can be set as json string in data-value attribute,
1094
+ e.g. data-value="{city: 'Moscow', street: 'Lenina'}"
1095
+ */
1096
+ this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true);
1057
1097
  if(typeof this.options.value === 'string') {
1058
- this.options.value = $.trim(this.options.value);
1098
+ this.value = this.input.str2value(this.options.value);
1099
+ } else {
1100
+ this.value = this.options.value;
1059
1101
  }
1060
- this.value = this.input.str2value(this.options.value);
1061
1102
  }
1062
1103
 
1063
- //add 'editable' class
1104
+ //add 'editable' class to every editable element
1064
1105
  this.$element.addClass('editable');
1065
1106
 
1066
1107
  //attach handler activating editable. In disabled mode it just prevent default action (useful for links)
@@ -1088,29 +1129,46 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1088
1129
  //if value was generated by text or value is empty, no sense to run autotext
1089
1130
  doAutotext = !isValueByText && this.value !== null && this.value !== undefined;
1090
1131
  doAutotext &= (this.options.autotext === 'always') || (this.options.autotext === 'auto' && !this.$element.text().length);
1091
- $.when(doAutotext ? this.input.value2html(this.value, this.$element) : true).then($.proxy(function() {
1132
+ $.when(doAutotext ? this.render() : true).then($.proxy(function() {
1092
1133
  if(this.options.disabled) {
1093
1134
  this.disable();
1094
1135
  } else {
1095
1136
  this.enable();
1096
1137
  }
1097
1138
  /**
1098
- Fired each time when element's text is rendered. Occurs on initialization and on each update of value.
1099
- Can be used for display customization.
1139
+ Fired when element was initialized by editable method.
1100
1140
 
1101
- @event render
1141
+ @event init
1102
1142
  @param {Object} event event object
1103
1143
  @param {Object} editable editable instance
1104
- @example
1105
- $('#action').on('render', function(e, editable) {
1106
- var colors = {0: "gray", 1: "green", 2: "blue", 3: "red"};
1107
- $(this).css("color", colors[editable.value]);
1108
- });
1144
+ @since 1.2.0
1109
1145
  **/
1110
- this.$element.triggerHandler('render', this);
1111
- this.isInit = false;
1146
+ this.$element.triggerHandler('init', this);
1112
1147
  }, this));
1113
1148
  },
1149
+
1150
+ /*
1151
+ Renders value into element's text.
1152
+ Can call custom display method from options.
1153
+ Can return deferred object.
1154
+ @method render()
1155
+ */
1156
+ render: function() {
1157
+ //do not display anything
1158
+ if(this.options.display === false) {
1159
+ return;
1160
+ }
1161
+ //if it is input with source, we pass callback in third param to be called when source is loaded
1162
+ if(this.input.options.hasOwnProperty('source')) {
1163
+ return this.input.value2html(this.value, this.$element[0], this.options.display);
1164
+ //if display method defined --> use it
1165
+ } else if(typeof this.options.display === 'function') {
1166
+ return this.options.display.call(this.$element[0], this.value);
1167
+ //else use input's original value2html() method
1168
+ } else {
1169
+ return this.input.value2html(this.value, this.$element[0]);
1170
+ }
1171
+ },
1114
1172
 
1115
1173
  /**
1116
1174
  Enables editable
@@ -1198,6 +1256,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1198
1256
  * set emptytext if element is empty (reverse: remove emptytext if needed)
1199
1257
  */
1200
1258
  handleEmpty: function () {
1259
+ //do not handle empty if we do not display anything
1260
+ if(this.options.display === false) {
1261
+ return;
1262
+ }
1263
+
1201
1264
  var emptyClass = 'editable-empty';
1202
1265
  //emptytext shown only for enabled
1203
1266
  if(!this.options.disabled) {
@@ -1218,7 +1281,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1218
1281
  /**
1219
1282
  Shows container with form
1220
1283
  @method show()
1221
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
1284
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
1222
1285
  **/
1223
1286
  show: function (closeAll) {
1224
1287
  if(this.options.disabled) {
@@ -1228,14 +1291,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1228
1291
  //init editableContainer: popover, tooltip, inline, etc..
1229
1292
  if(!this.container) {
1230
1293
  var containerOptions = $.extend({}, this.options, {
1231
- value: this.value,
1232
- autohide: false //element will take care to show/hide container
1294
+ value: this.value
1233
1295
  });
1234
1296
  this.$element.editableContainer(containerOptions);
1235
- this.$element.on({
1236
- save: $.proxy(this.save, this),
1237
- cancel: $.proxy(this.hide, this)
1238
- });
1297
+ this.$element.on("save.internal", $.proxy(this.save, this));
1239
1298
  this.container = this.$element.data('editableContainer');
1240
1299
  } else if(this.container.tip().is(':visible')) {
1241
1300
  return;
@@ -1253,17 +1312,12 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1253
1312
  if(this.container) {
1254
1313
  this.container.hide();
1255
1314
  }
1256
-
1257
- //return focus on element
1258
- if (this.options.enablefocus && this.options.toggle === 'click') {
1259
- this.$element.focus();
1260
- }
1261
1315
  },
1262
1316
 
1263
1317
  /**
1264
1318
  Toggles container visibility (show / hide)
1265
1319
  @method toggle()
1266
- @param {boolean} closeAll Wether to close all other editable containers when showing this one. Default true.
1320
+ @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
1267
1321
  **/
1268
1322
  toggle: function(closeAll) {
1269
1323
  if(this.container && this.container.tip().is(':visible')) {
@@ -1278,13 +1332,13 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1278
1332
  */
1279
1333
  save: function(e, params) {
1280
1334
  //if url is not user's function and value was not sent to server and value changed --> mark element with unsaved css.
1281
- if(typeof this.options.url !== 'function' && params.response === undefined && this.input.value2str(this.value) !== this.input.value2str(params.newValue)) {
1335
+ if(typeof this.options.url !== 'function' && this.options.display !== false && params.response === undefined && this.input.value2str(this.value) !== this.input.value2str(params.newValue)) {
1282
1336
  this.$element.addClass('editable-unsaved');
1283
1337
  } else {
1284
1338
  this.$element.removeClass('editable-unsaved');
1285
1339
  }
1286
1340
 
1287
- this.hide();
1341
+ // this.hide();
1288
1342
  this.setValue(params.newValue);
1289
1343
 
1290
1344
  /**
@@ -1319,7 +1373,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1319
1373
  Sets new value of editable
1320
1374
  @method setValue(value, convertStr)
1321
1375
  @param {mixed} value new value
1322
- @param {boolean} convertStr wether to convert value from string to internal format
1376
+ @param {boolean} convertStr whether to convert value from string to internal format
1323
1377
  **/
1324
1378
  setValue: function(value, convertStr) {
1325
1379
  if(convertStr) {
@@ -1330,10 +1384,9 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1330
1384
  if(this.container) {
1331
1385
  this.container.option('value', this.value);
1332
1386
  }
1333
- $.when(this.input.value2html(this.value, this.$element))
1387
+ $.when(this.render())
1334
1388
  .then($.proxy(function() {
1335
1389
  this.handleEmpty();
1336
- this.$element.triggerHandler('render', this);
1337
1390
  }, this));
1338
1391
  },
1339
1392
 
@@ -1345,7 +1398,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1345
1398
  if(this.container) {
1346
1399
  this.container.activate();
1347
1400
  }
1348
- }
1401
+ }
1349
1402
  };
1350
1403
 
1351
1404
  /* EDITABLE PLUGIN DEFINITION
@@ -1376,7 +1429,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1376
1429
  $('#username, #fullname').editable('validate');
1377
1430
  // possible result:
1378
1431
  {
1379
- username: "username is requied",
1432
+ username: "username is required",
1380
1433
  fullname: "fullname should be minimum 3 letters length"
1381
1434
  }
1382
1435
  **/
@@ -1405,21 +1458,23 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1405
1458
  this.each(function () {
1406
1459
  var $this = $(this), data = $this.data(datakey);
1407
1460
  if (data && data.value !== undefined && data.value !== null) {
1408
- result[data.options.name] = data.input.value2str(data.value);
1461
+ result[data.options.name] = data.input.value2submit(data.value);
1409
1462
  }
1410
1463
  });
1411
1464
  return result;
1412
1465
 
1413
1466
  /**
1414
- This method collects values from several editable elements and submit them all to server.
1415
- It is designed mainly for <a href="#newrecord">creating new records</a>.
1467
+ This method collects values from several editable elements and submit them all to server.
1468
+ Internally it runs client-side validation for all fields and submits only in case of success.
1469
+ See <a href="#newrecord">creating new records</a> for details.
1416
1470
 
1417
1471
  @method submit(options)
1418
1472
  @param {object} options
1419
1473
  @param {object} options.url url to submit data
1420
1474
  @param {object} options.data additional data to submit
1421
- @param {function} options.error(obj) error handler (called on both client-side and server-side validation errors)
1422
- @param {function} options.success(obj) success handler
1475
+ @param {object} options.ajaxOptions additional ajax options
1476
+ @param {function} options.error(obj) error handler
1477
+ @param {function} options.success(obj,config) success handler
1423
1478
  @returns {Object} jQuery object
1424
1479
  **/
1425
1480
  case 'submit': //collects value, validate and submit to server for creating new record
@@ -1428,35 +1483,32 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1428
1483
  errors = this.editable('validate'),
1429
1484
  values;
1430
1485
 
1431
- if(typeof config.error !== 'function') {
1432
- config.error = function() {};
1433
- }
1434
-
1435
1486
  if($.isEmptyObject(errors)) {
1436
1487
  values = this.editable('getValue');
1437
1488
  if(config.data) {
1438
1489
  $.extend(values, config.data);
1439
- }
1440
- $.ajax({
1441
- type: 'POST',
1490
+ }
1491
+
1492
+ $.ajax($.extend({
1442
1493
  url: config.url,
1443
1494
  data: values,
1444
- dataType: 'json'
1445
- }).success(function(response) {
1446
- if(typeof response === 'object' && response.id) {
1447
- $elems.editable('option', 'pk', response.id);
1448
- $elems.removeClass('editable-unsaved');
1449
- if(typeof config.success === 'function') {
1450
- config.success.apply($elems, arguments);
1451
- }
1452
- } else { //server-side validation error
1495
+ type: 'POST'
1496
+ }, config.ajaxOptions))
1497
+ .success(function(response) {
1498
+ //successful response 200 OK
1499
+ if(typeof config.success === 'function') {
1500
+ config.success.call($elems, response, config);
1501
+ }
1502
+ })
1503
+ .error(function(){ //ajax error
1504
+ if(typeof config.error === 'function') {
1453
1505
  config.error.apply($elems, arguments);
1454
1506
  }
1455
- }).error(function(){ //ajax error
1456
- config.error.apply($elems, arguments);
1457
1507
  });
1458
1508
  } else { //client-side validation error
1459
- config.error.call($elems, {errors: errors});
1509
+ if(typeof config.error === 'function') {
1510
+ config.error.call($elems, errors);
1511
+ }
1460
1512
  }
1461
1513
  return this;
1462
1514
  }
@@ -1512,7 +1564,6 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1512
1564
  @default 'click'
1513
1565
  **/
1514
1566
  toggle: 'click',
1515
-
1516
1567
  /**
1517
1568
  Text shown when element is empty.
1518
1569
 
@@ -1522,7 +1573,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1522
1573
  **/
1523
1574
  emptytext: 'Empty',
1524
1575
  /**
1525
- Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Usefull for select and date.
1576
+ Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Useful for select and date.
1526
1577
  For example, if dropdown list is <code>{1: 'a', 2: 'b'}</code> and element's value set to <code>1</code>, it's html will be automatically set to <code>'a'</code>.
1527
1578
  <code>auto</code> - text will be automatically set only if element is empty.
1528
1579
  <code>always|never</code> - always(never) try to set element's text.
@@ -1533,38 +1584,52 @@ Makes editable any HTML element on the page. Applied as jQuery method.
1533
1584
  **/
1534
1585
  autotext: 'auto',
1535
1586
  /**
1536
- Wether to return focus on element after form is closed.
1537
- This allows fully keyboard input.
1538
-
1539
- @property enablefocus
1540
- @type boolean
1541
- @default false
1542
- **/
1543
- enablefocus: false,
1544
- /**
1545
1587
  Initial value of input. Taken from <code>data-value</code> or element's text.
1546
1588
 
1547
1589
  @property value
1548
1590
  @type mixed
1549
1591
  @default element's text
1550
1592
  **/
1551
- value: null
1593
+ value: null,
1594
+ /**
1595
+ Callback to perform custom displaying of value in element's text.
1596
+ If <code>null</code>, default input's value2html() will be called.
1597
+ If <code>false</code>, no displaying methods will be called, element's text will no change.
1598
+ Runs under element's scope.
1599
+ Second parameter __sourceData__ is passed for inputs with source (select, checklist).
1600
+
1601
+ @property display
1602
+ @type function|boolean
1603
+ @default null
1604
+ @since 1.2.0
1605
+ @example
1606
+ display: function(value, sourceData) {
1607
+ var escapedValue = $('<div>').text(value).html();
1608
+ $(this).html('<b>'+escapedValue+'</b>');
1609
+ }
1610
+ **/
1611
+ display: null
1552
1612
  };
1553
1613
 
1554
- }(window.jQuery));
1614
+ }(window.jQuery));
1615
+
1555
1616
  /**
1556
- Abstract editable input class.
1557
- To create your own input you should inherit from this class.
1617
+ AbstractInput - base class for all editable inputs.
1618
+ It defines interface to be implemented by any input type.
1619
+ To create your own input you can inherit from this class.
1558
1620
 
1559
- @class abstract
1621
+ @class abstractinput
1560
1622
  **/
1561
1623
  (function ($) {
1562
1624
 
1563
- var Abstract = function () { };
1625
+ //types
1626
+ $.fn.editabletypes = {};
1627
+
1628
+ var AbstractInput = function () { };
1564
1629
 
1565
- Abstract.prototype = {
1630
+ AbstractInput.prototype = {
1566
1631
  /**
1567
- Iinitializes input
1632
+ Initializes input
1568
1633
 
1569
1634
  @method init()
1570
1635
  **/
@@ -1577,7 +1642,7 @@ To create your own input you should inherit from this class.
1577
1642
  },
1578
1643
 
1579
1644
  /**
1580
- Renders input. Can return jQuery deferred object.
1645
+ Renders input from tpl. Can return jQuery deferred object.
1581
1646
 
1582
1647
  @method render()
1583
1648
  **/
@@ -1600,8 +1665,7 @@ To create your own input you should inherit from this class.
1600
1665
  @param {DOMElement} element
1601
1666
  **/
1602
1667
  value2html: function(value, element) {
1603
- var html = this.escape(value);
1604
- $(element).html(html);
1668
+ $(element).text(value);
1605
1669
  },
1606
1670
 
1607
1671
  /**
@@ -1616,7 +1680,7 @@ To create your own input you should inherit from this class.
1616
1680
  },
1617
1681
 
1618
1682
  /**
1619
- Converts value to string (for submiting to server)
1683
+ Converts value to string (for internal compare). For submitting to server used value2submit().
1620
1684
 
1621
1685
  @method value2str(value)
1622
1686
  @param {mixed} value
@@ -1637,6 +1701,17 @@ To create your own input you should inherit from this class.
1637
1701
  return str;
1638
1702
  },
1639
1703
 
1704
+ /**
1705
+ Converts value for submitting to server
1706
+
1707
+ @method value2submit(value)
1708
+ @param {mixed} value
1709
+ @returns {mixed}
1710
+ **/
1711
+ value2submit: function(value) {
1712
+ return value;
1713
+ },
1714
+
1640
1715
  /**
1641
1716
  Sets value of input.
1642
1717
 
@@ -1668,11 +1743,11 @@ To create your own input you should inherit from this class.
1668
1743
  },
1669
1744
 
1670
1745
  /**
1671
- Creares input.
1746
+ Creates input.
1672
1747
 
1673
1748
  @method clear()
1674
1749
  **/
1675
- clear: function() {
1750
+ clear: function() {
1676
1751
  this.$input.val(null);
1677
1752
  },
1678
1753
 
@@ -1684,14 +1759,14 @@ To create your own input you should inherit from this class.
1684
1759
  },
1685
1760
 
1686
1761
  /**
1687
- attach handler to automatically submit form when value changed (usefull when buttons not shown)
1762
+ attach handler to automatically submit form when value changed (useful when buttons not shown)
1688
1763
  **/
1689
1764
  autosubmit: function() {
1690
1765
 
1691
1766
  }
1692
1767
  };
1693
1768
 
1694
- Abstract.defaults = {
1769
+ AbstractInput.defaults = {
1695
1770
  /**
1696
1771
  HTML template of input. Normally you should not change it.
1697
1772
 
@@ -1702,12 +1777,12 @@ To create your own input you should inherit from this class.
1702
1777
  tpl: '',
1703
1778
  /**
1704
1779
  CSS class automatically applied to input
1705
-
1780
+
1706
1781
  @property inputclass
1707
1782
  @type string
1708
- @default span2
1783
+ @default input-medium
1709
1784
  **/
1710
- inputclass: 'span2',
1785
+ inputclass: 'input-medium',
1711
1786
  /**
1712
1787
  Name attribute of input
1713
1788
 
@@ -1718,14 +1793,15 @@ To create your own input you should inherit from this class.
1718
1793
  name: null
1719
1794
  };
1720
1795
 
1721
- $.extend($.fn.editableform.types, {abstract: Abstract});
1796
+ $.extend($.fn.editabletypes, {abstractinput: AbstractInput});
1722
1797
 
1723
- }(window.jQuery));
1798
+ }(window.jQuery));
1799
+
1724
1800
  /**
1725
1801
  List - abstract class for inputs that have source option loaded from js array or via ajax
1726
1802
 
1727
1803
  @class list
1728
- @extends abstract
1804
+ @extends abstractinput
1729
1805
  **/
1730
1806
  (function ($) {
1731
1807
 
@@ -1733,7 +1809,7 @@ List - abstract class for inputs that have source option loaded from js array or
1733
1809
 
1734
1810
  };
1735
1811
 
1736
- $.fn.editableform.utils.inherit(List, $.fn.editableform.types.abstract);
1812
+ $.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput);
1737
1813
 
1738
1814
  $.extend(List.prototype, {
1739
1815
  render: function () {
@@ -1757,13 +1833,18 @@ List - abstract class for inputs that have source option loaded from js array or
1757
1833
  return null; //can't set value by text
1758
1834
  },
1759
1835
 
1760
- value2html: function (value, element) {
1836
+ value2html: function (value, element, display) {
1761
1837
  var deferred = $.Deferred();
1762
1838
  this.onSourceReady(function () {
1763
- this.value2htmlFinal(value, element);
1839
+ if(typeof display === 'function') {
1840
+ //custom display method
1841
+ display.call(element, value, this.sourceData);
1842
+ } else {
1843
+ this.value2htmlFinal(value, element);
1844
+ }
1764
1845
  deferred.resolve();
1765
1846
  }, function () {
1766
- List.superclass.value2html(this.options.sourceError, element);
1847
+ //do nothing with element
1767
1848
  deferred.resolve();
1768
1849
  });
1769
1850
 
@@ -1781,7 +1862,7 @@ List - abstract class for inputs that have source option loaded from js array or
1781
1862
 
1782
1863
  // try parse json in single quotes (for double quotes jquery does automatically)
1783
1864
  try {
1784
- this.options.source = $.fn.editableform.utils.tryParseJson(this.options.source, false);
1865
+ this.options.source = $.fn.editableutils.tryParseJson(this.options.source, false);
1785
1866
  } catch (e) {
1786
1867
  error.call(this);
1787
1868
  return;
@@ -1789,32 +1870,35 @@ List - abstract class for inputs that have source option loaded from js array or
1789
1870
 
1790
1871
  //loading from url
1791
1872
  if (typeof this.options.source === 'string') {
1792
- var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''),
1793
- cache;
1873
+ //try to get from cache
1874
+ if(this.options.sourceCache) {
1875
+ var cacheID = this.options.source + (this.options.name ? '-' + this.options.name : ''),
1876
+ cache;
1794
1877
 
1795
- if (!$(document).data(cacheID)) {
1796
- $(document).data(cacheID, {});
1797
- }
1798
- cache = $(document).data(cacheID);
1878
+ if (!$(document).data(cacheID)) {
1879
+ $(document).data(cacheID, {});
1880
+ }
1881
+ cache = $(document).data(cacheID);
1799
1882
 
1800
- //check for cached data
1801
- if (cache.loading === false && cache.sourceData) { //take source from cache
1802
- this.sourceData = cache.sourceData;
1803
- success.call(this);
1804
- return;
1805
- } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
1806
- cache.callbacks.push($.proxy(function () {
1883
+ //check for cached data
1884
+ if (cache.loading === false && cache.sourceData) { //take source from cache
1807
1885
  this.sourceData = cache.sourceData;
1808
1886
  success.call(this);
1809
- }, this));
1887
+ return;
1888
+ } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
1889
+ cache.callbacks.push($.proxy(function () {
1890
+ this.sourceData = cache.sourceData;
1891
+ success.call(this);
1892
+ }, this));
1810
1893
 
1811
- //also collecting error callbacks
1812
- cache.err_callbacks.push($.proxy(error, this));
1813
- return;
1814
- } else { //no cache yet, activate it
1815
- cache.loading = true;
1816
- cache.callbacks = [];
1817
- cache.err_callbacks = [];
1894
+ //also collecting error callbacks
1895
+ cache.err_callbacks.push($.proxy(error, this));
1896
+ return;
1897
+ } else { //no cache yet, activate it
1898
+ cache.loading = true;
1899
+ cache.callbacks = [];
1900
+ cache.err_callbacks = [];
1901
+ }
1818
1902
  }
1819
1903
 
1820
1904
  //loading sourceData from server
@@ -1825,23 +1909,32 @@ List - abstract class for inputs that have source option loaded from js array or
1825
1909
  data: this.options.name ? {name: this.options.name} : {},
1826
1910
  dataType: 'json',
1827
1911
  success: $.proxy(function (data) {
1828
- cache.loading = false;
1912
+ if(cache) {
1913
+ cache.loading = false;
1914
+ }
1829
1915
  this.sourceData = this.makeArray(data);
1830
1916
  if($.isArray(this.sourceData)) {
1831
1917
  this.doPrepend();
1832
- //store result in cache
1833
- cache.sourceData = this.sourceData;
1834
1918
  success.call(this);
1835
- $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields
1919
+ if(cache) {
1920
+ //store result in cache
1921
+ cache.sourceData = this.sourceData;
1922
+ $.each(cache.callbacks, function () { this.call(); }); //run success callbacks for other fields
1923
+ }
1836
1924
  } else {
1837
1925
  error.call(this);
1838
- $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
1926
+ if(cache) {
1927
+ $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
1928
+ }
1839
1929
  }
1840
1930
  }, this),
1841
1931
  error: $.proxy(function () {
1842
- cache.loading = false;
1843
1932
  error.call(this);
1844
- $.each(cache.err_callbacks, function () { this.call(); }); //run error callbacks for other fields
1933
+ if(cache) {
1934
+ cache.loading = false;
1935
+ //run error callbacks for other fields
1936
+ $.each(cache.err_callbacks, function () { this.call(); });
1937
+ }
1845
1938
  }, this)
1846
1939
  });
1847
1940
  } else { //options as json/array
@@ -1862,7 +1955,7 @@ List - abstract class for inputs that have source option loaded from js array or
1862
1955
 
1863
1956
  if(!$.isArray(this.prependData)) {
1864
1957
  //try parse json in single quotes
1865
- this.options.prepend = $.fn.editableform.utils.tryParseJson(this.options.prepend, true);
1958
+ this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
1866
1959
  if (typeof this.options.prepend === 'string') {
1867
1960
  this.options.prepend = {'': this.options.prepend};
1868
1961
  }
@@ -1943,19 +2036,20 @@ List - abstract class for inputs that have source option loaded from js array or
1943
2036
 
1944
2037
  });
1945
2038
 
1946
- List.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
2039
+ List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
1947
2040
  /**
1948
2041
  Source data for list. If string - considered ajax url to load items. Otherwise should be an array.
1949
2042
  Array format is: <code>[{value: 1, text: "text"}, {...}]</code><br>
1950
2043
  For compability it also supports format <code>{value1: "text1", value2: "text2" ...}</code> but it does not guarantee elements order.
1951
-
2044
+ If source is **string**, results will be cached for fields with the same source and name. See also <code>sourceCache</code> option.
2045
+
1952
2046
  @property source
1953
2047
  @type string|array|object
1954
2048
  @default null
1955
2049
  **/
1956
2050
  source:null,
1957
2051
  /**
1958
- Data automatically prepended to the begining of dropdown list.
2052
+ Data automatically prepended to the beginning of dropdown list.
1959
2053
 
1960
2054
  @property prepend
1961
2055
  @type string|array|object
@@ -1969,17 +2063,27 @@ List - abstract class for inputs that have source option loaded from js array or
1969
2063
  @type string
1970
2064
  @default Error when loading list
1971
2065
  **/
1972
- sourceError: 'Error when loading list'
2066
+ sourceError: 'Error when loading list',
2067
+ /**
2068
+ if <code>true</code> and source is **string url** - results will be cached for fields with the same source and name.
2069
+ Usefull for editable grids.
2070
+
2071
+ @property sourceCache
2072
+ @type boolean
2073
+ @default true
2074
+ @since 1.2.0
2075
+ **/
2076
+ sourceCache: true
1973
2077
  });
1974
2078
 
1975
- $.fn.editableform.types.list = List;
2079
+ $.fn.editabletypes.list = List;
1976
2080
 
1977
2081
  }(window.jQuery));
1978
2082
  /**
1979
2083
  Text input
1980
2084
 
1981
2085
  @class text
1982
- @extends abstract
2086
+ @extends abstractinput
1983
2087
  @final
1984
2088
  @example
1985
2089
  <a href="#" id="username" data-type="text" data-pk="1">awesome</a>
@@ -1997,18 +2101,18 @@ $(function(){
1997
2101
  this.init('text', options, Text.defaults);
1998
2102
  };
1999
2103
 
2000
- $.fn.editableform.utils.inherit(Text, $.fn.editableform.types.abstract);
2104
+ $.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput);
2001
2105
 
2002
2106
  $.extend(Text.prototype, {
2003
2107
  activate: function() {
2004
2108
  if(this.$input.is(':visible')) {
2005
2109
  this.$input.focus();
2006
- $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length);
2110
+ $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
2007
2111
  }
2008
2112
  }
2009
2113
  });
2010
2114
 
2011
- Text.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
2115
+ Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2012
2116
  /**
2013
2117
  @property tpl
2014
2118
  @default <input type="text">
@@ -2024,7 +2128,7 @@ $(function(){
2024
2128
  placeholder: null
2025
2129
  });
2026
2130
 
2027
- $.fn.editableform.types.text = Text;
2131
+ $.fn.editabletypes.text = Text;
2028
2132
 
2029
2133
  }(window.jQuery));
2030
2134
 
@@ -2032,7 +2136,7 @@ $(function(){
2032
2136
  Textarea input
2033
2137
 
2034
2138
  @class textarea
2035
- @extends abstract
2139
+ @extends abstractinput
2036
2140
  @final
2037
2141
  @example
2038
2142
  <a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a>
@@ -2051,7 +2155,7 @@ $(function(){
2051
2155
  this.init('textarea', options, Textarea.defaults);
2052
2156
  };
2053
2157
 
2054
- $.fn.editableform.utils.inherit(Textarea, $.fn.editableform.types.abstract);
2158
+ $.fn.editableutils.inherit(Textarea, $.fn.editabletypes.abstractinput);
2055
2159
 
2056
2160
  $.extend(Textarea.prototype, {
2057
2161
  render: function () {
@@ -2090,13 +2194,13 @@ $(function(){
2090
2194
 
2091
2195
  activate: function() {
2092
2196
  if(this.$input.is(':visible')) {
2093
- $.fn.editableform.utils.setCursorPosition(this.$input.get(0), this.$input.val().length);
2197
+ $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
2094
2198
  this.$input.focus();
2095
2199
  }
2096
2200
  }
2097
2201
  });
2098
2202
 
2099
- Textarea.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
2203
+ Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2100
2204
  /**
2101
2205
  @property tpl
2102
2206
  @default <textarea></textarea>
@@ -2104,9 +2208,9 @@ $(function(){
2104
2208
  tpl:'<textarea></textarea>',
2105
2209
  /**
2106
2210
  @property inputclass
2107
- @default span3
2211
+ @default input-large
2108
2212
  **/
2109
- inputclass:'span3',
2213
+ inputclass: 'input-large',
2110
2214
  /**
2111
2215
  Placeholder attribute of input. Shown when input is empty.
2112
2216
 
@@ -2114,12 +2218,13 @@ $(function(){
2114
2218
  @type string
2115
2219
  @default null
2116
2220
  **/
2117
- placeholder: null
2221
+ placeholder: null
2118
2222
  });
2119
2223
 
2120
- $.fn.editableform.types.textarea = Textarea;
2224
+ $.fn.editabletypes.textarea = Textarea;
2121
2225
 
2122
- }(window.jQuery));
2226
+ }(window.jQuery));
2227
+
2123
2228
  /**
2124
2229
  Select (dropdown)
2125
2230
 
@@ -2148,7 +2253,7 @@ $(function(){
2148
2253
  this.init('select', options, Select.defaults);
2149
2254
  };
2150
2255
 
2151
- $.fn.editableform.utils.inherit(Select, $.fn.editableform.types.list);
2256
+ $.fn.editableutils.inherit(Select, $.fn.editabletypes.list);
2152
2257
 
2153
2258
  $.extend(Select.prototype, {
2154
2259
  renderList: function() {
@@ -2159,6 +2264,13 @@ $(function(){
2159
2264
  for(var i=0; i<this.sourceData.length; i++) {
2160
2265
  this.$input.append($('<option>', {value: this.sourceData[i].value}).text(this.sourceData[i].text));
2161
2266
  }
2267
+
2268
+ //enter submit
2269
+ this.$input.on('keydown.editable', function (e) {
2270
+ if (e.which === 13) {
2271
+ $(this).closest('form').submit();
2272
+ }
2273
+ });
2162
2274
  },
2163
2275
 
2164
2276
  value2htmlFinal: function(value, element) {
@@ -2170,13 +2282,13 @@ $(function(){
2170
2282
  },
2171
2283
 
2172
2284
  autosubmit: function() {
2173
- this.$input.on('change', function(){
2285
+ this.$input.off('keydown.editable').on('change.editable', function(){
2174
2286
  $(this).closest('form').submit();
2175
2287
  });
2176
2288
  }
2177
2289
  });
2178
2290
 
2179
- Select.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {
2291
+ Select.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
2180
2292
  /**
2181
2293
  @property tpl
2182
2294
  @default <select></select>
@@ -2184,7 +2296,7 @@ $(function(){
2184
2296
  tpl:'<select></select>'
2185
2297
  });
2186
2298
 
2187
- $.fn.editableform.types.select = Select;
2299
+ $.fn.editabletypes.select = Select;
2188
2300
 
2189
2301
  }(window.jQuery));
2190
2302
  /**
@@ -2216,7 +2328,7 @@ $(function(){
2216
2328
  this.init('checklist', options, Checklist.defaults);
2217
2329
  };
2218
2330
 
2219
- $.fn.editableform.utils.inherit(Checklist, $.fn.editableform.types.list);
2331
+ $.fn.editableutils.inherit(Checklist, $.fn.editabletypes.list);
2220
2332
 
2221
2333
  $.extend(Checklist.prototype, {
2222
2334
  renderList: function() {
@@ -2238,10 +2350,8 @@ $(function(){
2238
2350
  },
2239
2351
 
2240
2352
  value2str: function(value) {
2241
- return $.isArray(value) ? value.join($.trim(this.options.separator)) : '';
2242
- //it is also possible to sent as array
2243
- //return value;
2244
- },
2353
+ return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : '';
2354
+ },
2245
2355
 
2246
2356
  //parse separated string
2247
2357
  str2value: function(str) {
@@ -2284,19 +2394,18 @@ $(function(){
2284
2394
 
2285
2395
  //collect text of checked boxes
2286
2396
  value2htmlFinal: function(value, element) {
2287
- var selected = [], item, i, html = '';
2288
- if($.isArray(value) && value.length <= this.options.limit) {
2289
- for(i=0; i<value.length; i++){
2290
- item = this.itemByVal(value[i]);
2291
- if(item) {
2292
- selected.push($('<div>').text(item.text).html());
2293
- }
2294
- }
2295
- html = selected.join(this.options.viewseparator);
2296
- } else {
2297
- html = this.options.limitText.replace('{checked}', $.isArray(value) ? value.length : 0).replace('{count}', this.sourceData.length);
2397
+ var html = [],
2398
+ /*jslint eqeq: true*/
2399
+ checked = $.grep(this.sourceData, function(o){
2400
+ return $.grep(value, function(v){ return v == o.value; }).length;
2401
+ });
2402
+ /*jslint eqeq: false*/
2403
+ if(checked.length) {
2404
+ $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
2405
+ $(element).html(html.join('<br>'));
2406
+ } else {
2407
+ $(element).empty();
2298
2408
  }
2299
- $(element).html(html);
2300
2409
  },
2301
2410
 
2302
2411
  activate: function() {
@@ -2312,7 +2421,7 @@ $(function(){
2312
2421
  }
2313
2422
  });
2314
2423
 
2315
- Checklist.defaults = $.extend({}, $.fn.editableform.types.list.defaults, {
2424
+ Checklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
2316
2425
  /**
2317
2426
  @property tpl
2318
2427
  @default <div></div>
@@ -2322,50 +2431,212 @@ $(function(){
2322
2431
  /**
2323
2432
  @property inputclass
2324
2433
  @type string
2325
- @default span2 editable-checklist
2434
+ @default editable-checklist
2326
2435
  **/
2327
- inputclass: 'span2 editable-checklist',
2436
+ inputclass: 'editable-checklist',
2328
2437
 
2329
2438
  /**
2330
- Separator of values in string when sending to server
2439
+ Separator of values when reading from 'data-value' string
2331
2440
 
2332
2441
  @property separator
2333
2442
  @type string
2334
2443
  @default ', '
2335
2444
  **/
2336
- separator: ',',
2337
- /**
2338
- Separator of text when display as element content.
2445
+ separator: ','
2446
+ });
2339
2447
 
2340
- @property viewseparator
2341
- @type string
2342
- @default '<br>'
2343
- **/
2344
- viewseparator: '<br>',
2345
- /**
2346
- Maximum number of items shown as element content.
2347
- If checked more items - <code>limitText</code> will be shown.
2448
+ $.fn.editabletypes.checklist = Checklist;
2348
2449
 
2349
- @property limit
2350
- @type integer
2351
- @default 4
2352
- **/
2353
- limit: 4,
2354
- /**
2355
- Text shown when count of checked items is greater than <code>limit</code> parameter.
2356
- You can use <code>{checked}</code> and <code>{count}</code> placeholders.
2450
+ }(window.jQuery));
2451
+
2452
+ /**
2453
+ HTML5 input types.
2454
+ Following types are supported:
2455
+
2456
+ * password
2457
+ * email
2458
+ * url
2459
+ * tel
2460
+ * number
2461
+ * range
2462
+
2463
+ Learn more about html5 inputs:
2464
+ http://www.w3.org/wiki/HTML5_form_additions
2465
+ To check browser compatibility please see:
2466
+ https://developer.mozilla.org/en-US/docs/HTML/Element/Input
2467
+
2468
+ @class html5types
2469
+ @extends text
2470
+ @final
2471
+ @since 1.3.0
2472
+ @example
2473
+ <a href="#" id="email" data-type="email" data-pk="1">admin@example.com</a>
2474
+ <script>
2475
+ $(function(){
2476
+ $('#email').editable({
2477
+ url: '/post',
2478
+ title: 'Enter email'
2479
+ });
2480
+ });
2481
+ </script>
2482
+ **/
2357
2483
 
2358
- @property limitText
2359
- @type string
2360
- @default 'Selected {checked} of {count}'
2361
- **/
2362
- limitText: 'Selected {checked} of {count}'
2484
+ /**
2485
+ @property tpl
2486
+ @default depends on type
2487
+ **/
2488
+
2489
+ /*
2490
+ Password
2491
+ */
2492
+ (function ($) {
2493
+ var Password = function (options) {
2494
+ this.init('password', options, Password.defaults);
2495
+ };
2496
+ $.fn.editableutils.inherit(Password, $.fn.editabletypes.text);
2497
+ $.extend(Password.prototype, {
2498
+ //do not display password, show '[hidden]' instead
2499
+ value2html: function(value, element) {
2500
+ if(value) {
2501
+ $(element).text('[hidden]');
2502
+ } else {
2503
+ $(element).empty();
2504
+ }
2505
+ },
2506
+ //as password not displayed, should not set value by html
2507
+ html2value: function(html) {
2508
+ return null;
2509
+ }
2510
+ });
2511
+ Password.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
2512
+ tpl: '<input type="password">'
2363
2513
  });
2514
+ $.fn.editabletypes.password = Password;
2515
+ }(window.jQuery));
2364
2516
 
2365
- $.fn.editableform.types.checklist = Checklist;
2366
2517
 
2518
+ /*
2519
+ Email
2520
+ */
2521
+ (function ($) {
2522
+ var Email = function (options) {
2523
+ this.init('email', options, Email.defaults);
2524
+ };
2525
+ $.fn.editableutils.inherit(Email, $.fn.editabletypes.text);
2526
+ Email.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
2527
+ tpl: '<input type="email">'
2528
+ });
2529
+ $.fn.editabletypes.email = Email;
2367
2530
  }(window.jQuery));
2368
-
2531
+
2532
+
2533
+ /*
2534
+ Url
2535
+ */
2536
+ (function ($) {
2537
+ var Url = function (options) {
2538
+ this.init('url', options, Url.defaults);
2539
+ };
2540
+ $.fn.editableutils.inherit(Url, $.fn.editabletypes.text);
2541
+ Url.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
2542
+ tpl: '<input type="url">'
2543
+ });
2544
+ $.fn.editabletypes.url = Url;
2545
+ }(window.jQuery));
2546
+
2547
+
2548
+ /*
2549
+ Tel
2550
+ */
2551
+ (function ($) {
2552
+ var Tel = function (options) {
2553
+ this.init('tel', options, Tel.defaults);
2554
+ };
2555
+ $.fn.editableutils.inherit(Tel, $.fn.editabletypes.text);
2556
+ Tel.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
2557
+ tpl: '<input type="tel">'
2558
+ });
2559
+ $.fn.editabletypes.tel = Tel;
2560
+ }(window.jQuery));
2561
+
2562
+
2563
+ /*
2564
+ Number
2565
+ */
2566
+ (function ($) {
2567
+ var NumberInput = function (options) {
2568
+ this.init('number', options, NumberInput.defaults);
2569
+ };
2570
+ $.fn.editableutils.inherit(NumberInput, $.fn.editabletypes.text);
2571
+ $.extend(NumberInput.prototype, {
2572
+ render: function () {
2573
+ NumberInput.superclass.render.call(this);
2574
+
2575
+ if (this.options.min !== null) {
2576
+ this.$input.attr('min', this.options.min);
2577
+ }
2578
+
2579
+ if (this.options.max !== null) {
2580
+ this.$input.attr('max', this.options.max);
2581
+ }
2582
+
2583
+ if (this.options.step !== null) {
2584
+ this.$input.attr('step', this.options.step);
2585
+ }
2586
+ }
2587
+ });
2588
+ NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
2589
+ tpl: '<input type="number">',
2590
+ inputclass: 'input-mini',
2591
+ min: null,
2592
+ max: null,
2593
+ step: null
2594
+ });
2595
+ $.fn.editabletypes.number = NumberInput;
2596
+ }(window.jQuery));
2597
+
2598
+
2599
+ /*
2600
+ Range (inherit from number)
2601
+ */
2602
+ (function ($) {
2603
+ var Range = function (options) {
2604
+ this.init('range', options, Range.defaults);
2605
+ };
2606
+ $.fn.editableutils.inherit(Range, $.fn.editabletypes.number);
2607
+ $.extend(Range.prototype, {
2608
+ render: function () {
2609
+ this.$input = $(this.options.tpl);
2610
+ var $slider = this.$input.filter('input');
2611
+ if(this.options.inputclass) {
2612
+ $slider.addClass(this.options.inputclass);
2613
+ }
2614
+ if (this.options.min !== null) {
2615
+ $slider.attr('min', this.options.min);
2616
+ }
2617
+
2618
+ if (this.options.max !== null) {
2619
+ $slider.attr('max', this.options.max);
2620
+ }
2621
+
2622
+ if (this.options.step !== null) {
2623
+ $slider.attr('step', this.options.step);
2624
+ }
2625
+
2626
+ $slider.on('input', function(){
2627
+ $(this).siblings('output').text($(this).val());
2628
+ });
2629
+ },
2630
+ activate: function() {
2631
+ this.$input.filter('input').focus();
2632
+ }
2633
+ });
2634
+ Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, {
2635
+ tpl: '<input type="range"><output style="width: 30px; display: inline-block"></output>',
2636
+ inputclass: 'input-medium'
2637
+ });
2638
+ $.fn.editabletypes.range = Range;
2639
+ }(window.jQuery));
2369
2640
  /*
2370
2641
  Editableform based on Twitter Bootstrap
2371
2642
  */
@@ -2432,10 +2703,6 @@ Editableform based on Twitter Bootstrap
2432
2703
  innerHide: function () {
2433
2704
  this.$form.hide(this.options.anim, $.proxy(function() {
2434
2705
  this.$element.show();
2435
- //return focus on element
2436
- if (this.options.enablefocus) {
2437
- this.$element.focus();
2438
- }
2439
2706
  }, this));
2440
2707
  },
2441
2708
 
@@ -2446,8 +2713,7 @@ Editableform based on Twitter Bootstrap
2446
2713
 
2447
2714
  //defaults
2448
2715
  $.fn.editableContainer.defaults = $.extend({}, $.fn.editableContainer.defaults, {
2449
- anim: 'fast',
2450
- enablefocus: false
2716
+ anim: 'fast'
2451
2717
  });
2452
2718
 
2453
2719
 
@@ -2458,7 +2724,7 @@ Description and examples: http://vitalets.github.com/bootstrap-datepicker.
2458
2724
  For localization you can include js file from here: https://github.com/eternicode/bootstrap-datepicker/tree/master/js/locales
2459
2725
 
2460
2726
  @class date
2461
- @extends abstract
2727
+ @extends abstractinput
2462
2728
  @final
2463
2729
  @example
2464
2730
  <a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-original-title="Select date">15/05/1984</a>
@@ -2481,7 +2747,7 @@ $(function(){
2481
2747
  this.init('date', options, Date.defaults);
2482
2748
 
2483
2749
  //set popular options directly from settings or data-* attributes
2484
- var directOptions = $.fn.editableform.utils.sliceObj(this.options, ['format']);
2750
+ var directOptions = $.fn.editableutils.sliceObj(this.options, ['format']);
2485
2751
 
2486
2752
  //overriding datepicker config (as by default jQuery extend() is not recursive)
2487
2753
  this.options.datepicker = $.extend({}, Date.defaults.datepicker, directOptions, options.datepicker);
@@ -2502,7 +2768,7 @@ $(function(){
2502
2768
  this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);
2503
2769
  };
2504
2770
 
2505
- $.fn.editableform.utils.inherit(Date, $.fn.editableform.types.abstract);
2771
+ $.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
2506
2772
 
2507
2773
  $.extend(Date.prototype, {
2508
2774
  render: function () {
@@ -2533,7 +2799,11 @@ $(function(){
2533
2799
 
2534
2800
  str2value: function(str) {
2535
2801
  return str ? this.dpg.parseDate(str, this.parsedFormat, this.options.datepicker.language) : null;
2536
- },
2802
+ },
2803
+
2804
+ value2submit: function(value) {
2805
+ return this.value2str(value);
2806
+ },
2537
2807
 
2538
2808
  value2input: function(value) {
2539
2809
  this.$input.datepicker('update', value);
@@ -2562,7 +2832,7 @@ $(function(){
2562
2832
 
2563
2833
  });
2564
2834
 
2565
- Date.defaults = $.extend({}, $.fn.editableform.types.abstract.defaults, {
2835
+ Date.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
2566
2836
  /**
2567
2837
  @property tpl
2568
2838
  @default <div></div>
@@ -2619,7 +2889,7 @@ $(function(){
2619
2889
  clear: '&times; clear'
2620
2890
  });
2621
2891
 
2622
- $.fn.editableform.types.date = Date;
2892
+ $.fn.editabletypes.date = Date;
2623
2893
 
2624
2894
  }(window.jQuery));
2625
2895
 
@@ -2911,13 +3181,13 @@ $(function(){
2911
3181
  startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
2912
3182
  endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
2913
3183
  endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
2914
- currentDate = this.date.valueOf(),
3184
+ currentDate = this.date && this.date.valueOf(),
2915
3185
  today = new Date();
2916
3186
  this.picker.find('.datepicker-days thead th:eq(1)')
2917
3187
  .text(dates[this.language].months[month]+' '+year);
2918
3188
  this.picker.find('tfoot th.today')
2919
3189
  .text(dates[this.language].today)
2920
- .toggle(this.todayBtn);
3190
+ .toggle(this.todayBtn !== false);
2921
3191
  this.updateNavArrows();
2922
3192
  this.fillMonths();
2923
3193
  var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
@@ -2946,7 +3216,7 @@ $(function(){
2946
3216
  prevMonth.getUTCDate() == today.getDate()) {
2947
3217
  clsName += ' today';
2948
3218
  }
2949
- if (prevMonth.valueOf() == currentDate) {
3219
+ if (currentDate && prevMonth.valueOf() == currentDate) {
2950
3220
  clsName += ' active';
2951
3221
  }
2952
3222
  if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate) {
@@ -2959,14 +3229,14 @@ $(function(){
2959
3229
  prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
2960
3230
  }
2961
3231
  this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
2962
- var currentYear = this.date.getUTCFullYear();
3232
+ var currentYear = this.date && this.date.getUTCFullYear();
2963
3233
 
2964
3234
  var months = this.picker.find('.datepicker-months')
2965
3235
  .find('th:eq(1)')
2966
3236
  .text(year)
2967
3237
  .end()
2968
3238
  .find('span').removeClass('active');
2969
- if (currentYear == year) {
3239
+ if (currentYear && currentYear == year) {
2970
3240
  months.eq(this.date.getUTCMonth()).addClass('active');
2971
3241
  }
2972
3242
  if (year < startYear || year > endYear) {
@@ -3054,10 +3324,7 @@ $(function(){
3054
3324
  break;
3055
3325
  case 'today':
3056
3326
  var date = new Date();
3057
- date.setUTCHours(0);
3058
- date.setUTCMinutes(0);
3059
- date.setUTCSeconds(0);
3060
- date.setUTCMilliseconds(0);
3327
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
3061
3328
 
3062
3329
  this.showMode(-2);
3063
3330
  var which = this.todayBtn == 'linked' ? null : 'view';
@@ -3280,7 +3547,17 @@ $(function(){
3280
3547
  if (dir) {
3281
3548
  this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
3282
3549
  }
3283
- this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
3550
+ /*
3551
+ vitalets: fixing bug of very special conditions:
3552
+ jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
3553
+ Method show() does not set display css correctly and datepicker is not shown.
3554
+ Changed to .css('display', 'block') solve the problem.
3555
+ See https://github.com/vitalets/x-editable/issues/37
3556
+
3557
+ In jquery 1.7.2+ everything works fine.
3558
+ */
3559
+ //this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
3560
+ this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
3284
3561
  this.updateNavArrows();
3285
3562
  }
3286
3563
  };
@@ -3398,7 +3675,7 @@ $(function(){
3398
3675
  val, filtered, part;
3399
3676
  setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
3400
3677
  setters_map['dd'] = setters_map['d'];
3401
- date = UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
3678
+ date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
3402
3679
  if (parts.length == format.parts.length) {
3403
3680
  for (var i=0, cnt = format.parts.length; i < cnt; i++) {
3404
3681
  val = parseInt(parts[i], 10);