bootstrap-editable-rails 0.0.3 → 0.0.4
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 +12 -6
- data/lib/bootstrap-editable-rails/version.rb +1 -1
- data/vendor/assets/images/clear.png +0 -0
- data/vendor/assets/javascripts/bootstrap-editable-rails.js.coffee +11 -4
- data/vendor/assets/javascripts/bootstrap-editable.js +2166 -483
- data/vendor/assets/javascripts/bootstrap-editable.min.js +3 -3
- data/vendor/assets/stylesheets/bootstrap-editable.css +169 -142
- metadata +5 -6
- data/vendor/assets/javascripts/bootstrap-editable-inline.js +0 -3769
- data/vendor/assets/javascripts/bootstrap-editable-inline.min.js +0 -5
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.
|
5
|
+
This gem is based on X-editable (v1.4.1) which is the new version of Bootstrap Editable.
|
6
6
|
|
7
7
|
https://github.com/vitalets/x-editable
|
8
8
|
|
@@ -35,15 +35,13 @@ Write the top of `app/assets/javascripts/application.js` like this:
|
|
35
35
|
//= require_tree .
|
36
36
|
```
|
37
37
|
|
38
|
-
(You can choose `bootstrap-editable-inline`)
|
39
|
-
|
40
38
|
and need to load `bootstrap-editable.css` at the place where you like.
|
41
39
|
|
42
40
|
### HTML
|
43
41
|
|
44
|
-
Follow the documents above.
|
42
|
+
Follow the documents of X-editable above.
|
45
43
|
|
46
|
-
Additional required attribute
|
44
|
+
Additional required attribute is `resource`.
|
47
45
|
|
48
46
|
```html
|
49
47
|
<a href="#" id="username" data-type="text" data-resource="post" data-name="username" data-url="/posts/1" data-original-title="Enter username">superuser</a>
|
@@ -55,6 +53,14 @@ then, sends `PUT /posts/1` request with the body:
|
|
55
53
|
post[username]=superuser
|
56
54
|
```
|
57
55
|
|
56
|
+
When using `textarea` type, `textarea_format` helper method for formatting line breaks is available.
|
57
|
+
|
58
|
+
```html
|
59
|
+
<a href="#" id="body" data-type="textarea" data-resource="post" data-name="body" data-url="/posts/1" data-original-title="Enter body">
|
60
|
+
<%= textarea_format(@post.body) %>
|
61
|
+
</a>
|
62
|
+
```
|
63
|
+
|
58
64
|
### Controller
|
59
65
|
|
60
66
|
PostsController receives the parameters
|
@@ -65,7 +71,7 @@ PostsController receives the parameters
|
|
65
71
|
|
66
72
|
and must respond with 2xx (means _success_) status code if successful.
|
67
73
|
|
68
|
-
For example, scaffold
|
74
|
+
For example, scaffold works well by 204 because default dataType is json.
|
69
75
|
|
70
76
|
```ruby
|
71
77
|
def update
|
Binary file
|
@@ -10,14 +10,21 @@ jQuery ($) ->
|
|
10
10
|
# TODO: should not send when create new object
|
11
11
|
if typeof originalUrl == 'function' # user's function
|
12
12
|
originalUrl.call(@options.scope, params)
|
13
|
-
else
|
13
|
+
else if originalUrl? && @options.send != 'never'
|
14
|
+
# send ajax to server and return deferred object
|
14
15
|
obj = {}
|
15
|
-
data = {}
|
16
16
|
obj[params.name] = params.value
|
17
|
-
|
17
|
+
# support custom inputtypes (eg address)
|
18
|
+
if resource
|
19
|
+
params[resource] = obj
|
20
|
+
else
|
21
|
+
params = obj
|
22
|
+
delete params.name
|
23
|
+
delete params.value
|
24
|
+
delete params.pk
|
18
25
|
$.ajax($.extend({
|
19
26
|
url : originalUrl
|
20
|
-
data :
|
27
|
+
data : params
|
21
28
|
type : 'PUT' # TODO: should be 'POST' when create new object
|
22
29
|
dataType: 'json'
|
23
30
|
}, @options.ajaxOptions))
|
@@ -1,7 +1,7 @@
|
|
1
|
-
/*! X-editable - v1.
|
1
|
+
/*! X-editable - v1.4.1
|
2
2
|
* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
|
3
3
|
* http://github.com/vitalets/x-editable
|
4
|
-
* Copyright (c)
|
4
|
+
* Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */
|
5
5
|
|
6
6
|
/**
|
7
7
|
Form with single input element, two buttons and two states: normal/loading.
|
@@ -16,28 +16,21 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
16
16
|
|
17
17
|
var EditableForm = function (div, options) {
|
18
18
|
this.options = $.extend({}, $.fn.editableform.defaults, options);
|
19
|
-
this.$div = $(div); //div, containing form. Not form tag
|
19
|
+
this.$div = $(div); //div, containing form. Not form tag. Not editable-element.
|
20
20
|
if(!this.options.scope) {
|
21
21
|
this.options.scope = this;
|
22
22
|
}
|
23
|
-
|
23
|
+
//nothing shown after init
|
24
24
|
};
|
25
25
|
|
26
26
|
EditableForm.prototype = {
|
27
27
|
constructor: EditableForm,
|
28
28
|
initInput: function() { //called once
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
|
35
|
-
this.input = new TypeConstructor(typeOptions);
|
36
|
-
} else {
|
37
|
-
$.error('Unknown type: '+ this.options.type);
|
38
|
-
return;
|
39
|
-
}
|
40
|
-
|
29
|
+
//take input from options (as it is created in editable-element)
|
30
|
+
this.input = this.options.input;
|
31
|
+
|
32
|
+
//set initial value
|
33
|
+
//todo: may be add check: typeof str === 'string' ?
|
41
34
|
this.value = this.input.str2value(this.options.value);
|
42
35
|
},
|
43
36
|
initTemplate: function() {
|
@@ -52,47 +45,49 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
52
45
|
@method render
|
53
46
|
**/
|
54
47
|
render: function() {
|
48
|
+
//init loader
|
55
49
|
this.$loading = $($.fn.editableform.loading);
|
56
50
|
this.$div.empty().append(this.$loading);
|
57
|
-
this.showLoading();
|
58
51
|
|
59
52
|
//init form template and buttons
|
60
|
-
this.initTemplate();
|
53
|
+
this.initTemplate();
|
61
54
|
if(this.options.showbuttons) {
|
62
55
|
this.initButtons();
|
63
56
|
} else {
|
64
57
|
this.$form.find('.editable-buttons').remove();
|
65
58
|
}
|
66
59
|
|
60
|
+
//show loading state
|
61
|
+
this.showLoading();
|
62
|
+
|
67
63
|
/**
|
68
64
|
Fired when rendering starts
|
69
65
|
@event rendering
|
70
66
|
@param {Object} event event object
|
71
67
|
**/
|
72
68
|
this.$div.triggerHandler('rendering');
|
69
|
+
|
70
|
+
//init input
|
71
|
+
this.initInput();
|
72
|
+
|
73
|
+
//append input to form
|
74
|
+
this.input.prerender();
|
75
|
+
this.$form.find('div.editable-input').append(this.input.$tpl);
|
73
76
|
|
77
|
+
//append form to container
|
78
|
+
this.$div.append(this.$form);
|
79
|
+
|
74
80
|
//render input
|
75
81
|
$.when(this.input.render())
|
76
82
|
.then($.proxy(function () {
|
77
|
-
//input
|
78
|
-
this.$form.find('div.editable-input').append(this.input.$input);
|
79
|
-
|
80
|
-
//automatically submit inputs when no buttons shown
|
83
|
+
//setup input to submit automatically when no buttons shown
|
81
84
|
if(!this.options.showbuttons) {
|
82
85
|
this.input.autosubmit();
|
83
86
|
}
|
84
|
-
|
85
|
-
//"clear" link
|
86
|
-
if(this.input.$clear) {
|
87
|
-
this.$form.find('div.editable-input').append($('<div class="editable-clear">').append(this.input.$clear));
|
88
|
-
}
|
89
|
-
|
90
|
-
//append form to container
|
91
|
-
this.$div.append(this.$form);
|
92
87
|
|
93
88
|
//attach 'cancel' handler
|
94
89
|
this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
|
95
|
-
|
90
|
+
|
96
91
|
if(this.input.error) {
|
97
92
|
this.error(this.input.error);
|
98
93
|
this.$form.find('.editable-submit').attr('disabled', true);
|
@@ -116,6 +111,11 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
116
111
|
this.$div.triggerHandler('rendered');
|
117
112
|
|
118
113
|
this.showForm();
|
114
|
+
|
115
|
+
//call postrender method to perform actions required visibility of form
|
116
|
+
if(this.input.postrender) {
|
117
|
+
this.input.postrender();
|
118
|
+
}
|
119
119
|
}, this));
|
120
120
|
},
|
121
121
|
cancel: function() {
|
@@ -127,11 +127,17 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
127
127
|
this.$div.triggerHandler('cancel');
|
128
128
|
},
|
129
129
|
showLoading: function() {
|
130
|
-
var w;
|
130
|
+
var w, h;
|
131
131
|
if(this.$form) {
|
132
|
-
//set loading size equal to form
|
133
|
-
this.$
|
134
|
-
this.$
|
132
|
+
//set loading size equal to form
|
133
|
+
w = this.$form.outerWidth();
|
134
|
+
h = this.$form.outerHeight();
|
135
|
+
if(w) {
|
136
|
+
this.$loading.width(w);
|
137
|
+
}
|
138
|
+
if(h) {
|
139
|
+
this.$loading.height(h);
|
140
|
+
}
|
135
141
|
this.$form.hide();
|
136
142
|
} else {
|
137
143
|
//stretch loading to fill container width
|
@@ -159,14 +165,23 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
159
165
|
|
160
166
|
error: function(msg) {
|
161
167
|
var $group = this.$form.find('.control-group'),
|
162
|
-
|
168
|
+
$block = this.$form.find('.editable-error-block'),
|
169
|
+
lines;
|
163
170
|
|
164
171
|
if(msg === false) {
|
165
172
|
$group.removeClass($.fn.editableform.errorGroupClass);
|
166
173
|
$block.removeClass($.fn.editableform.errorBlockClass).empty().hide();
|
167
174
|
} else {
|
175
|
+
//convert newline to <br> for more pretty error display
|
176
|
+
if(msg) {
|
177
|
+
lines = msg.split("\n");
|
178
|
+
for (var i = 0; i < lines.length; i++) {
|
179
|
+
lines[i] = $('<div>').text(lines[i]).html();
|
180
|
+
}
|
181
|
+
msg = lines.join('<br>');
|
182
|
+
}
|
168
183
|
$group.addClass($.fn.editableform.errorGroupClass);
|
169
|
-
$block.addClass($.fn.editableform.errorBlockClass).
|
184
|
+
$block.addClass($.fn.editableform.errorBlockClass).html(msg).show();
|
170
185
|
}
|
171
186
|
},
|
172
187
|
|
@@ -218,6 +233,7 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
218
233
|
}
|
219
234
|
|
220
235
|
//if success callback returns object like {newValue: <something>} --> use that value instead of submitted
|
236
|
+
//it is usefull if you want to chnage value in url-function
|
221
237
|
if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
|
222
238
|
newValue = res.newValue;
|
223
239
|
}
|
@@ -299,10 +315,15 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
299
315
|
},
|
300
316
|
|
301
317
|
option: function(key, value) {
|
302
|
-
this.options
|
318
|
+
if(key in this.options) {
|
319
|
+
this.options[key] = value;
|
320
|
+
}
|
321
|
+
|
303
322
|
if(key === 'value') {
|
304
323
|
this.setValue(value);
|
305
324
|
}
|
325
|
+
|
326
|
+
//do not pass option to input as it is passed in editable-element
|
306
327
|
},
|
307
328
|
|
308
329
|
setValue: function(value, convertStr) {
|
@@ -311,6 +332,11 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
311
332
|
} else {
|
312
333
|
this.value = value;
|
313
334
|
}
|
335
|
+
|
336
|
+
//if form is visible, update input
|
337
|
+
if(this.$form && this.$form.is(':visible')) {
|
338
|
+
this.input.value2input(this.value);
|
339
|
+
}
|
314
340
|
}
|
315
341
|
};
|
316
342
|
|
@@ -363,18 +389,25 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
363
389
|
type: 'text',
|
364
390
|
/**
|
365
391
|
Url for submit, e.g. <code>'/post'</code>
|
366
|
-
If function - it will be called instead of ajax. Function
|
392
|
+
If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks.
|
367
393
|
|
368
394
|
@property url
|
369
395
|
@type string|function
|
370
396
|
@default null
|
371
397
|
@example
|
372
398
|
url: function(params) {
|
399
|
+
var d = new $.Deferred;
|
373
400
|
if(params.value === 'abc') {
|
374
|
-
|
375
|
-
return d.reject('field cannot be "abc"'); //returning error via deferred object
|
401
|
+
return d.reject('error message'); //returning error via deferred object
|
376
402
|
} else {
|
377
|
-
|
403
|
+
//async saving data in js model
|
404
|
+
someModel.asyncSaveMethod({
|
405
|
+
...,
|
406
|
+
success: function(){
|
407
|
+
d.resolve();
|
408
|
+
}
|
409
|
+
});
|
410
|
+
return d.promise();
|
378
411
|
}
|
379
412
|
}
|
380
413
|
**/
|
@@ -463,21 +496,21 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
463
496
|
/**
|
464
497
|
Additional options for ajax request.
|
465
498
|
List of values: http://api.jquery.com/jQuery.ajax
|
466
|
-
|
499
|
+
|
467
500
|
@property ajaxOptions
|
468
501
|
@type object
|
469
502
|
@default null
|
470
503
|
@since 1.1.1
|
504
|
+
@example
|
505
|
+
ajaxOptions: {
|
506
|
+
type: 'put',
|
507
|
+
dataType: 'json'
|
508
|
+
}
|
471
509
|
**/
|
472
510
|
ajaxOptions: null,
|
473
511
|
/**
|
474
512
|
Whether to show buttons or not.
|
475
|
-
Form without buttons
|
476
|
-
@example
|
477
|
-
ajaxOptions: {
|
478
|
-
method: 'PUT',
|
479
|
-
dataType: 'xml'
|
480
|
-
}
|
513
|
+
Form without buttons is auto-submitted.
|
481
514
|
|
482
515
|
@property showbuttons
|
483
516
|
@type boolean
|
@@ -621,19 +654,22 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
621
654
|
return newObj;
|
622
655
|
},
|
623
656
|
|
624
|
-
|
625
|
-
|
657
|
+
/*
|
658
|
+
exclude complex objects from $.data() before pass to config
|
626
659
|
*/
|
627
660
|
getConfigData: function($element) {
|
628
661
|
var data = {};
|
629
662
|
$.each($element.data(), function(k, v) {
|
630
|
-
if(typeof v !== 'object' || (v && typeof v === 'object' && v.constructor === Object)) {
|
663
|
+
if(typeof v !== 'object' || (v && typeof v === 'object' && (v.constructor === Object || v.constructor === Array))) {
|
631
664
|
data[k] = v;
|
632
665
|
}
|
633
666
|
});
|
634
667
|
return data;
|
635
668
|
},
|
636
669
|
|
670
|
+
/*
|
671
|
+
returns keys of object
|
672
|
+
*/
|
637
673
|
objectKeys: function(o) {
|
638
674
|
if (Object.keys) {
|
639
675
|
return Object.keys(o);
|
@@ -657,9 +693,95 @@ Editableform is linked with one of input types, e.g. 'text', 'select' etc.
|
|
657
693
|
**/
|
658
694
|
escape: function(str) {
|
659
695
|
return $('<div>').text(str).html();
|
660
|
-
}
|
696
|
+
},
|
697
|
+
|
698
|
+
/*
|
699
|
+
returns array items from sourceData having value property equal or inArray of 'value'
|
700
|
+
*/
|
701
|
+
itemsByValue: function(value, sourceData, valueProp) {
|
702
|
+
if(!sourceData || value === null) {
|
703
|
+
return [];
|
704
|
+
}
|
705
|
+
|
706
|
+
valueProp = valueProp || 'value';
|
707
|
+
|
708
|
+
var isValArray = $.isArray(value),
|
709
|
+
result = [],
|
710
|
+
that = this;
|
711
|
+
|
712
|
+
$.each(sourceData, function(i, o) {
|
713
|
+
if(o.children) {
|
714
|
+
result = result.concat(that.itemsByValue(value, o.children));
|
715
|
+
} else {
|
716
|
+
/*jslint eqeq: true*/
|
717
|
+
if(isValArray) {
|
718
|
+
if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) {
|
719
|
+
result.push(o);
|
720
|
+
}
|
721
|
+
} else {
|
722
|
+
if(value == (o && typeof o === 'object' ? o[valueProp] : o)) {
|
723
|
+
result.push(o);
|
724
|
+
}
|
725
|
+
}
|
726
|
+
/*jslint eqeq: false*/
|
727
|
+
}
|
728
|
+
});
|
729
|
+
|
730
|
+
return result;
|
731
|
+
},
|
732
|
+
|
733
|
+
/*
|
734
|
+
Returns input by options: type, mode.
|
735
|
+
*/
|
736
|
+
createInput: function(options) {
|
737
|
+
var TypeConstructor, typeOptions, input,
|
738
|
+
type = options.type;
|
739
|
+
|
740
|
+
//`date` is some kind of virtual type that is transformed to one of exact types
|
741
|
+
//depending on mode and core lib
|
742
|
+
if(type === 'date') {
|
743
|
+
//inline
|
744
|
+
if(options.mode === 'inline') {
|
745
|
+
if($.fn.editabletypes.datefield) {
|
746
|
+
type = 'datefield';
|
747
|
+
} else if($.fn.editabletypes.dateuifield) {
|
748
|
+
type = 'dateuifield';
|
749
|
+
}
|
750
|
+
//popup
|
751
|
+
} else {
|
752
|
+
if($.fn.editabletypes.date) {
|
753
|
+
type = 'date';
|
754
|
+
} else if($.fn.editabletypes.dateui) {
|
755
|
+
type = 'dateui';
|
756
|
+
}
|
757
|
+
}
|
758
|
+
|
759
|
+
//if type still `date` and not exist in types, replace with `combodate` that is base input
|
760
|
+
if(type === 'date' && !$.fn.editabletypes.date) {
|
761
|
+
type = 'combodate';
|
762
|
+
}
|
763
|
+
}
|
764
|
+
|
765
|
+
//change wysihtml5 to textarea for jquery UI and plain versions
|
766
|
+
if(type === 'wysihtml5' && !$.fn.editabletypes[type]) {
|
767
|
+
type = 'textarea';
|
768
|
+
}
|
769
|
+
|
770
|
+
//create input of specified type. Input will be used for converting value, not in form
|
771
|
+
if(typeof $.fn.editabletypes[type] === 'function') {
|
772
|
+
TypeConstructor = $.fn.editabletypes[type];
|
773
|
+
typeOptions = this.sliceObj(options, this.objectKeys(TypeConstructor.defaults));
|
774
|
+
input = new TypeConstructor(typeOptions);
|
775
|
+
return input;
|
776
|
+
} else {
|
777
|
+
$.error('Unknown type: '+ type);
|
778
|
+
return false;
|
779
|
+
}
|
780
|
+
}
|
781
|
+
|
661
782
|
};
|
662
|
-
}(window.jQuery));
|
783
|
+
}(window.jQuery));
|
784
|
+
|
663
785
|
/**
|
664
786
|
Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
|
665
787
|
This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
|
@@ -671,19 +793,27 @@ Applied as jQuery method.
|
|
671
793
|
**/
|
672
794
|
(function ($) {
|
673
795
|
|
674
|
-
var
|
796
|
+
var Popup = function (element, options) {
|
675
797
|
this.init(element, options);
|
676
798
|
};
|
799
|
+
|
800
|
+
var Inline = function (element, options) {
|
801
|
+
this.init(element, options);
|
802
|
+
};
|
677
803
|
|
678
804
|
//methods
|
679
|
-
|
805
|
+
Popup.prototype = {
|
680
806
|
containerName: null, //tbd in child class
|
681
807
|
innerCss: null, //tbd in child class
|
682
808
|
init: function(element, options) {
|
683
809
|
this.$element = $(element);
|
684
|
-
//
|
685
|
-
this.options = $.extend({}, $.fn.editableContainer.defaults,
|
810
|
+
//since 1.4.1 container do not use data-* directly as they already merged into options.
|
811
|
+
this.options = $.extend({}, $.fn.editableContainer.defaults, options);
|
686
812
|
this.splitOptions();
|
813
|
+
|
814
|
+
//set scope of form callbacks to element
|
815
|
+
this.formOptions.scope = this.$element[0];
|
816
|
+
|
687
817
|
this.initContainer();
|
688
818
|
|
689
819
|
//bind 'destroyed' listener to destroy container when element is removed from dom
|
@@ -691,7 +821,7 @@ Applied as jQuery method.
|
|
691
821
|
this.destroy();
|
692
822
|
}, this));
|
693
823
|
|
694
|
-
//attach document
|
824
|
+
//attach document handler to close containers on click / escape
|
695
825
|
if(!$(document).data('editable-handlers-attached')) {
|
696
826
|
//close all on escape
|
697
827
|
$(document).on('keyup.editable', function (e) {
|
@@ -703,15 +833,22 @@ Applied as jQuery method.
|
|
703
833
|
|
704
834
|
//close containers when click outside
|
705
835
|
$(document).on('click.editable', function(e) {
|
706
|
-
var $target = $(e.target)
|
836
|
+
var $target = $(e.target), i,
|
837
|
+
exclude_classes = ['.editable-container',
|
838
|
+
'.ui-datepicker-header',
|
839
|
+
'.modal-backdrop',
|
840
|
+
'.bootstrap-wysihtml5-insert-image-modal',
|
841
|
+
'.bootstrap-wysihtml5-insert-link-modal'];
|
707
842
|
|
708
|
-
//if click inside
|
709
|
-
|
710
|
-
|
711
|
-
|
712
|
-
|
713
|
-
EditableContainer.prototype.closeOthers(e.target);
|
843
|
+
//if click inside one of exclude classes --> no nothing
|
844
|
+
for(i=0; i<exclude_classes.length; i++) {
|
845
|
+
if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
|
846
|
+
return;
|
847
|
+
}
|
714
848
|
}
|
849
|
+
|
850
|
+
//close all open containers (except one - target)
|
851
|
+
Popup.prototype.closeOthers(e.target);
|
715
852
|
});
|
716
853
|
|
717
854
|
$(document).data('editable-handlers-attached', true);
|
@@ -723,6 +860,7 @@ Applied as jQuery method.
|
|
723
860
|
this.containerOptions = {};
|
724
861
|
this.formOptions = {};
|
725
862
|
var cDef = $.fn[this.containerName].defaults;
|
863
|
+
//keys defined in container defaults go to container, others go to form
|
726
864
|
for(var k in this.options) {
|
727
865
|
if(k in cDef) {
|
728
866
|
this.containerOptions[k] = this.options[k];
|
@@ -732,20 +870,37 @@ Applied as jQuery method.
|
|
732
870
|
}
|
733
871
|
},
|
734
872
|
|
873
|
+
/*
|
874
|
+
Returns jquery object of container
|
875
|
+
@method tip()
|
876
|
+
*/
|
877
|
+
tip: function() {
|
878
|
+
return this.container() ? this.container().$tip : null;
|
879
|
+
},
|
880
|
+
|
881
|
+
/* returns container object */
|
882
|
+
container: function() {
|
883
|
+
return this.$element.data(this.containerName);
|
884
|
+
},
|
885
|
+
|
886
|
+
call: function() {
|
887
|
+
this.$element[this.containerName].apply(this.$element, arguments);
|
888
|
+
},
|
889
|
+
|
735
890
|
initContainer: function(){
|
736
891
|
this.call(this.containerOptions);
|
737
892
|
},
|
738
893
|
|
739
|
-
|
740
|
-
this
|
741
|
-
this.$form = $('<div>')
|
894
|
+
renderForm: function() {
|
895
|
+
this.$form
|
742
896
|
.editableform(this.formOptions)
|
743
897
|
.on({
|
744
|
-
save: $.proxy(this.save, this),
|
745
|
-
|
746
|
-
|
898
|
+
save: $.proxy(this.save, this), //click on submit button (value changed)
|
899
|
+
nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)
|
900
|
+
cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on calcel button
|
747
901
|
show: $.proxy(this.setPosition, this), //re-position container every time form is shown (occurs each time after loading state)
|
748
902
|
rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
|
903
|
+
resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed
|
749
904
|
rendered: $.proxy(function(){
|
750
905
|
/**
|
751
906
|
Fired when container is shown and form is rendered (for select will wait for loading dropdown options)
|
@@ -760,31 +915,16 @@ Applied as jQuery method.
|
|
760
915
|
**/
|
761
916
|
this.$element.triggerHandler('shown');
|
762
917
|
}, this)
|
763
|
-
})
|
764
|
-
|
918
|
+
})
|
919
|
+
.editableform('render');
|
765
920
|
},
|
766
921
|
|
767
|
-
/*
|
768
|
-
Returns jquery object of container
|
769
|
-
@method tip()
|
770
|
-
*/
|
771
|
-
tip: function() {
|
772
|
-
return this.container().$tip;
|
773
|
-
},
|
774
|
-
|
775
|
-
container: function() {
|
776
|
-
return this.$element.data(this.containerName);
|
777
|
-
},
|
778
|
-
|
779
|
-
call: function() {
|
780
|
-
this.$element[this.containerName].apply(this.$element, arguments);
|
781
|
-
},
|
782
|
-
|
783
922
|
/**
|
784
923
|
Shows container with form
|
785
924
|
@method show()
|
786
925
|
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
|
787
|
-
**/
|
926
|
+
**/
|
927
|
+
/* Note: poshytip owerwrites this method totally! */
|
788
928
|
show: function (closeAll) {
|
789
929
|
this.$element.addClass('editable-open');
|
790
930
|
if(closeAll !== false) {
|
@@ -792,16 +932,37 @@ Applied as jQuery method.
|
|
792
932
|
this.closeOthers(this.$element[0]);
|
793
933
|
}
|
794
934
|
|
935
|
+
//show container itself
|
795
936
|
this.innerShow();
|
796
|
-
},
|
797
|
-
|
798
|
-
/* internal show method. To be overwritten in child classes */
|
799
|
-
innerShow: function () {
|
800
|
-
this.call('show');
|
801
937
|
this.tip().addClass('editable-container');
|
802
|
-
|
803
|
-
|
804
|
-
|
938
|
+
|
939
|
+
/*
|
940
|
+
Currently, form is re-rendered on every show.
|
941
|
+
The main reason is that we dont know, what container will do with content when closed:
|
942
|
+
remove(), detach() or just hide().
|
943
|
+
|
944
|
+
Detaching form itself before hide and re-insert before show is good solution,
|
945
|
+
but visually it looks ugly, as container changes size before hide.
|
946
|
+
*/
|
947
|
+
|
948
|
+
//if form already exist - delete previous data
|
949
|
+
if(this.$form) {
|
950
|
+
//todo: destroy prev data!
|
951
|
+
//this.$form.destroy();
|
952
|
+
}
|
953
|
+
|
954
|
+
this.$form = $('<div>');
|
955
|
+
|
956
|
+
//insert form into container body
|
957
|
+
if(this.tip().is(this.innerCss)) {
|
958
|
+
//for inline container
|
959
|
+
this.tip().append(this.$form);
|
960
|
+
} else {
|
961
|
+
this.tip().find(this.innerCss).append(this.$form);
|
962
|
+
}
|
963
|
+
|
964
|
+
//render form
|
965
|
+
this.renderForm();
|
805
966
|
},
|
806
967
|
|
807
968
|
/**
|
@@ -813,8 +974,10 @@ Applied as jQuery method.
|
|
813
974
|
if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
|
814
975
|
return;
|
815
976
|
}
|
977
|
+
|
816
978
|
this.$element.removeClass('editable-open');
|
817
979
|
this.innerHide();
|
980
|
+
|
818
981
|
/**
|
819
982
|
Fired when container was hidden. It occurs on both save or cancel.
|
820
983
|
|
@@ -832,9 +995,14 @@ Applied as jQuery method.
|
|
832
995
|
this.$element.triggerHandler('hidden', reason);
|
833
996
|
},
|
834
997
|
|
998
|
+
/* internal show method. To be overwritten in child classes */
|
999
|
+
innerShow: function () {
|
1000
|
+
|
1001
|
+
},
|
1002
|
+
|
835
1003
|
/* internal hide method. To be overwritten in child classes */
|
836
1004
|
innerHide: function () {
|
837
|
-
|
1005
|
+
|
838
1006
|
},
|
839
1007
|
|
840
1008
|
/**
|
@@ -843,7 +1011,7 @@ Applied as jQuery method.
|
|
843
1011
|
@param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
|
844
1012
|
**/
|
845
1013
|
toggle: function(closeAll) {
|
846
|
-
if(this.tip && this.tip().is(':visible')) {
|
1014
|
+
if(this.container() && this.tip() && this.tip().is(':visible')) {
|
847
1015
|
this.hide();
|
848
1016
|
} else {
|
849
1017
|
this.show(closeAll);
|
@@ -859,7 +1027,6 @@ Applied as jQuery method.
|
|
859
1027
|
},
|
860
1028
|
|
861
1029
|
save: function(e, params) {
|
862
|
-
this.hide('save');
|
863
1030
|
/**
|
864
1031
|
Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
|
865
1032
|
|
@@ -880,6 +1047,9 @@ Applied as jQuery method.
|
|
880
1047
|
});
|
881
1048
|
**/
|
882
1049
|
this.$element.triggerHandler('save', params);
|
1050
|
+
|
1051
|
+
//hide must be after trigger, as saving value may require methods od plugin, applied to input
|
1052
|
+
this.hide('save');
|
883
1053
|
},
|
884
1054
|
|
885
1055
|
/**
|
@@ -911,9 +1081,17 @@ Applied as jQuery method.
|
|
911
1081
|
@method destroy()
|
912
1082
|
**/
|
913
1083
|
destroy: function() {
|
914
|
-
this.
|
1084
|
+
this.hide();
|
1085
|
+
this.innerDestroy();
|
1086
|
+
this.$element.off('destroyed');
|
1087
|
+
this.$element.removeData('editableContainer');
|
915
1088
|
},
|
916
1089
|
|
1090
|
+
/* to be overwritten in child classes */
|
1091
|
+
innerDestroy: function() {
|
1092
|
+
|
1093
|
+
},
|
1094
|
+
|
917
1095
|
/*
|
918
1096
|
Closes other containers except one related to passed element.
|
919
1097
|
Other containers can be cancelled or submitted (depends on onblur option)
|
@@ -972,11 +1150,12 @@ Applied as jQuery method.
|
|
972
1150
|
return this.each(function () {
|
973
1151
|
var $this = $(this),
|
974
1152
|
dataKey = 'editableContainer',
|
975
|
-
data = $this.data(dataKey),
|
976
|
-
options = typeof option === 'object' && option
|
1153
|
+
data = $this.data(dataKey),
|
1154
|
+
options = typeof option === 'object' && option,
|
1155
|
+
Constructor = (options.mode === 'inline') ? Inline : Popup;
|
977
1156
|
|
978
1157
|
if (!data) {
|
979
|
-
$this.data(dataKey, (data = new
|
1158
|
+
$this.data(dataKey, (data = new Constructor(this, options)));
|
980
1159
|
}
|
981
1160
|
|
982
1161
|
if (typeof option === 'string') { //call method
|
@@ -985,8 +1164,9 @@ Applied as jQuery method.
|
|
985
1164
|
});
|
986
1165
|
};
|
987
1166
|
|
988
|
-
//store
|
989
|
-
$.fn.editableContainer.
|
1167
|
+
//store constructors
|
1168
|
+
$.fn.editableContainer.Popup = Popup;
|
1169
|
+
$.fn.editableContainer.Inline = Inline;
|
990
1170
|
|
991
1171
|
//defaults
|
992
1172
|
$.fn.editableContainer.defaults = {
|
@@ -1025,7 +1205,25 @@ Applied as jQuery method.
|
|
1025
1205
|
@default 'cancel'
|
1026
1206
|
@since 1.1.1
|
1027
1207
|
**/
|
1028
|
-
onblur: 'cancel'
|
1208
|
+
onblur: 'cancel',
|
1209
|
+
|
1210
|
+
/**
|
1211
|
+
Animation speed (inline mode)
|
1212
|
+
@property anim
|
1213
|
+
@type string
|
1214
|
+
@default 'fast'
|
1215
|
+
**/
|
1216
|
+
anim: 'fast',
|
1217
|
+
|
1218
|
+
/**
|
1219
|
+
Mode of editable, can be `popup` or `inline`
|
1220
|
+
|
1221
|
+
@property mode
|
1222
|
+
@type string
|
1223
|
+
@default 'popup'
|
1224
|
+
@since 1.4.0
|
1225
|
+
**/
|
1226
|
+
mode: 'popup'
|
1029
1227
|
};
|
1030
1228
|
|
1031
1229
|
/*
|
@@ -1042,6 +1240,58 @@ Applied as jQuery method.
|
|
1042
1240
|
|
1043
1241
|
}(window.jQuery));
|
1044
1242
|
|
1243
|
+
/**
|
1244
|
+
* Editable Inline
|
1245
|
+
* ---------------------
|
1246
|
+
*/
|
1247
|
+
(function ($) {
|
1248
|
+
|
1249
|
+
//copy prototype from EditableContainer
|
1250
|
+
//extend methods
|
1251
|
+
$.extend($.fn.editableContainer.Inline.prototype, $.fn.editableContainer.Popup.prototype, {
|
1252
|
+
containerName: 'editableform',
|
1253
|
+
innerCss: '.editable-inline',
|
1254
|
+
|
1255
|
+
initContainer: function(){
|
1256
|
+
//container is <span> element
|
1257
|
+
this.$tip = $('<span></span>').addClass('editable-inline');
|
1258
|
+
|
1259
|
+
//convert anim to miliseconds (int)
|
1260
|
+
if(!this.options.anim) {
|
1261
|
+
this.options.anim = 0;
|
1262
|
+
}
|
1263
|
+
},
|
1264
|
+
|
1265
|
+
splitOptions: function() {
|
1266
|
+
//all options are passed to form
|
1267
|
+
this.containerOptions = {};
|
1268
|
+
this.formOptions = this.options;
|
1269
|
+
},
|
1270
|
+
|
1271
|
+
tip: function() {
|
1272
|
+
return this.$tip;
|
1273
|
+
},
|
1274
|
+
|
1275
|
+
innerShow: function () {
|
1276
|
+
this.$element.hide();
|
1277
|
+
this.tip().insertAfter(this.$element).show();
|
1278
|
+
},
|
1279
|
+
|
1280
|
+
innerHide: function () {
|
1281
|
+
this.$tip.hide(this.options.anim, $.proxy(function() {
|
1282
|
+
this.$element.show();
|
1283
|
+
this.innerDestroy();
|
1284
|
+
}, this));
|
1285
|
+
},
|
1286
|
+
|
1287
|
+
innerDestroy: function() {
|
1288
|
+
if(this.tip()) {
|
1289
|
+
this.tip().empty().remove();
|
1290
|
+
}
|
1291
|
+
}
|
1292
|
+
});
|
1293
|
+
|
1294
|
+
}(window.jQuery));
|
1045
1295
|
/**
|
1046
1296
|
Makes editable any HTML element on the page. Applied as jQuery method.
|
1047
1297
|
|
@@ -1052,34 +1302,27 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1052
1302
|
|
1053
1303
|
var Editable = function (element, options) {
|
1054
1304
|
this.$element = $(element);
|
1055
|
-
|
1056
|
-
this.
|
1305
|
+
//data-* has more priority over js options: because dynamically created elements may change data-*
|
1306
|
+
this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element));
|
1307
|
+
if(this.options.selector) {
|
1308
|
+
this.initLive();
|
1309
|
+
} else {
|
1310
|
+
this.init();
|
1311
|
+
}
|
1057
1312
|
};
|
1058
1313
|
|
1059
1314
|
Editable.prototype = {
|
1060
1315
|
constructor: Editable,
|
1061
1316
|
init: function () {
|
1062
|
-
var
|
1063
|
-
|
1064
|
-
|
1065
|
-
finalize;
|
1066
|
-
|
1067
|
-
//editableContainer must be defined
|
1068
|
-
if(!$.fn.editableContainer) {
|
1069
|
-
$.error('You must define $.fn.editableContainer via including corresponding file (e.g. editable-popover.js)');
|
1070
|
-
return;
|
1071
|
-
}
|
1072
|
-
|
1317
|
+
var isValueByText = false,
|
1318
|
+
doAutotext, finalize;
|
1319
|
+
|
1073
1320
|
//name
|
1074
1321
|
this.options.name = this.options.name || this.$element.attr('id');
|
1075
1322
|
|
1076
1323
|
//create input of specified type. Input will be used for converting value, not in form
|
1077
|
-
|
1078
|
-
|
1079
|
-
this.typeOptions = $.fn.editableutils.sliceObj(this.options, $.fn.editableutils.objectKeys(TypeConstructor.defaults));
|
1080
|
-
this.input = new TypeConstructor(this.typeOptions);
|
1081
|
-
} else {
|
1082
|
-
$.error('Unknown type: '+ this.options.type);
|
1324
|
+
this.input = $.fn.editableutils.createInput(this.options);
|
1325
|
+
if(!this.input) {
|
1083
1326
|
return;
|
1084
1327
|
}
|
1085
1328
|
|
@@ -1108,8 +1351,10 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1108
1351
|
if(this.options.toggle !== 'manual') {
|
1109
1352
|
this.$element.addClass('editable-click');
|
1110
1353
|
this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){
|
1354
|
+
//prevent following link
|
1111
1355
|
e.preventDefault();
|
1112
|
-
|
1356
|
+
|
1357
|
+
//stop propagation not required because in document click handler it checks event target
|
1113
1358
|
//e.stopPropagation();
|
1114
1359
|
|
1115
1360
|
if(this.options.toggle === 'mouseenter') {
|
@@ -1140,30 +1385,54 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1140
1385
|
|
1141
1386
|
@event init
|
1142
1387
|
@param {Object} event event object
|
1143
|
-
@param {Object} editable editable instance
|
1388
|
+
@param {Object} editable editable instance (as here it cannot accessed via data('editable'))
|
1144
1389
|
@since 1.2.0
|
1390
|
+
@example
|
1391
|
+
$('#username').on('init', function(e, editable) {
|
1392
|
+
alert('initialized ' + editable.options.name);
|
1393
|
+
});
|
1145
1394
|
**/
|
1146
1395
|
this.$element.triggerHandler('init', this);
|
1147
1396
|
}, this));
|
1148
1397
|
},
|
1149
1398
|
|
1399
|
+
/*
|
1400
|
+
Initializes parent element for live editables
|
1401
|
+
*/
|
1402
|
+
initLive: function() {
|
1403
|
+
//store selector
|
1404
|
+
var selector = this.options.selector;
|
1405
|
+
//modify options for child elements
|
1406
|
+
this.options.selector = false;
|
1407
|
+
this.options.autotext = 'never';
|
1408
|
+
//listen toggle events
|
1409
|
+
this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
|
1410
|
+
var $target = $(e.target);
|
1411
|
+
if(!$target.data('editable')) {
|
1412
|
+
$target.editable(this.options).trigger(e);
|
1413
|
+
}
|
1414
|
+
}, this));
|
1415
|
+
},
|
1416
|
+
|
1150
1417
|
/*
|
1151
1418
|
Renders value into element's text.
|
1152
1419
|
Can call custom display method from options.
|
1153
1420
|
Can return deferred object.
|
1154
1421
|
@method render()
|
1422
|
+
@param {mixed} response server response (if exist) to pass into display function
|
1155
1423
|
*/
|
1156
|
-
render: function() {
|
1424
|
+
render: function(response) {
|
1157
1425
|
//do not display anything
|
1158
1426
|
if(this.options.display === false) {
|
1159
1427
|
return;
|
1160
1428
|
}
|
1161
|
-
|
1162
|
-
if
|
1163
|
-
|
1429
|
+
|
1430
|
+
//if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded
|
1431
|
+
if(this.input.value2htmlFinal) {
|
1432
|
+
return this.input.value2html(this.value, this.$element[0], this.options.display, response);
|
1164
1433
|
//if display method defined --> use it
|
1165
1434
|
} else if(typeof this.options.display === 'function') {
|
1166
|
-
return this.options.display.call(this.$element[0], this.value);
|
1435
|
+
return this.options.display.call(this.$element[0], this.value, response);
|
1167
1436
|
//else use input's original value2html() method
|
1168
1437
|
} else {
|
1169
1438
|
return this.input.value2html(this.value, this.$element[0]);
|
@@ -1177,7 +1446,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1177
1446
|
enable: function() {
|
1178
1447
|
this.options.disabled = false;
|
1179
1448
|
this.$element.removeClass('editable-disabled');
|
1180
|
-
this.handleEmpty();
|
1449
|
+
this.handleEmpty(this.isEmpty);
|
1181
1450
|
if(this.options.toggle !== 'manual') {
|
1182
1451
|
if(this.$element.attr('tabindex') === '-1') {
|
1183
1452
|
this.$element.removeAttr('tabindex');
|
@@ -1193,7 +1462,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1193
1462
|
this.options.disabled = true;
|
1194
1463
|
this.hide();
|
1195
1464
|
this.$element.addClass('editable-disabled');
|
1196
|
-
this.handleEmpty();
|
1465
|
+
this.handleEmpty(this.isEmpty);
|
1197
1466
|
//do not stop focus on this element
|
1198
1467
|
this.$element.attr('tabindex', -1);
|
1199
1468
|
},
|
@@ -1233,12 +1502,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1233
1502
|
|
1234
1503
|
//disabled
|
1235
1504
|
if(key === 'disabled') {
|
1236
|
-
|
1237
|
-
this.disable();
|
1238
|
-
} else {
|
1239
|
-
this.enable();
|
1240
|
-
}
|
1241
|
-
return;
|
1505
|
+
return value ? this.disable() : this.enable();
|
1242
1506
|
}
|
1243
1507
|
|
1244
1508
|
//value
|
@@ -1250,30 +1514,42 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1250
1514
|
if(this.container) {
|
1251
1515
|
this.container.option(key, value);
|
1252
1516
|
}
|
1517
|
+
|
1518
|
+
//pass option to input directly (as it points to the same in form)
|
1519
|
+
if(this.input.option) {
|
1520
|
+
this.input.option(key, value);
|
1521
|
+
}
|
1522
|
+
|
1253
1523
|
},
|
1254
1524
|
|
1255
1525
|
/*
|
1256
|
-
* set emptytext if element is empty
|
1526
|
+
* set emptytext if element is empty
|
1257
1527
|
*/
|
1258
|
-
handleEmpty: function () {
|
1528
|
+
handleEmpty: function (isEmpty) {
|
1259
1529
|
//do not handle empty if we do not display anything
|
1260
1530
|
if(this.options.display === false) {
|
1261
1531
|
return;
|
1262
1532
|
}
|
1263
1533
|
|
1264
|
-
|
1534
|
+
this.isEmpty = isEmpty !== undefined ? isEmpty : $.trim(this.$element.text()) === '';
|
1535
|
+
|
1265
1536
|
//emptytext shown only for enabled
|
1266
1537
|
if(!this.options.disabled) {
|
1267
|
-
if (
|
1268
|
-
this.$element.
|
1269
|
-
|
1270
|
-
|
1538
|
+
if (this.isEmpty) {
|
1539
|
+
this.$element.text(this.options.emptytext);
|
1540
|
+
if(this.options.emptyclass) {
|
1541
|
+
this.$element.addClass(this.options.emptyclass);
|
1542
|
+
}
|
1543
|
+
} else if(this.options.emptyclass) {
|
1544
|
+
this.$element.removeClass(this.options.emptyclass);
|
1271
1545
|
}
|
1272
1546
|
} else {
|
1273
1547
|
//below required if element disable property was changed
|
1274
|
-
if(this
|
1548
|
+
if(this.isEmpty) {
|
1275
1549
|
this.$element.empty();
|
1276
|
-
this
|
1550
|
+
if(this.options.emptyclass) {
|
1551
|
+
this.$element.removeClass(this.options.emptyclass);
|
1552
|
+
}
|
1277
1553
|
}
|
1278
1554
|
}
|
1279
1555
|
},
|
@@ -1291,9 +1567,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1291
1567
|
//init editableContainer: popover, tooltip, inline, etc..
|
1292
1568
|
if(!this.container) {
|
1293
1569
|
var containerOptions = $.extend({}, this.options, {
|
1294
|
-
value: this.value
|
1570
|
+
value: this.value,
|
1571
|
+
input: this.input //pass input to form (as it is already created)
|
1295
1572
|
});
|
1296
1573
|
this.$element.editableContainer(containerOptions);
|
1574
|
+
//listen `save` event
|
1297
1575
|
this.$element.on("save.internal", $.proxy(this.save, this));
|
1298
1576
|
this.container = this.$element.data('editableContainer');
|
1299
1577
|
} else if(this.container.tip().is(':visible')) {
|
@@ -1331,15 +1609,30 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1331
1609
|
* called when form was submitted
|
1332
1610
|
*/
|
1333
1611
|
save: function(e, params) {
|
1334
|
-
//
|
1335
|
-
if(
|
1336
|
-
|
1337
|
-
|
1338
|
-
|
1612
|
+
//mark element with unsaved class if needed
|
1613
|
+
if(this.options.unsavedclass) {
|
1614
|
+
/*
|
1615
|
+
Add unsaved css to element if:
|
1616
|
+
- url is not user's function
|
1617
|
+
- value was not sent to server
|
1618
|
+
- params.response === undefined, that means data was not sent
|
1619
|
+
- value changed
|
1620
|
+
*/
|
1621
|
+
var sent = false;
|
1622
|
+
sent = sent || typeof this.options.url === 'function';
|
1623
|
+
sent = sent || this.options.display === false;
|
1624
|
+
sent = sent || params.response !== undefined;
|
1625
|
+
sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue));
|
1626
|
+
|
1627
|
+
if(sent) {
|
1628
|
+
this.$element.removeClass(this.options.unsavedclass);
|
1629
|
+
} else {
|
1630
|
+
this.$element.addClass(this.options.unsavedclass);
|
1631
|
+
}
|
1339
1632
|
}
|
1340
1633
|
|
1341
|
-
|
1342
|
-
this.setValue(params.newValue);
|
1634
|
+
//set new value
|
1635
|
+
this.setValue(params.newValue, false, params.response);
|
1343
1636
|
|
1344
1637
|
/**
|
1345
1638
|
Fired when new value was submitted. You can use <code>$(this).data('editable')</code> to access to editable instance
|
@@ -1351,13 +1644,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1351
1644
|
@param {Object} params.response ajax response
|
1352
1645
|
@example
|
1353
1646
|
$('#username').on('save', function(e, params) {
|
1354
|
-
|
1355
|
-
var pk = $(this).data('editable').options.pk;
|
1356
|
-
if(params.response && params.response.success) {
|
1357
|
-
alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
|
1358
|
-
} else {
|
1359
|
-
alert('error!');
|
1360
|
-
}
|
1647
|
+
alert('Saved value: ' + params.newValue);
|
1361
1648
|
});
|
1362
1649
|
**/
|
1363
1650
|
//event itself is triggered by editableContainer. Description here is only for documentation
|
@@ -1375,7 +1662,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1375
1662
|
@param {mixed} value new value
|
1376
1663
|
@param {boolean} convertStr whether to convert value from string to internal format
|
1377
1664
|
**/
|
1378
|
-
setValue: function(value, convertStr) {
|
1665
|
+
setValue: function(value, convertStr, response) {
|
1379
1666
|
if(convertStr) {
|
1380
1667
|
this.value = this.input.str2value(value);
|
1381
1668
|
} else {
|
@@ -1384,7 +1671,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1384
1671
|
if(this.container) {
|
1385
1672
|
this.container.option('value', this.value);
|
1386
1673
|
}
|
1387
|
-
$.when(this.render())
|
1674
|
+
$.when(this.render(response))
|
1388
1675
|
.then($.proxy(function() {
|
1389
1676
|
this.handleEmpty();
|
1390
1677
|
}, this));
|
@@ -1398,7 +1685,28 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1398
1685
|
if(this.container) {
|
1399
1686
|
this.container.activate();
|
1400
1687
|
}
|
1401
|
-
}
|
1688
|
+
},
|
1689
|
+
|
1690
|
+
/**
|
1691
|
+
Removes editable feature from element
|
1692
|
+
@method destroy()
|
1693
|
+
**/
|
1694
|
+
destroy: function() {
|
1695
|
+
if(this.container) {
|
1696
|
+
this.container.destroy();
|
1697
|
+
}
|
1698
|
+
|
1699
|
+
if(this.options.toggle !== 'manual') {
|
1700
|
+
this.$element.removeClass('editable-click');
|
1701
|
+
this.$element.off(this.options.toggle + '.editable');
|
1702
|
+
}
|
1703
|
+
|
1704
|
+
this.$element.off("save.internal");
|
1705
|
+
|
1706
|
+
this.$element.removeClass('editable');
|
1707
|
+
this.$element.removeClass('editable-open');
|
1708
|
+
this.$element.removeData('editable');
|
1709
|
+
}
|
1402
1710
|
};
|
1403
1711
|
|
1404
1712
|
/* EDITABLE PLUGIN DEFINITION
|
@@ -1415,7 +1723,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1415
1723
|
url: '/post',
|
1416
1724
|
pk: 1
|
1417
1725
|
});
|
1418
|
-
**/
|
1726
|
+
**/
|
1419
1727
|
$.fn.editable = function (option) {
|
1420
1728
|
//special API methods returning non-jquery object
|
1421
1729
|
var result = {}, args = arguments, datakey = 'editable';
|
@@ -1432,7 +1740,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1432
1740
|
username: "username is required",
|
1433
1741
|
fullname: "fullname should be minimum 3 letters length"
|
1434
1742
|
}
|
1435
|
-
**/
|
1743
|
+
**/
|
1436
1744
|
case 'validate':
|
1437
1745
|
this.each(function () {
|
1438
1746
|
var $this = $(this), data = $this.data(datakey), error;
|
@@ -1453,7 +1761,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1453
1761
|
username: "superuser",
|
1454
1762
|
fullname: "John"
|
1455
1763
|
}
|
1456
|
-
**/
|
1764
|
+
**/
|
1457
1765
|
case 'getValue':
|
1458
1766
|
this.each(function () {
|
1459
1767
|
var $this = $(this), data = $this.data(datakey);
|
@@ -1472,11 +1780,11 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1472
1780
|
@param {object} options
|
1473
1781
|
@param {object} options.url url to submit data
|
1474
1782
|
@param {object} options.data additional data to submit
|
1475
|
-
@param {object} options.ajaxOptions additional ajax options
|
1783
|
+
@param {object} options.ajaxOptions additional ajax options
|
1476
1784
|
@param {function} options.error(obj) error handler
|
1477
1785
|
@param {function} options.success(obj,config) success handler
|
1478
1786
|
@returns {Object} jQuery object
|
1479
|
-
**/
|
1787
|
+
**/
|
1480
1788
|
case 'submit': //collects value, validate and submit to server for creating new record
|
1481
1789
|
var config = arguments[1] || {},
|
1482
1790
|
$elems = this,
|
@@ -1584,7 +1892,7 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1584
1892
|
**/
|
1585
1893
|
autotext: 'auto',
|
1586
1894
|
/**
|
1587
|
-
Initial value of input.
|
1895
|
+
Initial value of input. If not set, taken from element's text.
|
1588
1896
|
|
1589
1897
|
@property value
|
1590
1898
|
@type mixed
|
@@ -1593,10 +1901,21 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1593
1901
|
value: null,
|
1594
1902
|
/**
|
1595
1903
|
Callback to perform custom displaying of value in element's text.
|
1596
|
-
If
|
1597
|
-
If
|
1904
|
+
If `null`, default input's display used.
|
1905
|
+
If `false`, no displaying methods will be called, element's text will never change.
|
1598
1906
|
Runs under element's scope.
|
1599
|
-
|
1907
|
+
_Parameters:_
|
1908
|
+
|
1909
|
+
* `value` current value to be displayed
|
1910
|
+
* `response` server response (if display called after ajax submit), since 1.4.0
|
1911
|
+
|
1912
|
+
For **inputs with source** (select, checklist) parameters are different:
|
1913
|
+
|
1914
|
+
* `value` current value to be displayed
|
1915
|
+
* `sourceData` array of items for current input (e.g. dropdown items)
|
1916
|
+
* `response` server response (if display called after ajax submit), since 1.4.0
|
1917
|
+
|
1918
|
+
To get currently selected items use `$.fn.editableutils.itemsByValue(value, sourceData)`.
|
1600
1919
|
|
1601
1920
|
@property display
|
1602
1921
|
@type function|boolean
|
@@ -1604,11 +1923,63 @@ Makes editable any HTML element on the page. Applied as jQuery method.
|
|
1604
1923
|
@since 1.2.0
|
1605
1924
|
@example
|
1606
1925
|
display: function(value, sourceData) {
|
1607
|
-
|
1608
|
-
|
1926
|
+
//display checklist as comma-separated values
|
1927
|
+
var html = [],
|
1928
|
+
checked = $.fn.editableutils.itemsByValue(value, sourceData);
|
1929
|
+
|
1930
|
+
if(checked.length) {
|
1931
|
+
$.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
|
1932
|
+
$(this).html(html.join(', '));
|
1933
|
+
} else {
|
1934
|
+
$(this).empty();
|
1935
|
+
}
|
1609
1936
|
}
|
1610
1937
|
**/
|
1611
|
-
display: null
|
1938
|
+
display: null,
|
1939
|
+
/**
|
1940
|
+
Css class applied when editable text is empty.
|
1941
|
+
|
1942
|
+
@property emptyclass
|
1943
|
+
@type string
|
1944
|
+
@since 1.4.1
|
1945
|
+
@default editable-empty
|
1946
|
+
**/
|
1947
|
+
emptyclass: 'editable-empty',
|
1948
|
+
/**
|
1949
|
+
Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`).
|
1950
|
+
You may set it to `null` if you work with editables locally and submit them together.
|
1951
|
+
|
1952
|
+
@property unsavedclass
|
1953
|
+
@type string
|
1954
|
+
@since 1.4.1
|
1955
|
+
@default editable-unsaved
|
1956
|
+
**/
|
1957
|
+
unsavedclass: 'editable-unsaved',
|
1958
|
+
/**
|
1959
|
+
If a css selector is provided, editable will be delegated to the specified targets.
|
1960
|
+
Usefull for dynamically generated DOM elements.
|
1961
|
+
**Please note**, that delegated targets can't use `emptytext` and `autotext` options,
|
1962
|
+
as they are initialized after first click.
|
1963
|
+
|
1964
|
+
@property selector
|
1965
|
+
@type string
|
1966
|
+
@since 1.4.1
|
1967
|
+
@default null
|
1968
|
+
@example
|
1969
|
+
<div id="user">
|
1970
|
+
<a href="#" data-name="username" data-type="text" title="Username">awesome</a>
|
1971
|
+
<a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" title="Group">Operator</a>
|
1972
|
+
</div>
|
1973
|
+
|
1974
|
+
<script>
|
1975
|
+
$('#user').editable({
|
1976
|
+
selector: 'a',
|
1977
|
+
url: '/post',
|
1978
|
+
pk: 1
|
1979
|
+
});
|
1980
|
+
</script>
|
1981
|
+
**/
|
1982
|
+
selector: null
|
1612
1983
|
};
|
1613
1984
|
|
1614
1985
|
}(window.jQuery));
|
@@ -1635,26 +2006,27 @@ To create your own input you can inherit from this class.
|
|
1635
2006
|
**/
|
1636
2007
|
init: function(type, options, defaults) {
|
1637
2008
|
this.type = type;
|
1638
|
-
this.options = $.extend({}, defaults, options);
|
1639
|
-
|
1640
|
-
|
1641
|
-
|
2009
|
+
this.options = $.extend({}, defaults, options);
|
2010
|
+
},
|
2011
|
+
|
2012
|
+
/*
|
2013
|
+
this method called before render to init $tpl that is inserted in DOM
|
2014
|
+
*/
|
2015
|
+
prerender: function() {
|
2016
|
+
this.$tpl = $(this.options.tpl); //whole tpl as jquery object
|
2017
|
+
this.$input = this.$tpl; //control itself, can be changed in render method
|
2018
|
+
this.$clear = null; //clear button
|
2019
|
+
this.error = null; //error message, if input cannot be rendered
|
1642
2020
|
},
|
1643
2021
|
|
1644
2022
|
/**
|
1645
2023
|
Renders input from tpl. Can return jQuery deferred object.
|
2024
|
+
Can be overwritten in child objects
|
1646
2025
|
|
1647
2026
|
@method render()
|
1648
2027
|
**/
|
1649
2028
|
render: function() {
|
1650
|
-
|
1651
|
-
if(this.options.inputclass) {
|
1652
|
-
this.$input.addClass(this.options.inputclass);
|
1653
|
-
}
|
1654
|
-
|
1655
|
-
if (this.options.placeholder) {
|
1656
|
-
this.$input.attr('placeholder', this.options.placeholder);
|
1657
|
-
}
|
2029
|
+
|
1658
2030
|
},
|
1659
2031
|
|
1660
2032
|
/**
|
@@ -1691,7 +2063,7 @@ To create your own input you can inherit from this class.
|
|
1691
2063
|
},
|
1692
2064
|
|
1693
2065
|
/**
|
1694
|
-
Converts string received from server into value.
|
2066
|
+
Converts string received from server into value. Usually from `data-value` attribute.
|
1695
2067
|
|
1696
2068
|
@method str2value(str)
|
1697
2069
|
@param {string} str
|
@@ -1702,7 +2074,7 @@ To create your own input you can inherit from this class.
|
|
1702
2074
|
},
|
1703
2075
|
|
1704
2076
|
/**
|
1705
|
-
Converts value for submitting to server
|
2077
|
+
Converts value for submitting to server. Result can be string or object.
|
1706
2078
|
|
1707
2079
|
@method value2submit(value)
|
1708
2080
|
@param {mixed} value
|
@@ -1763,7 +2135,25 @@ To create your own input you can inherit from this class.
|
|
1763
2135
|
**/
|
1764
2136
|
autosubmit: function() {
|
1765
2137
|
|
2138
|
+
},
|
2139
|
+
|
2140
|
+
// -------- helper functions --------
|
2141
|
+
setClass: function() {
|
2142
|
+
if(this.options.inputclass) {
|
2143
|
+
this.$input.addClass(this.options.inputclass);
|
2144
|
+
}
|
2145
|
+
},
|
2146
|
+
|
2147
|
+
setAttr: function(attr) {
|
2148
|
+
if (this.options[attr]) {
|
2149
|
+
this.$input.attr(attr, this.options[attr]);
|
2150
|
+
}
|
2151
|
+
},
|
2152
|
+
|
2153
|
+
option: function(key, value) {
|
2154
|
+
this.options[key] = value;
|
1766
2155
|
}
|
2156
|
+
|
1767
2157
|
};
|
1768
2158
|
|
1769
2159
|
AbstractInput.defaults = {
|
@@ -1782,15 +2172,7 @@ To create your own input you can inherit from this class.
|
|
1782
2172
|
@type string
|
1783
2173
|
@default input-medium
|
1784
2174
|
**/
|
1785
|
-
inputclass: 'input-medium'
|
1786
|
-
/**
|
1787
|
-
Name attribute of input
|
1788
|
-
|
1789
|
-
@property name
|
1790
|
-
@type string
|
1791
|
-
@default null
|
1792
|
-
**/
|
1793
|
-
name: null
|
2175
|
+
inputclass: 'input-medium'
|
1794
2176
|
};
|
1795
2177
|
|
1796
2178
|
$.extend($.fn.editabletypes, {abstractinput: AbstractInput});
|
@@ -1813,11 +2195,9 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1813
2195
|
|
1814
2196
|
$.extend(List.prototype, {
|
1815
2197
|
render: function () {
|
1816
|
-
List.superclass.render.call(this);
|
1817
2198
|
var deferred = $.Deferred();
|
2199
|
+
|
1818
2200
|
this.error = null;
|
1819
|
-
this.sourceData = null;
|
1820
|
-
this.prependData = null;
|
1821
2201
|
this.onSourceReady(function () {
|
1822
2202
|
this.renderList();
|
1823
2203
|
deferred.resolve();
|
@@ -1833,20 +2213,24 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1833
2213
|
return null; //can't set value by text
|
1834
2214
|
},
|
1835
2215
|
|
1836
|
-
value2html: function (value, element, display) {
|
1837
|
-
var deferred = $.Deferred()
|
1838
|
-
|
1839
|
-
|
1840
|
-
|
1841
|
-
|
1842
|
-
|
1843
|
-
|
1844
|
-
|
1845
|
-
|
1846
|
-
|
1847
|
-
|
1848
|
-
|
1849
|
-
|
2216
|
+
value2html: function (value, element, display, response) {
|
2217
|
+
var deferred = $.Deferred(),
|
2218
|
+
success = function () {
|
2219
|
+
if(typeof display === 'function') {
|
2220
|
+
//custom display method
|
2221
|
+
display.call(element, value, this.sourceData, response);
|
2222
|
+
} else {
|
2223
|
+
this.value2htmlFinal(value, element);
|
2224
|
+
}
|
2225
|
+
deferred.resolve();
|
2226
|
+
};
|
2227
|
+
|
2228
|
+
//for null value just call success without loading source
|
2229
|
+
if(value === null) {
|
2230
|
+
success.call(this);
|
2231
|
+
} else {
|
2232
|
+
this.onSourceReady(success, function () { deferred.resolve(); });
|
2233
|
+
}
|
1850
2234
|
|
1851
2235
|
return deferred.promise();
|
1852
2236
|
},
|
@@ -1872,7 +2256,7 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1872
2256
|
if (typeof this.options.source === 'string') {
|
1873
2257
|
//try to get from cache
|
1874
2258
|
if(this.options.sourceCache) {
|
1875
|
-
var cacheID = this.options.source
|
2259
|
+
var cacheID = this.options.source,
|
1876
2260
|
cache;
|
1877
2261
|
|
1878
2262
|
if (!$(document).data(cacheID)) {
|
@@ -1883,11 +2267,13 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1883
2267
|
//check for cached data
|
1884
2268
|
if (cache.loading === false && cache.sourceData) { //take source from cache
|
1885
2269
|
this.sourceData = cache.sourceData;
|
2270
|
+
this.doPrepend();
|
1886
2271
|
success.call(this);
|
1887
2272
|
return;
|
1888
2273
|
} else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
|
1889
2274
|
cache.callbacks.push($.proxy(function () {
|
1890
2275
|
this.sourceData = cache.sourceData;
|
2276
|
+
this.doPrepend();
|
1891
2277
|
success.call(this);
|
1892
2278
|
}, this));
|
1893
2279
|
|
@@ -1906,7 +2292,6 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1906
2292
|
url: this.options.source,
|
1907
2293
|
type: 'get',
|
1908
2294
|
cache: false,
|
1909
|
-
data: this.options.name ? {name: this.options.name} : {},
|
1910
2295
|
dataType: 'json',
|
1911
2296
|
success: $.proxy(function (data) {
|
1912
2297
|
if(cache) {
|
@@ -1914,17 +2299,19 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1914
2299
|
}
|
1915
2300
|
this.sourceData = this.makeArray(data);
|
1916
2301
|
if($.isArray(this.sourceData)) {
|
1917
|
-
this.doPrepend();
|
1918
|
-
success.call(this);
|
1919
2302
|
if(cache) {
|
1920
2303
|
//store result in cache
|
1921
2304
|
cache.sourceData = this.sourceData;
|
1922
|
-
|
2305
|
+
//run success callbacks for other fields waiting for this source
|
2306
|
+
$.each(cache.callbacks, function () { this.call(); });
|
1923
2307
|
}
|
2308
|
+
this.doPrepend();
|
2309
|
+
success.call(this);
|
1924
2310
|
} else {
|
1925
2311
|
error.call(this);
|
1926
2312
|
if(cache) {
|
1927
|
-
|
2313
|
+
//run error callbacks for other fields waiting for this source
|
2314
|
+
$.each(cache.err_callbacks, function () { this.call(); });
|
1928
2315
|
}
|
1929
2316
|
}
|
1930
2317
|
}, this),
|
@@ -1937,8 +2324,13 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1937
2324
|
}
|
1938
2325
|
}, this)
|
1939
2326
|
});
|
1940
|
-
} else { //options as json/array
|
1941
|
-
|
2327
|
+
} else { //options as json/array/function
|
2328
|
+
if ($.isFunction(this.options.source)) {
|
2329
|
+
this.sourceData = this.makeArray(this.options.source());
|
2330
|
+
} else {
|
2331
|
+
this.sourceData = this.makeArray(this.options.source);
|
2332
|
+
}
|
2333
|
+
|
1942
2334
|
if($.isArray(this.sourceData)) {
|
1943
2335
|
this.doPrepend();
|
1944
2336
|
success.call(this);
|
@@ -1959,7 +2351,11 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1959
2351
|
if (typeof this.options.prepend === 'string') {
|
1960
2352
|
this.options.prepend = {'': this.options.prepend};
|
1961
2353
|
}
|
1962
|
-
|
2354
|
+
if (typeof this.options.prepend === 'function') {
|
2355
|
+
this.prependData = this.makeArray(this.options.prepend());
|
2356
|
+
} else {
|
2357
|
+
this.prependData = this.makeArray(this.options.prepend);
|
2358
|
+
}
|
1963
2359
|
}
|
1964
2360
|
|
1965
2361
|
if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
|
@@ -1985,35 +2381,45 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
1985
2381
|
* convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
|
1986
2382
|
*/
|
1987
2383
|
makeArray: function(data) {
|
1988
|
-
var count, obj, result = [],
|
2384
|
+
var count, obj, result = [], item, iterateItem;
|
1989
2385
|
if(!data || typeof data === 'string') {
|
1990
2386
|
return null;
|
1991
2387
|
}
|
1992
2388
|
|
1993
2389
|
if($.isArray(data)) { //array
|
1994
|
-
|
2390
|
+
/*
|
2391
|
+
function to iterate inside item of array if item is object.
|
2392
|
+
Caclulates count of keys in item and store in obj.
|
2393
|
+
*/
|
2394
|
+
iterateItem = function (k, v) {
|
1995
2395
|
obj = {value: k, text: v};
|
1996
2396
|
if(count++ >= 2) {
|
1997
|
-
return false;// exit each if
|
2397
|
+
return false;// exit from `each` if item has more than one key.
|
1998
2398
|
}
|
1999
2399
|
};
|
2000
2400
|
|
2001
2401
|
for(var i = 0; i < data.length; i++) {
|
2002
|
-
|
2003
|
-
|
2004
|
-
|
2005
|
-
|
2402
|
+
item = data[i];
|
2403
|
+
if(typeof item === 'object') {
|
2404
|
+
count = 0; //count of keys inside item
|
2405
|
+
$.each(item, iterateItem);
|
2406
|
+
//case: [{val1: 'text1'}, {val2: 'text2} ...]
|
2407
|
+
if(count === 1) {
|
2006
2408
|
result.push(obj);
|
2007
|
-
|
2008
|
-
|
2009
|
-
|
2010
|
-
|
2409
|
+
//case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
|
2410
|
+
} else if(count > 1) {
|
2411
|
+
//removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
|
2412
|
+
if(item.children) {
|
2413
|
+
item.children = this.makeArray(item.children);
|
2414
|
+
}
|
2415
|
+
result.push(item);
|
2011
2416
|
}
|
2012
2417
|
} else {
|
2013
|
-
|
2418
|
+
//case: ['text1', 'text2' ...]
|
2419
|
+
result.push({value: item, text: item});
|
2014
2420
|
}
|
2015
2421
|
}
|
2016
|
-
} else { //
|
2422
|
+
} else { //case: {val1: 'text1', val2: 'text2, ...}
|
2017
2423
|
$.each(data, function (k, v) {
|
2018
2424
|
result.push({value: k, text: v});
|
2019
2425
|
});
|
@@ -2021,41 +2427,45 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
2021
2427
|
return result;
|
2022
2428
|
},
|
2023
2429
|
|
2024
|
-
|
2025
|
-
|
2026
|
-
if(
|
2027
|
-
|
2028
|
-
/*jshint eqeqeq: false*/
|
2029
|
-
if(this.sourceData[i].value == val) {
|
2030
|
-
/*jshint eqeqeq: true*/
|
2031
|
-
return this.sourceData[i];
|
2032
|
-
}
|
2033
|
-
}
|
2430
|
+
option: function(key, value) {
|
2431
|
+
this.options[key] = value;
|
2432
|
+
if(key === 'source') {
|
2433
|
+
this.sourceData = null;
|
2034
2434
|
}
|
2435
|
+
if(key === 'prepend') {
|
2436
|
+
this.prependData = null;
|
2437
|
+
}
|
2035
2438
|
}
|
2036
2439
|
|
2037
2440
|
});
|
2038
2441
|
|
2039
2442
|
List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
2040
2443
|
/**
|
2041
|
-
Source data for list.
|
2042
|
-
|
2043
|
-
For compability
|
2044
|
-
|
2444
|
+
Source data for list.
|
2445
|
+
If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`
|
2446
|
+
For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
|
2447
|
+
|
2448
|
+
If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
|
2449
|
+
|
2450
|
+
If **function**, it should return data in format above (since 1.4.0).
|
2045
2451
|
|
2452
|
+
Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).
|
2453
|
+
`[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]`
|
2454
|
+
|
2455
|
+
|
2046
2456
|
@property source
|
2047
|
-
@type string|array|object
|
2457
|
+
@type string | array | object | function
|
2048
2458
|
@default null
|
2049
2459
|
**/
|
2050
|
-
source:null,
|
2460
|
+
source: null,
|
2051
2461
|
/**
|
2052
2462
|
Data automatically prepended to the beginning of dropdown list.
|
2053
2463
|
|
2054
2464
|
@property prepend
|
2055
|
-
@type string|array|object
|
2465
|
+
@type string | array | object | function
|
2056
2466
|
@default false
|
2057
2467
|
**/
|
2058
|
-
prepend:false,
|
2468
|
+
prepend: false,
|
2059
2469
|
/**
|
2060
2470
|
Error message when list cannot be loaded (e.g. ajax error)
|
2061
2471
|
|
@@ -2065,8 +2475,8 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
2065
2475
|
**/
|
2066
2476
|
sourceError: 'Error when loading list',
|
2067
2477
|
/**
|
2068
|
-
if <code>true</code> and source is **string url** - results will be cached for fields with the same source
|
2069
|
-
Usefull for editable
|
2478
|
+
if <code>true</code> and source is **string url** - results will be cached for fields with the same source.
|
2479
|
+
Usefull for editable column in grid to prevent extra requests.
|
2070
2480
|
|
2071
2481
|
@property sourceCache
|
2072
2482
|
@type boolean
|
@@ -2078,7 +2488,8 @@ List - abstract class for inputs that have source option loaded from js array or
|
|
2078
2488
|
|
2079
2489
|
$.fn.editabletypes.list = List;
|
2080
2490
|
|
2081
|
-
}(window.jQuery));
|
2491
|
+
}(window.jQuery));
|
2492
|
+
|
2082
2493
|
/**
|
2083
2494
|
Text input
|
2084
2495
|
|
@@ -2104,12 +2515,67 @@ $(function(){
|
|
2104
2515
|
$.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput);
|
2105
2516
|
|
2106
2517
|
$.extend(Text.prototype, {
|
2518
|
+
render: function() {
|
2519
|
+
this.renderClear();
|
2520
|
+
this.setClass();
|
2521
|
+
this.setAttr('placeholder');
|
2522
|
+
},
|
2523
|
+
|
2107
2524
|
activate: function() {
|
2108
2525
|
if(this.$input.is(':visible')) {
|
2109
2526
|
this.$input.focus();
|
2110
2527
|
$.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
|
2528
|
+
if(this.toggleClear) {
|
2529
|
+
this.toggleClear();
|
2530
|
+
}
|
2111
2531
|
}
|
2112
|
-
}
|
2532
|
+
},
|
2533
|
+
|
2534
|
+
//render clear button
|
2535
|
+
renderClear: function() {
|
2536
|
+
if (this.options.clear) {
|
2537
|
+
this.$clear = $('<span class="editable-clear-x"></span>');
|
2538
|
+
this.$input.after(this.$clear)
|
2539
|
+
.css('padding-right', 20)
|
2540
|
+
.keyup($.proxy(this.toggleClear, this))
|
2541
|
+
.parent().css('position', 'relative');
|
2542
|
+
|
2543
|
+
this.$clear.click($.proxy(this.clear, this));
|
2544
|
+
}
|
2545
|
+
},
|
2546
|
+
|
2547
|
+
postrender: function() {
|
2548
|
+
if(this.$clear) {
|
2549
|
+
//can position clear button only here, when form is shown and height can be calculated
|
2550
|
+
var h = this.$input.outerHeight() || 20,
|
2551
|
+
delta = (h - this.$clear.height()) / 2;
|
2552
|
+
|
2553
|
+
//workaround for plain-popup
|
2554
|
+
if(delta < 3) {
|
2555
|
+
delta = 3;
|
2556
|
+
}
|
2557
|
+
|
2558
|
+
this.$clear.css({top: delta, right: delta});
|
2559
|
+
}
|
2560
|
+
},
|
2561
|
+
|
2562
|
+
//show / hide clear button
|
2563
|
+
toggleClear: function() {
|
2564
|
+
if(!this.$clear) {
|
2565
|
+
return;
|
2566
|
+
}
|
2567
|
+
|
2568
|
+
if(this.$input.val().length) {
|
2569
|
+
this.$clear.show();
|
2570
|
+
} else {
|
2571
|
+
this.$clear.hide();
|
2572
|
+
}
|
2573
|
+
},
|
2574
|
+
|
2575
|
+
clear: function() {
|
2576
|
+
this.$clear.hide();
|
2577
|
+
this.$input.val('').focus();
|
2578
|
+
}
|
2113
2579
|
});
|
2114
2580
|
|
2115
2581
|
Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
@@ -2125,7 +2591,16 @@ $(function(){
|
|
2125
2591
|
@type string
|
2126
2592
|
@default null
|
2127
2593
|
**/
|
2128
|
-
placeholder: null
|
2594
|
+
placeholder: null,
|
2595
|
+
|
2596
|
+
/**
|
2597
|
+
Whether to show `clear` button
|
2598
|
+
|
2599
|
+
@property clear
|
2600
|
+
@type boolean
|
2601
|
+
@default true
|
2602
|
+
**/
|
2603
|
+
clear: true
|
2129
2604
|
});
|
2130
2605
|
|
2131
2606
|
$.fn.editabletypes.text = Text;
|
@@ -2144,7 +2619,8 @@ Textarea input
|
|
2144
2619
|
$(function(){
|
2145
2620
|
$('#comments').editable({
|
2146
2621
|
url: '/post',
|
2147
|
-
title: 'Enter comments'
|
2622
|
+
title: 'Enter comments',
|
2623
|
+
rows: 10
|
2148
2624
|
});
|
2149
2625
|
});
|
2150
2626
|
</script>
|
@@ -2159,8 +2635,10 @@ $(function(){
|
|
2159
2635
|
|
2160
2636
|
$.extend(Textarea.prototype, {
|
2161
2637
|
render: function () {
|
2162
|
-
|
2163
|
-
|
2638
|
+
this.setClass();
|
2639
|
+
this.setAttr('placeholder');
|
2640
|
+
this.setAttr('rows');
|
2641
|
+
|
2164
2642
|
//ctrl + enter
|
2165
2643
|
this.$input.keydown(function (e) {
|
2166
2644
|
if (e.ctrlKey && e.which === 13) {
|
@@ -2185,43 +2663,56 @@ $(function(){
|
|
2185
2663
|
if(!html) {
|
2186
2664
|
return '';
|
2187
2665
|
}
|
2666
|
+
|
2667
|
+
var regex = new RegExp(String.fromCharCode(10), 'g');
|
2188
2668
|
var lines = html.split(/<br\s*\/?>/i);
|
2189
2669
|
for (var i = 0; i < lines.length; i++) {
|
2190
|
-
|
2670
|
+
var text = $('<div>').html(lines[i]).text();
|
2671
|
+
|
2672
|
+
// Remove newline characters (\n) to avoid them being converted by value2html() method
|
2673
|
+
// thus adding extra <br> tags
|
2674
|
+
text = text.replace(regex, '');
|
2675
|
+
|
2676
|
+
lines[i] = text;
|
2191
2677
|
}
|
2192
|
-
return lines.join("\n");
|
2193
|
-
},
|
2678
|
+
return lines.join("\n");
|
2679
|
+
},
|
2194
2680
|
|
2195
2681
|
activate: function() {
|
2196
|
-
|
2197
|
-
|
2198
|
-
this.$input.focus();
|
2199
|
-
}
|
2200
|
-
}
|
2682
|
+
$.fn.editabletypes.text.prototype.activate.call(this);
|
2683
|
+
}
|
2201
2684
|
});
|
2202
2685
|
|
2203
2686
|
Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
2204
2687
|
/**
|
2205
|
-
@property tpl
|
2688
|
+
@property tpl
|
2206
2689
|
@default <textarea></textarea>
|
2207
|
-
**/
|
2690
|
+
**/
|
2208
2691
|
tpl:'<textarea></textarea>',
|
2209
2692
|
/**
|
2210
|
-
@property inputclass
|
2693
|
+
@property inputclass
|
2211
2694
|
@default input-large
|
2212
|
-
**/
|
2695
|
+
**/
|
2213
2696
|
inputclass: 'input-large',
|
2214
2697
|
/**
|
2215
2698
|
Placeholder attribute of input. Shown when input is empty.
|
2216
2699
|
|
2217
|
-
@property placeholder
|
2700
|
+
@property placeholder
|
2218
2701
|
@type string
|
2219
2702
|
@default null
|
2220
|
-
**/
|
2221
|
-
placeholder: null
|
2703
|
+
**/
|
2704
|
+
placeholder: null,
|
2705
|
+
/**
|
2706
|
+
Number of rows in textarea
|
2707
|
+
|
2708
|
+
@property rows
|
2709
|
+
@type integer
|
2710
|
+
@default 7
|
2711
|
+
**/
|
2712
|
+
rows: 7
|
2222
2713
|
});
|
2223
2714
|
|
2224
|
-
$.fn.editabletypes.textarea = Textarea;
|
2715
|
+
$.fn.editabletypes.textarea = Textarea;
|
2225
2716
|
|
2226
2717
|
}(window.jQuery));
|
2227
2718
|
|
@@ -2257,13 +2748,24 @@ $(function(){
|
|
2257
2748
|
|
2258
2749
|
$.extend(Select.prototype, {
|
2259
2750
|
renderList: function() {
|
2260
|
-
|
2261
|
-
return;
|
2262
|
-
}
|
2751
|
+
this.$input.empty();
|
2263
2752
|
|
2264
|
-
|
2265
|
-
|
2266
|
-
|
2753
|
+
var fillItems = function($el, data) {
|
2754
|
+
if($.isArray(data)) {
|
2755
|
+
for(var i=0; i<data.length; i++) {
|
2756
|
+
if(data[i].children) {
|
2757
|
+
$el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children));
|
2758
|
+
} else {
|
2759
|
+
$el.append($('<option>', {value: data[i].value}).text(data[i].text));
|
2760
|
+
}
|
2761
|
+
}
|
2762
|
+
}
|
2763
|
+
return $el;
|
2764
|
+
};
|
2765
|
+
|
2766
|
+
fillItems(this.$input, this.sourceData);
|
2767
|
+
|
2768
|
+
this.setClass();
|
2267
2769
|
|
2268
2770
|
//enter submit
|
2269
2771
|
this.$input.on('keydown.editable', function (e) {
|
@@ -2274,11 +2776,14 @@ $(function(){
|
|
2274
2776
|
},
|
2275
2777
|
|
2276
2778
|
value2htmlFinal: function(value, element) {
|
2277
|
-
var text = '',
|
2278
|
-
|
2279
|
-
|
2779
|
+
var text = '',
|
2780
|
+
items = $.fn.editableutils.itemsByValue(value, this.sourceData);
|
2781
|
+
|
2782
|
+
if(items.length) {
|
2783
|
+
text = items[0].text;
|
2280
2784
|
}
|
2281
|
-
|
2785
|
+
|
2786
|
+
$(element).text(text);
|
2282
2787
|
},
|
2283
2788
|
|
2284
2789
|
autosubmit: function() {
|
@@ -2333,6 +2838,9 @@ $(function(){
|
|
2333
2838
|
$.extend(Checklist.prototype, {
|
2334
2839
|
renderList: function() {
|
2335
2840
|
var $label, $div;
|
2841
|
+
|
2842
|
+
this.$tpl.empty();
|
2843
|
+
|
2336
2844
|
if(!$.isArray(this.sourceData)) {
|
2337
2845
|
return;
|
2338
2846
|
}
|
@@ -2340,13 +2848,15 @@ $(function(){
|
|
2340
2848
|
for(var i=0; i<this.sourceData.length; i++) {
|
2341
2849
|
$label = $('<label>').append($('<input>', {
|
2342
2850
|
type: 'checkbox',
|
2343
|
-
value: this.sourceData[i].value
|
2344
|
-
name: this.options.name
|
2851
|
+
value: this.sourceData[i].value
|
2345
2852
|
}))
|
2346
2853
|
.append($('<span>').text(' '+this.sourceData[i].text));
|
2347
2854
|
|
2348
|
-
$('<div>').append($label).appendTo(this.$
|
2855
|
+
$('<div>').append($label).appendTo(this.$tpl);
|
2349
2856
|
}
|
2857
|
+
|
2858
|
+
this.$input = this.$tpl.find('input[type="checkbox"]');
|
2859
|
+
this.setClass();
|
2350
2860
|
},
|
2351
2861
|
|
2352
2862
|
value2str: function(value) {
|
@@ -2367,17 +2877,16 @@ $(function(){
|
|
2367
2877
|
|
2368
2878
|
//set checked on required checkboxes
|
2369
2879
|
value2input: function(value) {
|
2370
|
-
|
2371
|
-
$checks.removeAttr('checked');
|
2880
|
+
this.$input.prop('checked', false);
|
2372
2881
|
if($.isArray(value) && value.length) {
|
2373
|
-
|
2882
|
+
this.$input.each(function(i, el) {
|
2374
2883
|
var $el = $(el);
|
2375
2884
|
// cannot use $.inArray as it performs strict comparison
|
2376
2885
|
$.each(value, function(j, val){
|
2377
2886
|
/*jslint eqeq: true*/
|
2378
2887
|
if($el.val() == val) {
|
2379
2888
|
/*jslint eqeq: false*/
|
2380
|
-
$el.
|
2889
|
+
$el.prop('checked', true);
|
2381
2890
|
}
|
2382
2891
|
});
|
2383
2892
|
});
|
@@ -2386,7 +2895,7 @@ $(function(){
|
|
2386
2895
|
|
2387
2896
|
input2value: function() {
|
2388
2897
|
var checked = [];
|
2389
|
-
this.$input.
|
2898
|
+
this.$input.filter(':checked').each(function(i, el) {
|
2390
2899
|
checked.push($(el).val());
|
2391
2900
|
});
|
2392
2901
|
return checked;
|
@@ -2395,11 +2904,8 @@ $(function(){
|
|
2395
2904
|
//collect text of checked boxes
|
2396
2905
|
value2htmlFinal: function(value, element) {
|
2397
2906
|
var html = [],
|
2398
|
-
|
2399
|
-
|
2400
|
-
return $.grep(value, function(v){ return v == o.value; }).length;
|
2401
|
-
});
|
2402
|
-
/*jslint eqeq: false*/
|
2907
|
+
checked = $.fn.editableutils.itemsByValue(value, this.sourceData);
|
2908
|
+
|
2403
2909
|
if(checked.length) {
|
2404
2910
|
$.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
|
2405
2911
|
$(element).html(html.join('<br>'));
|
@@ -2409,11 +2915,11 @@ $(function(){
|
|
2409
2915
|
},
|
2410
2916
|
|
2411
2917
|
activate: function() {
|
2412
|
-
this.$input.
|
2918
|
+
this.$input.first().focus();
|
2413
2919
|
},
|
2414
2920
|
|
2415
2921
|
autosubmit: function() {
|
2416
|
-
this.$input.
|
2922
|
+
this.$input.on('keydown', function(e){
|
2417
2923
|
if (e.which === 13) {
|
2418
2924
|
$(this).closest('form').submit();
|
2419
2925
|
}
|
@@ -2426,21 +2932,21 @@ $(function(){
|
|
2426
2932
|
@property tpl
|
2427
2933
|
@default <div></div>
|
2428
2934
|
**/
|
2429
|
-
tpl:'<div></div>',
|
2935
|
+
tpl:'<div class="editable-checklist"></div>',
|
2430
2936
|
|
2431
2937
|
/**
|
2432
2938
|
@property inputclass
|
2433
2939
|
@type string
|
2434
|
-
@default
|
2940
|
+
@default null
|
2435
2941
|
**/
|
2436
|
-
inputclass:
|
2942
|
+
inputclass: null,
|
2437
2943
|
|
2438
2944
|
/**
|
2439
|
-
Separator of values when reading from
|
2945
|
+
Separator of values when reading from `data-value` attribute
|
2440
2946
|
|
2441
2947
|
@property separator
|
2442
2948
|
@type string
|
2443
|
-
@default ',
|
2949
|
+
@default ','
|
2444
2950
|
**/
|
2445
2951
|
separator: ','
|
2446
2952
|
});
|
@@ -2571,18 +3077,9 @@ Number
|
|
2571
3077
|
$.extend(NumberInput.prototype, {
|
2572
3078
|
render: function () {
|
2573
3079
|
NumberInput.superclass.render.call(this);
|
2574
|
-
|
2575
|
-
|
2576
|
-
|
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
|
-
}
|
3080
|
+
this.setAttr('min');
|
3081
|
+
this.setAttr('max');
|
3082
|
+
this.setAttr('step');
|
2586
3083
|
}
|
2587
3084
|
});
|
2588
3085
|
NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
|
@@ -2606,29 +3103,19 @@ Range (inherit from number)
|
|
2606
3103
|
$.fn.editableutils.inherit(Range, $.fn.editabletypes.number);
|
2607
3104
|
$.extend(Range.prototype, {
|
2608
3105
|
render: function () {
|
2609
|
-
this.$input =
|
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
|
-
}
|
3106
|
+
this.$input = this.$tpl.filter('input');
|
2621
3107
|
|
2622
|
-
|
2623
|
-
|
2624
|
-
|
3108
|
+
this.setClass();
|
3109
|
+
this.setAttr('min');
|
3110
|
+
this.setAttr('max');
|
3111
|
+
this.setAttr('step');
|
2625
3112
|
|
2626
|
-
|
3113
|
+
this.$input.on('input', function(){
|
2627
3114
|
$(this).siblings('output').text($(this).val());
|
2628
3115
|
});
|
2629
3116
|
},
|
2630
3117
|
activate: function() {
|
2631
|
-
this.$input.
|
3118
|
+
this.$input.focus();
|
2632
3119
|
}
|
2633
3120
|
});
|
2634
3121
|
Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, {
|
@@ -2637,6 +3124,783 @@ Range (inherit from number)
|
|
2637
3124
|
});
|
2638
3125
|
$.fn.editabletypes.range = Range;
|
2639
3126
|
}(window.jQuery));
|
3127
|
+
/**
|
3128
|
+
Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2.
|
3129
|
+
Please see [original docs](http://ivaynberg.github.com/select2) for detailed description and options.
|
3130
|
+
You should manually include select2 distributive:
|
3131
|
+
|
3132
|
+
<link href="select2/select2.css" rel="stylesheet" type="text/css"></link>
|
3133
|
+
<script src="select2/select2.js"></script>
|
3134
|
+
|
3135
|
+
@class select2
|
3136
|
+
@extends abstractinput
|
3137
|
+
@since 1.4.1
|
3138
|
+
@final
|
3139
|
+
@example
|
3140
|
+
<a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-original-title="Select country"></a>
|
3141
|
+
<script>
|
3142
|
+
$(function(){
|
3143
|
+
$('#country').editable({
|
3144
|
+
source: [
|
3145
|
+
{id: 'gb', text: 'Great Britain'},
|
3146
|
+
{id: 'us', text: 'United States'},
|
3147
|
+
{id: 'ru', text: 'Russia'}
|
3148
|
+
],
|
3149
|
+
select2: {
|
3150
|
+
multiple: true
|
3151
|
+
}
|
3152
|
+
});
|
3153
|
+
});
|
3154
|
+
</script>
|
3155
|
+
**/
|
3156
|
+
(function ($) {
|
3157
|
+
|
3158
|
+
var Constructor = function (options) {
|
3159
|
+
this.init('select2', options, Constructor.defaults);
|
3160
|
+
|
3161
|
+
options.select2 = options.select2 || {};
|
3162
|
+
|
3163
|
+
var that = this,
|
3164
|
+
mixin = {
|
3165
|
+
placeholder: options.placeholder
|
3166
|
+
};
|
3167
|
+
|
3168
|
+
//detect whether it is multi-valued
|
3169
|
+
this.isMultiple = options.select2.tags || options.select2.multiple;
|
3170
|
+
|
3171
|
+
//if not `tags` mode, we need define init set data from source
|
3172
|
+
if(!options.select2.tags) {
|
3173
|
+
if(options.source) {
|
3174
|
+
mixin.data = options.source;
|
3175
|
+
}
|
3176
|
+
|
3177
|
+
//this function can be defaulted in seletc2. See https://github.com/ivaynberg/select2/issues/710
|
3178
|
+
mixin.initSelection = function (element, callback) {
|
3179
|
+
var val = that.str2value(element.val()),
|
3180
|
+
data = $.fn.editableutils.itemsByValue(val, mixin.data, 'id');
|
3181
|
+
|
3182
|
+
//for single-valued mode should not use array. Take first element instead.
|
3183
|
+
if($.isArray(data) && data.length && !that.isMultiple) {
|
3184
|
+
data = data[0];
|
3185
|
+
}
|
3186
|
+
|
3187
|
+
callback(data);
|
3188
|
+
};
|
3189
|
+
}
|
3190
|
+
|
3191
|
+
//overriding objects in config (as by default jQuery extend() is not recursive)
|
3192
|
+
this.options.select2 = $.extend({}, Constructor.defaults.select2, mixin, options.select2);
|
3193
|
+
};
|
3194
|
+
|
3195
|
+
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
|
3196
|
+
|
3197
|
+
$.extend(Constructor.prototype, {
|
3198
|
+
render: function() {
|
3199
|
+
this.setClass();
|
3200
|
+
//apply select2
|
3201
|
+
this.$input.select2(this.options.select2);
|
3202
|
+
|
3203
|
+
//trigger resize of editableform to re-position container in multi-valued mode
|
3204
|
+
if(this.isMultiple) {
|
3205
|
+
this.$input.on('change', function() {
|
3206
|
+
$(this).closest('form').parent().triggerHandler('resize');
|
3207
|
+
});
|
3208
|
+
}
|
3209
|
+
|
3210
|
+
},
|
3211
|
+
|
3212
|
+
value2html: function(value, element) {
|
3213
|
+
var text = '', data;
|
3214
|
+
if(this.$input) { //when submitting form
|
3215
|
+
data = this.$input.select2('data');
|
3216
|
+
} else { //on init (autotext)
|
3217
|
+
//here select2 instance not created yet and data may be even not loaded.
|
3218
|
+
//we can check data/tags property of select config and if exist lookup text
|
3219
|
+
if(this.options.select2.tags) {
|
3220
|
+
data = value;
|
3221
|
+
} else if(this.options.select2.data) {
|
3222
|
+
data = $.fn.editableutils.itemsByValue(value, this.options.select2.data, 'id');
|
3223
|
+
}
|
3224
|
+
}
|
3225
|
+
|
3226
|
+
if($.isArray(data)) {
|
3227
|
+
//collect selected data and show with separator
|
3228
|
+
text = [];
|
3229
|
+
$.each(data, function(k, v){
|
3230
|
+
text.push(v && typeof v === 'object' ? v.text : v);
|
3231
|
+
});
|
3232
|
+
} else if(data) {
|
3233
|
+
text = data.text;
|
3234
|
+
}
|
3235
|
+
|
3236
|
+
text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
|
3237
|
+
|
3238
|
+
$(element).text(text);
|
3239
|
+
},
|
3240
|
+
|
3241
|
+
html2value: function(html) {
|
3242
|
+
return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
|
3243
|
+
},
|
3244
|
+
|
3245
|
+
value2input: function(value) {
|
3246
|
+
this.$input.val(value).trigger('change');
|
3247
|
+
},
|
3248
|
+
|
3249
|
+
input2value: function() {
|
3250
|
+
return this.$input.select2('val');
|
3251
|
+
},
|
3252
|
+
|
3253
|
+
str2value: function(str, separator) {
|
3254
|
+
if(typeof str !== 'string' || !this.isMultiple) {
|
3255
|
+
return str;
|
3256
|
+
}
|
3257
|
+
|
3258
|
+
separator = separator || this.options.select2.separator || $.fn.select2.defaults.separator;
|
3259
|
+
|
3260
|
+
var val, i, l;
|
3261
|
+
|
3262
|
+
if (str === null || str.length < 1) {
|
3263
|
+
return null;
|
3264
|
+
}
|
3265
|
+
val = str.split(separator);
|
3266
|
+
for (i = 0, l = val.length; i < l; i = i + 1) {
|
3267
|
+
val[i] = $.trim(val[i]);
|
3268
|
+
}
|
3269
|
+
|
3270
|
+
return val;
|
3271
|
+
}
|
3272
|
+
|
3273
|
+
});
|
3274
|
+
|
3275
|
+
Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
3276
|
+
/**
|
3277
|
+
@property tpl
|
3278
|
+
@default <input type="hidden">
|
3279
|
+
**/
|
3280
|
+
tpl:'<input type="hidden">',
|
3281
|
+
/**
|
3282
|
+
Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
|
3283
|
+
|
3284
|
+
@property select2
|
3285
|
+
@type object
|
3286
|
+
@default null
|
3287
|
+
**/
|
3288
|
+
select2: null,
|
3289
|
+
/**
|
3290
|
+
Placeholder attribute of select
|
3291
|
+
|
3292
|
+
@property placeholder
|
3293
|
+
@type string
|
3294
|
+
@default null
|
3295
|
+
**/
|
3296
|
+
placeholder: null,
|
3297
|
+
/**
|
3298
|
+
Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
|
3299
|
+
Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
|
3300
|
+
E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
|
3301
|
+
|
3302
|
+
@property source
|
3303
|
+
@type array
|
3304
|
+
@default null
|
3305
|
+
**/
|
3306
|
+
source: null,
|
3307
|
+
/**
|
3308
|
+
Separator used to display tags.
|
3309
|
+
|
3310
|
+
@property viewseparator
|
3311
|
+
@type string
|
3312
|
+
@default ', '
|
3313
|
+
**/
|
3314
|
+
viewseparator: ', '
|
3315
|
+
});
|
3316
|
+
|
3317
|
+
$.fn.editabletypes.select2 = Constructor;
|
3318
|
+
|
3319
|
+
}(window.jQuery));
|
3320
|
+
/**
|
3321
|
+
* Combodate - 1.0.1
|
3322
|
+
* Dropdown date and time picker.
|
3323
|
+
* Converts text input into dropdowns to pick day, month, year, hour, minute and second.
|
3324
|
+
* Uses momentjs as datetime library http://momentjs.com.
|
3325
|
+
* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang
|
3326
|
+
*
|
3327
|
+
* Author: Vitaliy Potapov
|
3328
|
+
* Project page: http://github.com/vitalets/combodate
|
3329
|
+
* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
|
3330
|
+
**/
|
3331
|
+
(function ($) {
|
3332
|
+
|
3333
|
+
var Combodate = function (element, options) {
|
3334
|
+
this.$element = $(element);
|
3335
|
+
if(!this.$element.is('input')) {
|
3336
|
+
$.error('Combodate should be applied to INPUT element');
|
3337
|
+
return;
|
3338
|
+
}
|
3339
|
+
this.options = $.extend({}, $.fn.combodate.defaults, options, this.$element.data());
|
3340
|
+
this.init();
|
3341
|
+
};
|
3342
|
+
|
3343
|
+
Combodate.prototype = {
|
3344
|
+
constructor: Combodate,
|
3345
|
+
init: function () {
|
3346
|
+
this.map = {
|
3347
|
+
//key regexp moment.method
|
3348
|
+
day: ['D', 'date'],
|
3349
|
+
month: ['M', 'month'],
|
3350
|
+
year: ['Y', 'year'],
|
3351
|
+
hour: ['[Hh]', 'hours'],
|
3352
|
+
minute: ['m', 'minutes'],
|
3353
|
+
second: ['s', 'seconds'],
|
3354
|
+
ampm: ['[Aa]', '']
|
3355
|
+
};
|
3356
|
+
|
3357
|
+
this.$widget = $('<span class="combodate"></span>').html(this.getTemplate());
|
3358
|
+
|
3359
|
+
this.initCombos();
|
3360
|
+
|
3361
|
+
//update original input on change
|
3362
|
+
this.$widget.on('change', 'select', $.proxy(function(){
|
3363
|
+
this.$element.val(this.getValue());
|
3364
|
+
}, this));
|
3365
|
+
|
3366
|
+
this.$widget.find('select').css('width', 'auto');
|
3367
|
+
|
3368
|
+
//hide original input and insert widget
|
3369
|
+
this.$element.hide().after(this.$widget);
|
3370
|
+
|
3371
|
+
//set initial value
|
3372
|
+
this.setValue(this.$element.val() || this.options.value);
|
3373
|
+
},
|
3374
|
+
|
3375
|
+
/*
|
3376
|
+
Replace tokens in template with <select> elements
|
3377
|
+
*/
|
3378
|
+
getTemplate: function() {
|
3379
|
+
var tpl = this.options.template;
|
3380
|
+
|
3381
|
+
//first pass
|
3382
|
+
$.each(this.map, function(k, v) {
|
3383
|
+
v = v[0];
|
3384
|
+
var r = new RegExp(v+'+'),
|
3385
|
+
token = v.length > 1 ? v.substring(1, 2) : v;
|
3386
|
+
|
3387
|
+
tpl = tpl.replace(r, '{'+token+'}');
|
3388
|
+
});
|
3389
|
+
|
3390
|
+
//replace spaces with
|
3391
|
+
tpl = tpl.replace(/ /g, ' ');
|
3392
|
+
|
3393
|
+
//second pass
|
3394
|
+
$.each(this.map, function(k, v) {
|
3395
|
+
v = v[0];
|
3396
|
+
var token = v.length > 1 ? v.substring(1, 2) : v;
|
3397
|
+
|
3398
|
+
tpl = tpl.replace('{'+token+'}', '<select class="'+k+'"></select>');
|
3399
|
+
});
|
3400
|
+
|
3401
|
+
return tpl;
|
3402
|
+
},
|
3403
|
+
|
3404
|
+
/*
|
3405
|
+
Initialize combos that presents in template
|
3406
|
+
*/
|
3407
|
+
initCombos: function() {
|
3408
|
+
var that = this;
|
3409
|
+
$.each(this.map, function(k, v) {
|
3410
|
+
var $c = that.$widget.find('.'+k), f, items;
|
3411
|
+
if($c.length) {
|
3412
|
+
that['$'+k] = $c; //set properties like this.$day, this.$month etc.
|
3413
|
+
f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); //define method name to fill items, e.g `fillDays`
|
3414
|
+
items = that[f]();
|
3415
|
+
that['$'+k].html(that.renderItems(items));
|
3416
|
+
}
|
3417
|
+
});
|
3418
|
+
},
|
3419
|
+
|
3420
|
+
/*
|
3421
|
+
Initialize items of combos. Handles `firstItem` option
|
3422
|
+
*/
|
3423
|
+
initItems: function(key) {
|
3424
|
+
var values = [];
|
3425
|
+
if(this.options.firstItem === 'name') {
|
3426
|
+
var header = typeof moment.relativeTime[key] === 'function' ? moment.relativeTime[key](1, true, key, false) : moment.relativeTime[key];
|
3427
|
+
//take last entry (see momentjs lang files structure)
|
3428
|
+
header = header.split(' ').reverse()[0];
|
3429
|
+
values.push(['', header]);
|
3430
|
+
} else if(this.options.firstItem === 'empty') {
|
3431
|
+
values.push(['', '']);
|
3432
|
+
}
|
3433
|
+
return values;
|
3434
|
+
},
|
3435
|
+
|
3436
|
+
/*
|
3437
|
+
render items to string of <option> tags
|
3438
|
+
*/
|
3439
|
+
renderItems: function(items) {
|
3440
|
+
var str = [];
|
3441
|
+
for(var i=0; i<items.length; i++) {
|
3442
|
+
str.push('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
|
3443
|
+
}
|
3444
|
+
return str.join("\n");
|
3445
|
+
},
|
3446
|
+
|
3447
|
+
/*
|
3448
|
+
fill day
|
3449
|
+
*/
|
3450
|
+
fillDay: function() {
|
3451
|
+
var items = this.initItems('d'), name, i,
|
3452
|
+
twoDigit = this.options.template.indexOf('DD') !== -1;
|
3453
|
+
|
3454
|
+
for(i=1; i<=31; i++) {
|
3455
|
+
name = twoDigit ? this.leadZero(i) : i;
|
3456
|
+
items.push([i, name]);
|
3457
|
+
}
|
3458
|
+
return items;
|
3459
|
+
},
|
3460
|
+
|
3461
|
+
/*
|
3462
|
+
fill month
|
3463
|
+
*/
|
3464
|
+
fillMonth: function() {
|
3465
|
+
var items = this.initItems('M'), name, i,
|
3466
|
+
longNames = this.options.template.indexOf('MMMM') !== -1,
|
3467
|
+
shortNames = this.options.template.indexOf('MMM') !== -1,
|
3468
|
+
twoDigit = this.options.template.indexOf('MM') !== -1;
|
3469
|
+
|
3470
|
+
for(i=0; i<=11; i++) {
|
3471
|
+
if(longNames) {
|
3472
|
+
name = moment.months[i];
|
3473
|
+
} else if(shortNames) {
|
3474
|
+
name = moment.monthsShort[i];
|
3475
|
+
} else if(twoDigit) {
|
3476
|
+
name = this.leadZero(i+1);
|
3477
|
+
} else {
|
3478
|
+
name = i+1;
|
3479
|
+
}
|
3480
|
+
items.push([i, name]);
|
3481
|
+
}
|
3482
|
+
return items;
|
3483
|
+
},
|
3484
|
+
|
3485
|
+
/*
|
3486
|
+
fill year
|
3487
|
+
*/
|
3488
|
+
fillYear: function() {
|
3489
|
+
var items = this.initItems('y'), name, i,
|
3490
|
+
longNames = this.options.template.indexOf('YYYY') !== -1;
|
3491
|
+
|
3492
|
+
for(i=this.options.maxYear; i>=this.options.minYear; i--) {
|
3493
|
+
name = longNames ? i : (i+'').substring(2);
|
3494
|
+
items.push([i, name]);
|
3495
|
+
}
|
3496
|
+
return items;
|
3497
|
+
},
|
3498
|
+
|
3499
|
+
/*
|
3500
|
+
fill hour
|
3501
|
+
*/
|
3502
|
+
fillHour: function() {
|
3503
|
+
var items = this.initItems('h'), name, i,
|
3504
|
+
h12 = this.options.template.indexOf('h') !== -1,
|
3505
|
+
h24 = this.options.template.indexOf('H') !== -1,
|
3506
|
+
twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
|
3507
|
+
max = h12 ? 12 : 23;
|
3508
|
+
|
3509
|
+
for(i=0; i<=max; i++) {
|
3510
|
+
name = twoDigit ? this.leadZero(i) : i;
|
3511
|
+
items.push([i, name]);
|
3512
|
+
}
|
3513
|
+
return items;
|
3514
|
+
},
|
3515
|
+
|
3516
|
+
/*
|
3517
|
+
fill minute
|
3518
|
+
*/
|
3519
|
+
fillMinute: function() {
|
3520
|
+
var items = this.initItems('m'), name, i,
|
3521
|
+
twoDigit = this.options.template.indexOf('mm') !== -1;
|
3522
|
+
|
3523
|
+
for(i=0; i<=59; i+= this.options.minuteStep) {
|
3524
|
+
name = twoDigit ? this.leadZero(i) : i;
|
3525
|
+
items.push([i, name]);
|
3526
|
+
}
|
3527
|
+
return items;
|
3528
|
+
},
|
3529
|
+
|
3530
|
+
/*
|
3531
|
+
fill second
|
3532
|
+
*/
|
3533
|
+
fillSecond: function() {
|
3534
|
+
var items = this.initItems('s'), name, i,
|
3535
|
+
twoDigit = this.options.template.indexOf('ss') !== -1;
|
3536
|
+
|
3537
|
+
for(i=0; i<=59; i+= this.options.secondStep) {
|
3538
|
+
name = twoDigit ? this.leadZero(i) : i;
|
3539
|
+
items.push([i, name]);
|
3540
|
+
}
|
3541
|
+
return items;
|
3542
|
+
},
|
3543
|
+
|
3544
|
+
/*
|
3545
|
+
fill ampm
|
3546
|
+
*/
|
3547
|
+
fillAmpm: function() {
|
3548
|
+
var ampmL = this.options.template.indexOf('a') !== -1,
|
3549
|
+
ampmU = this.options.template.indexOf('A') !== -1,
|
3550
|
+
items = [
|
3551
|
+
['am', ampmL ? 'am' : 'AM'],
|
3552
|
+
['pm', ampmL ? 'pm' : 'PM']
|
3553
|
+
];
|
3554
|
+
return items;
|
3555
|
+
},
|
3556
|
+
|
3557
|
+
/*
|
3558
|
+
Returns current date value.
|
3559
|
+
If format not specified - `options.format` used.
|
3560
|
+
If format = `null` - Moment object returned.
|
3561
|
+
*/
|
3562
|
+
getValue: function(format) {
|
3563
|
+
var dt, values = {},
|
3564
|
+
that = this,
|
3565
|
+
notSelected = false;
|
3566
|
+
|
3567
|
+
//getting selected values
|
3568
|
+
$.each(this.map, function(k, v) {
|
3569
|
+
if(k === 'ampm') {
|
3570
|
+
return;
|
3571
|
+
}
|
3572
|
+
var def = k === 'day' ? 1 : 0;
|
3573
|
+
|
3574
|
+
values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def;
|
3575
|
+
|
3576
|
+
if(isNaN(values[k])) {
|
3577
|
+
notSelected = true;
|
3578
|
+
return false;
|
3579
|
+
}
|
3580
|
+
});
|
3581
|
+
|
3582
|
+
//if at least one visible combo not selected - return empty string
|
3583
|
+
if(notSelected) {
|
3584
|
+
return '';
|
3585
|
+
}
|
3586
|
+
|
3587
|
+
//convert hours if 12h format
|
3588
|
+
if(this.$ampm) {
|
3589
|
+
values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
|
3590
|
+
if(values.hour === 24) {
|
3591
|
+
values.hour = 0;
|
3592
|
+
}
|
3593
|
+
}
|
3594
|
+
|
3595
|
+
dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
|
3596
|
+
|
3597
|
+
//highlight invalid date
|
3598
|
+
this.highlight(dt);
|
3599
|
+
|
3600
|
+
format = format === undefined ? this.options.format : format;
|
3601
|
+
if(format === null) {
|
3602
|
+
return dt.isValid() ? dt : null;
|
3603
|
+
} else {
|
3604
|
+
return dt.isValid() ? dt.format(format) : '';
|
3605
|
+
}
|
3606
|
+
},
|
3607
|
+
|
3608
|
+
setValue: function(value) {
|
3609
|
+
if(!value) {
|
3610
|
+
return;
|
3611
|
+
}
|
3612
|
+
|
3613
|
+
var dt = typeof value === 'string' ? moment(value, this.options.format) : moment(value),
|
3614
|
+
that = this,
|
3615
|
+
values = {};
|
3616
|
+
|
3617
|
+
if(dt.isValid()) {
|
3618
|
+
//read values from date object
|
3619
|
+
$.each(this.map, function(k, v) {
|
3620
|
+
if(k === 'ampm') {
|
3621
|
+
return;
|
3622
|
+
}
|
3623
|
+
values[k] = dt[v[1]]();
|
3624
|
+
});
|
3625
|
+
|
3626
|
+
if(this.$ampm) {
|
3627
|
+
if(values.hour > 12) {
|
3628
|
+
values.hour -= 12;
|
3629
|
+
values.ampm = 'pm';
|
3630
|
+
} else {
|
3631
|
+
values.ampm = 'am';
|
3632
|
+
}
|
3633
|
+
}
|
3634
|
+
|
3635
|
+
$.each(values, function(k, v) {
|
3636
|
+
if(that['$'+k]) {
|
3637
|
+
that['$'+k].val(v);
|
3638
|
+
}
|
3639
|
+
});
|
3640
|
+
|
3641
|
+
this.$element.val(dt.format(this.options.format));
|
3642
|
+
}
|
3643
|
+
},
|
3644
|
+
|
3645
|
+
/*
|
3646
|
+
highlight combos if date is invalid
|
3647
|
+
*/
|
3648
|
+
highlight: function(dt) {
|
3649
|
+
if(!dt.isValid()) {
|
3650
|
+
if(this.options.errorClass) {
|
3651
|
+
this.$widget.addClass(this.options.errorClass);
|
3652
|
+
} else {
|
3653
|
+
//store original border color
|
3654
|
+
if(!this.borderColor) {
|
3655
|
+
this.borderColor = this.$widget.find('select').css('border-color');
|
3656
|
+
}
|
3657
|
+
this.$widget.find('select').css('border-color', 'red');
|
3658
|
+
}
|
3659
|
+
} else {
|
3660
|
+
if(this.options.errorClass) {
|
3661
|
+
this.$widget.removeClass(this.options.errorClass);
|
3662
|
+
} else {
|
3663
|
+
this.$widget.find('select').css('border-color', this.borderColor);
|
3664
|
+
}
|
3665
|
+
}
|
3666
|
+
},
|
3667
|
+
|
3668
|
+
leadZero: function(v) {
|
3669
|
+
return v <= 9 ? '0' + v : v;
|
3670
|
+
},
|
3671
|
+
|
3672
|
+
destroy: function() {
|
3673
|
+
this.$widget.remove();
|
3674
|
+
this.$element.removeData('combodate').show();
|
3675
|
+
}
|
3676
|
+
|
3677
|
+
//todo: clear method
|
3678
|
+
};
|
3679
|
+
|
3680
|
+
$.fn.combodate = function ( option ) {
|
3681
|
+
var d, args = Array.apply(null, arguments);
|
3682
|
+
args.shift();
|
3683
|
+
|
3684
|
+
//getValue returns date as string / object (not jQuery object)
|
3685
|
+
if(option === 'getValue' && this.length && (d = this.eq(0).data('combodate'))) {
|
3686
|
+
return d.getValue.apply(d, args);
|
3687
|
+
}
|
3688
|
+
|
3689
|
+
return this.each(function () {
|
3690
|
+
var $this = $(this),
|
3691
|
+
data = $this.data('combodate'),
|
3692
|
+
options = typeof option == 'object' && option;
|
3693
|
+
if (!data) {
|
3694
|
+
$this.data('combodate', (data = new Combodate(this, options)));
|
3695
|
+
}
|
3696
|
+
if (typeof option == 'string' && typeof data[option] == 'function') {
|
3697
|
+
data[option].apply(data, args);
|
3698
|
+
}
|
3699
|
+
});
|
3700
|
+
};
|
3701
|
+
|
3702
|
+
$.fn.combodate.defaults = {
|
3703
|
+
//in this format value stored in original input
|
3704
|
+
format: 'DD-MM-YYYY HH:mm',
|
3705
|
+
//in this format items in dropdowns are displayed
|
3706
|
+
template: 'D / MMM / YYYY H : mm',
|
3707
|
+
//initial value, can be `new Date()`
|
3708
|
+
value: null,
|
3709
|
+
minYear: 1970,
|
3710
|
+
maxYear: 2015,
|
3711
|
+
minuteStep: 5,
|
3712
|
+
secondStep: 1,
|
3713
|
+
firstItem: 'empty', //'name', 'empty', 'none'
|
3714
|
+
errorClass: null
|
3715
|
+
};
|
3716
|
+
|
3717
|
+
}(window.jQuery));
|
3718
|
+
/**
|
3719
|
+
Combodate input - dropdown date and time picker.
|
3720
|
+
Based on [combodate](http://vitalets.github.com/combodate) plugin. To use it you should manually include [momentjs](http://momentjs.com).
|
3721
|
+
|
3722
|
+
<script src="js/moment.min.js"></script>
|
3723
|
+
|
3724
|
+
Allows to input:
|
3725
|
+
|
3726
|
+
* only date
|
3727
|
+
* only time
|
3728
|
+
* both date and time
|
3729
|
+
|
3730
|
+
Please note, that format is taken from momentjs and **not compatible** with bootstrap-datepicker / jquery UI datepicker.
|
3731
|
+
Internally value stored as `momentjs` object.
|
3732
|
+
|
3733
|
+
@class combodate
|
3734
|
+
@extends abstractinput
|
3735
|
+
@final
|
3736
|
+
@since 1.4.0
|
3737
|
+
@example
|
3738
|
+
<a href="#" id="dob" data-type="combodate" data-pk="1" data-url="/post" data-value="1984-05-15" data-original-title="Select date"></a>
|
3739
|
+
<script>
|
3740
|
+
$(function(){
|
3741
|
+
$('#dob').editable({
|
3742
|
+
format: 'YYYY-MM-DD',
|
3743
|
+
viewformat: 'DD.MM.YYYY',
|
3744
|
+
template: 'D / MMMM / YYYY',
|
3745
|
+
combodate: {
|
3746
|
+
minYear: 2000,
|
3747
|
+
maxYear: 2015,
|
3748
|
+
minuteStep: 1
|
3749
|
+
}
|
3750
|
+
}
|
3751
|
+
});
|
3752
|
+
});
|
3753
|
+
</script>
|
3754
|
+
**/
|
3755
|
+
|
3756
|
+
/*global moment*/
|
3757
|
+
|
3758
|
+
(function ($) {
|
3759
|
+
|
3760
|
+
var Constructor = function (options) {
|
3761
|
+
this.init('combodate', options, Constructor.defaults);
|
3762
|
+
|
3763
|
+
//by default viewformat equals to format
|
3764
|
+
if(!this.options.viewformat) {
|
3765
|
+
this.options.viewformat = this.options.format;
|
3766
|
+
}
|
3767
|
+
|
3768
|
+
//overriding combodate config (as by default jQuery extend() is not recursive)
|
3769
|
+
this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, {
|
3770
|
+
format: this.options.format,
|
3771
|
+
template: this.options.template
|
3772
|
+
});
|
3773
|
+
};
|
3774
|
+
|
3775
|
+
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);
|
3776
|
+
|
3777
|
+
$.extend(Constructor.prototype, {
|
3778
|
+
render: function () {
|
3779
|
+
this.$input.combodate(this.options.combodate);
|
3780
|
+
|
3781
|
+
//"clear" link
|
3782
|
+
/*
|
3783
|
+
if(this.options.clear) {
|
3784
|
+
this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
|
3785
|
+
e.preventDefault();
|
3786
|
+
e.stopPropagation();
|
3787
|
+
this.clear();
|
3788
|
+
}, this));
|
3789
|
+
|
3790
|
+
this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
|
3791
|
+
}
|
3792
|
+
*/
|
3793
|
+
},
|
3794
|
+
|
3795
|
+
value2html: function(value, element) {
|
3796
|
+
var text = value ? value.format(this.options.viewformat) : '';
|
3797
|
+
$(element).text(text);
|
3798
|
+
},
|
3799
|
+
|
3800
|
+
html2value: function(html) {
|
3801
|
+
return html ? moment(html, this.options.viewformat) : null;
|
3802
|
+
},
|
3803
|
+
|
3804
|
+
value2str: function(value) {
|
3805
|
+
return value ? value.format(this.options.format) : '';
|
3806
|
+
},
|
3807
|
+
|
3808
|
+
str2value: function(str) {
|
3809
|
+
return str ? moment(str, this.options.format) : null;
|
3810
|
+
},
|
3811
|
+
|
3812
|
+
value2submit: function(value) {
|
3813
|
+
return this.value2str(value);
|
3814
|
+
},
|
3815
|
+
|
3816
|
+
value2input: function(value) {
|
3817
|
+
this.$input.combodate('setValue', value);
|
3818
|
+
},
|
3819
|
+
|
3820
|
+
input2value: function() {
|
3821
|
+
return this.$input.combodate('getValue', null);
|
3822
|
+
},
|
3823
|
+
|
3824
|
+
activate: function() {
|
3825
|
+
this.$input.siblings('.combodate').find('select').eq(0).focus();
|
3826
|
+
},
|
3827
|
+
|
3828
|
+
/*
|
3829
|
+
clear: function() {
|
3830
|
+
this.$input.data('datepicker').date = null;
|
3831
|
+
this.$input.find('.active').removeClass('active');
|
3832
|
+
},
|
3833
|
+
*/
|
3834
|
+
|
3835
|
+
autosubmit: function() {
|
3836
|
+
|
3837
|
+
}
|
3838
|
+
|
3839
|
+
});
|
3840
|
+
|
3841
|
+
Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
|
3842
|
+
/**
|
3843
|
+
@property tpl
|
3844
|
+
@default <input type="text">
|
3845
|
+
**/
|
3846
|
+
tpl:'<input type="text">',
|
3847
|
+
/**
|
3848
|
+
@property inputclass
|
3849
|
+
@default null
|
3850
|
+
**/
|
3851
|
+
inputclass: null,
|
3852
|
+
/**
|
3853
|
+
Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
|
3854
|
+
See list of tokens in [momentjs docs](http://momentjs.com/docs/#/parsing/string-format)
|
3855
|
+
|
3856
|
+
@property format
|
3857
|
+
@type string
|
3858
|
+
@default YYYY-MM-DD
|
3859
|
+
**/
|
3860
|
+
format:'YYYY-MM-DD',
|
3861
|
+
/**
|
3862
|
+
Format used for displaying date. Also applied when converting date from element's text on init.
|
3863
|
+
If not specified equals to `format`.
|
3864
|
+
|
3865
|
+
@property viewformat
|
3866
|
+
@type string
|
3867
|
+
@default null
|
3868
|
+
**/
|
3869
|
+
viewformat: null,
|
3870
|
+
/**
|
3871
|
+
Template used for displaying dropdowns.
|
3872
|
+
|
3873
|
+
@property template
|
3874
|
+
@type string
|
3875
|
+
@default D / MMM / YYYY
|
3876
|
+
**/
|
3877
|
+
template: 'D / MMM / YYYY',
|
3878
|
+
/**
|
3879
|
+
Configuration of combodate.
|
3880
|
+
Full list of options: http://vitalets.github.com/combodate/#docs
|
3881
|
+
|
3882
|
+
@property combodate
|
3883
|
+
@type object
|
3884
|
+
@default null
|
3885
|
+
**/
|
3886
|
+
combodate: null
|
3887
|
+
|
3888
|
+
/*
|
3889
|
+
(not implemented yet)
|
3890
|
+
Text shown as clear date button.
|
3891
|
+
If <code>false</code> clear button will not be rendered.
|
3892
|
+
|
3893
|
+
@property clear
|
3894
|
+
@type boolean|string
|
3895
|
+
@default 'x clear'
|
3896
|
+
*/
|
3897
|
+
//clear: '× clear'
|
3898
|
+
});
|
3899
|
+
|
3900
|
+
$.fn.editabletypes.combodate = Constructor;
|
3901
|
+
|
3902
|
+
}(window.jQuery));
|
3903
|
+
|
2640
3904
|
/*
|
2641
3905
|
Editableform based on Twitter Bootstrap
|
2642
3906
|
*/
|
@@ -2666,7 +3930,7 @@ Editableform based on Twitter Bootstrap
|
|
2666
3930
|
(function ($) {
|
2667
3931
|
|
2668
3932
|
//extend methods
|
2669
|
-
$.extend($.fn.editableContainer.
|
3933
|
+
$.extend($.fn.editableContainer.Popup.prototype, {
|
2670
3934
|
containerName: 'popover',
|
2671
3935
|
//for compatibility with bootstrap <= 2.2.1 (content inserted into <p> instead of directly .popover-content)
|
2672
3936
|
innerCss: $($.fn.popover.defaults.template).find('p').length ? '.popover-content p' : '.popover-content',
|
@@ -2675,10 +3939,39 @@ Editableform based on Twitter Bootstrap
|
|
2675
3939
|
$.extend(this.containerOptions, {
|
2676
3940
|
trigger: 'manual',
|
2677
3941
|
selector: false,
|
2678
|
-
content: ' '
|
3942
|
+
content: ' ',
|
3943
|
+
template: $.fn.popover.defaults.template
|
2679
3944
|
});
|
3945
|
+
|
3946
|
+
//as template property is used in inputs, hide it from popover
|
3947
|
+
var t;
|
3948
|
+
if(this.$element.data('template')) {
|
3949
|
+
t = this.$element.data('template');
|
3950
|
+
this.$element.removeData('template');
|
3951
|
+
}
|
3952
|
+
|
2680
3953
|
this.call(this.containerOptions);
|
2681
|
-
|
3954
|
+
|
3955
|
+
if(t) {
|
3956
|
+
//restore data('template')
|
3957
|
+
this.$element.data('template', t);
|
3958
|
+
}
|
3959
|
+
},
|
3960
|
+
|
3961
|
+
/* show */
|
3962
|
+
innerShow: function () {
|
3963
|
+
this.call('show');
|
3964
|
+
},
|
3965
|
+
|
3966
|
+
/* hide */
|
3967
|
+
innerHide: function () {
|
3968
|
+
this.call('hide');
|
3969
|
+
},
|
3970
|
+
|
3971
|
+
/* destroy */
|
3972
|
+
innerDestroy: function() {
|
3973
|
+
this.call('destroy');
|
3974
|
+
},
|
2682
3975
|
|
2683
3976
|
setContainerOption: function(key, value) {
|
2684
3977
|
this.container().options[key] = value;
|
@@ -2742,18 +4035,13 @@ Editableform based on Twitter Bootstrap
|
|
2742
4035
|
}
|
2743
4036
|
});
|
2744
4037
|
|
2745
|
-
//defaults
|
2746
|
-
/*
|
2747
|
-
$.fn.editableContainer.defaults = $.extend({}, $.fn.popover.defaults, $.fn.editableContainer.defaults, {
|
2748
|
-
|
2749
|
-
});
|
2750
|
-
*/
|
2751
|
-
|
2752
4038
|
}(window.jQuery));
|
2753
4039
|
/**
|
2754
4040
|
Bootstrap-datepicker.
|
2755
|
-
Description and examples:
|
2756
|
-
For
|
4041
|
+
Description and examples: https://github.com/eternicode/bootstrap-datepicker.
|
4042
|
+
For **i18n** you should include js file from here: https://github.com/eternicode/bootstrap-datepicker/tree/master/js/locales
|
4043
|
+
and set `language` option.
|
4044
|
+
Since 1.4.0 date has different appearance in **popup** and **inline** modes.
|
2757
4045
|
|
2758
4046
|
@class date
|
2759
4047
|
@extends abstractinput
|
@@ -2777,45 +4065,52 @@ $(function(){
|
|
2777
4065
|
|
2778
4066
|
var Date = function (options) {
|
2779
4067
|
this.init('date', options, Date.defaults);
|
2780
|
-
|
2781
|
-
//set popular options directly from settings or data-* attributes
|
2782
|
-
var directOptions = $.fn.editableutils.sliceObj(this.options, ['format']);
|
2783
|
-
|
2784
|
-
//overriding datepicker config (as by default jQuery extend() is not recursive)
|
2785
|
-
this.options.datepicker = $.extend({}, Date.defaults.datepicker, directOptions, options.datepicker);
|
2786
|
-
|
2787
|
-
//by default viewformat equals to format
|
2788
|
-
if(!this.options.viewformat) {
|
2789
|
-
this.options.viewformat = this.options.datepicker.format;
|
2790
|
-
}
|
2791
|
-
|
2792
|
-
//language
|
2793
|
-
this.options.datepicker.language = this.options.datepicker.language || 'en';
|
2794
|
-
|
2795
|
-
//store DPglobal
|
2796
|
-
this.dpg = $.fn.datepicker.DPGlobal;
|
2797
|
-
|
2798
|
-
//store parsed formats
|
2799
|
-
this.parsedFormat = this.dpg.parseFormat(this.options.datepicker.format);
|
2800
|
-
this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);
|
4068
|
+
this.initPicker(options, Date.defaults);
|
2801
4069
|
};
|
2802
4070
|
|
2803
4071
|
$.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);
|
2804
4072
|
|
2805
4073
|
$.extend(Date.prototype, {
|
4074
|
+
initPicker: function(options, defaults) {
|
4075
|
+
//'format' is set directly from settings or data-* attributes
|
4076
|
+
|
4077
|
+
//by default viewformat equals to format
|
4078
|
+
if(!this.options.viewformat) {
|
4079
|
+
this.options.viewformat = this.options.format;
|
4080
|
+
}
|
4081
|
+
|
4082
|
+
//overriding datepicker config (as by default jQuery extend() is not recursive)
|
4083
|
+
//since 1.4 datepicker internally uses viewformat instead of format. Format is for submit only
|
4084
|
+
this.options.datepicker = $.extend({}, defaults.datepicker, options.datepicker, {
|
4085
|
+
format: this.options.viewformat
|
4086
|
+
});
|
4087
|
+
|
4088
|
+
//language
|
4089
|
+
this.options.datepicker.language = this.options.datepicker.language || 'en';
|
4090
|
+
|
4091
|
+
//store DPglobal
|
4092
|
+
this.dpg = $.fn.datepicker.DPGlobal;
|
4093
|
+
|
4094
|
+
//store parsed formats
|
4095
|
+
this.parsedFormat = this.dpg.parseFormat(this.options.format);
|
4096
|
+
this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);
|
4097
|
+
},
|
4098
|
+
|
2806
4099
|
render: function () {
|
2807
|
-
Date.superclass.render.call(this);
|
2808
4100
|
this.$input.datepicker(this.options.datepicker);
|
2809
|
-
|
4101
|
+
|
4102
|
+
//"clear" link
|
2810
4103
|
if(this.options.clear) {
|
2811
4104
|
this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
|
2812
4105
|
e.preventDefault();
|
2813
4106
|
e.stopPropagation();
|
2814
4107
|
this.clear();
|
2815
4108
|
}, this));
|
2816
|
-
|
4109
|
+
|
4110
|
+
this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));
|
4111
|
+
}
|
2817
4112
|
},
|
2818
|
-
|
4113
|
+
|
2819
4114
|
value2html: function(value, element) {
|
2820
4115
|
var text = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
|
2821
4116
|
Date.superclass.value2html(text, element);
|
@@ -2869,12 +4164,12 @@ $(function(){
|
|
2869
4164
|
@property tpl
|
2870
4165
|
@default <div></div>
|
2871
4166
|
**/
|
2872
|
-
tpl:'<div></div>',
|
4167
|
+
tpl:'<div class="editable-date well"></div>',
|
2873
4168
|
/**
|
2874
4169
|
@property inputclass
|
2875
|
-
@default
|
4170
|
+
@default null
|
2876
4171
|
**/
|
2877
|
-
inputclass:
|
4172
|
+
inputclass: null,
|
2878
4173
|
/**
|
2879
4174
|
Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
|
2880
4175
|
Possible tokens are: <code>d, dd, m, mm, yy, yyyy</code>
|
@@ -2925,6 +4220,84 @@ $(function(){
|
|
2925
4220
|
|
2926
4221
|
}(window.jQuery));
|
2927
4222
|
|
4223
|
+
/**
|
4224
|
+
Bootstrap datefield input - modification for inline mode.
|
4225
|
+
Shows normal <input type="text"> and binds popup datepicker.
|
4226
|
+
Automatically shown in inline mode.
|
4227
|
+
|
4228
|
+
@class datefield
|
4229
|
+
@extends date
|
4230
|
+
|
4231
|
+
@since 1.4.0
|
4232
|
+
**/
|
4233
|
+
(function ($) {
|
4234
|
+
|
4235
|
+
var DateField = function (options) {
|
4236
|
+
this.init('datefield', options, DateField.defaults);
|
4237
|
+
this.initPicker(options, DateField.defaults);
|
4238
|
+
};
|
4239
|
+
|
4240
|
+
$.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);
|
4241
|
+
|
4242
|
+
$.extend(DateField.prototype, {
|
4243
|
+
render: function () {
|
4244
|
+
this.$input = this.$tpl.find('input');
|
4245
|
+
this.setClass();
|
4246
|
+
this.setAttr('placeholder');
|
4247
|
+
|
4248
|
+
this.$tpl.datepicker(this.options.datepicker);
|
4249
|
+
|
4250
|
+
//need to disable original event handlers
|
4251
|
+
this.$input.off('focus keydown');
|
4252
|
+
|
4253
|
+
//update value of datepicker
|
4254
|
+
this.$input.keyup($.proxy(function(){
|
4255
|
+
this.$tpl.removeData('date');
|
4256
|
+
this.$tpl.datepicker('update');
|
4257
|
+
}, this));
|
4258
|
+
|
4259
|
+
},
|
4260
|
+
|
4261
|
+
value2input: function(value) {
|
4262
|
+
this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
|
4263
|
+
this.$tpl.datepicker('update');
|
4264
|
+
},
|
4265
|
+
|
4266
|
+
input2value: function() {
|
4267
|
+
return this.html2value(this.$input.val());
|
4268
|
+
},
|
4269
|
+
|
4270
|
+
activate: function() {
|
4271
|
+
$.fn.editabletypes.text.prototype.activate.call(this);
|
4272
|
+
},
|
4273
|
+
|
4274
|
+
autosubmit: function() {
|
4275
|
+
//reset autosubmit to empty
|
4276
|
+
}
|
4277
|
+
});
|
4278
|
+
|
4279
|
+
DateField.defaults = $.extend({}, $.fn.editabletypes.date.defaults, {
|
4280
|
+
/**
|
4281
|
+
@property tpl
|
4282
|
+
**/
|
4283
|
+
tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
|
4284
|
+
/**
|
4285
|
+
@property inputclass
|
4286
|
+
@default 'input-small'
|
4287
|
+
**/
|
4288
|
+
inputclass: 'input-small',
|
4289
|
+
|
4290
|
+
/* datepicker config */
|
4291
|
+
datepicker: {
|
4292
|
+
weekStart: 0,
|
4293
|
+
startView: 0,
|
4294
|
+
autoclose: true
|
4295
|
+
}
|
4296
|
+
});
|
4297
|
+
|
4298
|
+
$.fn.editabletypes.datefield = DateField;
|
4299
|
+
|
4300
|
+
}(window.jQuery));
|
2928
4301
|
/* =========================================================
|
2929
4302
|
* bootstrap-datepicker.js
|
2930
4303
|
* http://www.eyecon.ro/bootstrap-datepicker
|
@@ -2963,51 +4336,45 @@ $(function(){
|
|
2963
4336
|
this.element = $(element);
|
2964
4337
|
this.language = options.language||this.element.data('date-language')||"en";
|
2965
4338
|
this.language = this.language in dates ? this.language : "en";
|
4339
|
+
this.isRTL = dates[this.language].rtl||false;
|
2966
4340
|
this.format = DPGlobal.parseFormat(options.format||this.element.data('date-format')||'mm/dd/yyyy');
|
2967
|
-
|
4341
|
+
this.isInline = false;
|
2968
4342
|
this.isInput = this.element.is('input');
|
2969
4343
|
this.component = this.element.is('.date') ? this.element.find('.add-on') : false;
|
2970
4344
|
this.hasInput = this.component && this.element.find('input').length;
|
2971
4345
|
if(this.component && this.component.length === 0)
|
2972
4346
|
this.component = false;
|
2973
4347
|
|
2974
|
-
|
2975
|
-
this.element.on({
|
2976
|
-
focus: $.proxy(this.show, this),
|
2977
|
-
keyup: $.proxy(this.update, this),
|
2978
|
-
keydown: $.proxy(this.keydown, this)
|
2979
|
-
});
|
2980
|
-
} else if(this.component && this.hasInput) { //component: input + button
|
2981
|
-
// For components that are not readonly, allow keyboard nav
|
2982
|
-
this.element.find('input').on({
|
2983
|
-
focus: $.proxy(this.show, this),
|
2984
|
-
keyup: $.proxy(this.update, this),
|
2985
|
-
keydown: $.proxy(this.keydown, this)
|
2986
|
-
});
|
2987
|
-
|
2988
|
-
this.component.on('click', $.proxy(this.show, this));
|
2989
|
-
} else if(this.element.is('div')) { //inline datepicker
|
2990
|
-
this.isInline = true;
|
2991
|
-
} else {
|
2992
|
-
this.element.on('click', $.proxy(this.show, this));
|
2993
|
-
}
|
2994
|
-
|
2995
|
-
this.picker = $(DPGlobal.template)
|
2996
|
-
.appendTo(this.isInline ? this.element : 'body')
|
2997
|
-
.on({
|
2998
|
-
click: $.proxy(this.click, this),
|
2999
|
-
mousedown: $.proxy(this.mousedown, this)
|
3000
|
-
});
|
3001
|
-
|
3002
|
-
if(this.isInline) {
|
3003
|
-
this.picker.addClass('datepicker-inline');
|
3004
|
-
} else {
|
3005
|
-
this.picker.addClass('dropdown-menu');
|
3006
|
-
}
|
4348
|
+
this._attachEvents();
|
3007
4349
|
|
4350
|
+
this.forceParse = true;
|
4351
|
+
if ('forceParse' in options) {
|
4352
|
+
this.forceParse = options.forceParse;
|
4353
|
+
} else if ('dateForceParse' in this.element.data()) {
|
4354
|
+
this.forceParse = this.element.data('date-force-parse');
|
4355
|
+
}
|
4356
|
+
|
4357
|
+
|
4358
|
+
this.picker = $(DPGlobal.template)
|
4359
|
+
.appendTo(this.isInline ? this.element : 'body')
|
4360
|
+
.on({
|
4361
|
+
click: $.proxy(this.click, this),
|
4362
|
+
mousedown: $.proxy(this.mousedown, this)
|
4363
|
+
});
|
4364
|
+
|
4365
|
+
if(this.isInline) {
|
4366
|
+
this.picker.addClass('datepicker-inline');
|
4367
|
+
} else {
|
4368
|
+
this.picker.addClass('datepicker-dropdown dropdown-menu');
|
4369
|
+
}
|
4370
|
+
if (this.isRTL){
|
4371
|
+
this.picker.addClass('datepicker-rtl');
|
4372
|
+
this.picker.find('.prev i, .next i')
|
4373
|
+
.toggleClass('icon-arrow-left icon-arrow-right');
|
4374
|
+
}
|
3008
4375
|
$(document).on('mousedown', function (e) {
|
3009
4376
|
// Clicked outside the datepicker, hide it
|
3010
|
-
if ($(e.target).closest('.datepicker').length
|
4377
|
+
if ($(e.target).closest('.datepicker').length === 0) {
|
3011
4378
|
that.hide();
|
3012
4379
|
}
|
3013
4380
|
});
|
@@ -3026,6 +4393,7 @@ $(function(){
|
|
3026
4393
|
this.keyboardNavigation = this.element.data('date-keyboard-navigation');
|
3027
4394
|
}
|
3028
4395
|
|
4396
|
+
this.viewMode = this.startViewMode = 0;
|
3029
4397
|
switch(options.startView || this.element.data('date-start-view')){
|
3030
4398
|
case 2:
|
3031
4399
|
case 'decade':
|
@@ -3035,11 +4403,6 @@ $(function(){
|
|
3035
4403
|
case 'year':
|
3036
4404
|
this.viewMode = this.startViewMode = 1;
|
3037
4405
|
break;
|
3038
|
-
case 0:
|
3039
|
-
case 'month':
|
3040
|
-
default:
|
3041
|
-
this.viewMode = this.startViewMode = 0;
|
3042
|
-
break;
|
3043
4406
|
}
|
3044
4407
|
|
3045
4408
|
this.todayBtn = (options.todayBtn||this.element.data('date-today-btn')||false);
|
@@ -3049,21 +4412,73 @@ $(function(){
|
|
3049
4412
|
this.weekEnd = ((this.weekStart + 6) % 7);
|
3050
4413
|
this.startDate = -Infinity;
|
3051
4414
|
this.endDate = Infinity;
|
4415
|
+
this.daysOfWeekDisabled = [];
|
3052
4416
|
this.setStartDate(options.startDate||this.element.data('date-startdate'));
|
3053
4417
|
this.setEndDate(options.endDate||this.element.data('date-enddate'));
|
4418
|
+
this.setDaysOfWeekDisabled(options.daysOfWeekDisabled||this.element.data('date-days-of-week-disabled'));
|
3054
4419
|
this.fillDow();
|
3055
4420
|
this.fillMonths();
|
3056
4421
|
this.update();
|
3057
4422
|
this.showMode();
|
3058
4423
|
|
3059
|
-
|
3060
|
-
|
3061
|
-
|
4424
|
+
if(this.isInline) {
|
4425
|
+
this.show();
|
4426
|
+
}
|
3062
4427
|
};
|
3063
4428
|
|
3064
4429
|
Datepicker.prototype = {
|
3065
4430
|
constructor: Datepicker,
|
3066
4431
|
|
4432
|
+
_events: [],
|
4433
|
+
_attachEvents: function(){
|
4434
|
+
this._detachEvents();
|
4435
|
+
if (this.isInput) { // single input
|
4436
|
+
this._events = [
|
4437
|
+
[this.element, {
|
4438
|
+
focus: $.proxy(this.show, this),
|
4439
|
+
keyup: $.proxy(this.update, this),
|
4440
|
+
keydown: $.proxy(this.keydown, this)
|
4441
|
+
}]
|
4442
|
+
];
|
4443
|
+
}
|
4444
|
+
else if (this.component && this.hasInput){ // component: input + button
|
4445
|
+
this._events = [
|
4446
|
+
// For components that are not readonly, allow keyboard nav
|
4447
|
+
[this.element.find('input'), {
|
4448
|
+
focus: $.proxy(this.show, this),
|
4449
|
+
keyup: $.proxy(this.update, this),
|
4450
|
+
keydown: $.proxy(this.keydown, this)
|
4451
|
+
}],
|
4452
|
+
[this.component, {
|
4453
|
+
click: $.proxy(this.show, this)
|
4454
|
+
}]
|
4455
|
+
];
|
4456
|
+
}
|
4457
|
+
else if (this.element.is('div')) { // inline datepicker
|
4458
|
+
this.isInline = true;
|
4459
|
+
}
|
4460
|
+
else {
|
4461
|
+
this._events = [
|
4462
|
+
[this.element, {
|
4463
|
+
click: $.proxy(this.show, this)
|
4464
|
+
}]
|
4465
|
+
];
|
4466
|
+
}
|
4467
|
+
for (var i=0, el, ev; i<this._events.length; i++){
|
4468
|
+
el = this._events[i][0];
|
4469
|
+
ev = this._events[i][1];
|
4470
|
+
el.on(ev);
|
4471
|
+
}
|
4472
|
+
},
|
4473
|
+
_detachEvents: function(){
|
4474
|
+
for (var i=0, el, ev; i<this._events.length; i++){
|
4475
|
+
el = this._events[i][0];
|
4476
|
+
ev = this._events[i][1];
|
4477
|
+
el.off(ev);
|
4478
|
+
}
|
4479
|
+
this._events = [];
|
4480
|
+
},
|
4481
|
+
|
3067
4482
|
show: function(e) {
|
3068
4483
|
this.picker.show();
|
3069
4484
|
this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
|
@@ -3081,7 +4496,7 @@ $(function(){
|
|
3081
4496
|
},
|
3082
4497
|
|
3083
4498
|
hide: function(e){
|
3084
|
-
|
4499
|
+
if(this.isInline) return;
|
3085
4500
|
this.picker.hide();
|
3086
4501
|
$(window).off('resize', this.place);
|
3087
4502
|
this.viewMode = this.startViewMode;
|
@@ -3089,7 +4504,14 @@ $(function(){
|
|
3089
4504
|
if (!this.isInput) {
|
3090
4505
|
$(document).off('mousedown', this.hide);
|
3091
4506
|
}
|
3092
|
-
|
4507
|
+
|
4508
|
+
if (
|
4509
|
+
this.forceParse &&
|
4510
|
+
(
|
4511
|
+
this.isInput && this.element.val() ||
|
4512
|
+
this.hasInput && this.element.find('input').val()
|
4513
|
+
)
|
4514
|
+
)
|
3093
4515
|
this.setValue();
|
3094
4516
|
this.element.trigger({
|
3095
4517
|
type: 'hide',
|
@@ -3097,9 +4519,15 @@ $(function(){
|
|
3097
4519
|
});
|
3098
4520
|
},
|
3099
4521
|
|
4522
|
+
remove: function() {
|
4523
|
+
this._detachEvents();
|
4524
|
+
this.picker.remove();
|
4525
|
+
delete this.element.data().datepicker;
|
4526
|
+
},
|
4527
|
+
|
3100
4528
|
getDate: function() {
|
3101
4529
|
var d = this.getUTCDate();
|
3102
|
-
return new Date(d.getTime() + (d.getTimezoneOffset()*60000))
|
4530
|
+
return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
|
3103
4531
|
},
|
3104
4532
|
|
3105
4533
|
getUTCDate: function() {
|
@@ -3119,18 +4547,19 @@ $(function(){
|
|
3119
4547
|
var formatted = this.getFormattedDate();
|
3120
4548
|
if (!this.isInput) {
|
3121
4549
|
if (this.component){
|
3122
|
-
this.element.find('input').
|
4550
|
+
this.element.find('input').val(formatted);
|
3123
4551
|
}
|
3124
4552
|
this.element.data('date', formatted);
|
3125
4553
|
} else {
|
3126
|
-
this.element.
|
4554
|
+
this.element.val(formatted);
|
3127
4555
|
}
|
3128
4556
|
},
|
3129
4557
|
|
3130
|
-
|
3131
|
-
|
3132
|
-
|
3133
|
-
|
4558
|
+
getFormattedDate: function(format) {
|
4559
|
+
if (format === undefined)
|
4560
|
+
format = this.format;
|
4561
|
+
return DPGlobal.formatDate(this.date, format, this.language);
|
4562
|
+
},
|
3134
4563
|
|
3135
4564
|
setStartDate: function(startDate){
|
3136
4565
|
this.startDate = startDate||-Infinity;
|
@@ -3150,32 +4579,46 @@ $(function(){
|
|
3150
4579
|
this.updateNavArrows();
|
3151
4580
|
},
|
3152
4581
|
|
4582
|
+
setDaysOfWeekDisabled: function(daysOfWeekDisabled){
|
4583
|
+
this.daysOfWeekDisabled = daysOfWeekDisabled||[];
|
4584
|
+
if (!$.isArray(this.daysOfWeekDisabled)) {
|
4585
|
+
this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
|
4586
|
+
}
|
4587
|
+
this.daysOfWeekDisabled = $.map(this.daysOfWeekDisabled, function (d) {
|
4588
|
+
return parseInt(d, 10);
|
4589
|
+
});
|
4590
|
+
this.update();
|
4591
|
+
this.updateNavArrows();
|
4592
|
+
},
|
4593
|
+
|
3153
4594
|
place: function(){
|
3154
|
-
|
4595
|
+
if(this.isInline) return;
|
3155
4596
|
var zIndex = parseInt(this.element.parents().filter(function() {
|
3156
4597
|
return $(this).css('z-index') != 'auto';
|
3157
4598
|
}).first().css('z-index'))+10;
|
3158
4599
|
var offset = this.component ? this.component.offset() : this.element.offset();
|
4600
|
+
var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
|
3159
4601
|
this.picker.css({
|
3160
|
-
top: offset.top +
|
4602
|
+
top: offset.top + height,
|
3161
4603
|
left: offset.left,
|
3162
4604
|
zIndex: zIndex
|
3163
4605
|
});
|
3164
4606
|
},
|
3165
4607
|
|
3166
4608
|
update: function(){
|
3167
|
-
|
3168
|
-
|
3169
|
-
|
3170
|
-
|
3171
|
-
|
3172
|
-
|
3173
|
-
|
4609
|
+
var date, fromArgs = false;
|
4610
|
+
if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
|
4611
|
+
date = arguments[0];
|
4612
|
+
fromArgs = true;
|
4613
|
+
} else {
|
4614
|
+
date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
|
4615
|
+
}
|
3174
4616
|
|
3175
4617
|
this.date = DPGlobal.parseDate(date, this.format, this.language);
|
3176
4618
|
|
3177
|
-
|
4619
|
+
if(fromArgs) this.setValue();
|
3178
4620
|
|
4621
|
+
var oldViewDate = this.viewDate;
|
3179
4622
|
if (this.date < this.startDate) {
|
3180
4623
|
this.viewDate = new Date(this.startDate);
|
3181
4624
|
} else if (this.date > this.endDate) {
|
@@ -3183,12 +4626,19 @@ $(function(){
|
|
3183
4626
|
} else {
|
3184
4627
|
this.viewDate = new Date(this.date);
|
3185
4628
|
}
|
4629
|
+
|
4630
|
+
if (oldViewDate && oldViewDate.getTime() != this.viewDate.getTime()){
|
4631
|
+
this.element.trigger({
|
4632
|
+
type: 'changeDate',
|
4633
|
+
date: this.viewDate
|
4634
|
+
});
|
4635
|
+
}
|
3186
4636
|
this.fill();
|
3187
4637
|
},
|
3188
4638
|
|
3189
4639
|
fillDow: function(){
|
3190
|
-
var dowCnt = this.weekStart
|
3191
|
-
|
4640
|
+
var dowCnt = this.weekStart,
|
4641
|
+
html = '<tr>';
|
3192
4642
|
while (dowCnt < this.weekStart + 7) {
|
3193
4643
|
html += '<th class="dow">'+dates[this.language].daysMin[(dowCnt++)%7]+'</th>';
|
3194
4644
|
}
|
@@ -3197,8 +4647,8 @@ $(function(){
|
|
3197
4647
|
},
|
3198
4648
|
|
3199
4649
|
fillMonths: function(){
|
3200
|
-
var html = ''
|
3201
|
-
|
4650
|
+
var html = '',
|
4651
|
+
i = 0;
|
3202
4652
|
while (i < 12) {
|
3203
4653
|
html += '<span class="month">'+dates[this.language].monthsShort[i++]+'</span>';
|
3204
4654
|
}
|
@@ -3251,7 +4701,8 @@ $(function(){
|
|
3251
4701
|
if (currentDate && prevMonth.valueOf() == currentDate) {
|
3252
4702
|
clsName += ' active';
|
3253
4703
|
}
|
3254
|
-
if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate
|
4704
|
+
if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
|
4705
|
+
$.inArray(prevMonth.getUTCDay(), this.daysOfWeekDisabled) !== -1) {
|
3255
4706
|
clsName += ' disabled';
|
3256
4707
|
}
|
3257
4708
|
html.push('<td class="day'+clsName+'">'+prevMonth.getUTCDate() + '</td>');
|
@@ -3392,7 +4843,7 @@ $(function(){
|
|
3392
4843
|
var year = this.viewDate.getUTCFullYear(),
|
3393
4844
|
month = this.viewDate.getUTCMonth();
|
3394
4845
|
if (target.is('.old')) {
|
3395
|
-
if (month
|
4846
|
+
if (month === 0) {
|
3396
4847
|
month = 11;
|
3397
4848
|
year -= 1;
|
3398
4849
|
} else {
|
@@ -3432,8 +4883,8 @@ $(function(){
|
|
3432
4883
|
}
|
3433
4884
|
if (element) {
|
3434
4885
|
element.change();
|
3435
|
-
if (this.autoclose) {
|
3436
|
-
|
4886
|
+
if (this.autoclose && (!which || which == 'date')) {
|
4887
|
+
this.hide();
|
3437
4888
|
}
|
3438
4889
|
}
|
3439
4890
|
},
|
@@ -3579,16 +5030,16 @@ $(function(){
|
|
3579
5030
|
if (dir) {
|
3580
5031
|
this.viewMode = Math.max(0, Math.min(2, this.viewMode + dir));
|
3581
5032
|
}
|
3582
|
-
|
3583
|
-
|
3584
|
-
|
3585
|
-
|
3586
|
-
|
3587
|
-
|
3588
|
-
|
3589
|
-
|
3590
|
-
|
3591
|
-
|
5033
|
+
/*
|
5034
|
+
vitalets: fixing bug of very special conditions:
|
5035
|
+
jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
|
5036
|
+
Method show() does not set display css correctly and datepicker is not shown.
|
5037
|
+
Changed to .css('display', 'block') solve the problem.
|
5038
|
+
See https://github.com/vitalets/x-editable/issues/37
|
5039
|
+
|
5040
|
+
In jquery 1.7.2+ everything works fine.
|
5041
|
+
*/
|
5042
|
+
//this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
|
3592
5043
|
this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
|
3593
5044
|
this.updateNavArrows();
|
3594
5045
|
}
|
@@ -3622,7 +5073,7 @@ $(function(){
|
|
3622
5073
|
monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
|
3623
5074
|
today: "Today"
|
3624
5075
|
}
|
3625
|
-
}
|
5076
|
+
};
|
3626
5077
|
|
3627
5078
|
var DPGlobal = {
|
3628
5079
|
modes: [
|
@@ -3642,28 +5093,28 @@ $(function(){
|
|
3642
5093
|
navStep: 10
|
3643
5094
|
}],
|
3644
5095
|
isLeapYear: function (year) {
|
3645
|
-
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0))
|
5096
|
+
return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
|
3646
5097
|
},
|
3647
5098
|
getDaysInMonth: function (year, month) {
|
3648
|
-
return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month]
|
5099
|
+
return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
|
3649
5100
|
},
|
3650
|
-
validParts: /dd?|mm?|MM?|yy(?:yy)?/g,
|
3651
|
-
nonpunctuation: /[^ -\/:-@\[-`{-~\t\n\r]+/g,
|
5101
|
+
validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
|
5102
|
+
nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
|
3652
5103
|
parseFormat: function(format){
|
3653
5104
|
// IE treats \0 as a string end in inputs (truncating the value),
|
3654
5105
|
// so it's a bad format delimiter, anyway
|
3655
5106
|
var separators = format.replace(this.validParts, '\0').split('\0'),
|
3656
5107
|
parts = format.match(this.validParts);
|
3657
|
-
if (!separators || !separators.length || !parts || parts.length
|
5108
|
+
if (!separators || !separators.length || !parts || parts.length === 0){
|
3658
5109
|
throw new Error("Invalid date format.");
|
3659
5110
|
}
|
3660
5111
|
return {separators: separators, parts: parts};
|
3661
5112
|
},
|
3662
5113
|
parseDate: function(date, format, language) {
|
3663
5114
|
if (date instanceof Date) return date;
|
3664
|
-
if (/^[
|
3665
|
-
var part_re = /([
|
3666
|
-
parts = date.match(/([
|
5115
|
+
if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
|
5116
|
+
var part_re = /([\-+]\d+)([dmwy])/,
|
5117
|
+
parts = date.match(/([\-+]\d+)([dmwy])/g),
|
3667
5118
|
part, dir;
|
3668
5119
|
date = new Date();
|
3669
5120
|
for (var i=0; i<parts.length; i++) {
|
@@ -3708,10 +5159,18 @@ $(function(){
|
|
3708
5159
|
setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
|
3709
5160
|
setters_map['dd'] = setters_map['d'];
|
3710
5161
|
date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
|
3711
|
-
|
3712
|
-
|
5162
|
+
var fparts = format.parts.slice();
|
5163
|
+
// Remove noop parts
|
5164
|
+
if (parts.length != fparts.length) {
|
5165
|
+
fparts = $(fparts).filter(function(i,p){
|
5166
|
+
return $.inArray(p, setters_order) !== -1;
|
5167
|
+
}).toArray();
|
5168
|
+
}
|
5169
|
+
// Process remainder
|
5170
|
+
if (parts.length == fparts.length) {
|
5171
|
+
for (var i=0, cnt = fparts.length; i < cnt; i++) {
|
3713
5172
|
val = parseInt(parts[i], 10);
|
3714
|
-
part =
|
5173
|
+
part = fparts[i];
|
3715
5174
|
if (isNaN(val)) {
|
3716
5175
|
switch(part) {
|
3717
5176
|
case 'MM':
|
@@ -3736,8 +5195,8 @@ $(function(){
|
|
3736
5195
|
}
|
3737
5196
|
for (var i=0, s; i<setters_order.length; i++){
|
3738
5197
|
s = setters_order[i];
|
3739
|
-
if (s in parsed)
|
3740
|
-
setters_map[s](date, parsed[s])
|
5198
|
+
if (s in parsed && !isNaN(parsed[s]))
|
5199
|
+
setters_map[s](date, parsed[s]);
|
3741
5200
|
}
|
3742
5201
|
}
|
3743
5202
|
return date;
|
@@ -3745,6 +5204,8 @@ $(function(){
|
|
3745
5204
|
formatDate: function(date, format, language){
|
3746
5205
|
var val = {
|
3747
5206
|
d: date.getUTCDate(),
|
5207
|
+
D: dates[language].daysShort[date.getUTCDay()],
|
5208
|
+
DD: dates[language].days[date.getUTCDay()],
|
3748
5209
|
m: date.getUTCMonth() + 1,
|
3749
5210
|
M: dates[language].monthsShort[date.getUTCMonth()],
|
3750
5211
|
MM: dates[language].months[date.getUTCMonth()],
|
@@ -3757,7 +5218,7 @@ $(function(){
|
|
3757
5218
|
seps = $.extend([], format.separators);
|
3758
5219
|
for (var i=0, cnt = format.parts.length; i < cnt; i++) {
|
3759
5220
|
if (seps.length)
|
3760
|
-
date.push(seps.shift())
|
5221
|
+
date.push(seps.shift());
|
3761
5222
|
date.push(val[format.parts[i]]);
|
3762
5223
|
}
|
3763
5224
|
return date.join('');
|
@@ -3795,7 +5256,229 @@ $(function(){
|
|
3795
5256
|
'</table>'+
|
3796
5257
|
'</div>'+
|
3797
5258
|
'</div>';
|
3798
|
-
|
3799
|
-
|
3800
|
-
|
5259
|
+
|
5260
|
+
$.fn.datepicker.DPGlobal = DPGlobal;
|
5261
|
+
|
3801
5262
|
}( window.jQuery );
|
5263
|
+
|
5264
|
+
/**
|
5265
|
+
Typeahead input (bootstrap only). Based on Twitter Bootstrap [typeahead](http://twitter.github.com/bootstrap/javascript.html#typeahead).
|
5266
|
+
Depending on `source` format typeahead operates in two modes:
|
5267
|
+
|
5268
|
+
* **strings**:
|
5269
|
+
When `source` defined as array of strings, e.g. `['text1', 'text2', 'text3' ...]`.
|
5270
|
+
User can submit one of these strings or any text entered in input (even if it is not matching source).
|
5271
|
+
|
5272
|
+
* **objects**:
|
5273
|
+
When `source` defined as array of objects, e.g. `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`.
|
5274
|
+
User can submit only values that are in source (otherwise `null` is submitted). This is more like *dropdown* behavior.
|
5275
|
+
|
5276
|
+
@class typeahead
|
5277
|
+
@extends list
|
5278
|
+
@since 1.4.1
|
5279
|
+
@final
|
5280
|
+
@example
|
5281
|
+
<a href="#" id="country" data-type="typeahead" data-pk="1" data-url="/post" data-original-title="Input country"></a>
|
5282
|
+
<script>
|
5283
|
+
$(function(){
|
5284
|
+
$('#country').editable({
|
5285
|
+
value: 'ru',
|
5286
|
+
source: [
|
5287
|
+
{value: 'gb', text: 'Great Britain'},
|
5288
|
+
{value: 'us', text: 'United States'},
|
5289
|
+
{value: 'ru', text: 'Russia'}
|
5290
|
+
]
|
5291
|
+
}
|
5292
|
+
});
|
5293
|
+
});
|
5294
|
+
</script>
|
5295
|
+
**/
|
5296
|
+
(function ($) {
|
5297
|
+
|
5298
|
+
var Constructor = function (options) {
|
5299
|
+
this.init('typeahead', options, Constructor.defaults);
|
5300
|
+
|
5301
|
+
//overriding objects in config (as by default jQuery extend() is not recursive)
|
5302
|
+
this.options.typeahead = $.extend({}, Constructor.defaults.typeahead, {
|
5303
|
+
//set default methods for typeahead to work with objects
|
5304
|
+
matcher: this.matcher,
|
5305
|
+
sorter: this.sorter,
|
5306
|
+
highlighter: this.highlighter,
|
5307
|
+
updater: this.updater
|
5308
|
+
}, options.typeahead);
|
5309
|
+
};
|
5310
|
+
|
5311
|
+
$.fn.editableutils.inherit(Constructor, $.fn.editabletypes.list);
|
5312
|
+
|
5313
|
+
$.extend(Constructor.prototype, {
|
5314
|
+
renderList: function() {
|
5315
|
+
this.$input = this.$tpl.is('input') ? this.$tpl : this.$tpl.find('input[type="text"]');
|
5316
|
+
|
5317
|
+
//set source of typeahead
|
5318
|
+
this.options.typeahead.source = this.sourceData;
|
5319
|
+
|
5320
|
+
//apply typeahead
|
5321
|
+
this.$input.typeahead(this.options.typeahead);
|
5322
|
+
|
5323
|
+
//attach own render method
|
5324
|
+
this.$input.data('typeahead').render = $.proxy(this.typeaheadRender, this.$input.data('typeahead'));
|
5325
|
+
|
5326
|
+
this.renderClear();
|
5327
|
+
this.setClass();
|
5328
|
+
this.setAttr('placeholder');
|
5329
|
+
},
|
5330
|
+
|
5331
|
+
value2htmlFinal: function(value, element) {
|
5332
|
+
if(this.getIsObjects()) {
|
5333
|
+
var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
|
5334
|
+
$(element).text(items.length ? items[0].text : '');
|
5335
|
+
} else {
|
5336
|
+
$(element).text(value);
|
5337
|
+
}
|
5338
|
+
},
|
5339
|
+
|
5340
|
+
html2value: function (html) {
|
5341
|
+
return html ? html : null;
|
5342
|
+
},
|
5343
|
+
|
5344
|
+
value2input: function(value) {
|
5345
|
+
if(this.getIsObjects()) {
|
5346
|
+
var items = $.fn.editableutils.itemsByValue(value, this.sourceData);
|
5347
|
+
this.$input.data('value', value).val(items.length ? items[0].text : '');
|
5348
|
+
} else {
|
5349
|
+
this.$input.val(value);
|
5350
|
+
}
|
5351
|
+
},
|
5352
|
+
|
5353
|
+
input2value: function() {
|
5354
|
+
if(this.getIsObjects()) {
|
5355
|
+
var value = this.$input.data('value'),
|
5356
|
+
items = $.fn.editableutils.itemsByValue(value, this.sourceData);
|
5357
|
+
|
5358
|
+
if(items.length && items[0].text.toLowerCase() === this.$input.val().toLowerCase()) {
|
5359
|
+
return value;
|
5360
|
+
} else {
|
5361
|
+
return null; //entered string not found in source
|
5362
|
+
}
|
5363
|
+
} else {
|
5364
|
+
return this.$input.val();
|
5365
|
+
}
|
5366
|
+
},
|
5367
|
+
|
5368
|
+
/*
|
5369
|
+
if in sourceData values <> texts, typeahead in "objects" mode:
|
5370
|
+
user must pick some value from list, otherwise `null` returned.
|
5371
|
+
if all values == texts put typeahead in "strings" mode:
|
5372
|
+
anything what entered is submited.
|
5373
|
+
*/
|
5374
|
+
getIsObjects: function() {
|
5375
|
+
if(this.isObjects === undefined) {
|
5376
|
+
this.isObjects = false;
|
5377
|
+
for(var i=0; i<this.sourceData.length; i++) {
|
5378
|
+
if(this.sourceData[i].value !== this.sourceData[i].text) {
|
5379
|
+
this.isObjects = true;
|
5380
|
+
break;
|
5381
|
+
}
|
5382
|
+
}
|
5383
|
+
}
|
5384
|
+
return this.isObjects;
|
5385
|
+
},
|
5386
|
+
|
5387
|
+
/*
|
5388
|
+
Methods borrowed from text input
|
5389
|
+
*/
|
5390
|
+
activate: $.fn.editabletypes.text.prototype.activate,
|
5391
|
+
renderClear: $.fn.editabletypes.text.prototype.renderClear,
|
5392
|
+
postrender: $.fn.editabletypes.text.prototype.postrender,
|
5393
|
+
toggleClear: $.fn.editabletypes.text.prototype.toggleClear,
|
5394
|
+
clear: function() {
|
5395
|
+
$.fn.editabletypes.text.prototype.clear.call(this);
|
5396
|
+
this.$input.data('value', '');
|
5397
|
+
},
|
5398
|
+
|
5399
|
+
|
5400
|
+
/*
|
5401
|
+
Typeahead option methods used as defaults
|
5402
|
+
*/
|
5403
|
+
/*jshint eqeqeq:false, curly: false, laxcomma: true*/
|
5404
|
+
matcher: function (item) {
|
5405
|
+
return $.fn.typeahead.Constructor.prototype.matcher.call(this, item.text);
|
5406
|
+
},
|
5407
|
+
sorter: function (items) {
|
5408
|
+
var beginswith = []
|
5409
|
+
, caseSensitive = []
|
5410
|
+
, caseInsensitive = []
|
5411
|
+
, item
|
5412
|
+
, text;
|
5413
|
+
|
5414
|
+
while (item = items.shift()) {
|
5415
|
+
text = item.text;
|
5416
|
+
if (!text.toLowerCase().indexOf(this.query.toLowerCase())) beginswith.push(item);
|
5417
|
+
else if (~text.indexOf(this.query)) caseSensitive.push(item);
|
5418
|
+
else caseInsensitive.push(item);
|
5419
|
+
}
|
5420
|
+
|
5421
|
+
return beginswith.concat(caseSensitive, caseInsensitive);
|
5422
|
+
},
|
5423
|
+
highlighter: function (item) {
|
5424
|
+
return $.fn.typeahead.Constructor.prototype.highlighter.call(this, item.text);
|
5425
|
+
},
|
5426
|
+
updater: function (item) {
|
5427
|
+
item = this.$menu.find('.active').data('item');
|
5428
|
+
this.$element.data('value', item.value);
|
5429
|
+
return item.text;
|
5430
|
+
},
|
5431
|
+
|
5432
|
+
|
5433
|
+
/*
|
5434
|
+
Overwrite typeahead's render method to store objects.
|
5435
|
+
There are a lot of disscussion in bootstrap repo on this point and still no result.
|
5436
|
+
See https://github.com/twitter/bootstrap/issues/5967
|
5437
|
+
|
5438
|
+
This function just store item in via jQuery data() method instead of attr('data-value')
|
5439
|
+
*/
|
5440
|
+
typeaheadRender: function (items) {
|
5441
|
+
var that = this;
|
5442
|
+
|
5443
|
+
items = $(items).map(function (i, item) {
|
5444
|
+
// i = $(that.options.item).attr('data-value', item)
|
5445
|
+
i = $(that.options.item).data('item', item);
|
5446
|
+
i.find('a').html(that.highlighter(item));
|
5447
|
+
return i[0];
|
5448
|
+
});
|
5449
|
+
|
5450
|
+
items.first().addClass('active');
|
5451
|
+
this.$menu.html(items);
|
5452
|
+
return this;
|
5453
|
+
}
|
5454
|
+
/*jshint eqeqeq: true, curly: true, laxcomma: false*/
|
5455
|
+
|
5456
|
+
});
|
5457
|
+
|
5458
|
+
Constructor.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
|
5459
|
+
/**
|
5460
|
+
@property tpl
|
5461
|
+
@default <input type="text">
|
5462
|
+
**/
|
5463
|
+
tpl:'<input type="text">',
|
5464
|
+
/**
|
5465
|
+
Configuration of typeahead. [Full list of options](http://twitter.github.com/bootstrap/javascript.html#typeahead).
|
5466
|
+
|
5467
|
+
@property typeahead
|
5468
|
+
@type object
|
5469
|
+
@default null
|
5470
|
+
**/
|
5471
|
+
typeahead: null,
|
5472
|
+
/**
|
5473
|
+
Whether to show `clear` button
|
5474
|
+
|
5475
|
+
@property clear
|
5476
|
+
@type boolean
|
5477
|
+
@default true
|
5478
|
+
**/
|
5479
|
+
clear: true
|
5480
|
+
});
|
5481
|
+
|
5482
|
+
$.fn.editabletypes.typeahead = Constructor;
|
5483
|
+
|
5484
|
+
}(window.jQuery));
|