bootstrap-tagsinput-rails 0.3.2.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -0
- data/lib/bootstrap/tagsinput/rails/version.rb +1 -1
- data/vendor/assets/javascripts/bootstrap-tagsinput-angular.js +87 -80
- data/vendor/assets/javascripts/bootstrap-tagsinput-angular.min.js +7 -0
- data/vendor/assets/javascripts/bootstrap-tagsinput.js +617 -405
- data/vendor/assets/javascripts/bootstrap-tagsinput.min.js +6 -7
- data/vendor/assets/javascripts/bootstrap-tagsinput.min.js.map +1 -1
- data/vendor/assets/stylesheets/bootstrap-tagsinput.css +3 -1
- data/vendor/assets/stylesheets/{bootstrap-tagsinput.less → bootstrap-tagsinput.scss} +50 -48
- metadata +16 -16
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1f6fe08284f7aba9ccc3149d99de703f7c805714
|
|
4
|
+
data.tar.gz: e3430b9c3fcc1895e2eea24dfe7bd5abc3f869fa
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 4f9f0d6036feb6f1a6cd435bc36474fac13313668d88dcb8b6aafdf8179433385b8bbd5a0d68ef77268c1a03402349f3e3fdbeb67a611d8c8edfc84146c2d41c
|
|
7
|
+
data.tar.gz: ab28e5b5518897f9a6c1f26099fc709230d4d216e4935b74ff988cd330c27d8638fcc616486c99dbbb3ba28c3ae0387c6caf1da4080fdb1d419bfd1bb015d8b2
|
data/README.md
CHANGED
|
@@ -4,6 +4,8 @@ Original Git source - https://github.com/timschlechter/bootstrap-tagsinput
|
|
|
4
4
|
|
|
5
5
|
To gemify the assets of `bootstrap-tagsinput` jQuery plugin for Rails >= 3.1
|
|
6
6
|
|
|
7
|
+
[](http://badge.fury.io/rb/bootstrap-tagsinput-rails)
|
|
8
|
+
|
|
7
9
|
## Compatibility
|
|
8
10
|
|
|
9
11
|
Designed for Bootstrap 2.3.2 and 3
|
|
@@ -42,6 +44,13 @@ in form view, you should add `data-role='tagsinput'` within input tag as the fol
|
|
|
42
44
|
<%= f.input :tag_list, input_html:{data:{role:'tagsinput'}} %>
|
|
43
45
|
```
|
|
44
46
|
|
|
47
|
+
Or if using Rails 4 with Bootstrap, use the following,
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
<%= f.text_field :tag_list, 'data-role'=>'tagsinput' %>
|
|
51
|
+
|
|
52
|
+
```
|
|
53
|
+
|
|
45
54
|
That's it
|
|
46
55
|
|
|
47
56
|
## Contributing
|
|
@@ -1,80 +1,87 @@
|
|
|
1
|
-
angular.module('bootstrap-tagsinput', [])
|
|
2
|
-
.directive('bootstrapTagsinput', [function() {
|
|
3
|
-
|
|
4
|
-
function getItemProperty(scope, property) {
|
|
5
|
-
if (!property)
|
|
6
|
-
return undefined;
|
|
7
|
-
|
|
8
|
-
if (angular.isFunction(scope.$parent[property]))
|
|
9
|
-
return scope.$parent[property];
|
|
10
|
-
|
|
11
|
-
return function(item) {
|
|
12
|
-
return item[property];
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
return {
|
|
17
|
-
restrict: 'EA',
|
|
18
|
-
scope: {
|
|
19
|
-
model: '=ngModel'
|
|
20
|
-
},
|
|
21
|
-
template: '<select multiple></select>',
|
|
22
|
-
replace: false,
|
|
23
|
-
link: function(scope, element, attrs) {
|
|
24
|
-
$(function() {
|
|
25
|
-
if (!angular.isArray(scope.model))
|
|
26
|
-
scope.model = [];
|
|
27
|
-
|
|
28
|
-
var select = $('select', element);
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
1
|
+
angular.module('bootstrap-tagsinput', [])
|
|
2
|
+
.directive('bootstrapTagsinput', [function() {
|
|
3
|
+
|
|
4
|
+
function getItemProperty(scope, property) {
|
|
5
|
+
if (!property)
|
|
6
|
+
return undefined;
|
|
7
|
+
|
|
8
|
+
if (angular.isFunction(scope.$parent[property]))
|
|
9
|
+
return scope.$parent[property];
|
|
10
|
+
|
|
11
|
+
return function(item) {
|
|
12
|
+
return item[property];
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return {
|
|
17
|
+
restrict: 'EA',
|
|
18
|
+
scope: {
|
|
19
|
+
model: '=ngModel'
|
|
20
|
+
},
|
|
21
|
+
template: '<select multiple></select>',
|
|
22
|
+
replace: false,
|
|
23
|
+
link: function(scope, element, attrs) {
|
|
24
|
+
$(function() {
|
|
25
|
+
if (!angular.isArray(scope.model))
|
|
26
|
+
scope.model = [];
|
|
27
|
+
|
|
28
|
+
var select = $('select', element);
|
|
29
|
+
var typeaheadSourceArray = attrs.typeaheadSource ? attrs.typeaheadSource.split('.') : null;
|
|
30
|
+
var typeaheadSource = typeaheadSourceArray ?
|
|
31
|
+
(typeaheadSourceArray.length > 1 ?
|
|
32
|
+
scope.$parent[typeaheadSourceArray[0]][typeaheadSourceArray[1]]
|
|
33
|
+
: scope.$parent[typeaheadSourceArray[0]])
|
|
34
|
+
: null;
|
|
35
|
+
|
|
36
|
+
select.tagsinput(scope.$parent[attrs.options || ''] || {
|
|
37
|
+
typeahead : {
|
|
38
|
+
source : angular.isFunction(typeaheadSource) ? typeaheadSource : null
|
|
39
|
+
},
|
|
40
|
+
itemValue: getItemProperty(scope, attrs.itemvalue),
|
|
41
|
+
itemText : getItemProperty(scope, attrs.itemtext),
|
|
42
|
+
confirmKeys : getItemProperty(scope, attrs.confirmkeys) ? JSON.parse(attrs.confirmkeys) : [13],
|
|
43
|
+
tagClass : angular.isFunction(scope.$parent[attrs.tagclass]) ? scope.$parent[attrs.tagclass] : function(item) { return attrs.tagclass; }
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
for (var i = 0; i < scope.model.length; i++) {
|
|
47
|
+
select.tagsinput('add', scope.model[i]);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
select.on('itemAdded', function(event) {
|
|
51
|
+
if (scope.model.indexOf(event.item) === -1)
|
|
52
|
+
scope.model.push(event.item);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
select.on('itemRemoved', function(event) {
|
|
56
|
+
var idx = scope.model.indexOf(event.item);
|
|
57
|
+
if (idx !== -1)
|
|
58
|
+
scope.model.splice(idx, 1);
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
// create a shallow copy of model's current state, needed to determine
|
|
62
|
+
// diff when model changes
|
|
63
|
+
var prev = scope.model.slice();
|
|
64
|
+
scope.$watch("model", function() {
|
|
65
|
+
var added = scope.model.filter(function(i) {return prev.indexOf(i) === -1;}),
|
|
66
|
+
removed = prev.filter(function(i) {return scope.model.indexOf(i) === -1;}),
|
|
67
|
+
i;
|
|
68
|
+
|
|
69
|
+
prev = scope.model.slice();
|
|
70
|
+
|
|
71
|
+
// Remove tags no longer in binded model
|
|
72
|
+
for (i = 0; i < removed.length; i++) {
|
|
73
|
+
select.tagsinput('remove', removed[i]);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Refresh remaining tags
|
|
77
|
+
select.tagsinput('refresh');
|
|
78
|
+
|
|
79
|
+
// Add new items in model as tags
|
|
80
|
+
for (i = 0; i < added.length; i++) {
|
|
81
|
+
select.tagsinput('add', added[i]);
|
|
82
|
+
}
|
|
83
|
+
}, true);
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
}]);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* bootstrap-tagsinput v0.4.2 by Tim Schlechter
|
|
3
|
+
*
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
angular.module("bootstrap-tagsinput",[]).directive("bootstrapTagsinput",[function(){function a(a,b){return b?angular.isFunction(a.$parent[b])?a.$parent[b]:function(a){return a[b]}:void 0}return{restrict:"EA",scope:{model:"=ngModel"},template:"<select multiple></select>",replace:!1,link:function(b,c,d){$(function(){angular.isArray(b.model)||(b.model=[]);var e=$("select",c),f=d.typeaheadSource?d.typeaheadSource.split("."):null,g=f?f.length>1?b.$parent[f[0]][f[1]]:b.$parent[f[0]]:null;e.tagsinput(b.$parent[d.options||""]||{typeahead:{source:angular.isFunction(g)?g:null},itemValue:a(b,d.itemvalue),itemText:a(b,d.itemtext),confirmKeys:a(b,d.confirmkeys)?JSON.parse(d.confirmkeys):[13],tagClass:angular.isFunction(b.$parent[d.tagclass])?b.$parent[d.tagclass]:function(){return d.tagclass}});for(var h=0;h<b.model.length;h++)e.tagsinput("add",b.model[h]);e.on("itemAdded",function(a){-1===b.model.indexOf(a.item)&&b.model.push(a.item)}),e.on("itemRemoved",function(a){var c=b.model.indexOf(a.item);-1!==c&&b.model.splice(c,1)});var i=b.model.slice();b.$watch("model",function(){var a,c=b.model.filter(function(a){return-1===i.indexOf(a)}),d=i.filter(function(a){return-1===b.model.indexOf(a)});for(i=b.model.slice(),a=0;a<d.length;a++)e.tagsinput("remove",d[a]);for(e.tagsinput("refresh"),a=0;a<c.length;a++)e.tagsinput("add",c[a])},!0)})}}}]);
|
|
7
|
+
//# sourceMappingURL=bootstrap-tagsinput.min.js.map
|
|
@@ -1,405 +1,617 @@
|
|
|
1
|
-
(function ($) {
|
|
2
|
-
"use strict";
|
|
3
|
-
|
|
4
|
-
var defaultOptions = {
|
|
5
|
-
tagClass: function(item) {
|
|
6
|
-
return 'label label-info';
|
|
7
|
-
},
|
|
8
|
-
itemValue: function(item) {
|
|
9
|
-
return item ? item.toString() : item;
|
|
10
|
-
},
|
|
11
|
-
itemText: function(item) {
|
|
12
|
-
return this.itemValue(item);
|
|
13
|
-
},
|
|
14
|
-
freeInput
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
this
|
|
31
|
-
|
|
32
|
-
this
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
//
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
self.$element.trigger(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
self
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
})
|
|
1
|
+
(function ($) {
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
var defaultOptions = {
|
|
5
|
+
tagClass: function(item) {
|
|
6
|
+
return 'label label-info';
|
|
7
|
+
},
|
|
8
|
+
itemValue: function(item) {
|
|
9
|
+
return item ? item.toString() : item;
|
|
10
|
+
},
|
|
11
|
+
itemText: function(item) {
|
|
12
|
+
return this.itemValue(item);
|
|
13
|
+
},
|
|
14
|
+
freeInput: true,
|
|
15
|
+
addOnBlur: true,
|
|
16
|
+
maxTags: undefined,
|
|
17
|
+
maxChars: undefined,
|
|
18
|
+
confirmKeys: [13, 44],
|
|
19
|
+
onTagExists: function(item, $tag) {
|
|
20
|
+
$tag.hide().fadeIn();
|
|
21
|
+
},
|
|
22
|
+
trimValue: false,
|
|
23
|
+
allowDuplicates: false
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Constructor function
|
|
28
|
+
*/
|
|
29
|
+
function TagsInput(element, options) {
|
|
30
|
+
this.itemsArray = [];
|
|
31
|
+
|
|
32
|
+
this.$element = $(element);
|
|
33
|
+
this.$element.hide();
|
|
34
|
+
|
|
35
|
+
this.isSelect = (element.tagName === 'SELECT');
|
|
36
|
+
this.multiple = (this.isSelect && element.hasAttribute('multiple'));
|
|
37
|
+
this.objectItems = options && options.itemValue;
|
|
38
|
+
this.placeholderText = element.hasAttribute('placeholder') ? this.$element.attr('placeholder') : '';
|
|
39
|
+
this.inputSize = Math.max(1, this.placeholderText.length);
|
|
40
|
+
|
|
41
|
+
this.$container = $('<div class="bootstrap-tagsinput"></div>');
|
|
42
|
+
this.$input = $('<input type="text" placeholder="' + this.placeholderText + '"/>').appendTo(this.$container);
|
|
43
|
+
|
|
44
|
+
this.$element.after(this.$container);
|
|
45
|
+
|
|
46
|
+
var inputWidth = (this.inputSize < 3 ? 3 : this.inputSize) + "em";
|
|
47
|
+
this.$input.get(0).style.cssText = "width: " + inputWidth + " !important;";
|
|
48
|
+
this.build(options);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
TagsInput.prototype = {
|
|
52
|
+
constructor: TagsInput,
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Adds the given item as a new tag. Pass true to dontPushVal to prevent
|
|
56
|
+
* updating the elements val()
|
|
57
|
+
*/
|
|
58
|
+
add: function(item, dontPushVal) {
|
|
59
|
+
var self = this;
|
|
60
|
+
|
|
61
|
+
if (self.options.maxTags && self.itemsArray.length >= self.options.maxTags)
|
|
62
|
+
return;
|
|
63
|
+
|
|
64
|
+
// Ignore falsey values, except false
|
|
65
|
+
if (item !== false && !item)
|
|
66
|
+
return;
|
|
67
|
+
|
|
68
|
+
// Trim value
|
|
69
|
+
if (typeof item === "string" && self.options.trimValue) {
|
|
70
|
+
item = $.trim(item);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Throw an error when trying to add an object while the itemValue option was not set
|
|
74
|
+
if (typeof item === "object" && !self.objectItems)
|
|
75
|
+
throw("Can't add objects when itemValue option is not set");
|
|
76
|
+
|
|
77
|
+
// Ignore strings only containg whitespace
|
|
78
|
+
if (item.toString().match(/^\s*$/))
|
|
79
|
+
return;
|
|
80
|
+
|
|
81
|
+
// If SELECT but not multiple, remove current tag
|
|
82
|
+
if (self.isSelect && !self.multiple && self.itemsArray.length > 0)
|
|
83
|
+
self.remove(self.itemsArray[0]);
|
|
84
|
+
|
|
85
|
+
if (typeof item === "string" && this.$element[0].tagName === 'INPUT') {
|
|
86
|
+
var items = item.split(',');
|
|
87
|
+
if (items.length > 1) {
|
|
88
|
+
for (var i = 0; i < items.length; i++) {
|
|
89
|
+
this.add(items[i], true);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!dontPushVal)
|
|
93
|
+
self.pushVal();
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
var itemValue = self.options.itemValue(item),
|
|
99
|
+
itemText = self.options.itemText(item),
|
|
100
|
+
tagClass = self.options.tagClass(item);
|
|
101
|
+
|
|
102
|
+
// Ignore items allready added
|
|
103
|
+
var existing = $.grep(self.itemsArray, function(item) { return self.options.itemValue(item) === itemValue; } )[0];
|
|
104
|
+
if (existing && !self.options.allowDuplicates) {
|
|
105
|
+
// Invoke onTagExists
|
|
106
|
+
if (self.options.onTagExists) {
|
|
107
|
+
var $existingTag = $(".tag", self.$container).filter(function() { return $(this).data("item") === existing; });
|
|
108
|
+
self.options.onTagExists(item, $existingTag);
|
|
109
|
+
}
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// if length greater than limit
|
|
114
|
+
if (self.items().toString().length + item.length + 1 > self.options.maxInputLength)
|
|
115
|
+
return;
|
|
116
|
+
|
|
117
|
+
// raise beforeItemAdd arg
|
|
118
|
+
var beforeItemAddEvent = $.Event('beforeItemAdd', { item: item, cancel: false });
|
|
119
|
+
self.$element.trigger(beforeItemAddEvent);
|
|
120
|
+
if (beforeItemAddEvent.cancel)
|
|
121
|
+
return;
|
|
122
|
+
|
|
123
|
+
// register item in internal array and map
|
|
124
|
+
self.itemsArray.push(item);
|
|
125
|
+
|
|
126
|
+
// add a tag element
|
|
127
|
+
var $tag = $('<span class="tag ' + htmlEncode(tagClass) + '">' + htmlEncode(itemText) + '<span data-role="remove"></span></span>');
|
|
128
|
+
$tag.data('item', item);
|
|
129
|
+
self.findInputWrapper().before($tag);
|
|
130
|
+
$tag.after(' ');
|
|
131
|
+
|
|
132
|
+
// add <option /> if item represents a value not present in one of the <select />'s options
|
|
133
|
+
if (self.isSelect && !$('option[value="' + encodeURIComponent(itemValue) + '"]',self.$element)[0]) {
|
|
134
|
+
var $option = $('<option selected>' + htmlEncode(itemText) + '</option>');
|
|
135
|
+
$option.data('item', item);
|
|
136
|
+
$option.attr('value', itemValue);
|
|
137
|
+
self.$element.append($option);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (!dontPushVal)
|
|
141
|
+
self.pushVal();
|
|
142
|
+
|
|
143
|
+
// Add class when reached maxTags
|
|
144
|
+
if (self.options.maxTags === self.itemsArray.length || self.items().toString().length === self.options.maxInputLength)
|
|
145
|
+
self.$container.addClass('bootstrap-tagsinput-max');
|
|
146
|
+
|
|
147
|
+
self.$element.trigger($.Event('itemAdded', { item: item }));
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Removes the given item. Pass true to dontPushVal to prevent updating the
|
|
152
|
+
* elements val()
|
|
153
|
+
*/
|
|
154
|
+
remove: function(item, dontPushVal) {
|
|
155
|
+
var self = this;
|
|
156
|
+
|
|
157
|
+
if (self.objectItems) {
|
|
158
|
+
if (typeof item === "object")
|
|
159
|
+
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == self.options.itemValue(item); } );
|
|
160
|
+
else
|
|
161
|
+
item = $.grep(self.itemsArray, function(other) { return self.options.itemValue(other) == item; } );
|
|
162
|
+
|
|
163
|
+
item = item[item.length-1];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (item) {
|
|
167
|
+
var beforeItemRemoveEvent = $.Event('beforeItemRemove', { item: item, cancel: false });
|
|
168
|
+
self.$element.trigger(beforeItemRemoveEvent);
|
|
169
|
+
if (beforeItemRemoveEvent.cancel)
|
|
170
|
+
return;
|
|
171
|
+
|
|
172
|
+
$('.tag', self.$container).filter(function() { return $(this).data('item') === item; }).remove();
|
|
173
|
+
$('option', self.$element).filter(function() { return $(this).data('item') === item; }).remove();
|
|
174
|
+
if($.inArray(item, self.itemsArray) !== -1)
|
|
175
|
+
self.itemsArray.splice($.inArray(item, self.itemsArray), 1);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (!dontPushVal)
|
|
179
|
+
self.pushVal();
|
|
180
|
+
|
|
181
|
+
// Remove class when reached maxTags
|
|
182
|
+
if (self.options.maxTags > self.itemsArray.length)
|
|
183
|
+
self.$container.removeClass('bootstrap-tagsinput-max');
|
|
184
|
+
|
|
185
|
+
self.$element.trigger($.Event('itemRemoved', { item: item }));
|
|
186
|
+
},
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Removes all items
|
|
190
|
+
*/
|
|
191
|
+
removeAll: function() {
|
|
192
|
+
var self = this;
|
|
193
|
+
|
|
194
|
+
$('.tag', self.$container).remove();
|
|
195
|
+
$('option', self.$element).remove();
|
|
196
|
+
|
|
197
|
+
while(self.itemsArray.length > 0)
|
|
198
|
+
self.itemsArray.pop();
|
|
199
|
+
|
|
200
|
+
self.pushVal();
|
|
201
|
+
},
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Refreshes the tags so they match the text/value of their corresponding
|
|
205
|
+
* item.
|
|
206
|
+
*/
|
|
207
|
+
refresh: function() {
|
|
208
|
+
var self = this;
|
|
209
|
+
$('.tag', self.$container).each(function() {
|
|
210
|
+
var $tag = $(this),
|
|
211
|
+
item = $tag.data('item'),
|
|
212
|
+
itemValue = self.options.itemValue(item),
|
|
213
|
+
itemText = self.options.itemText(item),
|
|
214
|
+
tagClass = self.options.tagClass(item);
|
|
215
|
+
|
|
216
|
+
// Update tag's class and inner text
|
|
217
|
+
$tag.attr('class', null);
|
|
218
|
+
$tag.addClass('tag ' + htmlEncode(tagClass));
|
|
219
|
+
$tag.contents().filter(function() {
|
|
220
|
+
return this.nodeType == 3;
|
|
221
|
+
})[0].nodeValue = htmlEncode(itemText);
|
|
222
|
+
|
|
223
|
+
if (self.isSelect) {
|
|
224
|
+
var option = $('option', self.$element).filter(function() { return $(this).data('item') === item; });
|
|
225
|
+
option.attr('value', itemValue);
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
},
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Returns the items added as tags
|
|
232
|
+
*/
|
|
233
|
+
items: function() {
|
|
234
|
+
return this.itemsArray;
|
|
235
|
+
},
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Assembly value by retrieving the value of each item, and set it on the
|
|
239
|
+
* element.
|
|
240
|
+
*/
|
|
241
|
+
pushVal: function() {
|
|
242
|
+
var self = this,
|
|
243
|
+
val = $.map(self.items(), function(item) {
|
|
244
|
+
return self.options.itemValue(item).toString();
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
self.$element.val(val, true).trigger('change');
|
|
248
|
+
},
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Initializes the tags input behaviour on the element
|
|
252
|
+
*/
|
|
253
|
+
build: function(options) {
|
|
254
|
+
var self = this;
|
|
255
|
+
|
|
256
|
+
self.options = $.extend({}, defaultOptions, options);
|
|
257
|
+
// When itemValue is set, freeInput should always be false
|
|
258
|
+
if (self.objectItems)
|
|
259
|
+
self.options.freeInput = false;
|
|
260
|
+
|
|
261
|
+
makeOptionItemFunction(self.options, 'itemValue');
|
|
262
|
+
makeOptionItemFunction(self.options, 'itemText');
|
|
263
|
+
makeOptionFunction(self.options, 'tagClass');
|
|
264
|
+
|
|
265
|
+
// Typeahead Bootstrap version 2.3.2
|
|
266
|
+
if (self.options.typeahead) {
|
|
267
|
+
var typeahead = self.options.typeahead || {};
|
|
268
|
+
|
|
269
|
+
makeOptionFunction(typeahead, 'source');
|
|
270
|
+
|
|
271
|
+
self.$input.typeahead($.extend({}, typeahead, {
|
|
272
|
+
source: function (query, process) {
|
|
273
|
+
function processItems(items) {
|
|
274
|
+
var texts = [];
|
|
275
|
+
|
|
276
|
+
for (var i = 0; i < items.length; i++) {
|
|
277
|
+
var text = self.options.itemText(items[i]);
|
|
278
|
+
map[text] = items[i];
|
|
279
|
+
texts.push(text);
|
|
280
|
+
}
|
|
281
|
+
process(texts);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
this.map = {};
|
|
285
|
+
var map = this.map,
|
|
286
|
+
data = typeahead.source(query);
|
|
287
|
+
|
|
288
|
+
if ($.isFunction(data.success)) {
|
|
289
|
+
// support for Angular callbacks
|
|
290
|
+
data.success(processItems);
|
|
291
|
+
} else if ($.isFunction(data.then)) {
|
|
292
|
+
// support for Angular promises
|
|
293
|
+
data.then(processItems);
|
|
294
|
+
} else {
|
|
295
|
+
// support for functions and jquery promises
|
|
296
|
+
$.when(data)
|
|
297
|
+
.then(processItems);
|
|
298
|
+
}
|
|
299
|
+
},
|
|
300
|
+
updater: function (text) {
|
|
301
|
+
self.add(this.map[text]);
|
|
302
|
+
},
|
|
303
|
+
matcher: function (text) {
|
|
304
|
+
return (text.toLowerCase().indexOf(this.query.trim().toLowerCase()) !== -1);
|
|
305
|
+
},
|
|
306
|
+
sorter: function (texts) {
|
|
307
|
+
return texts.sort();
|
|
308
|
+
},
|
|
309
|
+
highlighter: function (text) {
|
|
310
|
+
var regex = new RegExp( '(' + this.query + ')', 'gi' );
|
|
311
|
+
return text.replace( regex, "<strong>$1</strong>" );
|
|
312
|
+
}
|
|
313
|
+
}));
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// typeahead.js
|
|
317
|
+
if (self.options.typeaheadjs) {
|
|
318
|
+
var typeaheadjs = self.options.typeaheadjs || {};
|
|
319
|
+
|
|
320
|
+
self.$input.typeahead(null, typeaheadjs).on('typeahead:selected', $.proxy(function (obj, datum) {
|
|
321
|
+
if (typeaheadjs.valueKey)
|
|
322
|
+
self.add(datum[typeaheadjs.valueKey]);
|
|
323
|
+
else
|
|
324
|
+
self.add(datum);
|
|
325
|
+
self.$input.typeahead('val', '');
|
|
326
|
+
}, self));
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
self.$container.on('click', $.proxy(function(event) {
|
|
330
|
+
if (! self.$element.attr('disabled')) {
|
|
331
|
+
self.$input.removeAttr('disabled');
|
|
332
|
+
}
|
|
333
|
+
self.$input.focus();
|
|
334
|
+
}, self));
|
|
335
|
+
|
|
336
|
+
if (self.options.addOnBlur && self.options.freeInput) {
|
|
337
|
+
self.$input.on('focusout', $.proxy(function(event) {
|
|
338
|
+
// HACK: only process on focusout when no typeahead opened, to
|
|
339
|
+
// avoid adding the typeahead text as tag
|
|
340
|
+
if ($('.typeahead, .twitter-typeahead', self.$container).length === 0) {
|
|
341
|
+
self.add(self.$input.val());
|
|
342
|
+
self.$input.val('');
|
|
343
|
+
}
|
|
344
|
+
}, self));
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
self.$container.on('keydown', 'input', $.proxy(function(event) {
|
|
349
|
+
var $input = $(event.target),
|
|
350
|
+
$inputWrapper = self.findInputWrapper();
|
|
351
|
+
|
|
352
|
+
if (self.$element.attr('disabled')) {
|
|
353
|
+
self.$input.attr('disabled', 'disabled');
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
switch (event.which) {
|
|
358
|
+
// BACKSPACE
|
|
359
|
+
case 8:
|
|
360
|
+
if (doGetCaretPosition($input[0]) === 0) {
|
|
361
|
+
var prev = $inputWrapper.prev();
|
|
362
|
+
if (prev) {
|
|
363
|
+
self.remove(prev.data('item'));
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
break;
|
|
367
|
+
|
|
368
|
+
// DELETE
|
|
369
|
+
case 46:
|
|
370
|
+
if (doGetCaretPosition($input[0]) === 0) {
|
|
371
|
+
var next = $inputWrapper.next();
|
|
372
|
+
if (next) {
|
|
373
|
+
self.remove(next.data('item'));
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
break;
|
|
377
|
+
|
|
378
|
+
// LEFT ARROW
|
|
379
|
+
case 37:
|
|
380
|
+
// Try to move the input before the previous tag
|
|
381
|
+
var $prevTag = $inputWrapper.prev();
|
|
382
|
+
if ($input.val().length === 0 && $prevTag[0]) {
|
|
383
|
+
$prevTag.before($inputWrapper);
|
|
384
|
+
$input.focus();
|
|
385
|
+
}
|
|
386
|
+
break;
|
|
387
|
+
// RIGHT ARROW
|
|
388
|
+
case 39:
|
|
389
|
+
// Try to move the input after the next tag
|
|
390
|
+
var $nextTag = $inputWrapper.next();
|
|
391
|
+
if ($input.val().length === 0 && $nextTag[0]) {
|
|
392
|
+
$nextTag.after($inputWrapper);
|
|
393
|
+
$input.focus();
|
|
394
|
+
}
|
|
395
|
+
break;
|
|
396
|
+
default:
|
|
397
|
+
// ignore
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// Reset internal input's size
|
|
401
|
+
var textLength = $input.val().length,
|
|
402
|
+
wordSpace = Math.ceil(textLength / 5),
|
|
403
|
+
size = textLength + wordSpace + 1;
|
|
404
|
+
$input.attr('size', Math.max(this.inputSize, $input.val().length));
|
|
405
|
+
}, self));
|
|
406
|
+
|
|
407
|
+
self.$container.on('keypress', 'input', $.proxy(function(event) {
|
|
408
|
+
var $input = $(event.target);
|
|
409
|
+
|
|
410
|
+
if (self.$element.attr('disabled')) {
|
|
411
|
+
self.$input.attr('disabled', 'disabled');
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
var text = $input.val(),
|
|
416
|
+
maxLengthReached = self.options.maxChars && text.length >= self.options.maxChars;
|
|
417
|
+
if (self.options.freeInput && (keyCombinationInList(event, self.options.confirmKeys) || maxLengthReached)) {
|
|
418
|
+
self.add(maxLengthReached ? text.substr(0, self.options.maxChars) : text);
|
|
419
|
+
$input.val('');
|
|
420
|
+
event.preventDefault();
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
// Reset internal input's size
|
|
424
|
+
var textLength = $input.val().length,
|
|
425
|
+
wordSpace = Math.ceil(textLength / 5),
|
|
426
|
+
size = textLength + wordSpace + 1;
|
|
427
|
+
$input.attr('size', Math.max(this.inputSize, $input.val().length));
|
|
428
|
+
}, self));
|
|
429
|
+
|
|
430
|
+
// Remove icon clicked
|
|
431
|
+
self.$container.on('click', '[data-role=remove]', $.proxy(function(event) {
|
|
432
|
+
if (self.$element.attr('disabled')) {
|
|
433
|
+
return;
|
|
434
|
+
}
|
|
435
|
+
self.remove($(event.target).closest('.tag').data('item'));
|
|
436
|
+
}, self));
|
|
437
|
+
|
|
438
|
+
// Only add existing value as tags when using strings as tags
|
|
439
|
+
if (self.options.itemValue === defaultOptions.itemValue) {
|
|
440
|
+
if (self.$element[0].tagName === 'INPUT') {
|
|
441
|
+
self.add(self.$element.val());
|
|
442
|
+
} else {
|
|
443
|
+
$('option', self.$element).each(function() {
|
|
444
|
+
self.add($(this).attr('value'), true);
|
|
445
|
+
});
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
},
|
|
449
|
+
|
|
450
|
+
/**
|
|
451
|
+
* Removes all tagsinput behaviour and unregsiter all event handlers
|
|
452
|
+
*/
|
|
453
|
+
destroy: function() {
|
|
454
|
+
var self = this;
|
|
455
|
+
|
|
456
|
+
// Unbind events
|
|
457
|
+
self.$container.off('keypress', 'input');
|
|
458
|
+
self.$container.off('click', '[role=remove]');
|
|
459
|
+
|
|
460
|
+
self.$container.remove();
|
|
461
|
+
self.$element.removeData('tagsinput');
|
|
462
|
+
self.$element.show();
|
|
463
|
+
},
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Sets focus on the tagsinput
|
|
467
|
+
*/
|
|
468
|
+
focus: function() {
|
|
469
|
+
this.$input.focus();
|
|
470
|
+
},
|
|
471
|
+
|
|
472
|
+
/**
|
|
473
|
+
* Returns the internal input element
|
|
474
|
+
*/
|
|
475
|
+
input: function() {
|
|
476
|
+
return this.$input;
|
|
477
|
+
},
|
|
478
|
+
|
|
479
|
+
/**
|
|
480
|
+
* Returns the element which is wrapped around the internal input. This
|
|
481
|
+
* is normally the $container, but typeahead.js moves the $input element.
|
|
482
|
+
*/
|
|
483
|
+
findInputWrapper: function() {
|
|
484
|
+
var elt = this.$input[0],
|
|
485
|
+
container = this.$container[0];
|
|
486
|
+
while(elt && elt.parentNode !== container)
|
|
487
|
+
elt = elt.parentNode;
|
|
488
|
+
|
|
489
|
+
return $(elt);
|
|
490
|
+
}
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
/**
|
|
494
|
+
* Register JQuery plugin
|
|
495
|
+
*/
|
|
496
|
+
$.fn.tagsinput = function(arg1, arg2) {
|
|
497
|
+
var results = [];
|
|
498
|
+
|
|
499
|
+
this.each(function() {
|
|
500
|
+
var tagsinput = $(this).data('tagsinput');
|
|
501
|
+
// Initialize a new tags input
|
|
502
|
+
if (!tagsinput) {
|
|
503
|
+
tagsinput = new TagsInput(this, arg1);
|
|
504
|
+
$(this).data('tagsinput', tagsinput);
|
|
505
|
+
results.push(tagsinput);
|
|
506
|
+
|
|
507
|
+
if (this.tagName === 'SELECT') {
|
|
508
|
+
$('option', $(this)).attr('selected', 'selected');
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
// Init tags from $(this).val()
|
|
512
|
+
$(this).val($(this).val());
|
|
513
|
+
} else if (!arg1 && !arg2) {
|
|
514
|
+
// tagsinput already exists
|
|
515
|
+
// no function, trying to init
|
|
516
|
+
results.push(tagsinput);
|
|
517
|
+
} else if(tagsinput[arg1] !== undefined) {
|
|
518
|
+
// Invoke function on existing tags input
|
|
519
|
+
var retVal = tagsinput[arg1](arg2);
|
|
520
|
+
if (retVal !== undefined)
|
|
521
|
+
results.push(retVal);
|
|
522
|
+
}
|
|
523
|
+
});
|
|
524
|
+
|
|
525
|
+
if ( typeof arg1 == 'string') {
|
|
526
|
+
// Return the results from the invoked function calls
|
|
527
|
+
return results.length > 1 ? results : results[0];
|
|
528
|
+
} else {
|
|
529
|
+
return results;
|
|
530
|
+
}
|
|
531
|
+
};
|
|
532
|
+
|
|
533
|
+
$.fn.tagsinput.Constructor = TagsInput;
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Most options support both a string or number as well as a function as
|
|
537
|
+
* option value. This function makes sure that the option with the given
|
|
538
|
+
* key in the given options is wrapped in a function
|
|
539
|
+
*/
|
|
540
|
+
function makeOptionItemFunction(options, key) {
|
|
541
|
+
if (typeof options[key] !== 'function') {
|
|
542
|
+
var propertyName = options[key];
|
|
543
|
+
options[key] = function(item) { return item[propertyName]; };
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
function makeOptionFunction(options, key) {
|
|
547
|
+
if (typeof options[key] !== 'function') {
|
|
548
|
+
var value = options[key];
|
|
549
|
+
options[key] = function() { return value; };
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
/**
|
|
553
|
+
* HtmlEncodes the given value
|
|
554
|
+
*/
|
|
555
|
+
var htmlEncodeContainer = $('<div />');
|
|
556
|
+
function htmlEncode(value) {
|
|
557
|
+
if (value) {
|
|
558
|
+
return htmlEncodeContainer.text(value).html();
|
|
559
|
+
} else {
|
|
560
|
+
return '';
|
|
561
|
+
}
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
/**
|
|
565
|
+
* Returns the position of the caret in the given input field
|
|
566
|
+
* http://flightschool.acylt.com/devnotes/caret-position-woes/
|
|
567
|
+
*/
|
|
568
|
+
function doGetCaretPosition(oField) {
|
|
569
|
+
var iCaretPos = 0;
|
|
570
|
+
if (document.selection) {
|
|
571
|
+
oField.focus ();
|
|
572
|
+
var oSel = document.selection.createRange();
|
|
573
|
+
oSel.moveStart ('character', -oField.value.length);
|
|
574
|
+
iCaretPos = oSel.text.length;
|
|
575
|
+
} else if (oField.selectionStart || oField.selectionStart == '0') {
|
|
576
|
+
iCaretPos = oField.selectionStart;
|
|
577
|
+
}
|
|
578
|
+
return (iCaretPos);
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
/**
|
|
582
|
+
* Returns boolean indicates whether user has pressed an expected key combination.
|
|
583
|
+
* @param object keyPressEvent: JavaScript event object, refer
|
|
584
|
+
* http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
|
|
585
|
+
* @param object lookupList: expected key combinations, as in:
|
|
586
|
+
* [13, {which: 188, shiftKey: true}]
|
|
587
|
+
*/
|
|
588
|
+
function keyCombinationInList(keyPressEvent, lookupList) {
|
|
589
|
+
var found = false;
|
|
590
|
+
$.each(lookupList, function (index, keyCombination) {
|
|
591
|
+
if (typeof (keyCombination) === 'number' && keyPressEvent.which === keyCombination) {
|
|
592
|
+
found = true;
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
|
|
596
|
+
if (keyPressEvent.which === keyCombination.which) {
|
|
597
|
+
var alt = !keyCombination.hasOwnProperty('altKey') || keyPressEvent.altKey === keyCombination.altKey,
|
|
598
|
+
shift = !keyCombination.hasOwnProperty('shiftKey') || keyPressEvent.shiftKey === keyCombination.shiftKey,
|
|
599
|
+
ctrl = !keyCombination.hasOwnProperty('ctrlKey') || keyPressEvent.ctrlKey === keyCombination.ctrlKey;
|
|
600
|
+
if (alt && shift && ctrl) {
|
|
601
|
+
found = true;
|
|
602
|
+
return false;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
|
|
607
|
+
return found;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Initialize tagsinput behaviour on inputs and selects which have
|
|
612
|
+
* data-role=tagsinput
|
|
613
|
+
*/
|
|
614
|
+
$(function() {
|
|
615
|
+
$("input[data-role=tagsinput], select[multiple][data-role=tagsinput]").tagsinput();
|
|
616
|
+
});
|
|
617
|
+
})(window.jQuery);
|