blazer 0.0.8 → 1.0.0
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.
Potentially problematic release.
This version of blazer might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +7 -0
- data/README.md +261 -45
- data/app/assets/javascripts/blazer/Sortable.js +1144 -0
- data/app/assets/javascripts/blazer/application.js +2 -1
- data/app/assets/javascripts/blazer/chartkick.js +935 -0
- data/app/assets/javascripts/blazer/selectize.js +391 -201
- data/app/assets/stylesheets/blazer/application.css +17 -2
- data/app/assets/stylesheets/blazer/selectize.default.css +3 -2
- data/app/controllers/blazer/base_controller.rb +48 -0
- data/app/controllers/blazer/checks_controller.rb +51 -0
- data/app/controllers/blazer/dashboards_controller.rb +94 -0
- data/app/controllers/blazer/queries_controller.rb +29 -101
- data/app/helpers/blazer/{queries_helper.rb → base_helper.rb} +1 -1
- data/app/mailers/blazer/check_mailer.rb +21 -0
- data/app/models/blazer/check.rb +28 -0
- data/app/models/blazer/connection.rb +0 -1
- data/app/models/blazer/dashboard.rb +12 -0
- data/app/models/blazer/dashboard_query.rb +9 -0
- data/app/models/blazer/query.rb +5 -0
- data/app/views/blazer/check_mailer/failing_checks.html.erb +6 -0
- data/app/views/blazer/check_mailer/state_change.html.erb +6 -0
- data/app/views/blazer/checks/_form.html.erb +28 -0
- data/app/views/blazer/checks/edit.html.erb +1 -0
- data/app/views/blazer/checks/index.html.erb +41 -0
- data/app/views/blazer/checks/new.html.erb +1 -0
- data/app/views/blazer/checks/run.html.erb +9 -0
- data/app/views/blazer/dashboards/_form.html.erb +86 -0
- data/app/views/blazer/dashboards/edit.html.erb +1 -0
- data/app/views/blazer/dashboards/index.html.erb +21 -0
- data/app/views/blazer/dashboards/new.html.erb +1 -0
- data/app/views/blazer/dashboards/show.html.erb +148 -0
- data/app/views/blazer/queries/_form.html.erb +16 -5
- data/app/views/blazer/queries/_tables.html +5 -0
- data/app/views/blazer/queries/index.html.erb +6 -0
- data/app/views/blazer/queries/run.html.erb +59 -44
- data/app/views/blazer/queries/show.html.erb +20 -16
- data/config/routes.rb +5 -0
- data/lib/blazer.rb +46 -2
- data/lib/blazer/data_source.rb +70 -0
- data/lib/blazer/engine.rb +6 -2
- data/lib/blazer/tasks.rb +12 -0
- data/lib/blazer/version.rb +1 -1
- data/lib/generators/blazer/templates/config.yml +26 -6
- data/lib/generators/blazer/templates/install.rb +21 -0
- metadata +27 -3
@@ -221,7 +221,7 @@
|
|
221
221
|
* @param {object} result
|
222
222
|
* @return {mixed}
|
223
223
|
*/
|
224
|
-
get_field
|
224
|
+
get_field = function(name, result) {
|
225
225
|
if (name === '$score') return result.score;
|
226
226
|
return self.items[result.id][name];
|
227
227
|
};
|
@@ -391,8 +391,8 @@
|
|
391
391
|
if (typeof a === 'number' && typeof b === 'number') {
|
392
392
|
return a > b ? 1 : (a < b ? -1 : 0);
|
393
393
|
}
|
394
|
-
a = String(a || '')
|
395
|
-
b = String(b || '')
|
394
|
+
a = asciifold(String(a || ''));
|
395
|
+
b = asciifold(String(b || ''));
|
396
396
|
if (a > b) return 1;
|
397
397
|
if (b > a) return -1;
|
398
398
|
return 0;
|
@@ -425,21 +425,44 @@
|
|
425
425
|
};
|
426
426
|
|
427
427
|
var DIACRITICS = {
|
428
|
-
'a': '[
|
428
|
+
'a': '[aÀÁÂÃÄÅàáâãäåĀāąĄ]',
|
429
429
|
'c': '[cÇçćĆčČ]',
|
430
430
|
'd': '[dđĐďĎ]',
|
431
|
-
'e': '[
|
432
|
-
'i': '[
|
433
|
-
'
|
434
|
-
'
|
431
|
+
'e': '[eÈÉÊËèéêëěĚĒēęĘ]',
|
432
|
+
'i': '[iÌÍÎÏìíîïĪī]',
|
433
|
+
'l': '[lłŁ]',
|
434
|
+
'n': '[nÑñňŇńŃ]',
|
435
|
+
'o': '[oÒÓÔÕÕÖØòóôõöøŌō]',
|
435
436
|
'r': '[rřŘ]',
|
436
|
-
's': '[
|
437
|
+
's': '[sŠšśŚ]',
|
437
438
|
't': '[tťŤ]',
|
438
|
-
'u': '[
|
439
|
+
'u': '[uÙÚÛÜùúûüůŮŪū]',
|
439
440
|
'y': '[yŸÿýÝ]',
|
440
|
-
'z': '[
|
441
|
+
'z': '[zŽžżŻźŹ]'
|
441
442
|
};
|
442
443
|
|
444
|
+
var asciifold = (function() {
|
445
|
+
var i, n, k, chunk;
|
446
|
+
var foreignletters = '';
|
447
|
+
var lookup = {};
|
448
|
+
for (k in DIACRITICS) {
|
449
|
+
if (DIACRITICS.hasOwnProperty(k)) {
|
450
|
+
chunk = DIACRITICS[k].substring(2, DIACRITICS[k].length - 1);
|
451
|
+
foreignletters += chunk;
|
452
|
+
for (i = 0, n = chunk.length; i < n; i++) {
|
453
|
+
lookup[chunk.charAt(i)] = k;
|
454
|
+
}
|
455
|
+
}
|
456
|
+
}
|
457
|
+
var regexp = new RegExp('[' + foreignletters + ']', 'g');
|
458
|
+
return function(str) {
|
459
|
+
return str.replace(regexp, function(foreignletter) {
|
460
|
+
return lookup[foreignletter];
|
461
|
+
}).toLowerCase();
|
462
|
+
};
|
463
|
+
})();
|
464
|
+
|
465
|
+
|
443
466
|
// export
|
444
467
|
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
|
445
468
|
|
@@ -585,8 +608,8 @@
|
|
585
608
|
}));
|
586
609
|
|
587
610
|
/**
|
588
|
-
* selectize.js (v0.
|
589
|
-
* Copyright (c) 2013 Brian Reavis & contributors
|
611
|
+
* selectize.js (v0.12.1)
|
612
|
+
* Copyright (c) 2013–2015 Brian Reavis & contributors
|
590
613
|
*
|
591
614
|
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this
|
592
615
|
* file except in compliance with the License. You may obtain a copy of the License at:
|
@@ -707,6 +730,8 @@
|
|
707
730
|
var TAG_SELECT = 1;
|
708
731
|
var TAG_INPUT = 2;
|
709
732
|
|
733
|
+
// for now, android support in general is too spotty to support validity
|
734
|
+
var SUPPORTS_VALIDITY_API = !/android/i.test(window.navigator.userAgent) && !!document.createElement('form').validity;
|
710
735
|
|
711
736
|
var isset = function(object) {
|
712
737
|
return typeof object !== 'undefined';
|
@@ -726,10 +751,10 @@
|
|
726
751
|
* 1 -> '1'
|
727
752
|
*
|
728
753
|
* @param {string} value
|
729
|
-
* @returns {string}
|
754
|
+
* @returns {string|null}
|
730
755
|
*/
|
731
756
|
var hash_key = function(value) {
|
732
|
-
if (typeof value === 'undefined' || value === null) return
|
757
|
+
if (typeof value === 'undefined' || value === null) return null;
|
733
758
|
if (typeof value === 'boolean') return value ? '1' : '0';
|
734
759
|
return value + '';
|
735
760
|
};
|
@@ -793,25 +818,6 @@
|
|
793
818
|
};
|
794
819
|
};
|
795
820
|
|
796
|
-
/**
|
797
|
-
* Builds a hash table out of an array of
|
798
|
-
* objects, using the specified `key` within
|
799
|
-
* each object.
|
800
|
-
*
|
801
|
-
* @param {string} key
|
802
|
-
* @param {mixed} objects
|
803
|
-
*/
|
804
|
-
var build_hash_table = function(key, objects) {
|
805
|
-
if (!$.isArray(objects)) return objects;
|
806
|
-
var i, n, table = {};
|
807
|
-
for (i = 0, n = objects.length; i < n; i++) {
|
808
|
-
if (objects[i].hasOwnProperty(key)) {
|
809
|
-
table[objects[i][key]] = objects[i];
|
810
|
-
}
|
811
|
-
}
|
812
|
-
return table;
|
813
|
-
};
|
814
|
-
|
815
821
|
/**
|
816
822
|
* Wraps `fn` so that it can only be invoked once.
|
817
823
|
*
|
@@ -1053,13 +1059,16 @@
|
|
1053
1059
|
input.selectize = self;
|
1054
1060
|
|
1055
1061
|
// detect rtl environment
|
1056
|
-
|
1062
|
+
var computedStyle = window.getComputedStyle && window.getComputedStyle(input, null);
|
1063
|
+
dir = computedStyle ? computedStyle.getPropertyValue('direction') : input.currentStyle && input.currentStyle.direction;
|
1057
1064
|
dir = dir || $input.parents('[dir]:first').attr('dir') || '';
|
1058
1065
|
|
1059
1066
|
// setup default state
|
1060
1067
|
$.extend(self, {
|
1068
|
+
order : 0,
|
1061
1069
|
settings : settings,
|
1062
1070
|
$input : $input,
|
1071
|
+
tabIndex : $input.attr('tabindex') || '',
|
1063
1072
|
tagType : input.tagName.toLowerCase() === 'select' ? TAG_SELECT : TAG_INPUT,
|
1064
1073
|
rtl : /rtl/i.test(dir),
|
1065
1074
|
|
@@ -1101,12 +1110,20 @@
|
|
1101
1110
|
self.sifter = new Sifter(this.options, {diacritics: settings.diacritics});
|
1102
1111
|
|
1103
1112
|
// build options table
|
1104
|
-
|
1105
|
-
|
1113
|
+
if (self.settings.options) {
|
1114
|
+
for (i = 0, n = self.settings.options.length; i < n; i++) {
|
1115
|
+
self.registerOption(self.settings.options[i]);
|
1116
|
+
}
|
1117
|
+
delete self.settings.options;
|
1118
|
+
}
|
1106
1119
|
|
1107
1120
|
// build optgroup table
|
1108
|
-
|
1109
|
-
|
1121
|
+
if (self.settings.optgroups) {
|
1122
|
+
for (i = 0, n = self.settings.optgroups.length; i < n; i++) {
|
1123
|
+
self.registerOptionGroup(self.settings.optgroups[i]);
|
1124
|
+
}
|
1125
|
+
delete self.settings.optgroups;
|
1126
|
+
}
|
1110
1127
|
|
1111
1128
|
// option-dependent defaults
|
1112
1129
|
self.settings.mode = self.settings.mode || (self.settings.maxItems === 1 ? 'single' : 'multi');
|
@@ -1114,16 +1131,6 @@
|
|
1114
1131
|
self.settings.hideSelected = self.settings.mode === 'multi';
|
1115
1132
|
}
|
1116
1133
|
|
1117
|
-
if (self.settings.create) {
|
1118
|
-
self.canCreate = function(input) {
|
1119
|
-
var filter = self.settings.createFilter;
|
1120
|
-
return input.length
|
1121
|
-
&& (typeof filter !== 'function' || filter.apply(self, [input]))
|
1122
|
-
&& (typeof filter !== 'string' || new RegExp(filter).test(input))
|
1123
|
-
&& (!(filter instanceof RegExp) || filter.test(input));
|
1124
|
-
};
|
1125
|
-
}
|
1126
|
-
|
1127
1134
|
self.initializePlugins(self.settings.plugins);
|
1128
1135
|
self.setupCallbacks();
|
1129
1136
|
self.setupTemplates();
|
@@ -1161,21 +1168,23 @@
|
|
1161
1168
|
var inputMode;
|
1162
1169
|
var timeout_blur;
|
1163
1170
|
var timeout_focus;
|
1164
|
-
var tab_index;
|
1165
1171
|
var classes;
|
1166
1172
|
var classes_plugins;
|
1167
1173
|
|
1168
1174
|
inputMode = self.settings.mode;
|
1169
|
-
tab_index = $input.attr('tabindex') || '';
|
1170
1175
|
classes = $input.attr('class') || '';
|
1171
1176
|
|
1172
1177
|
$wrapper = $('<div>').addClass(settings.wrapperClass).addClass(classes).addClass(inputMode);
|
1173
1178
|
$control = $('<div>').addClass(settings.inputClass).addClass('items').appendTo($wrapper);
|
1174
|
-
$control_input = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex',
|
1179
|
+
$control_input = $('<input type="text" autocomplete="off" />').appendTo($control).attr('tabindex', $input.is(':disabled') ? '-1' : self.tabIndex);
|
1175
1180
|
$dropdown_parent = $(settings.dropdownParent || $wrapper);
|
1176
|
-
$dropdown = $('<div>').addClass(settings.dropdownClass).addClass(
|
1181
|
+
$dropdown = $('<div>').addClass(settings.dropdownClass).addClass(inputMode).hide().appendTo($dropdown_parent);
|
1177
1182
|
$dropdown_content = $('<div>').addClass(settings.dropdownContentClass).appendTo($dropdown);
|
1178
1183
|
|
1184
|
+
if(self.settings.copyClassesToDropdown) {
|
1185
|
+
$dropdown.addClass(classes);
|
1186
|
+
}
|
1187
|
+
|
1179
1188
|
$wrapper.css({
|
1180
1189
|
width: $input[0].style.width
|
1181
1190
|
});
|
@@ -1194,6 +1203,12 @@
|
|
1194
1203
|
$control_input.attr('placeholder', settings.placeholder);
|
1195
1204
|
}
|
1196
1205
|
|
1206
|
+
// if splitOn was not passed in, construct it from the delimiter to allow pasting universally
|
1207
|
+
if (!self.settings.splitOn && self.settings.delimiter) {
|
1208
|
+
var delimiterEscaped = self.settings.delimiter.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
1209
|
+
self.settings.splitOn = new RegExp('\\s*' + delimiterEscaped + '+\\s*');
|
1210
|
+
}
|
1211
|
+
|
1197
1212
|
if ($input.attr('autocorrect')) {
|
1198
1213
|
$control_input.attr('autocorrect', $input.attr('autocorrect'));
|
1199
1214
|
}
|
@@ -1209,7 +1224,7 @@
|
|
1209
1224
|
self.$dropdown_content = $dropdown_content;
|
1210
1225
|
|
1211
1226
|
$dropdown.on('mouseenter', '[data-selectable]', function() { return self.onOptionHover.apply(self, arguments); });
|
1212
|
-
$dropdown.on('mousedown', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
|
1227
|
+
$dropdown.on('mousedown click', '[data-selectable]', function() { return self.onOptionSelect.apply(self, arguments); });
|
1213
1228
|
watchChildEvent($control, 'mousedown', '*:not(input)', function() { return self.onItemSelect.apply(self, arguments); });
|
1214
1229
|
autoGrow($control_input);
|
1215
1230
|
|
@@ -1249,7 +1264,7 @@
|
|
1249
1264
|
}
|
1250
1265
|
// blur on click outside
|
1251
1266
|
if (!self.$control.has(e.target).length && e.target !== self.$control[0]) {
|
1252
|
-
self.blur();
|
1267
|
+
self.blur(e.target);
|
1253
1268
|
}
|
1254
1269
|
}
|
1255
1270
|
});
|
@@ -1278,7 +1293,7 @@
|
|
1278
1293
|
}
|
1279
1294
|
|
1280
1295
|
// feature detect for the validation API
|
1281
|
-
if (
|
1296
|
+
if (SUPPORTS_VALIDITY_API) {
|
1282
1297
|
$input.on('invalid' + eventNS, function(e) {
|
1283
1298
|
e.preventDefault();
|
1284
1299
|
self.isInvalid = true;
|
@@ -1344,17 +1359,23 @@
|
|
1344
1359
|
*/
|
1345
1360
|
setupCallbacks: function() {
|
1346
1361
|
var key, fn, callbacks = {
|
1347
|
-
'initialize'
|
1348
|
-
'change'
|
1349
|
-
'item_add'
|
1350
|
-
'item_remove'
|
1351
|
-
'clear'
|
1352
|
-
'option_add'
|
1353
|
-
'option_remove'
|
1354
|
-
'option_clear'
|
1355
|
-
'
|
1356
|
-
'
|
1357
|
-
'
|
1362
|
+
'initialize' : 'onInitialize',
|
1363
|
+
'change' : 'onChange',
|
1364
|
+
'item_add' : 'onItemAdd',
|
1365
|
+
'item_remove' : 'onItemRemove',
|
1366
|
+
'clear' : 'onClear',
|
1367
|
+
'option_add' : 'onOptionAdd',
|
1368
|
+
'option_remove' : 'onOptionRemove',
|
1369
|
+
'option_clear' : 'onOptionClear',
|
1370
|
+
'optgroup_add' : 'onOptionGroupAdd',
|
1371
|
+
'optgroup_remove' : 'onOptionGroupRemove',
|
1372
|
+
'optgroup_clear' : 'onOptionGroupClear',
|
1373
|
+
'dropdown_open' : 'onDropdownOpen',
|
1374
|
+
'dropdown_close' : 'onDropdownClose',
|
1375
|
+
'type' : 'onType',
|
1376
|
+
'load' : 'onLoad',
|
1377
|
+
'focus' : 'onFocus',
|
1378
|
+
'blur' : 'onBlur'
|
1358
1379
|
};
|
1359
1380
|
|
1360
1381
|
for (key in callbacks) {
|
@@ -1427,7 +1448,6 @@
|
|
1427
1448
|
this.$input.trigger('change');
|
1428
1449
|
},
|
1429
1450
|
|
1430
|
-
|
1431
1451
|
/**
|
1432
1452
|
* Triggered on <input> paste.
|
1433
1453
|
*
|
@@ -1438,6 +1458,17 @@
|
|
1438
1458
|
var self = this;
|
1439
1459
|
if (self.isFull() || self.isInputHidden || self.isLocked) {
|
1440
1460
|
e.preventDefault();
|
1461
|
+
} else {
|
1462
|
+
// If a regex or string is included, this will split the pasted
|
1463
|
+
// input and create Items for each separate value
|
1464
|
+
if (self.settings.splitOn) {
|
1465
|
+
setTimeout(function() {
|
1466
|
+
var splitInput = $.trim(self.$control_input.val() || '').split(self.settings.splitOn);
|
1467
|
+
for (var i = 0, n = splitInput.length; i < n; i++) {
|
1468
|
+
self.createItem(splitInput[i]);
|
1469
|
+
}
|
1470
|
+
}, 0);
|
1471
|
+
}
|
1441
1472
|
}
|
1442
1473
|
},
|
1443
1474
|
|
@@ -1450,7 +1481,7 @@
|
|
1450
1481
|
onKeyPress: function(e) {
|
1451
1482
|
if (this.isLocked) return e && e.preventDefault();
|
1452
1483
|
var character = String.fromCharCode(e.keyCode || e.which);
|
1453
|
-
if (this.settings.create && character === this.settings.delimiter) {
|
1484
|
+
if (this.settings.create && this.settings.mode === 'multi' && character === this.settings.delimiter) {
|
1454
1485
|
this.createItem();
|
1455
1486
|
e.preventDefault();
|
1456
1487
|
return false;
|
@@ -1482,7 +1513,11 @@
|
|
1482
1513
|
}
|
1483
1514
|
break;
|
1484
1515
|
case KEY_ESC:
|
1485
|
-
self.
|
1516
|
+
if (self.isOpen) {
|
1517
|
+
e.preventDefault();
|
1518
|
+
e.stopPropagation();
|
1519
|
+
self.close();
|
1520
|
+
}
|
1486
1521
|
return;
|
1487
1522
|
case KEY_N:
|
1488
1523
|
if (!e.ctrlKey || e.altKey) break;
|
@@ -1509,8 +1544,8 @@
|
|
1509
1544
|
case KEY_RETURN:
|
1510
1545
|
if (self.isOpen && self.$activeOption) {
|
1511
1546
|
self.onOptionSelect({currentTarget: self.$activeOption});
|
1547
|
+
e.preventDefault();
|
1512
1548
|
}
|
1513
|
-
e.preventDefault();
|
1514
1549
|
return;
|
1515
1550
|
case KEY_LEFT:
|
1516
1551
|
self.advanceSelection(-1, e);
|
@@ -1521,7 +1556,12 @@
|
|
1521
1556
|
case KEY_TAB:
|
1522
1557
|
if (self.settings.selectOnTab && self.isOpen && self.$activeOption) {
|
1523
1558
|
self.onOptionSelect({currentTarget: self.$activeOption});
|
1524
|
-
|
1559
|
+
|
1560
|
+
// Default behaviour is to jump to the next field, we only want this
|
1561
|
+
// if the current field doesn't accept any more entries
|
1562
|
+
if (!self.isFull()) {
|
1563
|
+
e.preventDefault();
|
1564
|
+
}
|
1525
1565
|
}
|
1526
1566
|
if (self.settings.create && self.createItem()) {
|
1527
1567
|
e.preventDefault();
|
@@ -1585,8 +1625,8 @@
|
|
1585
1625
|
*/
|
1586
1626
|
onFocus: function(e) {
|
1587
1627
|
var self = this;
|
1628
|
+
var wasFocused = self.isFocused;
|
1588
1629
|
|
1589
|
-
self.isFocused = true;
|
1590
1630
|
if (self.isDisabled) {
|
1591
1631
|
self.blur();
|
1592
1632
|
e && e.preventDefault();
|
@@ -1594,8 +1634,11 @@
|
|
1594
1634
|
}
|
1595
1635
|
|
1596
1636
|
if (self.ignoreFocus) return;
|
1637
|
+
self.isFocused = true;
|
1597
1638
|
if (self.settings.preload === 'focus') self.onSearchChange('');
|
1598
1639
|
|
1640
|
+
if (!wasFocused) self.trigger('focus');
|
1641
|
+
|
1599
1642
|
if (!self.$activeItems.length) {
|
1600
1643
|
self.showInput();
|
1601
1644
|
self.setActiveItem(null);
|
@@ -1609,31 +1652,43 @@
|
|
1609
1652
|
* Triggered on <input> blur.
|
1610
1653
|
*
|
1611
1654
|
* @param {object} e
|
1612
|
-
* @
|
1655
|
+
* @param {Element} dest
|
1613
1656
|
*/
|
1614
|
-
onBlur: function(e) {
|
1657
|
+
onBlur: function(e, dest) {
|
1615
1658
|
var self = this;
|
1659
|
+
if (!self.isFocused) return;
|
1616
1660
|
self.isFocused = false;
|
1617
|
-
if (self.ignoreFocus) return;
|
1618
1661
|
|
1619
|
-
|
1620
|
-
|
1662
|
+
if (self.ignoreFocus) {
|
1663
|
+
return;
|
1664
|
+
} else if (!self.ignoreBlur && document.activeElement === self.$dropdown_content[0]) {
|
1665
|
+
// necessary to prevent IE closing the dropdown when the scrollbar is clicked
|
1621
1666
|
self.ignoreBlur = true;
|
1622
1667
|
self.onFocus(e);
|
1623
|
-
|
1624
1668
|
return;
|
1625
1669
|
}
|
1626
1670
|
|
1671
|
+
var deactivate = function() {
|
1672
|
+
self.close();
|
1673
|
+
self.setTextboxValue('');
|
1674
|
+
self.setActiveItem(null);
|
1675
|
+
self.setActiveOption(null);
|
1676
|
+
self.setCaret(self.items.length);
|
1677
|
+
self.refreshState();
|
1678
|
+
|
1679
|
+
// IE11 bug: element still marked as active
|
1680
|
+
(dest || document.body).focus();
|
1681
|
+
|
1682
|
+
self.ignoreFocus = false;
|
1683
|
+
self.trigger('blur');
|
1684
|
+
};
|
1685
|
+
|
1686
|
+
self.ignoreFocus = true;
|
1627
1687
|
if (self.settings.create && self.settings.createOnBlur) {
|
1628
|
-
self.createItem(false);
|
1688
|
+
self.createItem(null, false, deactivate);
|
1689
|
+
} else {
|
1690
|
+
deactivate();
|
1629
1691
|
}
|
1630
|
-
|
1631
|
-
self.close();
|
1632
|
-
self.setTextboxValue('');
|
1633
|
-
self.setActiveItem(null);
|
1634
|
-
self.setActiveOption(null);
|
1635
|
-
self.setCaret(self.items.length);
|
1636
|
-
self.refreshState();
|
1637
1692
|
},
|
1638
1693
|
|
1639
1694
|
/**
|
@@ -1665,14 +1720,20 @@
|
|
1665
1720
|
|
1666
1721
|
$target = $(e.currentTarget);
|
1667
1722
|
if ($target.hasClass('create')) {
|
1668
|
-
self.createItem()
|
1723
|
+
self.createItem(null, function() {
|
1724
|
+
if (self.settings.closeAfterSelect) {
|
1725
|
+
self.close();
|
1726
|
+
}
|
1727
|
+
});
|
1669
1728
|
} else {
|
1670
1729
|
value = $target.attr('data-value');
|
1671
|
-
if (value) {
|
1730
|
+
if (typeof value !== 'undefined') {
|
1672
1731
|
self.lastQuery = null;
|
1673
1732
|
self.setTextboxValue('');
|
1674
1733
|
self.addItem(value);
|
1675
|
-
if (
|
1734
|
+
if (self.settings.closeAfterSelect) {
|
1735
|
+
self.close();
|
1736
|
+
} else if (!self.settings.hideSelected && e.type && /mouse/.test(e.type)) {
|
1676
1737
|
self.setActiveOption(self.getOption(value));
|
1677
1738
|
}
|
1678
1739
|
}
|
@@ -1705,7 +1766,7 @@
|
|
1705
1766
|
*/
|
1706
1767
|
load: function(fn) {
|
1707
1768
|
var self = this;
|
1708
|
-
var $wrapper = self.$wrapper.addClass(
|
1769
|
+
var $wrapper = self.$wrapper.addClass(self.settings.loadingClass);
|
1709
1770
|
|
1710
1771
|
self.loading++;
|
1711
1772
|
fn.apply(self, [function(results) {
|
@@ -1715,7 +1776,7 @@
|
|
1715
1776
|
self.refreshOptions(self.isFocused && !self.isInputHidden);
|
1716
1777
|
}
|
1717
1778
|
if (!self.loading) {
|
1718
|
-
$wrapper.removeClass(
|
1779
|
+
$wrapper.removeClass(self.settings.loadingClass);
|
1719
1780
|
}
|
1720
1781
|
self.trigger('load', results);
|
1721
1782
|
}]);
|
@@ -1756,10 +1817,12 @@
|
|
1756
1817
|
*
|
1757
1818
|
* @param {mixed} value
|
1758
1819
|
*/
|
1759
|
-
setValue: function(value) {
|
1760
|
-
|
1761
|
-
|
1762
|
-
|
1820
|
+
setValue: function(value, silent) {
|
1821
|
+
var events = silent ? [] : ['change'];
|
1822
|
+
|
1823
|
+
debounce_events(this, events, function() {
|
1824
|
+
this.clear(silent);
|
1825
|
+
this.addItems(value, silent);
|
1763
1826
|
});
|
1764
1827
|
},
|
1765
1828
|
|
@@ -1903,11 +1966,7 @@
|
|
1903
1966
|
},
|
1904
1967
|
|
1905
1968
|
/**
|
1906
|
-
* Gives the control focus.
|
1907
|
-
* focus handlers won't be fired--causing the focus
|
1908
|
-
* to happen silently in the background.
|
1909
|
-
*
|
1910
|
-
* @param {boolean} trigger
|
1969
|
+
* Gives the control focus.
|
1911
1970
|
*/
|
1912
1971
|
focus: function() {
|
1913
1972
|
var self = this;
|
@@ -1923,9 +1982,12 @@
|
|
1923
1982
|
|
1924
1983
|
/**
|
1925
1984
|
* Forces the control out of focus.
|
1985
|
+
*
|
1986
|
+
* @param {Element} dest
|
1926
1987
|
*/
|
1927
|
-
blur: function() {
|
1928
|
-
this.$control_input.
|
1988
|
+
blur: function(dest) {
|
1989
|
+
this.$control_input[0].blur();
|
1990
|
+
this.onBlur(null, dest);
|
1929
1991
|
},
|
1930
1992
|
|
1931
1993
|
/**
|
@@ -1952,7 +2014,7 @@
|
|
1952
2014
|
var settings = this.settings;
|
1953
2015
|
var sort = settings.sortField;
|
1954
2016
|
if (typeof sort === 'string') {
|
1955
|
-
sort = {field: sort};
|
2017
|
+
sort = [{field: sort}];
|
1956
2018
|
}
|
1957
2019
|
|
1958
2020
|
return {
|
@@ -2026,7 +2088,7 @@
|
|
2026
2088
|
}
|
2027
2089
|
|
2028
2090
|
var self = this;
|
2029
|
-
var query = self.$control_input.val();
|
2091
|
+
var query = $.trim(self.$control_input.val());
|
2030
2092
|
var results = self.search(query);
|
2031
2093
|
var $dropdown_content = self.$dropdown_content;
|
2032
2094
|
var active_before = self.$activeOption && hash_key(self.$activeOption.attr('data-value'));
|
@@ -2039,15 +2101,7 @@
|
|
2039
2101
|
|
2040
2102
|
// render and group available options individually
|
2041
2103
|
groups = {};
|
2042
|
-
|
2043
|
-
if (self.settings.optgroupOrder) {
|
2044
|
-
groups_order = self.settings.optgroupOrder;
|
2045
|
-
for (i = 0; i < groups_order.length; i++) {
|
2046
|
-
groups[groups_order[i]] = [];
|
2047
|
-
}
|
2048
|
-
} else {
|
2049
|
-
groups_order = [];
|
2050
|
-
}
|
2104
|
+
groups_order = [];
|
2051
2105
|
|
2052
2106
|
for (i = 0; i < n; i++) {
|
2053
2107
|
option = self.options[results.items[i].id];
|
@@ -2068,6 +2122,15 @@
|
|
2068
2122
|
}
|
2069
2123
|
}
|
2070
2124
|
|
2125
|
+
// sort optgroups
|
2126
|
+
if (this.settings.lockOptgroupOrder) {
|
2127
|
+
groups_order.sort(function(a, b) {
|
2128
|
+
var a_order = self.optgroups[a].$order || 0;
|
2129
|
+
var b_order = self.optgroups[b].$order || 0;
|
2130
|
+
return a_order - b_order;
|
2131
|
+
});
|
2132
|
+
}
|
2133
|
+
|
2071
2134
|
// render optgroup headers & join groups
|
2072
2135
|
html = [];
|
2073
2136
|
for (i = 0, n = groups_order.length; i < n; i++) {
|
@@ -2102,7 +2165,7 @@
|
|
2102
2165
|
}
|
2103
2166
|
|
2104
2167
|
// add create option
|
2105
|
-
has_create_option = self.
|
2168
|
+
has_create_option = self.canCreate(query);
|
2106
2169
|
if (has_create_option) {
|
2107
2170
|
$dropdown_content.prepend(self.render('option_create', {input: query}));
|
2108
2171
|
$create = $($dropdown_content[0].childNodes[0]);
|
@@ -2146,10 +2209,10 @@
|
|
2146
2209
|
*
|
2147
2210
|
* this.addOption(data)
|
2148
2211
|
*
|
2149
|
-
* @param {object} data
|
2212
|
+
* @param {object|array} data
|
2150
2213
|
*/
|
2151
2214
|
addOption: function(data) {
|
2152
|
-
var i, n,
|
2215
|
+
var i, n, value, self = this;
|
2153
2216
|
|
2154
2217
|
if ($.isArray(data)) {
|
2155
2218
|
for (i = 0, n = data.length; i < n; i++) {
|
@@ -2158,13 +2221,40 @@
|
|
2158
2221
|
return;
|
2159
2222
|
}
|
2160
2223
|
|
2161
|
-
value =
|
2162
|
-
|
2224
|
+
if (value = self.registerOption(data)) {
|
2225
|
+
self.userOptions[value] = true;
|
2226
|
+
self.lastQuery = null;
|
2227
|
+
self.trigger('option_add', value, data);
|
2228
|
+
}
|
2229
|
+
},
|
2230
|
+
|
2231
|
+
/**
|
2232
|
+
* Registers an option to the pool of options.
|
2233
|
+
*
|
2234
|
+
* @param {object} data
|
2235
|
+
* @return {boolean|string}
|
2236
|
+
*/
|
2237
|
+
registerOption: function(data) {
|
2238
|
+
var key = hash_key(data[this.settings.valueField]);
|
2239
|
+
if ((!key || this.options.hasOwnProperty(key)) && !this.settings.allowEmptyOption) return false;
|
2240
|
+
data.$order = data.$order || ++this.order;
|
2241
|
+
this.options[key] = data;
|
2242
|
+
return key;
|
2243
|
+
},
|
2163
2244
|
|
2164
|
-
|
2165
|
-
|
2166
|
-
|
2167
|
-
|
2245
|
+
/**
|
2246
|
+
* Registers an option group to the pool of option groups.
|
2247
|
+
*
|
2248
|
+
* @param {object} data
|
2249
|
+
* @return {boolean|string}
|
2250
|
+
*/
|
2251
|
+
registerOptionGroup: function(data) {
|
2252
|
+
var key = hash_key(data[this.settings.optgroupValueField]);
|
2253
|
+
if (!key) return false;
|
2254
|
+
|
2255
|
+
data.$order = data.$order || ++this.order;
|
2256
|
+
this.optgroups[key] = data;
|
2257
|
+
return key;
|
2168
2258
|
},
|
2169
2259
|
|
2170
2260
|
/**
|
@@ -2175,8 +2265,32 @@
|
|
2175
2265
|
* @param {object} data
|
2176
2266
|
*/
|
2177
2267
|
addOptionGroup: function(id, data) {
|
2178
|
-
this.
|
2179
|
-
this.
|
2268
|
+
data[this.settings.optgroupValueField] = id;
|
2269
|
+
if (id = this.registerOptionGroup(data)) {
|
2270
|
+
this.trigger('optgroup_add', id, data);
|
2271
|
+
}
|
2272
|
+
},
|
2273
|
+
|
2274
|
+
/**
|
2275
|
+
* Removes an existing option group.
|
2276
|
+
*
|
2277
|
+
* @param {string} id
|
2278
|
+
*/
|
2279
|
+
removeOptionGroup: function(id) {
|
2280
|
+
if (this.optgroups.hasOwnProperty(id)) {
|
2281
|
+
delete this.optgroups[id];
|
2282
|
+
this.renderCache = {};
|
2283
|
+
this.trigger('optgroup_remove', id);
|
2284
|
+
}
|
2285
|
+
},
|
2286
|
+
|
2287
|
+
/**
|
2288
|
+
* Clears all existing option groups.
|
2289
|
+
*/
|
2290
|
+
clearOptionGroups: function() {
|
2291
|
+
this.optgroups = {};
|
2292
|
+
this.renderCache = {};
|
2293
|
+
this.trigger('optgroup_clear');
|
2180
2294
|
},
|
2181
2295
|
|
2182
2296
|
/**
|
@@ -2190,14 +2304,17 @@
|
|
2190
2304
|
updateOption: function(value, data) {
|
2191
2305
|
var self = this;
|
2192
2306
|
var $item, $item_new;
|
2193
|
-
var value_new, index_item, cache_items, cache_options;
|
2307
|
+
var value_new, index_item, cache_items, cache_options, order_old;
|
2194
2308
|
|
2195
2309
|
value = hash_key(value);
|
2196
2310
|
value_new = hash_key(data[self.settings.valueField]);
|
2197
2311
|
|
2198
2312
|
// sanity checks
|
2313
|
+
if (value === null) return;
|
2199
2314
|
if (!self.options.hasOwnProperty(value)) return;
|
2200
|
-
if (
|
2315
|
+
if (typeof value_new !== 'string') throw new Error('Value must be set in option data');
|
2316
|
+
|
2317
|
+
order_old = self.options[value].$order;
|
2201
2318
|
|
2202
2319
|
// update references
|
2203
2320
|
if (value_new !== value) {
|
@@ -2207,6 +2324,7 @@
|
|
2207
2324
|
self.items.splice(index_item, 1, value_new);
|
2208
2325
|
}
|
2209
2326
|
}
|
2327
|
+
data.$order = data.$order || order_old;
|
2210
2328
|
self.options[value_new] = data;
|
2211
2329
|
|
2212
2330
|
// invalidate render cache
|
@@ -2230,6 +2348,9 @@
|
|
2230
2348
|
$item.replaceWith($item_new);
|
2231
2349
|
}
|
2232
2350
|
|
2351
|
+
// invalidate last query because we might have updated the sortField
|
2352
|
+
self.lastQuery = null;
|
2353
|
+
|
2233
2354
|
// update dropdown contents
|
2234
2355
|
if (self.isOpen) {
|
2235
2356
|
self.refreshOptions(false);
|
@@ -2240,8 +2361,9 @@
|
|
2240
2361
|
* Removes a single option.
|
2241
2362
|
*
|
2242
2363
|
* @param {string} value
|
2364
|
+
* @param {boolean} silent
|
2243
2365
|
*/
|
2244
|
-
removeOption: function(value) {
|
2366
|
+
removeOption: function(value, silent) {
|
2245
2367
|
var self = this;
|
2246
2368
|
value = hash_key(value);
|
2247
2369
|
|
@@ -2254,7 +2376,7 @@
|
|
2254
2376
|
delete self.options[value];
|
2255
2377
|
self.lastQuery = null;
|
2256
2378
|
self.trigger('option_remove', value);
|
2257
|
-
self.removeItem(value);
|
2379
|
+
self.removeItem(value, silent);
|
2258
2380
|
},
|
2259
2381
|
|
2260
2382
|
/**
|
@@ -2309,7 +2431,7 @@
|
|
2309
2431
|
getElementWithValue: function(value, $els) {
|
2310
2432
|
value = hash_key(value);
|
2311
2433
|
|
2312
|
-
if (value) {
|
2434
|
+
if (typeof value !== 'undefined' && value !== null) {
|
2313
2435
|
for (var i = 0, n = $els.length; i < n; i++) {
|
2314
2436
|
if ($els[i].getAttribute('data-value') === value) {
|
2315
2437
|
return $($els[i]);
|
@@ -2336,12 +2458,13 @@
|
|
2336
2458
|
* at the current caret position.
|
2337
2459
|
*
|
2338
2460
|
* @param {string} value
|
2461
|
+
* @param {boolean} silent
|
2339
2462
|
*/
|
2340
|
-
addItems: function(values) {
|
2463
|
+
addItems: function(values, silent) {
|
2341
2464
|
var items = $.isArray(values) ? values : [values];
|
2342
2465
|
for (var i = 0, n = items.length; i < n; i++) {
|
2343
2466
|
this.isPending = (i < n - 1);
|
2344
|
-
this.addItem(items[i]);
|
2467
|
+
this.addItem(items[i], silent);
|
2345
2468
|
}
|
2346
2469
|
},
|
2347
2470
|
|
@@ -2350,9 +2473,12 @@
|
|
2350
2473
|
* at the current caret position.
|
2351
2474
|
*
|
2352
2475
|
* @param {string} value
|
2476
|
+
* @param {boolean} silent
|
2353
2477
|
*/
|
2354
|
-
addItem: function(value) {
|
2355
|
-
|
2478
|
+
addItem: function(value, silent) {
|
2479
|
+
var events = silent ? [] : ['change'];
|
2480
|
+
|
2481
|
+
debounce_events(this, events, function() {
|
2356
2482
|
var $item, $option, $options;
|
2357
2483
|
var self = this;
|
2358
2484
|
var inputMode = self.settings.mode;
|
@@ -2365,7 +2491,7 @@
|
|
2365
2491
|
}
|
2366
2492
|
|
2367
2493
|
if (!self.options.hasOwnProperty(value)) return;
|
2368
|
-
if (inputMode === 'single') self.clear();
|
2494
|
+
if (inputMode === 'single') self.clear(silent);
|
2369
2495
|
if (inputMode === 'multi' && self.isFull()) return;
|
2370
2496
|
|
2371
2497
|
$item = $(self.render('item', self.options[value]));
|
@@ -2398,7 +2524,7 @@
|
|
2398
2524
|
|
2399
2525
|
self.updatePlaceholder();
|
2400
2526
|
self.trigger('item_add', value, $item);
|
2401
|
-
self.updateOriginalInput();
|
2527
|
+
self.updateOriginalInput({silent: silent});
|
2402
2528
|
}
|
2403
2529
|
});
|
2404
2530
|
},
|
@@ -2409,7 +2535,7 @@
|
|
2409
2535
|
*
|
2410
2536
|
* @param {string} value
|
2411
2537
|
*/
|
2412
|
-
removeItem: function(value) {
|
2538
|
+
removeItem: function(value, silent) {
|
2413
2539
|
var self = this;
|
2414
2540
|
var $item, i, idx;
|
2415
2541
|
|
@@ -2427,7 +2553,7 @@
|
|
2427
2553
|
self.items.splice(i, 1);
|
2428
2554
|
self.lastQuery = null;
|
2429
2555
|
if (!self.settings.persist && self.userOptions.hasOwnProperty(value)) {
|
2430
|
-
self.removeOption(value);
|
2556
|
+
self.removeOption(value, silent);
|
2431
2557
|
}
|
2432
2558
|
|
2433
2559
|
if (i < self.caretPos) {
|
@@ -2436,9 +2562,9 @@
|
|
2436
2562
|
|
2437
2563
|
self.refreshState();
|
2438
2564
|
self.updatePlaceholder();
|
2439
|
-
self.updateOriginalInput();
|
2565
|
+
self.updateOriginalInput({silent: silent});
|
2440
2566
|
self.positionDropdown();
|
2441
|
-
self.trigger('item_remove', value);
|
2567
|
+
self.trigger('item_remove', value, $item);
|
2442
2568
|
}
|
2443
2569
|
},
|
2444
2570
|
|
@@ -2450,19 +2576,30 @@
|
|
2450
2576
|
* Once this completes, it will be added
|
2451
2577
|
* to the item list.
|
2452
2578
|
*
|
2579
|
+
* @param {string} value
|
2580
|
+
* @param {boolean} [triggerDropdown]
|
2581
|
+
* @param {function} [callback]
|
2453
2582
|
* @return {boolean}
|
2454
2583
|
*/
|
2455
|
-
createItem: function(triggerDropdown) {
|
2584
|
+
createItem: function(input, triggerDropdown) {
|
2456
2585
|
var self = this;
|
2457
|
-
var input = $.trim(self.$control_input.val() || '');
|
2458
2586
|
var caret = self.caretPos;
|
2459
|
-
|
2460
|
-
self.lock();
|
2587
|
+
input = input || $.trim(self.$control_input.val() || '');
|
2461
2588
|
|
2462
|
-
|
2589
|
+
var callback = arguments[arguments.length - 1];
|
2590
|
+
if (typeof callback !== 'function') callback = function() {};
|
2591
|
+
|
2592
|
+
if (typeof triggerDropdown !== 'boolean') {
|
2463
2593
|
triggerDropdown = true;
|
2464
2594
|
}
|
2465
2595
|
|
2596
|
+
if (!self.canCreate(input)) {
|
2597
|
+
callback();
|
2598
|
+
return false;
|
2599
|
+
}
|
2600
|
+
|
2601
|
+
self.lock();
|
2602
|
+
|
2466
2603
|
var setup = (typeof self.settings.create === 'function') ? this.settings.create : function(input) {
|
2467
2604
|
var data = {};
|
2468
2605
|
data[self.settings.labelField] = input;
|
@@ -2473,15 +2610,16 @@
|
|
2473
2610
|
var create = once(function(data) {
|
2474
2611
|
self.unlock();
|
2475
2612
|
|
2476
|
-
if (!data || typeof data !== 'object') return;
|
2613
|
+
if (!data || typeof data !== 'object') return callback();
|
2477
2614
|
var value = hash_key(data[self.settings.valueField]);
|
2478
|
-
if (
|
2615
|
+
if (typeof value !== 'string') return callback();
|
2479
2616
|
|
2480
2617
|
self.setTextboxValue('');
|
2481
2618
|
self.addOption(data);
|
2482
2619
|
self.setCaret(caret);
|
2483
2620
|
self.addItem(value);
|
2484
2621
|
self.refreshOptions(triggerDropdown && self.settings.mode !== 'single');
|
2622
|
+
callback(data);
|
2485
2623
|
});
|
2486
2624
|
|
2487
2625
|
var output = setup.apply(this, [input, create]);
|
@@ -2499,9 +2637,7 @@
|
|
2499
2637
|
this.lastQuery = null;
|
2500
2638
|
|
2501
2639
|
if (this.isSetup) {
|
2502
|
-
|
2503
|
-
this.addItem(this.items);
|
2504
|
-
}
|
2640
|
+
this.addItem(this.items);
|
2505
2641
|
}
|
2506
2642
|
|
2507
2643
|
this.refreshState();
|
@@ -2561,13 +2697,15 @@
|
|
2561
2697
|
* Refreshes the original <select> or <input>
|
2562
2698
|
* element to reflect the current state.
|
2563
2699
|
*/
|
2564
|
-
updateOriginalInput: function() {
|
2565
|
-
var i, n, options, self = this;
|
2700
|
+
updateOriginalInput: function(opts) {
|
2701
|
+
var i, n, options, label, self = this;
|
2702
|
+
opts = opts || {};
|
2566
2703
|
|
2567
2704
|
if (self.tagType === TAG_SELECT) {
|
2568
2705
|
options = [];
|
2569
2706
|
for (i = 0, n = self.items.length; i < n; i++) {
|
2570
|
-
|
2707
|
+
label = self.options[self.items[i]][self.settings.labelField] || '';
|
2708
|
+
options.push('<option value="' + escape_html(self.items[i]) + '" selected="selected">' + escape_html(label) + '</option>');
|
2571
2709
|
}
|
2572
2710
|
if (!options.length && !this.$input.attr('multiple')) {
|
2573
2711
|
options.push('<option value="" selected="selected"></option>');
|
@@ -2579,7 +2717,9 @@
|
|
2579
2717
|
}
|
2580
2718
|
|
2581
2719
|
if (self.isSetup) {
|
2582
|
-
|
2720
|
+
if (!opts.silent) {
|
2721
|
+
self.trigger('change', self.$input.val());
|
2722
|
+
}
|
2583
2723
|
}
|
2584
2724
|
},
|
2585
2725
|
|
@@ -2654,8 +2794,10 @@
|
|
2654
2794
|
/**
|
2655
2795
|
* Resets / clears all selected items
|
2656
2796
|
* from the control.
|
2797
|
+
*
|
2798
|
+
* @param {boolean} silent
|
2657
2799
|
*/
|
2658
|
-
clear: function() {
|
2800
|
+
clear: function(silent) {
|
2659
2801
|
var self = this;
|
2660
2802
|
|
2661
2803
|
if (!self.items.length) return;
|
@@ -2665,7 +2807,7 @@
|
|
2665
2807
|
self.setCaret(0);
|
2666
2808
|
self.setActiveItem(null);
|
2667
2809
|
self.updatePlaceholder();
|
2668
|
-
self.updateOriginalInput();
|
2810
|
+
self.updateOriginalInput({silent: silent});
|
2669
2811
|
self.refreshState();
|
2670
2812
|
self.showInput();
|
2671
2813
|
self.trigger('clear');
|
@@ -2876,6 +3018,7 @@
|
|
2876
3018
|
disable: function() {
|
2877
3019
|
var self = this;
|
2878
3020
|
self.$input.prop('disabled', true);
|
3021
|
+
self.$control_input.prop('disabled', true).prop('tabindex', -1);
|
2879
3022
|
self.isDisabled = true;
|
2880
3023
|
self.lock();
|
2881
3024
|
},
|
@@ -2887,6 +3030,7 @@
|
|
2887
3030
|
enable: function() {
|
2888
3031
|
var self = this;
|
2889
3032
|
self.$input.prop('disabled', false);
|
3033
|
+
self.$control_input.prop('disabled', false).prop('tabindex', self.tabIndex);
|
2890
3034
|
self.isDisabled = false;
|
2891
3035
|
self.unlock();
|
2892
3036
|
},
|
@@ -2937,7 +3081,7 @@
|
|
2937
3081
|
var html = '';
|
2938
3082
|
var cache = false;
|
2939
3083
|
var self = this;
|
2940
|
-
var regex_tag = /^[\t ]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
|
3084
|
+
var regex_tag = /^[\t \r\n]*<([a-z][a-z0-9\-_]*(?:\:[a-z][a-z0-9\-_]*)?)/i;
|
2941
3085
|
|
2942
3086
|
if (templateName === 'option' || templateName === 'item') {
|
2943
3087
|
value = hash_key(data[self.settings.valueField]);
|
@@ -2991,16 +3135,36 @@
|
|
2991
3135
|
} else {
|
2992
3136
|
delete self.renderCache[templateName];
|
2993
3137
|
}
|
2994
|
-
}
|
3138
|
+
},
|
2995
3139
|
|
3140
|
+
/**
|
3141
|
+
* Determines whether or not to display the
|
3142
|
+
* create item prompt, given a user input.
|
3143
|
+
*
|
3144
|
+
* @param {string} input
|
3145
|
+
* @return {boolean}
|
3146
|
+
*/
|
3147
|
+
canCreate: function(input) {
|
3148
|
+
var self = this;
|
3149
|
+
if (!self.settings.create) return false;
|
3150
|
+
var filter = self.settings.createFilter;
|
3151
|
+
return input.length
|
3152
|
+
&& (typeof filter !== 'function' || filter.apply(self, [input]))
|
3153
|
+
&& (typeof filter !== 'string' || new RegExp(filter).test(input))
|
3154
|
+
&& (!(filter instanceof RegExp) || filter.test(input));
|
3155
|
+
}
|
2996
3156
|
|
2997
3157
|
});
|
2998
3158
|
|
2999
3159
|
|
3000
3160
|
Selectize.count = 0;
|
3001
3161
|
Selectize.defaults = {
|
3162
|
+
options: [],
|
3163
|
+
optgroups: [],
|
3164
|
+
|
3002
3165
|
plugins: [],
|
3003
3166
|
delimiter: ',',
|
3167
|
+
splitOn: null, // regexp or string for splitting up values from a paste command
|
3004
3168
|
persist: true,
|
3005
3169
|
diacritics: true,
|
3006
3170
|
create: false,
|
@@ -3014,9 +3178,12 @@
|
|
3014
3178
|
addPrecedence: false,
|
3015
3179
|
selectOnTab: false,
|
3016
3180
|
preload: false,
|
3181
|
+
allowEmptyOption: false,
|
3182
|
+
closeAfterSelect: false,
|
3017
3183
|
|
3018
3184
|
scrollDuration: 60,
|
3019
3185
|
loadThrottle: 300,
|
3186
|
+
loadingClass: 'loading',
|
3020
3187
|
|
3021
3188
|
dataAttr: 'data-data',
|
3022
3189
|
optgroupField: 'optgroup',
|
@@ -3024,7 +3191,7 @@
|
|
3024
3191
|
labelField: 'text',
|
3025
3192
|
optgroupLabelField: 'label',
|
3026
3193
|
optgroupValueField: 'value',
|
3027
|
-
|
3194
|
+
lockOptgroupOrder: false,
|
3028
3195
|
|
3029
3196
|
sortField: '$order',
|
3030
3197
|
searchField: ['text'],
|
@@ -3038,21 +3205,26 @@
|
|
3038
3205
|
|
3039
3206
|
dropdownParent: null,
|
3040
3207
|
|
3208
|
+
copyClassesToDropdown: true,
|
3209
|
+
|
3041
3210
|
/*
|
3042
|
-
load
|
3043
|
-
score
|
3044
|
-
onInitialize
|
3045
|
-
onChange
|
3046
|
-
onItemAdd
|
3047
|
-
onItemRemove
|
3048
|
-
onClear
|
3049
|
-
onOptionAdd
|
3050
|
-
onOptionRemove
|
3051
|
-
onOptionClear
|
3052
|
-
|
3053
|
-
|
3054
|
-
|
3055
|
-
|
3211
|
+
load : null, // function(query, callback) { ... }
|
3212
|
+
score : null, // function(search) { ... }
|
3213
|
+
onInitialize : null, // function() { ... }
|
3214
|
+
onChange : null, // function(value) { ... }
|
3215
|
+
onItemAdd : null, // function(value, $item) { ... }
|
3216
|
+
onItemRemove : null, // function(value) { ... }
|
3217
|
+
onClear : null, // function() { ... }
|
3218
|
+
onOptionAdd : null, // function(value, data) { ... }
|
3219
|
+
onOptionRemove : null, // function(value) { ... }
|
3220
|
+
onOptionClear : null, // function() { ... }
|
3221
|
+
onOptionGroupAdd : null, // function(id, data) { ... }
|
3222
|
+
onOptionGroupRemove : null, // function(id) { ... }
|
3223
|
+
onOptionGroupClear : null, // function() { ... }
|
3224
|
+
onDropdownOpen : null, // function($dropdown) { ... }
|
3225
|
+
onDropdownClose : null, // function($dropdown) { ... }
|
3226
|
+
onType : null, // function(str) { ... }
|
3227
|
+
onDelete : null, // function(values) { ... }
|
3056
3228
|
*/
|
3057
3229
|
|
3058
3230
|
render: {
|
@@ -3066,6 +3238,7 @@
|
|
3066
3238
|
}
|
3067
3239
|
};
|
3068
3240
|
|
3241
|
+
|
3069
3242
|
$.fn.selectize = function(settings_user) {
|
3070
3243
|
var defaults = $.fn.selectize.defaults;
|
3071
3244
|
var settings = $.extend({}, defaults, settings_user);
|
@@ -3083,19 +3256,27 @@
|
|
3083
3256
|
* @param {object} settings_element
|
3084
3257
|
*/
|
3085
3258
|
var init_textbox = function($input, settings_element) {
|
3086
|
-
var i, n, values, option
|
3087
|
-
if (!value.length) return;
|
3259
|
+
var i, n, values, option;
|
3088
3260
|
|
3089
|
-
|
3090
|
-
for (i = 0, n = values.length; i < n; i++) {
|
3091
|
-
option = {};
|
3092
|
-
option[field_label] = values[i];
|
3093
|
-
option[field_value] = values[i];
|
3261
|
+
var data_raw = $input.attr(attr_data);
|
3094
3262
|
|
3095
|
-
|
3263
|
+
if (!data_raw) {
|
3264
|
+
var value = $.trim($input.val() || '');
|
3265
|
+
if (!settings.allowEmptyOption && !value.length) return;
|
3266
|
+
values = value.split(settings.delimiter);
|
3267
|
+
for (i = 0, n = values.length; i < n; i++) {
|
3268
|
+
option = {};
|
3269
|
+
option[field_label] = values[i];
|
3270
|
+
option[field_value] = values[i];
|
3271
|
+
settings_element.options.push(option);
|
3272
|
+
}
|
3273
|
+
settings_element.items = values;
|
3274
|
+
} else {
|
3275
|
+
settings_element.options = JSON.parse(data_raw);
|
3276
|
+
for (i = 0, n = settings_element.options.length; i < n; i++) {
|
3277
|
+
settings_element.items.push(settings_element.options[i][field_value]);
|
3278
|
+
}
|
3096
3279
|
}
|
3097
|
-
|
3098
|
-
settings_element.items = values;
|
3099
3280
|
};
|
3100
3281
|
|
3101
3282
|
/**
|
@@ -3107,6 +3288,7 @@
|
|
3107
3288
|
var init_select = function($input, settings_element) {
|
3108
3289
|
var i, n, tagName, $children, order = 0;
|
3109
3290
|
var options = settings_element.options;
|
3291
|
+
var optionsMap = {};
|
3110
3292
|
|
3111
3293
|
var readData = function($el) {
|
3112
3294
|
var data = attr_data && $el.attr(attr_data);
|
@@ -3117,37 +3299,36 @@
|
|
3117
3299
|
};
|
3118
3300
|
|
3119
3301
|
var addOption = function($option, group) {
|
3120
|
-
var value, option;
|
3121
|
-
|
3122
3302
|
$option = $($option);
|
3123
3303
|
|
3124
|
-
value = $option.attr('value')
|
3125
|
-
if (!value.
|
3304
|
+
var value = hash_key($option.attr('value'));
|
3305
|
+
if (!value && !settings.allowEmptyOption) return;
|
3126
3306
|
|
3127
3307
|
// if the option already exists, it's probably been
|
3128
3308
|
// duplicated in another optgroup. in this case, push
|
3129
3309
|
// the current group to the "optgroup" property on the
|
3130
3310
|
// existing option so that it's rendered in both places.
|
3131
|
-
if (
|
3311
|
+
if (optionsMap.hasOwnProperty(value)) {
|
3132
3312
|
if (group) {
|
3133
|
-
|
3134
|
-
|
3135
|
-
|
3136
|
-
|
3313
|
+
var arr = optionsMap[value][field_optgroup];
|
3314
|
+
if (!arr) {
|
3315
|
+
optionsMap[value][field_optgroup] = group;
|
3316
|
+
} else if (!$.isArray(arr)) {
|
3317
|
+
optionsMap[value][field_optgroup] = [arr, group];
|
3137
3318
|
} else {
|
3138
|
-
|
3319
|
+
arr.push(group);
|
3139
3320
|
}
|
3140
3321
|
}
|
3141
3322
|
return;
|
3142
3323
|
}
|
3143
3324
|
|
3144
|
-
option
|
3325
|
+
var option = readData($option) || {};
|
3145
3326
|
option[field_label] = option[field_label] || $option.text();
|
3146
3327
|
option[field_value] = option[field_value] || value;
|
3147
3328
|
option[field_optgroup] = option[field_optgroup] || group;
|
3148
3329
|
|
3149
|
-
|
3150
|
-
options
|
3330
|
+
optionsMap[value] = option;
|
3331
|
+
options.push(option);
|
3151
3332
|
|
3152
3333
|
if ($option.is(':selected')) {
|
3153
3334
|
settings_element.items.push(value);
|
@@ -3164,7 +3345,7 @@
|
|
3164
3345
|
optgroup = readData($optgroup) || {};
|
3165
3346
|
optgroup[field_optgroup_label] = id;
|
3166
3347
|
optgroup[field_optgroup_value] = id;
|
3167
|
-
settings_element.optgroups
|
3348
|
+
settings_element.optgroups.push(optgroup);
|
3168
3349
|
}
|
3169
3350
|
|
3170
3351
|
$options = $('option', $optgroup);
|
@@ -3192,10 +3373,15 @@
|
|
3192
3373
|
var instance;
|
3193
3374
|
var $input = $(this);
|
3194
3375
|
var tag_name = this.tagName.toLowerCase();
|
3376
|
+
var placeholder = $input.attr('placeholder') || $input.attr('data-placeholder');
|
3377
|
+
if (!placeholder && !settings.allowEmptyOption) {
|
3378
|
+
placeholder = $input.children('option[value=""]').text();
|
3379
|
+
}
|
3380
|
+
|
3195
3381
|
var settings_element = {
|
3196
|
-
'placeholder' :
|
3197
|
-
'options' :
|
3198
|
-
'optgroups' :
|
3382
|
+
'placeholder' : placeholder,
|
3383
|
+
'options' : [],
|
3384
|
+
'optgroups' : [],
|
3199
3385
|
'items' : []
|
3200
3386
|
};
|
3201
3387
|
|
@@ -3210,6 +3396,9 @@
|
|
3210
3396
|
};
|
3211
3397
|
|
3212
3398
|
$.fn.selectize.defaults = Selectize.defaults;
|
3399
|
+
$.fn.selectize.support = {
|
3400
|
+
validity: SUPPORTS_VALIDITY_API
|
3401
|
+
};
|
3213
3402
|
|
3214
3403
|
|
3215
3404
|
Selectize.define('drag_drop', function(options) {
|
@@ -3452,7 +3641,7 @@
|
|
3452
3641
|
return option[this.settings.labelField];
|
3453
3642
|
};
|
3454
3643
|
|
3455
|
-
this.onKeyDown = (function(
|
3644
|
+
this.onKeyDown = (function() {
|
3456
3645
|
var original = self.onKeyDown;
|
3457
3646
|
return function(e) {
|
3458
3647
|
var index, option;
|
@@ -3473,5 +3662,6 @@
|
|
3473
3662
|
})();
|
3474
3663
|
});
|
3475
3664
|
|
3665
|
+
|
3476
3666
|
return Selectize;
|
3477
3667
|
}));
|