bootstrap-editable-rails 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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));
|