jsl-ui 9.28.2 → 9.28.3
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.
- package/lib/components/IntervalComponent.d.ts +17 -17
- package/lib/components/IntervalComponent.js +65 -68
- package/lib/components/auto-suggest/AutoSuggestComponent.d.ts +22 -22
- package/lib/components/auto-suggest/AutoSuggestComponent.js +47 -49
- package/lib/components/button/ButtonComponent.d.ts +18 -18
- package/lib/components/button/ButtonComponent.js +29 -46
- package/lib/components/checkbox/CheckBoxComponent.d.ts +28 -28
- package/lib/components/checkbox/CheckBoxComponent.js +81 -98
- package/lib/components/date/DateComponent.d.ts +32 -32
- package/lib/components/date/DateComponent.js +89 -106
- package/lib/components/date-range/DateRangeComponent.d.ts +35 -35
- package/lib/components/date-range/DateRangeComponent.js +123 -136
- package/lib/components/date-time/DateTimeComponent.d.ts +15 -15
- package/lib/components/date-time/DateTimeComponent.js +39 -53
- package/lib/components/dialog/DialogComponent.d.ts +67 -67
- package/lib/components/dialog/DialogComponent.js +198 -251
- package/lib/components/drop-down/DropDownComponent.d.ts +27 -27
- package/lib/components/drop-down/DropDownComponent.js +60 -74
- package/lib/components/expander/ExpanderComponent.d.ts +27 -27
- package/lib/components/expander/ExpanderComponent.js +89 -90
- package/lib/components/form-field/FormComponent.d.ts +12 -12
- package/lib/components/form-field/FormComponent.js +23 -26
- package/lib/components/form-field/FormFieldComponent.d.ts +39 -39
- package/lib/components/form-field/FormFieldComponent.js +138 -140
- package/lib/components/grid/GridComponent.d.ts +26 -26
- package/lib/components/grid/GridComponent.js +110 -116
- package/lib/components/input/InputComponent.d.ts +33 -33
- package/lib/components/input/InputComponent.js +126 -147
- package/lib/components/loading/LoadingComponent.d.ts +8 -8
- package/lib/components/loading/LoadingComponent.js +20 -22
- package/lib/components/navigation/NavigationComponent.d.ts +34 -34
- package/lib/components/navigation/NavigationComponent.js +45 -70
- package/lib/components/notification/Notification.d.ts +19 -19
- package/lib/components/notification/Notification.js +65 -69
- package/lib/components/number/NumberComponent.d.ts +26 -26
- package/lib/components/number/NumberComponent.js +80 -98
- package/lib/components/overlay/OverlayComponent.d.ts +27 -27
- package/lib/components/overlay/OverlayComponent.js +137 -149
- package/lib/components/progress/ProgressComponent.d.ts +18 -18
- package/lib/components/progress/ProgressComponent.js +40 -42
- package/lib/components/ribbon-menu/RibbonMenuComponent.d.ts +12 -12
- package/lib/components/ribbon-menu/RibbonMenuComponent.js +23 -34
- package/lib/components/select/SelectComponent.d.ts +79 -79
- package/lib/components/select/SelectComponent.js +356 -369
- package/lib/components/select/SelectDDAreaComponent.d.ts +32 -32
- package/lib/components/select/SelectDDAreaComponent.js +303 -317
- package/lib/components/slider/nouislider.d.ts +44 -44
- package/lib/components/slider/nouislider.js +2158 -2158
- package/lib/components/stack-panel/SplitterComponent.d.ts +21 -21
- package/lib/components/stack-panel/SplitterComponent.js +124 -126
- package/lib/components/stack-panel/StackPanelComponent.d.ts +20 -20
- package/lib/components/stack-panel/StackPanelComponent.js +50 -64
- package/lib/components/tabs/TabsComponent.d.ts +40 -40
- package/lib/components/tabs/TabsComponent.js +200 -204
- package/lib/components/time/TimeComponent.d.ts +29 -29
- package/lib/components/time/TimeComponent.js +104 -113
- package/lib/components/tree-view/TreeViewComponent.d.ts +23 -23
- package/lib/components/tree-view/TreeViewComponent.js +43 -45
- package/lib/components/virtual-render/VirtualRenderComponent.d.ts +30 -30
- package/lib/components/virtual-render/VirtualRenderComponent.js +92 -104
- package/lib/core/culture.d.ts +5 -5
- package/lib/core/culture.js +385 -385
- package/lib/core/date.d.ts +14 -14
- package/lib/core/date.js +346 -350
- package/lib/core/helper.d.ts +13 -13
- package/lib/core/helper.js +100 -104
- package/lib/core/jq.d.ts +37 -37
- package/lib/core/jq.js +362 -371
- package/lib/core/lang.d.ts +24 -24
- package/lib/core/lang.js +24 -28
- package/lib/core/numeric.d.ts +7 -7
- package/lib/core/numeric.js +139 -143
- package/lib/core/polyfills.js +22 -22
- package/lib/core.d.ts +6 -6
- package/lib/core.js +6 -6
- package/lib/grid/filterDialogs/dateRangeFilter.d.ts +7 -7
- package/lib/grid/filterDialogs/dateRangeFilter.js +106 -156
- package/lib/grid/filterDialogs/excelFilter.d.ts +9 -9
- package/lib/grid/filterDialogs/excelFilter.js +250 -316
- package/lib/grid/plugins/ext.overlay.d.ts +2 -2
- package/lib/grid/plugins/ext.overlay.js +295 -295
- package/lib/grid/plugins/slick.cellrangedecorator.d.ts +12 -12
- package/lib/grid/plugins/slick.cellrangedecorator.js +68 -68
- package/lib/grid/plugins/slick.cellrangeselector.d.ts +1 -1
- package/lib/grid/plugins/slick.cellrangeselector.js +144 -144
- package/lib/grid/plugins/slick.cellselectionmodel.d.ts +1 -1
- package/lib/grid/plugins/slick.cellselectionmodel.js +194 -194
- package/lib/grid/plugins/slick.headerbuttons.d.ts +55 -55
- package/lib/grid/plugins/slick.headerbuttons.js +156 -156
- package/lib/grid/plugins/slick.headermenu.d.ts +74 -74
- package/lib/grid/plugins/slick.headermenu.js +260 -260
- package/lib/grid/plugins/slick.rowmovemanager.d.ts +18 -18
- package/lib/grid/plugins/slick.rowmovemanager.js +249 -249
- package/lib/grid/plugins/slick.rowselectionmodel.d.ts +1 -1
- package/lib/grid/plugins/slick.rowselectionmodel.js +167 -167
- package/lib/grid/slick.core.d.ts +91 -91
- package/lib/grid/slick.core.js +574 -574
- package/lib/grid/slick.creator.d.ts +81 -81
- package/lib/grid/slick.creator.js +726 -726
- package/lib/grid/slick.dataview.d.ts +71 -71
- package/lib/grid/slick.dataview.js +1069 -1080
- package/lib/grid/slick.editors.core.d.ts +4 -4
- package/lib/grid/slick.editors.core.js +235 -235
- package/lib/grid/slick.editors.date.d.ts +2 -2
- package/lib/grid/slick.editors.date.js +125 -125
- package/lib/grid/slick.editors.lookup.d.ts +14 -14
- package/lib/grid/slick.editors.lookup.js +352 -353
- package/lib/grid/slick.editors.nav.d.ts +6 -6
- package/lib/grid/slick.editors.nav.js +22 -22
- package/lib/grid/slick.formatters.d.ts +17 -17
- package/lib/grid/slick.formatters.js +144 -144
- package/lib/grid/slick.grid.d.ts +285 -285
- package/lib/grid/slick.grid.js +5409 -5412
- package/lib/grid/slick.groupitemmetadataprovider.d.ts +39 -39
- package/lib/grid/slick.groupitemmetadataprovider.js +165 -165
- package/lib/grid/slick.tree.d.ts +33 -33
- package/lib/grid/slick.tree.js +208 -220
- package/lib/grid.css +1 -705
- package/lib/grid.d.ts +13 -13
- package/lib/grid.js +13 -13
- package/lib/index.css +8 -2195
- package/lib/index.css.map +1 -1
- package/lib/index.d.ts +56 -55
- package/lib/index.js +61 -61
- package/lib/index.js.map +1 -1
- package/lib/interaction/dataconverter.d.ts +4 -4
- package/lib/interaction/dataconverter.js +0 -1
- package/lib/interaction/eventhandler.d.ts +13 -13
- package/lib/interaction/eventhandler.js +39 -42
- package/lib/interaction/moveable.d.ts +39 -39
- package/lib/interaction/moveable.js +160 -174
- package/lib/interaction/resizable.d.ts +37 -37
- package/lib/interaction/resizable.js +109 -123
- package/lib/ui/auto.suggest.d.ts +18 -18
- package/lib/ui/auto.suggest.js +244 -259
- package/lib/ui/date.picker/date-picker-options.d.ts +11 -11
- package/lib/ui/date.picker/date-picker-options.js +76 -76
- package/lib/ui/date.picker/lib/date-manip.d.ts +74 -74
- package/lib/ui/date.picker/lib/date-manip.js +120 -120
- package/lib/ui/date.picker/lib/dom.d.ts +23 -23
- package/lib/ui/date.picker/lib/dom.js +28 -28
- package/lib/ui/date.picker/lib/emitter.d.ts +13 -13
- package/lib/ui/date.picker/lib/emitter.js +49 -49
- package/lib/ui/date.picker/lib/fns.d.ts +23 -23
- package/lib/ui/date.picker/lib/fns.js +39 -43
- package/lib/ui/date.picker/mode/base-mode.d.ts +26 -26
- package/lib/ui/date.picker/mode/base-mode.js +248 -248
- package/lib/ui/date.picker/mode/dropdown-mode.d.ts +28 -28
- package/lib/ui/date.picker/mode/dropdown-mode.js +50 -50
- package/lib/ui/date.picker/mode/index.d.ts +28 -28
- package/lib/ui/date.picker/mode/index.js +18 -18
- package/lib/ui/date.picker/mode/modal-mode.d.ts +25 -25
- package/lib/ui/date.picker/mode/modal-mode.js +17 -17
- package/lib/ui/date.picker/mode/permanent-mode.d.ts +29 -29
- package/lib/ui/date.picker/mode/permanent-mode.js +19 -19
- package/lib/ui/date.picker/views/day-picker.d.ts +40 -40
- package/lib/ui/date.picker/views/day-picker.js +155 -155
- package/lib/ui/date.picker/views/month-picker.d.ts +26 -26
- package/lib/ui/date.picker/views/month-picker.js +65 -65
- package/lib/ui/date.picker/views/year-picker.d.ts +20 -20
- package/lib/ui/date.picker/views/year-picker.js +66 -66
- package/lib/ui/date.picker.d.ts +66 -66
- package/lib/ui/date.picker.js +32 -32
- package/lib/ui/dialog.d.ts +22 -22
- package/lib/ui/dialog.js +98 -113
- package/lib/ui/drop.down.d.ts +52 -52
- package/lib/ui/drop.down.js +2112 -2112
- package/lib/ui/number.input.d.ts +13 -13
- package/lib/ui/number.input.js +56 -67
- package/package.json +1 -1
package/lib/ui/drop.down.js
CHANGED
|
@@ -1,2112 +1,2112 @@
|
|
|
1
|
-
/*!
|
|
2
|
-
* Selectr 2.4.12
|
|
3
|
-
* http://mobius.ovh/docs/selectr
|
|
4
|
-
*
|
|
5
|
-
* Released under the MIT license
|
|
6
|
-
*/
|
|
7
|
-
import "./drop.down.css";
|
|
8
|
-
import { UILang } from "../core/lang";
|
|
9
|
-
|
|
10
|
-
(function () {
|
|
11
|
-
/**
|
|
12
|
-
* Event Emitter
|
|
13
|
-
*/
|
|
14
|
-
var Events = function () { };
|
|
15
|
-
/**
|
|
16
|
-
* Event Prototype
|
|
17
|
-
* @type {Object}
|
|
18
|
-
*/
|
|
19
|
-
Events.prototype = {
|
|
20
|
-
/**
|
|
21
|
-
* Add custom event listener
|
|
22
|
-
* @param {String} event Event type
|
|
23
|
-
* @param {Function} func Callback
|
|
24
|
-
* @return {Void}
|
|
25
|
-
*/
|
|
26
|
-
on: function (event, func) {
|
|
27
|
-
this._events = this._events || {};
|
|
28
|
-
this._events[event] = this._events[event] || [];
|
|
29
|
-
this._events[event].push(func);
|
|
30
|
-
},
|
|
31
|
-
/**
|
|
32
|
-
* Remove custom event listener
|
|
33
|
-
* @param {String} event Event type
|
|
34
|
-
* @param {Function} func Callback
|
|
35
|
-
* @return {Void}
|
|
36
|
-
*/
|
|
37
|
-
off: function (event, func) {
|
|
38
|
-
this._events = this._events || {};
|
|
39
|
-
if (event in this._events === false)
|
|
40
|
-
return;
|
|
41
|
-
this._events[event].splice(this._events[event].indexOf(func), 1);
|
|
42
|
-
},
|
|
43
|
-
/**
|
|
44
|
-
* Fire a custom event
|
|
45
|
-
* @param {String} event Event type
|
|
46
|
-
* @return {Void}
|
|
47
|
-
*/
|
|
48
|
-
emit: function (event /* , args... */) {
|
|
49
|
-
this._events = this._events || {};
|
|
50
|
-
if (event in this._events === false)
|
|
51
|
-
return;
|
|
52
|
-
for (var i = 0; i < this._events[event].length; i++) {
|
|
53
|
-
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
};
|
|
57
|
-
/**
|
|
58
|
-
* Event mixin
|
|
59
|
-
* @param {Object} obj
|
|
60
|
-
* @return {Object}
|
|
61
|
-
*/
|
|
62
|
-
Events.mixin = function (obj) {
|
|
63
|
-
var props = ['on', 'off', 'emit'];
|
|
64
|
-
for (var i = 0; i < props.length; i++) {
|
|
65
|
-
if (typeof obj === 'function') {
|
|
66
|
-
obj.prototype[props[i]] = Events.prototype[props[i]];
|
|
67
|
-
}
|
|
68
|
-
else {
|
|
69
|
-
obj[props[i]] = Events.prototype[props[i]];
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
return obj;
|
|
73
|
-
};
|
|
74
|
-
/**
|
|
75
|
-
* Helpers
|
|
76
|
-
* @type {Object}
|
|
77
|
-
*/
|
|
78
|
-
var util = {
|
|
79
|
-
escapeRegExp: function (str) {
|
|
80
|
-
// source from lodash 3.0.0
|
|
81
|
-
var _reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
|
|
82
|
-
var _reHasRegExpChar = new RegExp(_reRegExpChar.source);
|
|
83
|
-
return (str && _reHasRegExpChar.test(str)) ? str.replace(_reRegExpChar, '\\$&') : str;
|
|
84
|
-
},
|
|
85
|
-
extend: function (src, props) {
|
|
86
|
-
for (var prop in props) {
|
|
87
|
-
if (props.hasOwnProperty(prop)) {
|
|
88
|
-
var val = props[prop];
|
|
89
|
-
if (val && Object.prototype.toString.call(val) === "[object Object]") {
|
|
90
|
-
src[prop] = src[prop] || {};
|
|
91
|
-
util.extend(src[prop], val);
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
src[prop] = val;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
return src;
|
|
99
|
-
},
|
|
100
|
-
each: function (a, b, c) {
|
|
101
|
-
if ("[object Object]" === Object.prototype.toString.call(a)) {
|
|
102
|
-
for (var d in a) {
|
|
103
|
-
if (Object.prototype.hasOwnProperty.call(a, d)) {
|
|
104
|
-
b.call(c, d, a[d], a);
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
for (var e = 0, f = a.length; e < f; e++) {
|
|
110
|
-
b.call(c, e, a[e], a);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
},
|
|
114
|
-
createElement: function (e, a) {
|
|
115
|
-
var d = document, el = d.createElement(e);
|
|
116
|
-
if (a && "[object Object]" === Object.prototype.toString.call(a)) {
|
|
117
|
-
var i;
|
|
118
|
-
for (i in a) {
|
|
119
|
-
if (a[i] !== undefined) {
|
|
120
|
-
if (i in el)
|
|
121
|
-
el[i] = a[i];
|
|
122
|
-
else if ("html" === i)
|
|
123
|
-
el.innerHTML = a[i];
|
|
124
|
-
else
|
|
125
|
-
el.setAttribute(i, a[i]);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
return el;
|
|
130
|
-
},
|
|
131
|
-
hasClass: function (a, b) {
|
|
132
|
-
if (a) {
|
|
133
|
-
// if (a.classList == null) {
|
|
134
|
-
// debugger;
|
|
135
|
-
// }
|
|
136
|
-
return a.classList ? a.classList.contains(b) : !!a.className && !!a.className.match(new RegExp("(\\s|^)" + b + "(\\s|$)"));
|
|
137
|
-
}
|
|
138
|
-
},
|
|
139
|
-
addClass: function (a, b) {
|
|
140
|
-
if (!util.hasClass(a, b)) {
|
|
141
|
-
if (a.classList) {
|
|
142
|
-
a.classList.add(b);
|
|
143
|
-
}
|
|
144
|
-
else {
|
|
145
|
-
a.className = a.className.trim() + " " + b;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
},
|
|
149
|
-
removeClass: function (a, b) {
|
|
150
|
-
if (util.hasClass(a, b)) {
|
|
151
|
-
if (a.classList) {
|
|
152
|
-
a.classList.remove(b);
|
|
153
|
-
}
|
|
154
|
-
else {
|
|
155
|
-
a.className = a.className.replace(new RegExp("(^|\\s)" + b.split(" ").join("|") + "(\\s|$)", "gi"), " ");
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
},
|
|
159
|
-
closest: function (el, fn) {
|
|
160
|
-
return el && el !== document.body && (fn(el) ? el : util.closest(el.parentNode, fn));
|
|
161
|
-
},
|
|
162
|
-
isInt: function (val) {
|
|
163
|
-
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
|
|
164
|
-
},
|
|
165
|
-
debounce: function (a, b, c) {
|
|
166
|
-
var d;
|
|
167
|
-
return function () {
|
|
168
|
-
var e = this, f = arguments, g = function () {
|
|
169
|
-
d = null;
|
|
170
|
-
if (!c)
|
|
171
|
-
a.apply(e, f);
|
|
172
|
-
}, h = c && !d;
|
|
173
|
-
clearTimeout(d);
|
|
174
|
-
d = setTimeout(g, b);
|
|
175
|
-
if (h) {
|
|
176
|
-
a.apply(e, f);
|
|
177
|
-
}
|
|
178
|
-
};
|
|
179
|
-
},
|
|
180
|
-
rect: function (el, abs) {
|
|
181
|
-
var w = window;
|
|
182
|
-
var r = el.getBoundingClientRect();
|
|
183
|
-
var x = abs ? w.pageXOffset : 0;
|
|
184
|
-
var y = abs ? w.pageYOffset : 0;
|
|
185
|
-
return {
|
|
186
|
-
bottom: r.bottom + y,
|
|
187
|
-
height: r.height,
|
|
188
|
-
left: r.left + x,
|
|
189
|
-
right: r.right + x,
|
|
190
|
-
top: r.top + y,
|
|
191
|
-
width: r.width
|
|
192
|
-
};
|
|
193
|
-
},
|
|
194
|
-
includes: function (a, b) {
|
|
195
|
-
return a.indexOf(b) > -1;
|
|
196
|
-
},
|
|
197
|
-
startsWith: function (a, b) {
|
|
198
|
-
return a.substr(0, b.length) === b;
|
|
199
|
-
},
|
|
200
|
-
truncate: function (el) {
|
|
201
|
-
while (el.firstChild) {
|
|
202
|
-
el.removeChild(el.firstChild);
|
|
203
|
-
}
|
|
204
|
-
},
|
|
205
|
-
toDom: function (item) {
|
|
206
|
-
var elementData = {
|
|
207
|
-
class: "jsl-option",
|
|
208
|
-
role: "treeitem",
|
|
209
|
-
"aria-selected": false,
|
|
210
|
-
html: item.html,
|
|
211
|
-
textContent: item.html ? undefined : item.text
|
|
212
|
-
};
|
|
213
|
-
var opt = util.createElement("li", elementData);
|
|
214
|
-
opt.idx = item.idx;
|
|
215
|
-
if (item.disabled) {
|
|
216
|
-
opt.disabled = true;
|
|
217
|
-
util.addClass(opt, "disabled");
|
|
218
|
-
}
|
|
219
|
-
if (item.active) {
|
|
220
|
-
util.addClass(opt, "active");
|
|
221
|
-
}
|
|
222
|
-
if (item.selected) {
|
|
223
|
-
util.addClass(opt, "selected");
|
|
224
|
-
opt.selected = true;
|
|
225
|
-
}
|
|
226
|
-
return opt;
|
|
227
|
-
}
|
|
228
|
-
};
|
|
229
|
-
function isset(obj, prop) {
|
|
230
|
-
return obj.hasOwnProperty(prop) && (obj[prop] === true || obj[prop].length);
|
|
231
|
-
}
|
|
232
|
-
/**
|
|
233
|
-
* Append an item to the list
|
|
234
|
-
* @param {Object} item
|
|
235
|
-
* @param {Object} custom
|
|
236
|
-
* @return {Void}
|
|
237
|
-
*/
|
|
238
|
-
function appendItem(vitem, parent, custom) {
|
|
239
|
-
var item = vitem.dom = util.toDom(vitem);
|
|
240
|
-
if (vitem.optgroup && vitem.optgroup.parentNode == null) {
|
|
241
|
-
parent.appendChild(vitem.optgroup);
|
|
242
|
-
}
|
|
243
|
-
parent.appendChild(item);
|
|
244
|
-
// if (item.parentNode) {
|
|
245
|
-
// if (!item.parentNode.parentNode) {
|
|
246
|
-
// parent.appendChild(item.parentNode);
|
|
247
|
-
// }
|
|
248
|
-
// } else
|
|
249
|
-
// {
|
|
250
|
-
// parent.appendChild(item);
|
|
251
|
-
// }
|
|
252
|
-
// util.removeClass(item, "excluded");
|
|
253
|
-
if (!custom) {
|
|
254
|
-
// remove any <span> highlighting, without xss
|
|
255
|
-
item.textContent = item.textContent;
|
|
256
|
-
}
|
|
257
|
-
}
|
|
258
|
-
/**
|
|
259
|
-
* Render the item list
|
|
260
|
-
* @return {Void}
|
|
261
|
-
*/
|
|
262
|
-
var render = function () {
|
|
263
|
-
if (this.items.length) {
|
|
264
|
-
var f = document.createDocumentFragment();
|
|
265
|
-
if (this.config.pagination) {
|
|
266
|
-
var pages = this.pages.slice(0, this.pageIndex);
|
|
267
|
-
util.each(pages, function (i, items) {
|
|
268
|
-
util.each(items, function (j, item) {
|
|
269
|
-
appendItem(item, f, this.customOption);
|
|
270
|
-
}, this);
|
|
271
|
-
}, this);
|
|
272
|
-
}
|
|
273
|
-
else {
|
|
274
|
-
util.each(this.items, function (i, item) {
|
|
275
|
-
appendItem(item, f, this.customOption);
|
|
276
|
-
}, this);
|
|
277
|
-
}
|
|
278
|
-
// highlight first selected option if any; first option otherwise
|
|
279
|
-
if (f.childElementCount || f.childNodes.length) {
|
|
280
|
-
removeActive(this.tree);
|
|
281
|
-
this.navIndex = (f.querySelector(".jsl-option.selected") ||
|
|
282
|
-
f.querySelector(".jsl-option")).idx;
|
|
283
|
-
if (!this.config.multiple) {
|
|
284
|
-
util.addClass(this.items[this.navIndex].dom, "active");
|
|
285
|
-
this.items[this.navIndex].active = true;
|
|
286
|
-
}
|
|
287
|
-
}
|
|
288
|
-
this.tree.appendChild(f);
|
|
289
|
-
}
|
|
290
|
-
};
|
|
291
|
-
/**
|
|
292
|
-
* Dismiss / close the dropdown
|
|
293
|
-
* @param {obj} e
|
|
294
|
-
* @return {void}
|
|
295
|
-
*/
|
|
296
|
-
var dismiss = function (e) {
|
|
297
|
-
var target = e.target;
|
|
298
|
-
if (!this.container.contains(target) && (this.opened || util.hasClass(this.container, "notice"))) {
|
|
299
|
-
this.close();
|
|
300
|
-
}
|
|
301
|
-
};
|
|
302
|
-
/**
|
|
303
|
-
* Build a list item from the HTMLOptionElement
|
|
304
|
-
* @param {int} i HTMLOptionElement index
|
|
305
|
-
* @param {HTMLOptionElement} option
|
|
306
|
-
* @param {bool} group Has parent optgroup
|
|
307
|
-
* @return {void}
|
|
308
|
-
*/
|
|
309
|
-
var createItem = function (option, data) {
|
|
310
|
-
data = data || option;
|
|
311
|
-
var elementData = {
|
|
312
|
-
class: "jsl-option",
|
|
313
|
-
role: "treeitem",
|
|
314
|
-
"aria-selected": option.selected,
|
|
315
|
-
html: undefined,
|
|
316
|
-
textContent: undefined
|
|
317
|
-
};
|
|
318
|
-
if (this.customOption) {
|
|
319
|
-
elementData.html = this.config.renderOption(data); // asume xss prevention in custom render function
|
|
320
|
-
}
|
|
321
|
-
else {
|
|
322
|
-
elementData.textContent = option.textContent; // treat all as plain text
|
|
323
|
-
}
|
|
324
|
-
// var opt = util.createElement("li",elementData);
|
|
325
|
-
var opt = { idx: 0, disabled: option.disabled, html: elementData.html, text: elementData.textContent, parent: null, selected: option.selected };
|
|
326
|
-
opt.idx = option.idx;
|
|
327
|
-
opt.parent = option.parent;
|
|
328
|
-
this.items.push(opt);
|
|
329
|
-
if (option.defaultSelected) {
|
|
330
|
-
this.defaultSelected.push(option.idx);
|
|
331
|
-
}
|
|
332
|
-
// if (option.disabled) {
|
|
333
|
-
// opt.disabled = true;
|
|
334
|
-
// // util.addClass(opt, "disabled");
|
|
335
|
-
// }
|
|
336
|
-
// if (option.selected) {
|
|
337
|
-
// opt.selected = true;
|
|
338
|
-
// // util.addClass(opt, "selected");
|
|
339
|
-
// }
|
|
340
|
-
return opt;
|
|
341
|
-
};
|
|
342
|
-
var removeActive = function (container) {
|
|
343
|
-
var items = container.querySelectorAll("li.active");
|
|
344
|
-
for (
|
|
345
|
-
items[i].classList.remove("active");
|
|
346
|
-
}
|
|
347
|
-
};
|
|
348
|
-
/**
|
|
349
|
-
* Build the container
|
|
350
|
-
* @return {Void}
|
|
351
|
-
*/
|
|
352
|
-
var build = function () {
|
|
353
|
-
this.requiresPagination = this.config.pagination && this.config.pagination > 0;
|
|
354
|
-
// Set width
|
|
355
|
-
if (isset(this.config, "width")) {
|
|
356
|
-
if (util.isInt(this.config.width)) {
|
|
357
|
-
this.width = this.config.width + "px";
|
|
358
|
-
}
|
|
359
|
-
else {
|
|
360
|
-
if (this.config.width === "auto") {
|
|
361
|
-
this.width = "100%";
|
|
362
|
-
}
|
|
363
|
-
else { // if (util.includes(this.config.width, "%")) {
|
|
364
|
-
this.width = this.config.width;
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
}
|
|
368
|
-
this.container = util.createElement("div", {
|
|
369
|
-
class: "jsl-container"
|
|
370
|
-
});
|
|
371
|
-
// Custom className
|
|
372
|
-
if (this.config.customClass) {
|
|
373
|
-
util.addClass(this.container, this.config.customClass);
|
|
374
|
-
}
|
|
375
|
-
// Mobile device
|
|
376
|
-
if (this.mobileDevice) {
|
|
377
|
-
util.addClass(this.container, "jsl-mobile");
|
|
378
|
-
}
|
|
379
|
-
else {
|
|
380
|
-
util.addClass(this.container, "jsl-desktop");
|
|
381
|
-
}
|
|
382
|
-
// Hide the HTMLSelectElement and prevent focus
|
|
383
|
-
this.el.tabIndex = -1;
|
|
384
|
-
// Native dropdown
|
|
385
|
-
if (this.config.nativeDropdown || this.mobileDevice) {
|
|
386
|
-
util.addClass(this.el, "jsl-visible");
|
|
387
|
-
}
|
|
388
|
-
else {
|
|
389
|
-
util.addClass(this.el, "jsl-hidden");
|
|
390
|
-
}
|
|
391
|
-
this.selected = util.createElement("div", {
|
|
392
|
-
class: "jsl-selected",
|
|
393
|
-
disabled: this.disabled,
|
|
394
|
-
tabIndex: 0,
|
|
395
|
-
"aria-expanded": false
|
|
396
|
-
});
|
|
397
|
-
this.label = util.createElement(this.el.multiple ? "ul" : "span", {
|
|
398
|
-
class: "jsl-label"
|
|
399
|
-
});
|
|
400
|
-
var dropdown = util.createElement("div", {
|
|
401
|
-
class: "jsl-options-container"
|
|
402
|
-
});
|
|
403
|
-
this.tree = util.createElement("ul", {
|
|
404
|
-
class: "jsl-options",
|
|
405
|
-
role: "tree",
|
|
406
|
-
"aria-hidden": true,
|
|
407
|
-
"aria-expanded": false
|
|
408
|
-
});
|
|
409
|
-
this.notice = util.createElement("div", {
|
|
410
|
-
class: "jsl-notice"
|
|
411
|
-
});
|
|
412
|
-
this.el.setAttribute("aria-hidden", true);
|
|
413
|
-
if (this.disabled) {
|
|
414
|
-
this.el.disabled = true;
|
|
415
|
-
}
|
|
416
|
-
if (this.el.multiple) {
|
|
417
|
-
util.addClass(this.label, "jsl-tags");
|
|
418
|
-
util.addClass(this.container, "multiple");
|
|
419
|
-
// Collection of tags
|
|
420
|
-
this.tags = [];
|
|
421
|
-
// Collection of selected values
|
|
422
|
-
// #93 defaultSelected = false did not work as expected
|
|
423
|
-
this.selectedValues = (this.config.defaultSelected) ? this.getSelectedProperties('value') : [];
|
|
424
|
-
// Collection of selected indexes
|
|
425
|
-
this.selectedIndexes = this.getSelectedProperties('idx');
|
|
426
|
-
}
|
|
427
|
-
else {
|
|
428
|
-
// #93 defaultSelected = false did not work as expected
|
|
429
|
-
// these values were undefined
|
|
430
|
-
this.selectedValue = null;
|
|
431
|
-
this.selectedIndex = -1;
|
|
432
|
-
}
|
|
433
|
-
this.selected.appendChild(this.label);
|
|
434
|
-
if (this.config.clearable) {
|
|
435
|
-
this.selectClear = util.createElement("button", {
|
|
436
|
-
class: "jsl-clear",
|
|
437
|
-
type: "button"
|
|
438
|
-
});
|
|
439
|
-
this.container.appendChild(this.selectClear);
|
|
440
|
-
util.addClass(this.container, "clearable");
|
|
441
|
-
}
|
|
442
|
-
if (this.config.taggable) {
|
|
443
|
-
var li = util.createElement('li', {
|
|
444
|
-
class: 'input-tag'
|
|
445
|
-
});
|
|
446
|
-
this.input = util.createElement("input", {
|
|
447
|
-
class: "jsl-tag-input",
|
|
448
|
-
placeholder: this.config.tagPlaceholder,
|
|
449
|
-
tagIndex: 0,
|
|
450
|
-
autocomplete: "off",
|
|
451
|
-
autocorrect: "off",
|
|
452
|
-
autocapitalize: "off",
|
|
453
|
-
spellcheck: "false",
|
|
454
|
-
role: "textbox",
|
|
455
|
-
type: "search"
|
|
456
|
-
});
|
|
457
|
-
li.appendChild(this.input);
|
|
458
|
-
this.label.appendChild(li);
|
|
459
|
-
util.addClass(this.container, "taggable");
|
|
460
|
-
this.tagSeperators = [","];
|
|
461
|
-
if (this.config.tagSeperators) {
|
|
462
|
-
this.tagSeperators = this.tagSeperators.concat(this.config.tagSeperators);
|
|
463
|
-
var _aTempEscapedSeperators = [];
|
|
464
|
-
for (var _nTagSeperatorStepCount = 0; _nTagSeperatorStepCount < this.tagSeperators.length; _nTagSeperatorStepCount++) {
|
|
465
|
-
_aTempEscapedSeperators.push(util.escapeRegExp(this.tagSeperators[_nTagSeperatorStepCount]));
|
|
466
|
-
}
|
|
467
|
-
this.tagSeperatorsRegex = new RegExp(_aTempEscapedSeperators.join('|'), 'i');
|
|
468
|
-
}
|
|
469
|
-
else {
|
|
470
|
-
this.tagSeperatorsRegex = new RegExp(',', 'i');
|
|
471
|
-
}
|
|
472
|
-
}
|
|
473
|
-
if (this.config.searchable) {
|
|
474
|
-
this.input = util.createElement("input", {
|
|
475
|
-
class: "jsl-input",
|
|
476
|
-
tagIndex: -1,
|
|
477
|
-
autocomplete: "off",
|
|
478
|
-
autocorrect: "off",
|
|
479
|
-
autocapitalize: "off",
|
|
480
|
-
spellcheck: "false",
|
|
481
|
-
role: "textbox",
|
|
482
|
-
// type: "search",
|
|
483
|
-
type: "text",
|
|
484
|
-
placeholder: this.config.messages.searchPlaceholder
|
|
485
|
-
});
|
|
486
|
-
this.inputClear = util.createElement("button", {
|
|
487
|
-
class: "jsl-input-clear",
|
|
488
|
-
type: "button"
|
|
489
|
-
});
|
|
490
|
-
this.inputContainer = util.createElement("div", {
|
|
491
|
-
class: "jsl-input-container"
|
|
492
|
-
});
|
|
493
|
-
this.inputContainer.appendChild(this.input);
|
|
494
|
-
this.inputContainer.appendChild(this.inputClear);
|
|
495
|
-
dropdown.appendChild(this.inputContainer);
|
|
496
|
-
}
|
|
497
|
-
dropdown.appendChild(this.notice);
|
|
498
|
-
dropdown.appendChild(this.tree);
|
|
499
|
-
// List of items for the dropdown
|
|
500
|
-
this.items = [];
|
|
501
|
-
// Establish options
|
|
502
|
-
this.options = [];
|
|
503
|
-
// Check for options in the element
|
|
504
|
-
if (this.el.options.length) {
|
|
505
|
-
this.options = [].slice.call(this.el.options);
|
|
506
|
-
}
|
|
507
|
-
// Element may have optgroups so
|
|
508
|
-
// iterate element.children instead of element.options
|
|
509
|
-
var group = false, j = 0;
|
|
510
|
-
if (this.el.children.length) {
|
|
511
|
-
util.each(this.el.children, function (i, element) {
|
|
512
|
-
if (element.nodeName === "OPTGROUP") {
|
|
513
|
-
group = util.createElement("ul", {
|
|
514
|
-
class: "jsl-optgroup",
|
|
515
|
-
role: "group",
|
|
516
|
-
html: "<li class='jsl-optgroup--label'>" + element.label + "</li>"
|
|
517
|
-
});
|
|
518
|
-
util.each(element.children, function (x, el) {
|
|
519
|
-
el.idx = j;
|
|
520
|
-
var itm = createItem.call(this, el, group);
|
|
521
|
-
itm.optgroup = group;
|
|
522
|
-
// group.appendChild(util.toDom(itm));
|
|
523
|
-
j++;
|
|
524
|
-
}, this);
|
|
525
|
-
}
|
|
526
|
-
else {
|
|
527
|
-
element.idx = j;
|
|
528
|
-
createItem.call(this, element);
|
|
529
|
-
j++;
|
|
530
|
-
}
|
|
531
|
-
}, this);
|
|
532
|
-
}
|
|
533
|
-
// Options defined by the data option
|
|
534
|
-
if (this.config.data && Array.isArray(this.config.data)) {
|
|
535
|
-
this.data = [];
|
|
536
|
-
var optgroup = false, option;
|
|
537
|
-
group = false;
|
|
538
|
-
j = 0;
|
|
539
|
-
util.each(this.config.data, function (i, opt) {
|
|
540
|
-
// Check for group options
|
|
541
|
-
if (isset(opt, "children")) {
|
|
542
|
-
optgroup = util.createElement("optgroup", {
|
|
543
|
-
label: opt.text
|
|
544
|
-
});
|
|
545
|
-
group = util.createElement("ul", {
|
|
546
|
-
class: "jsl-optgroup",
|
|
547
|
-
role: "group",
|
|
548
|
-
html: "<li class='jsl-optgroup--label'>" + opt.text + "</li>"
|
|
549
|
-
});
|
|
550
|
-
util.each(opt.children, function (x, data) {
|
|
551
|
-
option = new Option(data.text, data.value, false, data.hasOwnProperty("selected") && data.selected === true);
|
|
552
|
-
option.disabled = isset(data, "disabled");
|
|
553
|
-
this.options.push(option);
|
|
554
|
-
optgroup.appendChild(option);
|
|
555
|
-
option.idx = j;
|
|
556
|
-
var itm = createItem.call(this, option, data);
|
|
557
|
-
itm.optgroup = group;
|
|
558
|
-
// group.appendChild(util.toDom(itm));
|
|
559
|
-
this.data[j] = data;
|
|
560
|
-
j++;
|
|
561
|
-
}, this);
|
|
562
|
-
this.el.appendChild(optgroup);
|
|
563
|
-
}
|
|
564
|
-
else {
|
|
565
|
-
option = new Option(opt.text, opt.value, false, opt.hasOwnProperty("selected") && opt.selected === true);
|
|
566
|
-
option.disabled = isset(opt, "disabled");
|
|
567
|
-
this.options.push(option);
|
|
568
|
-
option.idx = j;
|
|
569
|
-
createItem.call(this, option, opt);
|
|
570
|
-
this.data[j] = opt;
|
|
571
|
-
j++;
|
|
572
|
-
}
|
|
573
|
-
}, this);
|
|
574
|
-
}
|
|
575
|
-
this.setSelected(true);
|
|
576
|
-
var first;
|
|
577
|
-
this.navIndex = 0;
|
|
578
|
-
for (var i = 0; i < this.items.length; i++) {
|
|
579
|
-
first = this.items[i];
|
|
580
|
-
if (!first.disabled) {
|
|
581
|
-
first.active = true;
|
|
582
|
-
this.navIndex = i;
|
|
583
|
-
break;
|
|
584
|
-
}
|
|
585
|
-
// if (!util.hasClass(first, "disabled")) {
|
|
586
|
-
// util.addClass(first, "active");
|
|
587
|
-
// this.navIndex = i;
|
|
588
|
-
// break;
|
|
589
|
-
// }
|
|
590
|
-
}
|
|
591
|
-
// Check for pagination / infinite scroll
|
|
592
|
-
this.pageIndex = 1;
|
|
593
|
-
// Create the pages
|
|
594
|
-
this.paginate();
|
|
595
|
-
this.container.appendChild(this.selected);
|
|
596
|
-
this.container.appendChild(dropdown);
|
|
597
|
-
this.placeEl = util.createElement("div", {
|
|
598
|
-
class: "jsl-placeholder"
|
|
599
|
-
});
|
|
600
|
-
// Set the placeholder
|
|
601
|
-
this.setPlaceholder();
|
|
602
|
-
this.selected.appendChild(this.placeEl);
|
|
603
|
-
// Disable if required
|
|
604
|
-
if (this.disabled) {
|
|
605
|
-
this.disable();
|
|
606
|
-
}
|
|
607
|
-
this.el.parentNode.insertBefore(this.container, this.el);
|
|
608
|
-
this.container.appendChild(this.el);
|
|
609
|
-
};
|
|
610
|
-
/**
|
|
611
|
-
* Navigate through the dropdown
|
|
612
|
-
* @param {obj} e
|
|
613
|
-
* @return {void}
|
|
614
|
-
*/
|
|
615
|
-
var navigate = function (e) {
|
|
616
|
-
e = e || window.event;
|
|
617
|
-
// Filter out the keys we don"t want
|
|
618
|
-
if (!this.items.length || !this.opened || !util.includes([13, 38, 40], e.which)) {
|
|
619
|
-
this.navigating = false;
|
|
620
|
-
return;
|
|
621
|
-
}
|
|
622
|
-
e.preventDefault();
|
|
623
|
-
if (e.which === 13) {
|
|
624
|
-
if (this.noResults || (this.config.taggable && this.input.value.length > 0)) {
|
|
625
|
-
return false;
|
|
626
|
-
}
|
|
627
|
-
return this.change(this.navIndex);
|
|
628
|
-
}
|
|
629
|
-
var direction, prevEl = this.items[this.navIndex];
|
|
630
|
-
var lastIndex = this.navIndex;
|
|
631
|
-
switch (e.which) {
|
|
632
|
-
case 38:
|
|
633
|
-
direction = 0;
|
|
634
|
-
if (this.navIndex > 0) {
|
|
635
|
-
this.navIndex--;
|
|
636
|
-
}
|
|
637
|
-
break;
|
|
638
|
-
case 40:
|
|
639
|
-
direction = 1;
|
|
640
|
-
if (this.navIndex < this.items.length - 1) {
|
|
641
|
-
this.navIndex++;
|
|
642
|
-
}
|
|
643
|
-
}
|
|
644
|
-
this.navigating = true;
|
|
645
|
-
function validForSelection(itm) {
|
|
646
|
-
if (itm == null || itm.dom == null) {
|
|
647
|
-
return false;
|
|
648
|
-
}
|
|
649
|
-
return !util.hasClass(itm.dom, "disabled") && !util.hasClass(itm.dom, "excluded");
|
|
650
|
-
}
|
|
651
|
-
// Instead of wasting memory holding a copy of this.items
|
|
652
|
-
// with disabled / excluded options omitted, skip them instead
|
|
653
|
-
// while (util.hasClass(this.items[this.navIndex], "disabled") || util.hasClass(this.items[this.navIndex], "excluded")) {
|
|
654
|
-
while (!validForSelection(this.items[this.navIndex])) {
|
|
655
|
-
if (this.navIndex > 0 && this.navIndex < this.items.length - 1) {
|
|
656
|
-
if (direction) {
|
|
657
|
-
this.navIndex++;
|
|
658
|
-
}
|
|
659
|
-
else {
|
|
660
|
-
this.navIndex--;
|
|
661
|
-
}
|
|
662
|
-
}
|
|
663
|
-
else {
|
|
664
|
-
this.navIndex = lastIndex;
|
|
665
|
-
break;
|
|
666
|
-
}
|
|
667
|
-
if (this.searching) {
|
|
668
|
-
if (this.navIndex > this.tree.lastElementChild.idx) {
|
|
669
|
-
this.navIndex = this.tree.lastElementChild.idx;
|
|
670
|
-
break;
|
|
671
|
-
}
|
|
672
|
-
else if (this.navIndex < this.tree.firstElementChild.idx) {
|
|
673
|
-
this.navIndex = this.tree.firstElementChild.idx;
|
|
674
|
-
break;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
}
|
|
678
|
-
// Autoscroll the dropdown during navigation
|
|
679
|
-
var r = util.rect(this.items[this.navIndex].dom);
|
|
680
|
-
if (!direction) {
|
|
681
|
-
if (this.navIndex === 0) {
|
|
682
|
-
this.tree.scrollTop = 0;
|
|
683
|
-
}
|
|
684
|
-
else if (r.top - this.optsRect.top < 0) {
|
|
685
|
-
this.tree.scrollTop = this.tree.scrollTop + (r.top - this.optsRect.top);
|
|
686
|
-
}
|
|
687
|
-
}
|
|
688
|
-
else {
|
|
689
|
-
if (this.navIndex === 0) {
|
|
690
|
-
this.tree.scrollTop = 0;
|
|
691
|
-
}
|
|
692
|
-
else if ((r.top + r.height) > (this.optsRect.top + this.optsRect.height)) {
|
|
693
|
-
this.tree.scrollTop = this.tree.scrollTop + ((r.top + r.height) - (this.optsRect.top + this.optsRect.height));
|
|
694
|
-
}
|
|
695
|
-
// Load another page if needed
|
|
696
|
-
if (this.navIndex === this.tree.childElementCount - 1 && this.requiresPagination) {
|
|
697
|
-
load.call(this);
|
|
698
|
-
}
|
|
699
|
-
}
|
|
700
|
-
if (prevEl) {
|
|
701
|
-
removeActive(this.tree);
|
|
702
|
-
prevEl.active = false;
|
|
703
|
-
}
|
|
704
|
-
util.addClass(this.items[this.navIndex].dom, "active");
|
|
705
|
-
this.items[this.navIndex].active = true;
|
|
706
|
-
};
|
|
707
|
-
/**
|
|
708
|
-
* Add a tag
|
|
709
|
-
* @param {HTMLElement} item
|
|
710
|
-
*/
|
|
711
|
-
var addTag = function (item) {
|
|
712
|
-
var that = this, r;
|
|
713
|
-
var docFrag = document.createDocumentFragment();
|
|
714
|
-
var option = this.options[item.idx];
|
|
715
|
-
var data = this.data ? this.data[item.idx] : option;
|
|
716
|
-
var elementData = { class: "jsl-tag", html: undefined, textContent: undefined };
|
|
717
|
-
if (this.customSelected) {
|
|
718
|
-
elementData.html = this.config.renderSelection(data); // asume xss prevention in custom render function
|
|
719
|
-
}
|
|
720
|
-
else {
|
|
721
|
-
elementData.textContent = option.textContent;
|
|
722
|
-
}
|
|
723
|
-
var tag = util.createElement("li", elementData);
|
|
724
|
-
var btn = util.createElement("button", {
|
|
725
|
-
class: "jsl-tag-remove",
|
|
726
|
-
type: "button"
|
|
727
|
-
});
|
|
728
|
-
tag.appendChild(btn);
|
|
729
|
-
// Set property to check against later
|
|
730
|
-
tag.idx = item.idx;
|
|
731
|
-
tag.tag = option.value;
|
|
732
|
-
this.tags.push(tag);
|
|
733
|
-
if (this.config.sortSelected) {
|
|
734
|
-
var tags = this.tags.slice();
|
|
735
|
-
// Deal with values that contain numbers
|
|
736
|
-
r = function (val, arr) {
|
|
737
|
-
val.replace(/(\d+)|(\D+)/g, function (that, $1, $2) {
|
|
738
|
-
arr.push([$1 || Infinity, $2 || ""]);
|
|
739
|
-
});
|
|
740
|
-
};
|
|
741
|
-
tags.sort(function (a, b) {
|
|
742
|
-
var x = [], y = [], ac, bc;
|
|
743
|
-
if (that.config.sortSelected === true) {
|
|
744
|
-
ac = a.tag;
|
|
745
|
-
bc = b.tag;
|
|
746
|
-
}
|
|
747
|
-
else if (that.config.sortSelected === 'text') {
|
|
748
|
-
ac = a.textContent;
|
|
749
|
-
bc = b.textContent;
|
|
750
|
-
}
|
|
751
|
-
r(ac, x);
|
|
752
|
-
r(bc, y);
|
|
753
|
-
while (x.length && y.length) {
|
|
754
|
-
var ax = x.shift();
|
|
755
|
-
var by = y.shift();
|
|
756
|
-
var nn = (ax[0] - by[0]) || ax[1].localeCompare(by[1]);
|
|
757
|
-
if (nn)
|
|
758
|
-
return nn;
|
|
759
|
-
}
|
|
760
|
-
return x.length - y.length;
|
|
761
|
-
});
|
|
762
|
-
util.each(tags, function (i, tg) {
|
|
763
|
-
docFrag.appendChild(tg);
|
|
764
|
-
});
|
|
765
|
-
this.label.innerHTML = "";
|
|
766
|
-
}
|
|
767
|
-
else {
|
|
768
|
-
docFrag.appendChild(tag);
|
|
769
|
-
}
|
|
770
|
-
if (this.config.taggable) {
|
|
771
|
-
this.label.insertBefore(docFrag, this.input.parentNode);
|
|
772
|
-
}
|
|
773
|
-
else {
|
|
774
|
-
this.label.appendChild(docFrag);
|
|
775
|
-
}
|
|
776
|
-
};
|
|
777
|
-
/**
|
|
778
|
-
* Remove a tag
|
|
779
|
-
* @param {HTMLElement} item
|
|
780
|
-
* @return {void}
|
|
781
|
-
*/
|
|
782
|
-
var removeTag = function (item) {
|
|
783
|
-
var tag = false;
|
|
784
|
-
util.each(this.tags, function (i, t) {
|
|
785
|
-
if (t.idx === item.idx) {
|
|
786
|
-
tag = t;
|
|
787
|
-
}
|
|
788
|
-
}, this);
|
|
789
|
-
if (tag) {
|
|
790
|
-
this.label.removeChild(tag);
|
|
791
|
-
this.tags.splice(this.tags.indexOf(tag), 1);
|
|
792
|
-
}
|
|
793
|
-
};
|
|
794
|
-
/**
|
|
795
|
-
* Load the next page of items
|
|
796
|
-
* @return {void}
|
|
797
|
-
*/
|
|
798
|
-
var load = function () {
|
|
799
|
-
// sometimes atBottom = false when atBottom < 0.xxx pixel smaller than "this.pageIndex < this.pages.length"
|
|
800
|
-
var margin = 1; // solution: add a bit of margin
|
|
801
|
-
var tree = this.tree;
|
|
802
|
-
var scrollTop = tree.scrollTop;
|
|
803
|
-
var scrollHeight = tree.scrollHeight;
|
|
804
|
-
var offsetHeight = tree.offsetHeight;
|
|
805
|
-
var atBottom = scrollTop + margin >= (scrollHeight - offsetHeight);
|
|
806
|
-
if ((atBottom && this.pageIndex < this.pages.length)) {
|
|
807
|
-
var f = document.createDocumentFragment();
|
|
808
|
-
util.each(this.pages[this.pageIndex], function (i, item) {
|
|
809
|
-
appendItem(item, f, this.customOption);
|
|
810
|
-
}, this);
|
|
811
|
-
tree.appendChild(f);
|
|
812
|
-
this.pageIndex++;
|
|
813
|
-
this.emit("selectr.paginate", {
|
|
814
|
-
items: this.items.length,
|
|
815
|
-
total: this.data.length,
|
|
816
|
-
page: this.pageIndex,
|
|
817
|
-
pages: this.pages.length
|
|
818
|
-
});
|
|
819
|
-
}
|
|
820
|
-
};
|
|
821
|
-
/**
|
|
822
|
-
* Clear a search
|
|
823
|
-
* @return {void}
|
|
824
|
-
*/
|
|
825
|
-
var clearSearch = function () {
|
|
826
|
-
this.searchResults = null;
|
|
827
|
-
if (this.config.searchable || this.config.taggable) {
|
|
828
|
-
this.input.value = null;
|
|
829
|
-
this.searching = false;
|
|
830
|
-
if (this.config.searchable) {
|
|
831
|
-
util.removeClass(this.inputContainer, "active");
|
|
832
|
-
}
|
|
833
|
-
if (util.hasClass(this.container, "notice")) {
|
|
834
|
-
util.removeClass(this.container, "notice");
|
|
835
|
-
util.addClass(this.container, "open");
|
|
836
|
-
this.input.focus();
|
|
837
|
-
}
|
|
838
|
-
util.each(this.items, function (i, item) {
|
|
839
|
-
// Items that didn't match need the class
|
|
840
|
-
// removing to make them visible again
|
|
841
|
-
if (item.dom) {
|
|
842
|
-
util.removeClass(item.dom, "excluded");
|
|
843
|
-
}
|
|
844
|
-
// Remove the span element for underlining matched items
|
|
845
|
-
if (!this.customOption) {
|
|
846
|
-
// without xss
|
|
847
|
-
item.textContent = item.textContent;
|
|
848
|
-
}
|
|
849
|
-
}, this);
|
|
850
|
-
this.pageIndex = 1;
|
|
851
|
-
this.paginate();
|
|
852
|
-
}
|
|
853
|
-
};
|
|
854
|
-
// /**
|
|
855
|
-
// * Query matching for searches.
|
|
856
|
-
// * Wraps matching text in a span.jsl-match.
|
|
857
|
-
// *
|
|
858
|
-
// * @param {string} query
|
|
859
|
-
// * @param {HTMLOptionElement} option element
|
|
860
|
-
// * @return {bool} true if matched; false otherwise
|
|
861
|
-
// */
|
|
862
|
-
// var match = function (query, option) {
|
|
863
|
-
// var text = option.textContent;
|
|
864
|
-
// var RX = new RegExp(query, "ig");
|
|
865
|
-
// var result = RX.exec(text);
|
|
866
|
-
// if (result) {
|
|
867
|
-
// // #102 stop xss
|
|
868
|
-
// option.innerHTML = "";
|
|
869
|
-
// var span = document.createElement("span");
|
|
870
|
-
// span.classList.add("jsl-match");
|
|
871
|
-
// span.textContent = result[0];
|
|
872
|
-
// option.appendChild(document.createTextNode(text.substring(0, result.index)));
|
|
873
|
-
// option.appendChild(span);
|
|
874
|
-
// option.appendChild(document.createTextNode(text.substring(RX.lastIndex)));
|
|
875
|
-
// return true;
|
|
876
|
-
// }
|
|
877
|
-
// return false;
|
|
878
|
-
// };
|
|
879
|
-
// Main Lib
|
|
880
|
-
Selectr = function (el, config) {
|
|
881
|
-
if (!el) {
|
|
882
|
-
throw new Error("You must supply either a HTMLSelectElement or a CSS3 selector string.");
|
|
883
|
-
}
|
|
884
|
-
this.el = el;
|
|
885
|
-
// CSS3 selector string
|
|
886
|
-
if (typeof el === "string") {
|
|
887
|
-
this.el = document.querySelector(el);
|
|
888
|
-
}
|
|
889
|
-
if (this.el === null) {
|
|
890
|
-
throw new Error("The element you passed to Selectr can not be found.");
|
|
891
|
-
}
|
|
892
|
-
if (this.el.nodeName.toLowerCase() !== "select") {
|
|
893
|
-
throw new Error("The element you passed to Selectr is not a HTMLSelectElement.");
|
|
894
|
-
}
|
|
895
|
-
this.render(config);
|
|
896
|
-
};
|
|
897
|
-
/**
|
|
898
|
-
* Render the instance
|
|
899
|
-
* @param {object} config
|
|
900
|
-
* @return {void}
|
|
901
|
-
*/
|
|
902
|
-
Selectr.prototype.render = function (config) {
|
|
903
|
-
if (this.rendered)
|
|
904
|
-
return;
|
|
905
|
-
/**
|
|
906
|
-
* Default configuration options
|
|
907
|
-
* @type {Object}
|
|
908
|
-
*/
|
|
909
|
-
var defaultConfig = {
|
|
910
|
-
/**
|
|
911
|
-
* Emulates browser behaviour by selecting the first option by default
|
|
912
|
-
* @type {Boolean}
|
|
913
|
-
*/
|
|
914
|
-
defaultSelected: true,
|
|
915
|
-
/**
|
|
916
|
-
* Sets the width of the container
|
|
917
|
-
* @type {String}
|
|
918
|
-
*/
|
|
919
|
-
width: "auto",
|
|
920
|
-
/**
|
|
921
|
-
* Enables/ disables the container
|
|
922
|
-
* @type {Boolean}
|
|
923
|
-
*/
|
|
924
|
-
disabled: false,
|
|
925
|
-
/**
|
|
926
|
-
* Enables/ disables logic for mobile
|
|
927
|
-
* @type {Boolean}
|
|
928
|
-
*/
|
|
929
|
-
disabledMobile: true,
|
|
930
|
-
/**
|
|
931
|
-
* Enables / disables the search function
|
|
932
|
-
* @type {Boolean}
|
|
933
|
-
*/
|
|
934
|
-
searchable: true,
|
|
935
|
-
/**
|
|
936
|
-
* Enable disable the clear button
|
|
937
|
-
* @type {Boolean}
|
|
938
|
-
*/
|
|
939
|
-
clearable: false,
|
|
940
|
-
/**
|
|
941
|
-
* Sort the tags / multiselect options
|
|
942
|
-
* @type {Boolean}
|
|
943
|
-
*/
|
|
944
|
-
sortSelected: false,
|
|
945
|
-
/**
|
|
946
|
-
* Allow deselecting of select-one options
|
|
947
|
-
* @type {Boolean}
|
|
948
|
-
*/
|
|
949
|
-
allowDeselect: false,
|
|
950
|
-
/**
|
|
951
|
-
* Close the dropdown when scrolling (@AlexanderReiswich, #11)
|
|
952
|
-
* @type {Boolean}
|
|
953
|
-
*/
|
|
954
|
-
closeOnScroll: false,
|
|
955
|
-
/**
|
|
956
|
-
* Allow the use of the native dropdown (@jonnyscholes, #14)
|
|
957
|
-
* @type {Boolean}
|
|
958
|
-
*/
|
|
959
|
-
nativeDropdown: false,
|
|
960
|
-
/**
|
|
961
|
-
* Allow the use of native typing behavior for toggling, searching, selecting
|
|
962
|
-
* @type {boolean}
|
|
963
|
-
*/
|
|
964
|
-
nativeKeyboard: false,
|
|
965
|
-
/**
|
|
966
|
-
* Set the main placeholder
|
|
967
|
-
* @type {String}
|
|
968
|
-
*/
|
|
969
|
-
placeholder: UILang.dropDown.SelectOption,
|
|
970
|
-
/**
|
|
971
|
-
* Allow the tagging feature
|
|
972
|
-
* @type {Boolean}
|
|
973
|
-
*/
|
|
974
|
-
taggable: false,
|
|
975
|
-
/**
|
|
976
|
-
* Set the tag input placeholder (@labikmartin, #21, #22)
|
|
977
|
-
* @type {String}
|
|
978
|
-
*/
|
|
979
|
-
tagPlaceholder: UILang.dropDown.SelectOptionMultiple,
|
|
980
|
-
messages: {
|
|
981
|
-
noResults: UILang.dropDown.NoResults,
|
|
982
|
-
noOptions: UILang.dropDown.NoOptions,
|
|
983
|
-
maxSelections: UILang.dropDown.MaxSelection,
|
|
984
|
-
tagDuplicate: UILang.dropDown.AlreadyInUse,
|
|
985
|
-
searchPlaceholder: UILang.dropDown.Search
|
|
986
|
-
}
|
|
987
|
-
};
|
|
988
|
-
// add instance reference (#87)
|
|
989
|
-
this.el.selectr = this;
|
|
990
|
-
// Merge defaults with user set config
|
|
991
|
-
this.config = util.extend(defaultConfig, config);
|
|
992
|
-
// Store type
|
|
993
|
-
this.originalType = this.el.type;
|
|
994
|
-
// Store tabIndex
|
|
995
|
-
this.originalIndex = this.el.tabIndex;
|
|
996
|
-
// Store defaultSelected options for form reset
|
|
997
|
-
this.defaultSelected = [];
|
|
998
|
-
// Store the original option count
|
|
999
|
-
this.originalOptionCount = this.el.options.length;
|
|
1000
|
-
if (this.config.multiple || this.config.taggable) {
|
|
1001
|
-
this.el.multiple = true;
|
|
1002
|
-
}
|
|
1003
|
-
// Disabled?
|
|
1004
|
-
this.disabled = isset(this.config, "disabled");
|
|
1005
|
-
this.opened = false;
|
|
1006
|
-
if (this.config.taggable) {
|
|
1007
|
-
this.config.searchable = false;
|
|
1008
|
-
}
|
|
1009
|
-
this.navigating = false;
|
|
1010
|
-
this.mobileDevice = false;
|
|
1011
|
-
if (!this.config.disabledMobile && /Android|webOS|iPhone|iPad|BlackBerry|Windows Phone|Opera Mini|IEMobile|Mobile/i.test(navigator.userAgent)) {
|
|
1012
|
-
this.mobileDevice = true;
|
|
1013
|
-
}
|
|
1014
|
-
this.customOption = this.config.hasOwnProperty("renderOption") && typeof this.config.renderOption === "function";
|
|
1015
|
-
this.customSelected = this.config.hasOwnProperty("renderSelection") && typeof this.config.renderSelection === "function";
|
|
1016
|
-
this.supportsEventPassiveOption = this.detectEventPassiveOption();
|
|
1017
|
-
// Enable event emitter
|
|
1018
|
-
Events.mixin(this);
|
|
1019
|
-
build.call(this);
|
|
1020
|
-
this.bindEvents();
|
|
1021
|
-
this.update();
|
|
1022
|
-
this.optsRect = util.rect(this.tree);
|
|
1023
|
-
this.rendered = true;
|
|
1024
|
-
// Fixes macOS Safari bug #28
|
|
1025
|
-
if (!this.el.multiple) {
|
|
1026
|
-
this.el.selectedIndex = this.selectedIndex;
|
|
1027
|
-
}
|
|
1028
|
-
var that = this;
|
|
1029
|
-
setTimeout(function () {
|
|
1030
|
-
that.emit("selectr.init");
|
|
1031
|
-
}, 20);
|
|
1032
|
-
};
|
|
1033
|
-
Selectr.prototype.getSelected = function () {
|
|
1034
|
-
var selected = this.el.querySelectorAll('option:checked');
|
|
1035
|
-
return selected;
|
|
1036
|
-
};
|
|
1037
|
-
Selectr.prototype.getSelectedProperties = function (prop) {
|
|
1038
|
-
var selected = this.getSelected();
|
|
1039
|
-
var values = [].slice.call(selected)
|
|
1040
|
-
.map(function (option) { return option[prop]; })
|
|
1041
|
-
.filter(function (i) { return i !== null && i !== undefined; });
|
|
1042
|
-
return values;
|
|
1043
|
-
};
|
|
1044
|
-
/**
|
|
1045
|
-
* Feature detection: addEventListener passive option
|
|
1046
|
-
* https://dom.spec.whatwg.org/#dom-addeventlisteneroptions-passive
|
|
1047
|
-
* https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
|
|
1048
|
-
*/
|
|
1049
|
-
Selectr.prototype.detectEventPassiveOption = function () {
|
|
1050
|
-
var supportsPassiveOption = false;
|
|
1051
|
-
try {
|
|
1052
|
-
var opts = Object.defineProperty({}, 'passive', {
|
|
1053
|
-
get: function () {
|
|
1054
|
-
supportsPassiveOption = true;
|
|
1055
|
-
}
|
|
1056
|
-
});
|
|
1057
|
-
window.addEventListener('test', null, opts);
|
|
1058
|
-
}
|
|
1059
|
-
catch (e) { }
|
|
1060
|
-
return supportsPassiveOption;
|
|
1061
|
-
};
|
|
1062
|
-
/**
|
|
1063
|
-
* Attach the required event listeners
|
|
1064
|
-
*/
|
|
1065
|
-
Selectr.prototype.bindEvents = function () {
|
|
1066
|
-
var that = this;
|
|
1067
|
-
this.events = {};
|
|
1068
|
-
this.events.dismiss = dismiss.bind(this);
|
|
1069
|
-
this.events.navigate = navigate.bind(this);
|
|
1070
|
-
this.events.reset = this.reset.bind(this);
|
|
1071
|
-
if (this.config.nativeDropdown || this.mobileDevice) {
|
|
1072
|
-
this.container.addEventListener("touchstart", function (e) {
|
|
1073
|
-
if (e.changedTouches[0].target === that.el) {
|
|
1074
|
-
that.toggle();
|
|
1075
|
-
}
|
|
1076
|
-
}, this.supportsEventPassiveOption ? { passive: true } : false);
|
|
1077
|
-
this.container.addEventListener("click", function (e) {
|
|
1078
|
-
if (e.target === that.el) {
|
|
1079
|
-
that.toggle();
|
|
1080
|
-
}
|
|
1081
|
-
});
|
|
1082
|
-
var getChangedOptions = function (last, current) {
|
|
1083
|
-
var added = [], removed = last.slice(0);
|
|
1084
|
-
var idx;
|
|
1085
|
-
for (var i = 0; i < current.length; i++) {
|
|
1086
|
-
idx = removed.indexOf(current[i]);
|
|
1087
|
-
if (idx > -1)
|
|
1088
|
-
removed.splice(idx, 1);
|
|
1089
|
-
else
|
|
1090
|
-
added.push(current[i]);
|
|
1091
|
-
}
|
|
1092
|
-
return [added, removed];
|
|
1093
|
-
};
|
|
1094
|
-
// Listen for the change on the native select
|
|
1095
|
-
// and update accordingly
|
|
1096
|
-
this.el.addEventListener("change", function (e) {
|
|
1097
|
-
if (e.__selfTriggered) {
|
|
1098
|
-
return;
|
|
1099
|
-
}
|
|
1100
|
-
if (that.el.multiple) {
|
|
1101
|
-
var indexes = that.getSelectedProperties('idx');
|
|
1102
|
-
var changes = getChangedOptions(that.selectedIndexes, indexes);
|
|
1103
|
-
util.each(changes[0], function (i, idx) {
|
|
1104
|
-
that.select(idx);
|
|
1105
|
-
}, that);
|
|
1106
|
-
util.each(changes[1], function (i, idx) {
|
|
1107
|
-
that.deselect(idx);
|
|
1108
|
-
}, that);
|
|
1109
|
-
}
|
|
1110
|
-
else {
|
|
1111
|
-
if (that.el.selectedIndex > -1) {
|
|
1112
|
-
that.select(that.el.selectedIndex);
|
|
1113
|
-
}
|
|
1114
|
-
}
|
|
1115
|
-
});
|
|
1116
|
-
}
|
|
1117
|
-
// Open the dropdown with Enter key if focused
|
|
1118
|
-
if (this.config.nativeDropdown) {
|
|
1119
|
-
this.container.addEventListener("keydown", function (e) {
|
|
1120
|
-
if (e.key === "Enter" && that.selected === document.activeElement) {
|
|
1121
|
-
// show native dropdown
|
|
1122
|
-
that.toggle();
|
|
1123
|
-
// focus on it
|
|
1124
|
-
setTimeout(function () {
|
|
1125
|
-
that.el.focus();
|
|
1126
|
-
}, 200);
|
|
1127
|
-
}
|
|
1128
|
-
});
|
|
1129
|
-
}
|
|
1130
|
-
// Non-native dropdown
|
|
1131
|
-
this.selected.addEventListener("click", function (e) {
|
|
1132
|
-
if (!that.disabled) {
|
|
1133
|
-
that.toggle();
|
|
1134
|
-
}
|
|
1135
|
-
e.preventDefault();
|
|
1136
|
-
});
|
|
1137
|
-
if (this.config.nativeKeyboard) {
|
|
1138
|
-
var typing = '';
|
|
1139
|
-
var typingTimeout = null;
|
|
1140
|
-
this.selected.addEventListener("keydown", function (e) {
|
|
1141
|
-
// Do nothing if disabled, not focused, or modifier keys are pressed
|
|
1142
|
-
if (that.disabled ||
|
|
1143
|
-
that.selected !== document.activeElement ||
|
|
1144
|
-
(e.altKey || e.ctrlKey || e.metaKey)) {
|
|
1145
|
-
return;
|
|
1146
|
-
}
|
|
1147
|
-
// Open the dropdown on [enter], [ ], [↓], and [↑] keys
|
|
1148
|
-
if (e.key === " " ||
|
|
1149
|
-
(!that.opened && ["Enter", "ArrowUp", "ArrowDown"].indexOf(e.key) > -1)) {
|
|
1150
|
-
that.toggle();
|
|
1151
|
-
e.preventDefault();
|
|
1152
|
-
e.stopPropagation();
|
|
1153
|
-
return;
|
|
1154
|
-
}
|
|
1155
|
-
// Type to search if multiple; type to select otherwise
|
|
1156
|
-
// make sure e.key is a single, printable character
|
|
1157
|
-
// .length check is a short-circut to skip checking keys like "ArrowDown", etc.
|
|
1158
|
-
// prefer "codePoint" methods; they work with the full range of unicode
|
|
1159
|
-
if (e.key.length <= 2 &&
|
|
1160
|
-
String[String.fromCodePoint ? "fromCodePoint" : "fromCharCode"](e.key[String.codePointAt ? "codePointAt" : "charCodeAt"](0)) === e.key) {
|
|
1161
|
-
if (that.config.multiple) {
|
|
1162
|
-
that.open();
|
|
1163
|
-
if (that.config.searchable) {
|
|
1164
|
-
that.input.value = e.key;
|
|
1165
|
-
that.input.focus();
|
|
1166
|
-
that.search(null, that.options.exactSearch);
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
else {
|
|
1170
|
-
if (typingTimeout) {
|
|
1171
|
-
clearTimeout(typingTimeout);
|
|
1172
|
-
}
|
|
1173
|
-
typing += e.key;
|
|
1174
|
-
var found = that.search(typing, that.options.exactSearch);
|
|
1175
|
-
if (found && found.length) {
|
|
1176
|
-
that.clear();
|
|
1177
|
-
that.setValue(found[0].value);
|
|
1178
|
-
}
|
|
1179
|
-
setTimeout(function () { typing = ''; }, 1000);
|
|
1180
|
-
}
|
|
1181
|
-
e.preventDefault();
|
|
1182
|
-
e.stopPropagation();
|
|
1183
|
-
return;
|
|
1184
|
-
}
|
|
1185
|
-
});
|
|
1186
|
-
// Close the dropdown on [esc] key
|
|
1187
|
-
this.container.addEventListener("keyup", function (e) {
|
|
1188
|
-
if (that.opened && e.key === "Escape") {
|
|
1189
|
-
that.close();
|
|
1190
|
-
e.stopPropagation();
|
|
1191
|
-
// keep focus so we can re-open easily if desired
|
|
1192
|
-
that.selected.focus();
|
|
1193
|
-
}
|
|
1194
|
-
});
|
|
1195
|
-
}
|
|
1196
|
-
// Remove tag
|
|
1197
|
-
this.label.addEventListener("click", function (e) {
|
|
1198
|
-
if (!that.disabled && util.hasClass(e.target, "jsl-tag-remove")) {
|
|
1199
|
-
that.deselect(e.target.parentNode.idx);
|
|
1200
|
-
}
|
|
1201
|
-
});
|
|
1202
|
-
// Clear input
|
|
1203
|
-
if (this.selectClear) {
|
|
1204
|
-
this.selectClear.addEventListener("click", this.clear.bind(this));
|
|
1205
|
-
}
|
|
1206
|
-
// Prevent text selection
|
|
1207
|
-
this.tree.addEventListener("mousedown", function (e) {
|
|
1208
|
-
e.preventDefault();
|
|
1209
|
-
});
|
|
1210
|
-
// Select / deselect items
|
|
1211
|
-
this.tree.addEventListener("click", function (e) {
|
|
1212
|
-
var item = util.closest(e.target, function (el) {
|
|
1213
|
-
return el && util.hasClass(el, "jsl-option");
|
|
1214
|
-
});
|
|
1215
|
-
if (item) {
|
|
1216
|
-
if (!util.hasClass(item, "disabled")) {
|
|
1217
|
-
if (util.hasClass(item, "selected")) {
|
|
1218
|
-
if (that.el.multiple || !that.el.multiple && that.config.allowDeselect) {
|
|
1219
|
-
that.deselect(item.idx);
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
else {
|
|
1223
|
-
that.select(item.idx);
|
|
1224
|
-
}
|
|
1225
|
-
if (that.opened && !that.el.multiple) {
|
|
1226
|
-
that.close();
|
|
1227
|
-
}
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
e.preventDefault();
|
|
1231
|
-
e.stopPropagation();
|
|
1232
|
-
});
|
|
1233
|
-
// Mouseover list items
|
|
1234
|
-
this.tree.addEventListener("mouseover", function (e) {
|
|
1235
|
-
if (util.hasClass(e.target, "jsl-option")) {
|
|
1236
|
-
if (!util.hasClass(e.target, "disabled")) {
|
|
1237
|
-
removeActive(that.tree);
|
|
1238
|
-
util.addClass(e.target, "active");
|
|
1239
|
-
that.navIndex = -1;
|
|
1240
|
-
for (
|
|
1241
|
-
if (that.items[idx].dom === e.target) {
|
|
1242
|
-
that.navIndex = idx;
|
|
1243
|
-
}
|
|
1244
|
-
}
|
|
1245
|
-
// that.navIndex = [].slice.call(that.items).indexOf(e.target);
|
|
1246
|
-
}
|
|
1247
|
-
}
|
|
1248
|
-
});
|
|
1249
|
-
// Searchable
|
|
1250
|
-
if (this.config.searchable) {
|
|
1251
|
-
// Show / hide the search input clear button
|
|
1252
|
-
this.input.addEventListener("focus", function (e) {
|
|
1253
|
-
that.searching = true;
|
|
1254
|
-
});
|
|
1255
|
-
this.input.addEventListener("blur", function (e) {
|
|
1256
|
-
that.searching = false;
|
|
1257
|
-
});
|
|
1258
|
-
this.input.addEventListener("input", function (e) {
|
|
1259
|
-
that.search();
|
|
1260
|
-
if (!that.config.taggable) {
|
|
1261
|
-
// Show / hide the search input clear button
|
|
1262
|
-
if (this.value.length) {
|
|
1263
|
-
util.addClass(this.parentNode, "active");
|
|
1264
|
-
}
|
|
1265
|
-
else {
|
|
1266
|
-
util.removeClass(this.parentNode, "active");
|
|
1267
|
-
}
|
|
1268
|
-
}
|
|
1269
|
-
});
|
|
1270
|
-
// Clear the search input
|
|
1271
|
-
this.inputClear.addEventListener("click", function (e) {
|
|
1272
|
-
that.input.value = null;
|
|
1273
|
-
clearSearch.call(that);
|
|
1274
|
-
if (!that.tree.childElementCount) {
|
|
1275
|
-
render.call(that);
|
|
1276
|
-
}
|
|
1277
|
-
});
|
|
1278
|
-
}
|
|
1279
|
-
if (this.config.taggable) {
|
|
1280
|
-
this.input.addEventListener("keyup", function (e) {
|
|
1281
|
-
that.search();
|
|
1282
|
-
if (that.config.taggable && this.value.length) {
|
|
1283
|
-
var _sVal = this.value.trim();
|
|
1284
|
-
if (_sVal.length && (e.which === 13 || that.tagSeperatorsRegex.test(_sVal))) {
|
|
1285
|
-
var _sGrabbedTagValue = _sVal.replace(that.tagSeperatorsRegex, '');
|
|
1286
|
-
_sGrabbedTagValue = util.escapeRegExp(_sGrabbedTagValue);
|
|
1287
|
-
_sGrabbedTagValue = _sGrabbedTagValue.trim();
|
|
1288
|
-
var _oOption;
|
|
1289
|
-
if (_sGrabbedTagValue.length) {
|
|
1290
|
-
_oOption = that.add({
|
|
1291
|
-
value: _sGrabbedTagValue,
|
|
1292
|
-
textContent: _sGrabbedTagValue,
|
|
1293
|
-
selected: true
|
|
1294
|
-
}, true);
|
|
1295
|
-
}
|
|
1296
|
-
if (_oOption) {
|
|
1297
|
-
that.close();
|
|
1298
|
-
clearSearch.call(that);
|
|
1299
|
-
}
|
|
1300
|
-
else {
|
|
1301
|
-
this.value = '';
|
|
1302
|
-
that.setMessage(that.config.messages.tagDuplicate);
|
|
1303
|
-
}
|
|
1304
|
-
}
|
|
1305
|
-
}
|
|
1306
|
-
});
|
|
1307
|
-
}
|
|
1308
|
-
this.update = util.debounce(function () {
|
|
1309
|
-
// Optionally close dropdown on scroll / resize (#11)
|
|
1310
|
-
if (that.opened && that.config.closeOnScroll) {
|
|
1311
|
-
that.close();
|
|
1312
|
-
}
|
|
1313
|
-
if (that.width) {
|
|
1314
|
-
that.container.style.width = that.width;
|
|
1315
|
-
}
|
|
1316
|
-
that.invert();
|
|
1317
|
-
}, 50);
|
|
1318
|
-
if (this.requiresPagination) {
|
|
1319
|
-
this.paginateItems = util.debounce(function () {
|
|
1320
|
-
load.call(this);
|
|
1321
|
-
}, 50);
|
|
1322
|
-
this.tree.addEventListener("scroll", this.paginateItems.bind(this));
|
|
1323
|
-
}
|
|
1324
|
-
// Dismiss when clicking outside the container
|
|
1325
|
-
document.addEventListener("click", this.events.dismiss);
|
|
1326
|
-
this.container.addEventListener("keydown", this.events.navigate);
|
|
1327
|
-
window.addEventListener("resize", this.update);
|
|
1328
|
-
window.addEventListener("scroll", this.update);
|
|
1329
|
-
// remove event listeners on destroy()
|
|
1330
|
-
this.on('selectr.destroy', function () {
|
|
1331
|
-
document.removeEventListener("click", this.events.dismiss);
|
|
1332
|
-
this.container.removeEventListener("keydown", this.events.navigate);
|
|
1333
|
-
window.removeEventListener("resize", this.update);
|
|
1334
|
-
window.removeEventListener("scroll", this.update);
|
|
1335
|
-
});
|
|
1336
|
-
// Listen for form.reset() (@ambrooks, #13)
|
|
1337
|
-
if (this.el.form) {
|
|
1338
|
-
this.el.form.addEventListener("reset", this.events.reset);
|
|
1339
|
-
// remove listener on destroy()
|
|
1340
|
-
this.on('selectr.destroy', function () {
|
|
1341
|
-
this.el.form.removeEventListener("reset", this.events.reset);
|
|
1342
|
-
});
|
|
1343
|
-
}
|
|
1344
|
-
};
|
|
1345
|
-
/**
|
|
1346
|
-
* Check for selected options
|
|
1347
|
-
* @param {bool} reset
|
|
1348
|
-
*/
|
|
1349
|
-
Selectr.prototype.setSelected = function (reset) {
|
|
1350
|
-
// Select first option as with a native select-one element - #21, #24
|
|
1351
|
-
if (!this.config.data && !this.el.multiple && this.el.options.length) {
|
|
1352
|
-
// Browser has selected the first option by default
|
|
1353
|
-
if (this.el.selectedIndex === 0) {
|
|
1354
|
-
if (!this.el.options[0].defaultSelected && !this.config.defaultSelected) {
|
|
1355
|
-
this.el.selectedIndex = -1;
|
|
1356
|
-
}
|
|
1357
|
-
}
|
|
1358
|
-
this.selectedIndex = this.el.selectedIndex;
|
|
1359
|
-
if (this.selectedIndex > -1) {
|
|
1360
|
-
this.select(this.selectedIndex);
|
|
1361
|
-
}
|
|
1362
|
-
}
|
|
1363
|
-
// If we're changing a select-one to select-multiple via the config
|
|
1364
|
-
// and there are no selected options, the first option will be selected by the browser
|
|
1365
|
-
// Let's prevent that here.
|
|
1366
|
-
if (this.config.multiple && this.originalType === "select-one" && !this.config.data) {
|
|
1367
|
-
if (this.el.options[0].selected && !this.el.options[0].defaultSelected) {
|
|
1368
|
-
this.el.options[0].selected = false;
|
|
1369
|
-
}
|
|
1370
|
-
}
|
|
1371
|
-
util.each(this.options, function (i, option) {
|
|
1372
|
-
if (option.selected && option.defaultSelected) {
|
|
1373
|
-
this.select(option.idx);
|
|
1374
|
-
}
|
|
1375
|
-
}, this);
|
|
1376
|
-
if (this.config.selectedValue) {
|
|
1377
|
-
this.setValue(this.config.selectedValue);
|
|
1378
|
-
}
|
|
1379
|
-
if (this.config.data) {
|
|
1380
|
-
if (!this.el.multiple && this.config.defaultSelected && this.el.selectedIndex < 0 && this.config.data.length > 0) {
|
|
1381
|
-
this.select(0);
|
|
1382
|
-
}
|
|
1383
|
-
var j = 0;
|
|
1384
|
-
util.each(this.config.data, function (i, opt) {
|
|
1385
|
-
// Check for group options
|
|
1386
|
-
if (isset(opt, "children")) {
|
|
1387
|
-
util.each(opt.children, function (x, item) {
|
|
1388
|
-
if (item.hasOwnProperty("selected") && item.selected === true) {
|
|
1389
|
-
this.select(j);
|
|
1390
|
-
}
|
|
1391
|
-
j++;
|
|
1392
|
-
}, this);
|
|
1393
|
-
}
|
|
1394
|
-
else {
|
|
1395
|
-
if (opt.hasOwnProperty("selected") && opt.selected === true) {
|
|
1396
|
-
this.select(j);
|
|
1397
|
-
}
|
|
1398
|
-
j++;
|
|
1399
|
-
}
|
|
1400
|
-
}, this);
|
|
1401
|
-
}
|
|
1402
|
-
};
|
|
1403
|
-
/**
|
|
1404
|
-
* Destroy the instance
|
|
1405
|
-
* @return {void}
|
|
1406
|
-
*/
|
|
1407
|
-
Selectr.prototype.destroy = function () {
|
|
1408
|
-
if (!this.rendered)
|
|
1409
|
-
return;
|
|
1410
|
-
this.emit("selectr.destroy");
|
|
1411
|
-
// Revert to select-single if programtically set to multiple
|
|
1412
|
-
if (this.originalType === 'select-one') {
|
|
1413
|
-
this.el.multiple = false;
|
|
1414
|
-
}
|
|
1415
|
-
if (this.config.data) {
|
|
1416
|
-
this.el.innerHTML = "";
|
|
1417
|
-
}
|
|
1418
|
-
// Remove the className from select element
|
|
1419
|
-
util.removeClass(this.el, 'jsl-hidden');
|
|
1420
|
-
// Replace the container with the original select element
|
|
1421
|
-
this.container.parentNode.replaceChild(this.el, this.container);
|
|
1422
|
-
this.rendered = false;
|
|
1423
|
-
// remove reference
|
|
1424
|
-
delete this.el.selectr;
|
|
1425
|
-
};
|
|
1426
|
-
/**
|
|
1427
|
-
* Change an options state
|
|
1428
|
-
* @param {Number} index
|
|
1429
|
-
* @return {void}
|
|
1430
|
-
*/
|
|
1431
|
-
Selectr.prototype.change = function (index) {
|
|
1432
|
-
var item = this.items[index], option = this.options[index];
|
|
1433
|
-
if (option.disabled) {
|
|
1434
|
-
return;
|
|
1435
|
-
}
|
|
1436
|
-
if (option.selected && util.hasClass(item, "selected")) {
|
|
1437
|
-
this.deselect(index);
|
|
1438
|
-
}
|
|
1439
|
-
else {
|
|
1440
|
-
this.select(index);
|
|
1441
|
-
}
|
|
1442
|
-
if (this.opened && !this.el.multiple) {
|
|
1443
|
-
this.close();
|
|
1444
|
-
}
|
|
1445
|
-
};
|
|
1446
|
-
/**
|
|
1447
|
-
* Select an option
|
|
1448
|
-
* @param {Number} index
|
|
1449
|
-
* @return {void}
|
|
1450
|
-
*/
|
|
1451
|
-
Selectr.prototype.select = function (index) {
|
|
1452
|
-
var item = this.items[index], options = [].slice.call(this.el.options), option = this.options[index];
|
|
1453
|
-
if (this.el.multiple) {
|
|
1454
|
-
if (util.includes(this.selectedIndexes, index)) {
|
|
1455
|
-
return false;
|
|
1456
|
-
}
|
|
1457
|
-
if (this.config.maxSelections && this.tags.length === this.config.maxSelections) {
|
|
1458
|
-
this.setMessage(this.config.messages.maxSelections.replace("{max}", this.config.maxSelections), true);
|
|
1459
|
-
return false;
|
|
1460
|
-
}
|
|
1461
|
-
this.selectedValues.push(option.value);
|
|
1462
|
-
this.selectedIndexes.push(index);
|
|
1463
|
-
addTag.call(this, item);
|
|
1464
|
-
}
|
|
1465
|
-
else {
|
|
1466
|
-
var data = this.data ? this.data[index] : option;
|
|
1467
|
-
if (this.customSelected) {
|
|
1468
|
-
this.label.innerHTML = this.config.renderSelection(data);
|
|
1469
|
-
}
|
|
1470
|
-
else {
|
|
1471
|
-
// no xss
|
|
1472
|
-
this.label.textContent = option.textContent;
|
|
1473
|
-
}
|
|
1474
|
-
this.selectedValue = option.value;
|
|
1475
|
-
this.selectedIndex = index;
|
|
1476
|
-
util.each(this.options, function (i, o) {
|
|
1477
|
-
var opt = this.items[i];
|
|
1478
|
-
if (i !== index) {
|
|
1479
|
-
if (o) {
|
|
1480
|
-
util.removeClass(o, "selected");
|
|
1481
|
-
o.removeAttribute("selected");
|
|
1482
|
-
}
|
|
1483
|
-
opt.selected = false;
|
|
1484
|
-
opt.active = false;
|
|
1485
|
-
}
|
|
1486
|
-
}, this);
|
|
1487
|
-
}
|
|
1488
|
-
if (!util.includes(options, option)) {
|
|
1489
|
-
this.el.add(option);
|
|
1490
|
-
}
|
|
1491
|
-
item.selected = true;
|
|
1492
|
-
// item.dom.setAttribute("aria-selected", true);
|
|
1493
|
-
// util.addClass(item.dom, "selected");
|
|
1494
|
-
util.addClass(this.container, "has-selected");
|
|
1495
|
-
option.selected = true;
|
|
1496
|
-
option.setAttribute("selected", "");
|
|
1497
|
-
this.emit("selectr.change", option);
|
|
1498
|
-
this.emit("selectr.select", option);
|
|
1499
|
-
// fire native change event
|
|
1500
|
-
if ("createEvent" in document) {
|
|
1501
|
-
var evt = document.createEvent("HTMLEvents");
|
|
1502
|
-
evt.initEvent("change", true, true);
|
|
1503
|
-
evt.__selfTriggered = true;
|
|
1504
|
-
this.el.dispatchEvent(evt);
|
|
1505
|
-
}
|
|
1506
|
-
else {
|
|
1507
|
-
this.el.fireEvent("onchange");
|
|
1508
|
-
}
|
|
1509
|
-
};
|
|
1510
|
-
/**
|
|
1511
|
-
* Deselect an option
|
|
1512
|
-
* @param {Number} index
|
|
1513
|
-
* @return {void}
|
|
1514
|
-
*/
|
|
1515
|
-
Selectr.prototype.deselect = function (index, force) {
|
|
1516
|
-
var item = this.items[index], option = this.options[index];
|
|
1517
|
-
if (this.el.multiple) {
|
|
1518
|
-
var selIndex = this.selectedIndexes.indexOf(index);
|
|
1519
|
-
this.selectedIndexes.splice(selIndex, 1);
|
|
1520
|
-
var valIndex = this.selectedValues.indexOf(option.value);
|
|
1521
|
-
this.selectedValues.splice(valIndex, 1);
|
|
1522
|
-
removeTag.call(this, item);
|
|
1523
|
-
if (!this.tags.length) {
|
|
1524
|
-
util.removeClass(this.container, "has-selected");
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
else {
|
|
1528
|
-
if (!force && !this.config.clearable && !this.config.allowDeselect) {
|
|
1529
|
-
return false;
|
|
1530
|
-
}
|
|
1531
|
-
this.label.innerHTML = "";
|
|
1532
|
-
this.selectedValue = null;
|
|
1533
|
-
this.el.selectedIndex = this.selectedIndex = -1;
|
|
1534
|
-
util.removeClass(this.container, "has-selected");
|
|
1535
|
-
}
|
|
1536
|
-
this.items[index].selected = false;
|
|
1537
|
-
if (this.items[index].dom) {
|
|
1538
|
-
this.items[index].dom.setAttribute("aria-selected", false);
|
|
1539
|
-
util.removeClass(this.items[index].dom, "selected");
|
|
1540
|
-
this.items[index].dom.selected = false;
|
|
1541
|
-
}
|
|
1542
|
-
option.selected = false;
|
|
1543
|
-
option.removeAttribute("selected");
|
|
1544
|
-
this.emit("selectr.change", null);
|
|
1545
|
-
this.emit("selectr.deselect", option);
|
|
1546
|
-
// fire native change event
|
|
1547
|
-
if ("createEvent" in document) {
|
|
1548
|
-
var evt = document.createEvent("HTMLEvents");
|
|
1549
|
-
evt.initEvent("change", true, true);
|
|
1550
|
-
evt.__selfTriggered = true;
|
|
1551
|
-
this.el.dispatchEvent(evt);
|
|
1552
|
-
}
|
|
1553
|
-
else {
|
|
1554
|
-
this.el.fireEvent("onchange");
|
|
1555
|
-
}
|
|
1556
|
-
};
|
|
1557
|
-
/**
|
|
1558
|
-
* Programmatically set selected values
|
|
1559
|
-
* @param {String|Array} value - A string or an array of strings
|
|
1560
|
-
*/
|
|
1561
|
-
Selectr.prototype.setValue = function (value) {
|
|
1562
|
-
var isArray = Array.isArray(value);
|
|
1563
|
-
if (!isArray) {
|
|
1564
|
-
value = value.toString().trim();
|
|
1565
|
-
}
|
|
1566
|
-
// Can't pass array to select-one
|
|
1567
|
-
if (!this.el.multiple && isArray) {
|
|
1568
|
-
return false;
|
|
1569
|
-
}
|
|
1570
|
-
util.each(this.options, function (i, option) {
|
|
1571
|
-
if (isArray && (value.indexOf(option.value) > -1) || option.value === value) {
|
|
1572
|
-
this.change(option.idx);
|
|
1573
|
-
}
|
|
1574
|
-
}, this);
|
|
1575
|
-
};
|
|
1576
|
-
/**
|
|
1577
|
-
* Set the selected value(s)
|
|
1578
|
-
* @param {bool} toObject Return only the raw values or an object
|
|
1579
|
-
* @param {bool} toJson Return the object as a JSON string
|
|
1580
|
-
* @return {mixed} Array or String
|
|
1581
|
-
*/
|
|
1582
|
-
Selectr.prototype.getValue = function (toObject, toJson) {
|
|
1583
|
-
var value;
|
|
1584
|
-
if (this.el.multiple) {
|
|
1585
|
-
if (toObject) {
|
|
1586
|
-
if (this.selectedIndexes.length) {
|
|
1587
|
-
value = {};
|
|
1588
|
-
value.values = [];
|
|
1589
|
-
util.each(this.selectedIndexes, function (i, index) {
|
|
1590
|
-
var option = this.options[index];
|
|
1591
|
-
value.values[i] = {
|
|
1592
|
-
value: option.value,
|
|
1593
|
-
text: option.textContent
|
|
1594
|
-
};
|
|
1595
|
-
}, this);
|
|
1596
|
-
}
|
|
1597
|
-
}
|
|
1598
|
-
else {
|
|
1599
|
-
value = this.selectedValues.slice();
|
|
1600
|
-
}
|
|
1601
|
-
}
|
|
1602
|
-
else {
|
|
1603
|
-
if (toObject) {
|
|
1604
|
-
var option = this.options[this.selectedIndex];
|
|
1605
|
-
value = {
|
|
1606
|
-
value: option.value,
|
|
1607
|
-
text: option.textContent
|
|
1608
|
-
};
|
|
1609
|
-
}
|
|
1610
|
-
else {
|
|
1611
|
-
value = this.selectedValue;
|
|
1612
|
-
}
|
|
1613
|
-
}
|
|
1614
|
-
if (toObject && toJson) {
|
|
1615
|
-
value = JSON.stringify(value);
|
|
1616
|
-
}
|
|
1617
|
-
return value;
|
|
1618
|
-
};
|
|
1619
|
-
/**
|
|
1620
|
-
* Add a new option or options
|
|
1621
|
-
* @param {object} data
|
|
1622
|
-
*/
|
|
1623
|
-
Selectr.prototype.add = function (data, checkDuplicate) {
|
|
1624
|
-
if (data) {
|
|
1625
|
-
this.data = this.data || [];
|
|
1626
|
-
this.items = this.items || [];
|
|
1627
|
-
this.options = this.options || [];
|
|
1628
|
-
if (Array.isArray(data)) {
|
|
1629
|
-
// We have an array on items
|
|
1630
|
-
util.each(data, function (i, obj) {
|
|
1631
|
-
this.add(obj, checkDuplicate);
|
|
1632
|
-
}, this);
|
|
1633
|
-
}
|
|
1634
|
-
// User passed a single object to the method
|
|
1635
|
-
// or Selectr passed an object from an array
|
|
1636
|
-
else if ("[object Object]" === Object.prototype.toString.call(data)) {
|
|
1637
|
-
if (checkDuplicate) {
|
|
1638
|
-
var dupe = false;
|
|
1639
|
-
util.each(this.options, function (i, option) {
|
|
1640
|
-
if (option.value.toLowerCase() === data.value.toLowerCase()) {
|
|
1641
|
-
dupe = true;
|
|
1642
|
-
}
|
|
1643
|
-
});
|
|
1644
|
-
if (dupe) {
|
|
1645
|
-
return false;
|
|
1646
|
-
}
|
|
1647
|
-
}
|
|
1648
|
-
var option = util.createElement('option', data);
|
|
1649
|
-
this.data.push(data);
|
|
1650
|
-
// Add the new option to the list
|
|
1651
|
-
this.options.push(option);
|
|
1652
|
-
// Add the index for later use
|
|
1653
|
-
option.idx = this.options.length > 0 ? this.options.length - 1 : 0;
|
|
1654
|
-
// Create a new item
|
|
1655
|
-
createItem.call(this, option);
|
|
1656
|
-
// Select the item if required
|
|
1657
|
-
if (data.selected) {
|
|
1658
|
-
this.select(option.idx);
|
|
1659
|
-
}
|
|
1660
|
-
// We may have had an empty select so update
|
|
1661
|
-
// the placeholder to reflect the changes.
|
|
1662
|
-
this.setPlaceholder();
|
|
1663
|
-
return option;
|
|
1664
|
-
}
|
|
1665
|
-
// Recount the pages
|
|
1666
|
-
if (this.config.pagination) {
|
|
1667
|
-
this.paginate();
|
|
1668
|
-
}
|
|
1669
|
-
return true;
|
|
1670
|
-
}
|
|
1671
|
-
};
|
|
1672
|
-
/**
|
|
1673
|
-
* Remove an option or options
|
|
1674
|
-
* @param {Mixed} o Array, integer (index) or string (value)
|
|
1675
|
-
* @return {Void}
|
|
1676
|
-
*/
|
|
1677
|
-
Selectr.prototype.remove = function (o) {
|
|
1678
|
-
var options = [];
|
|
1679
|
-
if (Array.isArray(o)) {
|
|
1680
|
-
util.each(o, function (i, opt) {
|
|
1681
|
-
if (util.isInt(opt)) {
|
|
1682
|
-
options.push(this.getOptionByIndex(opt));
|
|
1683
|
-
}
|
|
1684
|
-
else if (typeof opt === "string") {
|
|
1685
|
-
options.push(this.getOptionByValue(opt));
|
|
1686
|
-
}
|
|
1687
|
-
}, this);
|
|
1688
|
-
}
|
|
1689
|
-
else if (util.isInt(o)) {
|
|
1690
|
-
options.push(this.getOptionByIndex(o));
|
|
1691
|
-
}
|
|
1692
|
-
else if (typeof o === "string") {
|
|
1693
|
-
options.push(this.getOptionByValue(o));
|
|
1694
|
-
}
|
|
1695
|
-
if (options.length) {
|
|
1696
|
-
var index;
|
|
1697
|
-
util.each(options, function (i, option) {
|
|
1698
|
-
index = option.idx;
|
|
1699
|
-
// Remove the HTMLOptionElement
|
|
1700
|
-
this.el.remove(option);
|
|
1701
|
-
// Remove the reference from the option array
|
|
1702
|
-
this.options.splice(index, 1);
|
|
1703
|
-
// If the item has a parentNode (group element) it needs to be removed
|
|
1704
|
-
// otherwise the render function will still append it to the dropdown
|
|
1705
|
-
var parentNode = this.items[index].parentNode;
|
|
1706
|
-
if (parentNode) {
|
|
1707
|
-
parentNode.removeChild(this.items[index]);
|
|
1708
|
-
}
|
|
1709
|
-
// Remove reference from the items array
|
|
1710
|
-
this.items.splice(index, 1);
|
|
1711
|
-
// Reset the indexes
|
|
1712
|
-
util.each(this.options, function (i, opt) {
|
|
1713
|
-
opt.idx = i;
|
|
1714
|
-
this.items[i].idx = i;
|
|
1715
|
-
}, this);
|
|
1716
|
-
}, this);
|
|
1717
|
-
// We may have had an empty select now so update
|
|
1718
|
-
// the placeholder to reflect the changes.
|
|
1719
|
-
this.setPlaceholder();
|
|
1720
|
-
// Recount the pages
|
|
1721
|
-
if (this.config.pagination) {
|
|
1722
|
-
this.paginate();
|
|
1723
|
-
}
|
|
1724
|
-
}
|
|
1725
|
-
};
|
|
1726
|
-
/**
|
|
1727
|
-
* Remove all options
|
|
1728
|
-
*/
|
|
1729
|
-
Selectr.prototype.removeAll = function () {
|
|
1730
|
-
// Clear any selected options
|
|
1731
|
-
this.clear(true);
|
|
1732
|
-
// Remove the HTMLOptionElements
|
|
1733
|
-
util.each(this.el.options, function (i, option) {
|
|
1734
|
-
this.el.remove(option);
|
|
1735
|
-
}, this);
|
|
1736
|
-
// Empty the dropdown
|
|
1737
|
-
util.truncate(this.tree);
|
|
1738
|
-
// Reset variables
|
|
1739
|
-
this.items = [];
|
|
1740
|
-
this.options = [];
|
|
1741
|
-
this.data = [];
|
|
1742
|
-
this.navIndex = 0;
|
|
1743
|
-
if (this.requiresPagination) {
|
|
1744
|
-
this.requiresPagination = false;
|
|
1745
|
-
this.pageIndex = 1;
|
|
1746
|
-
this.pages = [];
|
|
1747
|
-
}
|
|
1748
|
-
// Update the placeholder
|
|
1749
|
-
this.setPlaceholder();
|
|
1750
|
-
};
|
|
1751
|
-
/**
|
|
1752
|
-
* Perform a search
|
|
1753
|
-
* @param {string}|{null} query The query string (taken from user input if null)
|
|
1754
|
-
* @param {boolean} anchor Anchor search to beginning of strings (defaults to false)?
|
|
1755
|
-
* @return {Array} Search results, as an array of {text, value} objects
|
|
1756
|
-
*/
|
|
1757
|
-
Selectr.prototype.search = function (string, anchor) {
|
|
1758
|
-
if (this.navigating) {
|
|
1759
|
-
return;
|
|
1760
|
-
}
|
|
1761
|
-
// we're only going to alter the DOM for "live" searches
|
|
1762
|
-
var live = false;
|
|
1763
|
-
if (!string) {
|
|
1764
|
-
string = this.input.value;
|
|
1765
|
-
live = true;
|
|
1766
|
-
// Remove message and clear dropdown
|
|
1767
|
-
this.removeMessage();
|
|
1768
|
-
util.truncate(this.tree);
|
|
1769
|
-
}
|
|
1770
|
-
var results = [];
|
|
1771
|
-
var f = document.createDocumentFragment();
|
|
1772
|
-
string = string.trim().toLowerCase();
|
|
1773
|
-
if (string.length > 0) {
|
|
1774
|
-
var compare = anchor ? util.startsWith : util.includes;
|
|
1775
|
-
var counter = 0;
|
|
1776
|
-
this.searchResults = [];
|
|
1777
|
-
util.each(this.options, function (i, option) {
|
|
1778
|
-
var item = this.items[option.idx];
|
|
1779
|
-
var matches = compare(option.textContent.trim().toLowerCase(), string);
|
|
1780
|
-
if (matches && !option.disabled) {
|
|
1781
|
-
results.push({ text: option.textContent, value: option.value });
|
|
1782
|
-
if (live) {
|
|
1783
|
-
this.searchResults.push(item);
|
|
1784
|
-
if (counter < 50) {
|
|
1785
|
-
appendItem(item, f, this.customOption);
|
|
1786
|
-
counter++;
|
|
1787
|
-
util.removeClass(item.dom, "excluded");
|
|
1788
|
-
}
|
|
1789
|
-
// // Underline the matching results
|
|
1790
|
-
// if (!this.customOption) {
|
|
1791
|
-
// match(string, option);
|
|
1792
|
-
// }
|
|
1793
|
-
}
|
|
1794
|
-
}
|
|
1795
|
-
else if (live && item.dom) {
|
|
1796
|
-
util.addClass(item.dom, "excluded");
|
|
1797
|
-
}
|
|
1798
|
-
}, this);
|
|
1799
|
-
if (live) {
|
|
1800
|
-
// Append results
|
|
1801
|
-
if (!f.childElementCount && !f.childNodes.length) {
|
|
1802
|
-
if (!this.config.taggable) {
|
|
1803
|
-
this.noResults = true;
|
|
1804
|
-
this.setMessage(this.config.messages.noResults);
|
|
1805
|
-
}
|
|
1806
|
-
}
|
|
1807
|
-
else {
|
|
1808
|
-
// Highlight top result (@binary-koan #26)
|
|
1809
|
-
var prevEl = this.items[this.navIndex];
|
|
1810
|
-
var firstEl = f.querySelector(".jsl-option:not(.excluded)");
|
|
1811
|
-
this.noResults = false;
|
|
1812
|
-
removeActive(this.tree);
|
|
1813
|
-
// prevEl.active = false;
|
|
1814
|
-
this.navIndex = firstEl.idx;
|
|
1815
|
-
util.addClass(firstEl, "active");
|
|
1816
|
-
}
|
|
1817
|
-
this.tree.appendChild(f);
|
|
1818
|
-
}
|
|
1819
|
-
}
|
|
1820
|
-
else {
|
|
1821
|
-
clearSearch.call(this);
|
|
1822
|
-
render.call(this);
|
|
1823
|
-
return [];
|
|
1824
|
-
}
|
|
1825
|
-
this.pageIndex = 1;
|
|
1826
|
-
this.paginate();
|
|
1827
|
-
return results;
|
|
1828
|
-
};
|
|
1829
|
-
/**
|
|
1830
|
-
* Toggle the dropdown
|
|
1831
|
-
* @return {void}
|
|
1832
|
-
*/
|
|
1833
|
-
Selectr.prototype.toggle = function () {
|
|
1834
|
-
if (!this.disabled) {
|
|
1835
|
-
if (this.opened) {
|
|
1836
|
-
this.close();
|
|
1837
|
-
}
|
|
1838
|
-
else {
|
|
1839
|
-
this.open();
|
|
1840
|
-
}
|
|
1841
|
-
}
|
|
1842
|
-
};
|
|
1843
|
-
/**
|
|
1844
|
-
* Open the dropdown
|
|
1845
|
-
* @return {void}
|
|
1846
|
-
*/
|
|
1847
|
-
Selectr.prototype.open = function () {
|
|
1848
|
-
var that = this;
|
|
1849
|
-
if (!this.options.length) {
|
|
1850
|
-
return false;
|
|
1851
|
-
}
|
|
1852
|
-
if (!this.opened) {
|
|
1853
|
-
this.emit("selectr.open");
|
|
1854
|
-
}
|
|
1855
|
-
this.opened = true;
|
|
1856
|
-
if (this.mobileDevice || this.config.nativeDropdown) {
|
|
1857
|
-
util.addClass(this.container, "native-open");
|
|
1858
|
-
if (this.config.data) {
|
|
1859
|
-
// Dump the options into the select
|
|
1860
|
-
// otherwise the native dropdown will be empty
|
|
1861
|
-
util.each(this.options, function (i, option) {
|
|
1862
|
-
this.el.add(option);
|
|
1863
|
-
}, this);
|
|
1864
|
-
}
|
|
1865
|
-
return;
|
|
1866
|
-
}
|
|
1867
|
-
util.addClass(this.container, "open");
|
|
1868
|
-
render.call(this);
|
|
1869
|
-
this.invert();
|
|
1870
|
-
this.tree.scrollTop = 0;
|
|
1871
|
-
util.removeClass(this.container, "notice");
|
|
1872
|
-
this.selected.setAttribute("aria-expanded", true);
|
|
1873
|
-
this.tree.setAttribute("aria-hidden", false);
|
|
1874
|
-
this.tree.setAttribute("aria-expanded", true);
|
|
1875
|
-
if (this.config.searchable && !this.config.taggable) {
|
|
1876
|
-
setTimeout(function () {
|
|
1877
|
-
that.input.focus();
|
|
1878
|
-
// Allow tab focus
|
|
1879
|
-
that.input.tabIndex = 0;
|
|
1880
|
-
}, 10);
|
|
1881
|
-
}
|
|
1882
|
-
};
|
|
1883
|
-
/**
|
|
1884
|
-
* Close the dropdown
|
|
1885
|
-
* @return {void}
|
|
1886
|
-
*/
|
|
1887
|
-
Selectr.prototype.close = function () {
|
|
1888
|
-
if (this.opened) {
|
|
1889
|
-
this.emit("selectr.close");
|
|
1890
|
-
}
|
|
1891
|
-
this.opened = false;
|
|
1892
|
-
this.navigating = false;
|
|
1893
|
-
if (this.mobileDevice || this.config.nativeDropdown) {
|
|
1894
|
-
util.removeClass(this.container, "native-open");
|
|
1895
|
-
return;
|
|
1896
|
-
}
|
|
1897
|
-
var notice = util.hasClass(this.container, "notice");
|
|
1898
|
-
if (this.config.searchable && !notice) {
|
|
1899
|
-
this.input.blur();
|
|
1900
|
-
// Disable tab focus
|
|
1901
|
-
this.input.tabIndex = -1;
|
|
1902
|
-
this.searching = false;
|
|
1903
|
-
}
|
|
1904
|
-
if (notice) {
|
|
1905
|
-
util.removeClass(this.container, "notice");
|
|
1906
|
-
this.notice.textContent = "";
|
|
1907
|
-
}
|
|
1908
|
-
util.removeClass(this.container, "open");
|
|
1909
|
-
util.removeClass(this.container, "native-open");
|
|
1910
|
-
this.selected.setAttribute("aria-expanded", false);
|
|
1911
|
-
this.tree.setAttribute("aria-hidden", true);
|
|
1912
|
-
this.tree.setAttribute("aria-expanded", false);
|
|
1913
|
-
util.truncate(this.tree);
|
|
1914
|
-
clearSearch.call(this);
|
|
1915
|
-
};
|
|
1916
|
-
/**
|
|
1917
|
-
* Enable the element
|
|
1918
|
-
* @return {void}
|
|
1919
|
-
*/
|
|
1920
|
-
Selectr.prototype.enable = function () {
|
|
1921
|
-
this.disabled = false;
|
|
1922
|
-
this.el.disabled = false;
|
|
1923
|
-
this.selected.tabIndex = this.originalIndex;
|
|
1924
|
-
if (this.el.multiple) {
|
|
1925
|
-
util.each(this.tags, function (i, t) {
|
|
1926
|
-
t.lastElementChild.tabIndex = 0;
|
|
1927
|
-
});
|
|
1928
|
-
}
|
|
1929
|
-
util.removeClass(this.container, "jsl-disabled");
|
|
1930
|
-
};
|
|
1931
|
-
/**
|
|
1932
|
-
* Disable the element
|
|
1933
|
-
* @param {boolean} container Disable the container only (allow value submit with form)
|
|
1934
|
-
* @return {void}
|
|
1935
|
-
*/
|
|
1936
|
-
Selectr.prototype.disable = function (container) {
|
|
1937
|
-
if (!container) {
|
|
1938
|
-
this.el.disabled = true;
|
|
1939
|
-
}
|
|
1940
|
-
this.selected.tabIndex = -1;
|
|
1941
|
-
if (this.el.multiple) {
|
|
1942
|
-
util.each(this.tags, function (i, t) {
|
|
1943
|
-
t.lastElementChild.tabIndex = -1;
|
|
1944
|
-
});
|
|
1945
|
-
}
|
|
1946
|
-
this.disabled = true;
|
|
1947
|
-
util.addClass(this.container, "jsl-disabled");
|
|
1948
|
-
};
|
|
1949
|
-
/**
|
|
1950
|
-
* Reset to initial state
|
|
1951
|
-
* @return {void}
|
|
1952
|
-
*/
|
|
1953
|
-
Selectr.prototype.reset = function () {
|
|
1954
|
-
if (!this.disabled) {
|
|
1955
|
-
this.clear();
|
|
1956
|
-
this.setSelected(true);
|
|
1957
|
-
util.each(this.defaultSelected, function (i, idx) {
|
|
1958
|
-
this.select(idx);
|
|
1959
|
-
}, this);
|
|
1960
|
-
this.emit("selectr.reset");
|
|
1961
|
-
}
|
|
1962
|
-
};
|
|
1963
|
-
/**
|
|
1964
|
-
* Clear all selections
|
|
1965
|
-
* @return {void}
|
|
1966
|
-
*/
|
|
1967
|
-
Selectr.prototype.clear = function (force, isClearLast) {
|
|
1968
|
-
if (this.el.multiple) {
|
|
1969
|
-
// Loop over the selectedIndexes so we don't have to loop over all the options
|
|
1970
|
-
// which can be costly if there are a lot of them
|
|
1971
|
-
if (this.selectedIndexes.length) {
|
|
1972
|
-
// Copy the array or we'll get an error
|
|
1973
|
-
var indexes = this.selectedIndexes.slice();
|
|
1974
|
-
if (isClearLast) {
|
|
1975
|
-
this.deselect(indexes.slice(-1)[0]);
|
|
1976
|
-
}
|
|
1977
|
-
else {
|
|
1978
|
-
util.each(indexes, function (i, idx) {
|
|
1979
|
-
this.deselect(idx);
|
|
1980
|
-
}, this);
|
|
1981
|
-
}
|
|
1982
|
-
}
|
|
1983
|
-
}
|
|
1984
|
-
else {
|
|
1985
|
-
if (this.selectedIndex > -1) {
|
|
1986
|
-
this.deselect(this.selectedIndex, force);
|
|
1987
|
-
}
|
|
1988
|
-
}
|
|
1989
|
-
this.emit("selectr.clear");
|
|
1990
|
-
};
|
|
1991
|
-
/**
|
|
1992
|
-
* Return serialised data
|
|
1993
|
-
* @param {boolean} toJson
|
|
1994
|
-
* @return {mixed} Returns either an object or JSON string
|
|
1995
|
-
*/
|
|
1996
|
-
Selectr.prototype.serialise = function (toJson) {
|
|
1997
|
-
var data = [];
|
|
1998
|
-
util.each(this.options, function (i, option) {
|
|
1999
|
-
var obj = {
|
|
2000
|
-
value: option.value,
|
|
2001
|
-
text: option.textContent,
|
|
2002
|
-
selected: undefined,
|
|
2003
|
-
disabled: undefined
|
|
2004
|
-
};
|
|
2005
|
-
if (option.selected) {
|
|
2006
|
-
obj.selected = true;
|
|
2007
|
-
}
|
|
2008
|
-
if (option.disabled) {
|
|
2009
|
-
obj.disabled = true;
|
|
2010
|
-
}
|
|
2011
|
-
data[i] = obj;
|
|
2012
|
-
});
|
|
2013
|
-
return toJson ? JSON.stringify(data) : data;
|
|
2014
|
-
};
|
|
2015
|
-
/**
|
|
2016
|
-
* Localised version of serialise() method
|
|
2017
|
-
*/
|
|
2018
|
-
Selectr.prototype.serialize = function (toJson) {
|
|
2019
|
-
return this.serialise(toJson);
|
|
2020
|
-
};
|
|
2021
|
-
/**
|
|
2022
|
-
* Sets the placeholder
|
|
2023
|
-
* @param {String} placeholder
|
|
2024
|
-
*/
|
|
2025
|
-
Selectr.prototype.setPlaceholder = function (placeholder) {
|
|
2026
|
-
// Set the placeholder
|
|
2027
|
-
placeholder = placeholder || this.config.placeholder || this.el.getAttribute("placeholder");
|
|
2028
|
-
if (!this.options.length) {
|
|
2029
|
-
placeholder = this.config.messages.noOptions;
|
|
2030
|
-
}
|
|
2031
|
-
this.placeEl.innerHTML = placeholder;
|
|
2032
|
-
};
|
|
2033
|
-
/**
|
|
2034
|
-
* Paginate the option list
|
|
2035
|
-
* @return {Array}
|
|
2036
|
-
*/
|
|
2037
|
-
Selectr.prototype.paginate = function () {
|
|
2038
|
-
if (!this.requiresPagination) {
|
|
2039
|
-
return;
|
|
2040
|
-
}
|
|
2041
|
-
var items = this.searching ? this.searchResults : this.items;
|
|
2042
|
-
if (items.length) {
|
|
2043
|
-
var that = this;
|
|
2044
|
-
this.pages = items.map(function (v, i) {
|
|
2045
|
-
return i % that.config.pagination === 0 ? items.slice(i, i + that.config.pagination) : null;
|
|
2046
|
-
}).filter(function (pages) {
|
|
2047
|
-
return pages;
|
|
2048
|
-
});
|
|
2049
|
-
return this.pages;
|
|
2050
|
-
}
|
|
2051
|
-
};
|
|
2052
|
-
/**
|
|
2053
|
-
* Display a message
|
|
2054
|
-
* @param {String} message The message
|
|
2055
|
-
*/
|
|
2056
|
-
Selectr.prototype.setMessage = function (message, close) {
|
|
2057
|
-
if (close) {
|
|
2058
|
-
this.close();
|
|
2059
|
-
}
|
|
2060
|
-
util.addClass(this.container, "notice");
|
|
2061
|
-
this.notice.textContent = message;
|
|
2062
|
-
};
|
|
2063
|
-
/**
|
|
2064
|
-
* Dismiss the current message
|
|
2065
|
-
*/
|
|
2066
|
-
Selectr.prototype.removeMessage = function () {
|
|
2067
|
-
util.removeClass(this.container, "notice");
|
|
2068
|
-
this.notice.innerHTML = "";
|
|
2069
|
-
};
|
|
2070
|
-
/**
|
|
2071
|
-
* Keep the dropdown within the window
|
|
2072
|
-
* @return {void}
|
|
2073
|
-
*/
|
|
2074
|
-
Selectr.prototype.invert = function () {
|
|
2075
|
-
var rt = util.rect(this.selected), oh = this.tree.parentNode.offsetHeight, wh = window.innerHeight, doInvert = rt.top + rt.height + oh > wh;
|
|
2076
|
-
if (doInvert) {
|
|
2077
|
-
util.addClass(this.container, "inverted");
|
|
2078
|
-
this.isInverted = true;
|
|
2079
|
-
}
|
|
2080
|
-
else {
|
|
2081
|
-
util.removeClass(this.container, "inverted");
|
|
2082
|
-
this.isInverted = false;
|
|
2083
|
-
}
|
|
2084
|
-
this.optsRect = util.rect(this.tree);
|
|
2085
|
-
};
|
|
2086
|
-
/**
|
|
2087
|
-
* Get an option via it's index
|
|
2088
|
-
* @param {Integer} index The index of the HTMLOptionElement required
|
|
2089
|
-
* @return {HTMLOptionElement}
|
|
2090
|
-
*/
|
|
2091
|
-
Selectr.prototype.getOptionByIndex = function (index) {
|
|
2092
|
-
return this.options[index];
|
|
2093
|
-
};
|
|
2094
|
-
/**
|
|
2095
|
-
* Get an option via it's value
|
|
2096
|
-
* @param {String} value The value of the HTMLOptionElement required
|
|
2097
|
-
* @return {HTMLOptionElement}
|
|
2098
|
-
*/
|
|
2099
|
-
Selectr.prototype.getOptionByValue = function (value) {
|
|
2100
|
-
var option = false;
|
|
2101
|
-
for (var i = 0, l = this.options.length; i < l; i++) {
|
|
2102
|
-
if (this.options[i].value.trim() === value.toString().trim()) {
|
|
2103
|
-
option = this.options[i];
|
|
2104
|
-
break;
|
|
2105
|
-
}
|
|
2106
|
-
}
|
|
2107
|
-
return option;
|
|
2108
|
-
};
|
|
2109
|
-
})();
|
|
2110
|
-
export
|
|
2111
|
-
return new Selectr(input, config);
|
|
2112
|
-
};
|
|
1
|
+
/*!
|
|
2
|
+
* Selectr 2.4.12
|
|
3
|
+
* http://mobius.ovh/docs/selectr
|
|
4
|
+
*
|
|
5
|
+
* Released under the MIT license
|
|
6
|
+
*/
|
|
7
|
+
import "./drop.down.css";
|
|
8
|
+
import { UILang } from "../core/lang";
|
|
9
|
+
let Selectr;
|
|
10
|
+
(function () {
|
|
11
|
+
/**
|
|
12
|
+
* Event Emitter
|
|
13
|
+
*/
|
|
14
|
+
var Events = function () { };
|
|
15
|
+
/**
|
|
16
|
+
* Event Prototype
|
|
17
|
+
* @type {Object}
|
|
18
|
+
*/
|
|
19
|
+
Events.prototype = {
|
|
20
|
+
/**
|
|
21
|
+
* Add custom event listener
|
|
22
|
+
* @param {String} event Event type
|
|
23
|
+
* @param {Function} func Callback
|
|
24
|
+
* @return {Void}
|
|
25
|
+
*/
|
|
26
|
+
on: function (event, func) {
|
|
27
|
+
this._events = this._events || {};
|
|
28
|
+
this._events[event] = this._events[event] || [];
|
|
29
|
+
this._events[event].push(func);
|
|
30
|
+
},
|
|
31
|
+
/**
|
|
32
|
+
* Remove custom event listener
|
|
33
|
+
* @param {String} event Event type
|
|
34
|
+
* @param {Function} func Callback
|
|
35
|
+
* @return {Void}
|
|
36
|
+
*/
|
|
37
|
+
off: function (event, func) {
|
|
38
|
+
this._events = this._events || {};
|
|
39
|
+
if (event in this._events === false)
|
|
40
|
+
return;
|
|
41
|
+
this._events[event].splice(this._events[event].indexOf(func), 1);
|
|
42
|
+
},
|
|
43
|
+
/**
|
|
44
|
+
* Fire a custom event
|
|
45
|
+
* @param {String} event Event type
|
|
46
|
+
* @return {Void}
|
|
47
|
+
*/
|
|
48
|
+
emit: function (event /* , args... */) {
|
|
49
|
+
this._events = this._events || {};
|
|
50
|
+
if (event in this._events === false)
|
|
51
|
+
return;
|
|
52
|
+
for (var i = 0; i < this._events[event].length; i++) {
|
|
53
|
+
this._events[event][i].apply(this, Array.prototype.slice.call(arguments, 1));
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
/**
|
|
58
|
+
* Event mixin
|
|
59
|
+
* @param {Object} obj
|
|
60
|
+
* @return {Object}
|
|
61
|
+
*/
|
|
62
|
+
Events.mixin = function (obj) {
|
|
63
|
+
var props = ['on', 'off', 'emit'];
|
|
64
|
+
for (var i = 0; i < props.length; i++) {
|
|
65
|
+
if (typeof obj === 'function') {
|
|
66
|
+
obj.prototype[props[i]] = Events.prototype[props[i]];
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
obj[props[i]] = Events.prototype[props[i]];
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return obj;
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Helpers
|
|
76
|
+
* @type {Object}
|
|
77
|
+
*/
|
|
78
|
+
var util = {
|
|
79
|
+
escapeRegExp: function (str) {
|
|
80
|
+
// source from lodash 3.0.0
|
|
81
|
+
var _reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
|
|
82
|
+
var _reHasRegExpChar = new RegExp(_reRegExpChar.source);
|
|
83
|
+
return (str && _reHasRegExpChar.test(str)) ? str.replace(_reRegExpChar, '\\$&') : str;
|
|
84
|
+
},
|
|
85
|
+
extend: function (src, props) {
|
|
86
|
+
for (var prop in props) {
|
|
87
|
+
if (props.hasOwnProperty(prop)) {
|
|
88
|
+
var val = props[prop];
|
|
89
|
+
if (val && Object.prototype.toString.call(val) === "[object Object]") {
|
|
90
|
+
src[prop] = src[prop] || {};
|
|
91
|
+
util.extend(src[prop], val);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
src[prop] = val;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return src;
|
|
99
|
+
},
|
|
100
|
+
each: function (a, b, c) {
|
|
101
|
+
if ("[object Object]" === Object.prototype.toString.call(a)) {
|
|
102
|
+
for (var d in a) {
|
|
103
|
+
if (Object.prototype.hasOwnProperty.call(a, d)) {
|
|
104
|
+
b.call(c, d, a[d], a);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
for (var e = 0, f = a.length; e < f; e++) {
|
|
110
|
+
b.call(c, e, a[e], a);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
createElement: function (e, a) {
|
|
115
|
+
var d = document, el = d.createElement(e);
|
|
116
|
+
if (a && "[object Object]" === Object.prototype.toString.call(a)) {
|
|
117
|
+
var i;
|
|
118
|
+
for (i in a) {
|
|
119
|
+
if (a[i] !== undefined) {
|
|
120
|
+
if (i in el)
|
|
121
|
+
el[i] = a[i];
|
|
122
|
+
else if ("html" === i)
|
|
123
|
+
el.innerHTML = a[i];
|
|
124
|
+
else
|
|
125
|
+
el.setAttribute(i, a[i]);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
return el;
|
|
130
|
+
},
|
|
131
|
+
hasClass: function (a, b) {
|
|
132
|
+
if (a) {
|
|
133
|
+
// if (a.classList == null) {
|
|
134
|
+
// debugger;
|
|
135
|
+
// }
|
|
136
|
+
return a.classList ? a.classList.contains(b) : !!a.className && !!a.className.match(new RegExp("(\\s|^)" + b + "(\\s|$)"));
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
addClass: function (a, b) {
|
|
140
|
+
if (!util.hasClass(a, b)) {
|
|
141
|
+
if (a.classList) {
|
|
142
|
+
a.classList.add(b);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
a.className = a.className.trim() + " " + b;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
removeClass: function (a, b) {
|
|
150
|
+
if (util.hasClass(a, b)) {
|
|
151
|
+
if (a.classList) {
|
|
152
|
+
a.classList.remove(b);
|
|
153
|
+
}
|
|
154
|
+
else {
|
|
155
|
+
a.className = a.className.replace(new RegExp("(^|\\s)" + b.split(" ").join("|") + "(\\s|$)", "gi"), " ");
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
},
|
|
159
|
+
closest: function (el, fn) {
|
|
160
|
+
return el && el !== document.body && (fn(el) ? el : util.closest(el.parentNode, fn));
|
|
161
|
+
},
|
|
162
|
+
isInt: function (val) {
|
|
163
|
+
return typeof val === 'number' && isFinite(val) && Math.floor(val) === val;
|
|
164
|
+
},
|
|
165
|
+
debounce: function (a, b, c) {
|
|
166
|
+
var d;
|
|
167
|
+
return function () {
|
|
168
|
+
var e = this, f = arguments, g = function () {
|
|
169
|
+
d = null;
|
|
170
|
+
if (!c)
|
|
171
|
+
a.apply(e, f);
|
|
172
|
+
}, h = c && !d;
|
|
173
|
+
clearTimeout(d);
|
|
174
|
+
d = setTimeout(g, b);
|
|
175
|
+
if (h) {
|
|
176
|
+
a.apply(e, f);
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
rect: function (el, abs) {
|
|
181
|
+
var w = window;
|
|
182
|
+
var r = el.getBoundingClientRect();
|
|
183
|
+
var x = abs ? w.pageXOffset : 0;
|
|
184
|
+
var y = abs ? w.pageYOffset : 0;
|
|
185
|
+
return {
|
|
186
|
+
bottom: r.bottom + y,
|
|
187
|
+
height: r.height,
|
|
188
|
+
left: r.left + x,
|
|
189
|
+
right: r.right + x,
|
|
190
|
+
top: r.top + y,
|
|
191
|
+
width: r.width
|
|
192
|
+
};
|
|
193
|
+
},
|
|
194
|
+
includes: function (a, b) {
|
|
195
|
+
return a.indexOf(b) > -1;
|
|
196
|
+
},
|
|
197
|
+
startsWith: function (a, b) {
|
|
198
|
+
return a.substr(0, b.length) === b;
|
|
199
|
+
},
|
|
200
|
+
truncate: function (el) {
|
|
201
|
+
while (el.firstChild) {
|
|
202
|
+
el.removeChild(el.firstChild);
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
toDom: function (item) {
|
|
206
|
+
var elementData = {
|
|
207
|
+
class: "jsl-option",
|
|
208
|
+
role: "treeitem",
|
|
209
|
+
"aria-selected": false,
|
|
210
|
+
html: item.html,
|
|
211
|
+
textContent: item.html ? undefined : item.text
|
|
212
|
+
};
|
|
213
|
+
var opt = util.createElement("li", elementData);
|
|
214
|
+
opt.idx = item.idx;
|
|
215
|
+
if (item.disabled) {
|
|
216
|
+
opt.disabled = true;
|
|
217
|
+
util.addClass(opt, "disabled");
|
|
218
|
+
}
|
|
219
|
+
if (item.active) {
|
|
220
|
+
util.addClass(opt, "active");
|
|
221
|
+
}
|
|
222
|
+
if (item.selected) {
|
|
223
|
+
util.addClass(opt, "selected");
|
|
224
|
+
opt.selected = true;
|
|
225
|
+
}
|
|
226
|
+
return opt;
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
function isset(obj, prop) {
|
|
230
|
+
return obj.hasOwnProperty(prop) && (obj[prop] === true || obj[prop].length);
|
|
231
|
+
}
|
|
232
|
+
/**
|
|
233
|
+
* Append an item to the list
|
|
234
|
+
* @param {Object} item
|
|
235
|
+
* @param {Object} custom
|
|
236
|
+
* @return {Void}
|
|
237
|
+
*/
|
|
238
|
+
function appendItem(vitem, parent, custom) {
|
|
239
|
+
var item = vitem.dom = util.toDom(vitem);
|
|
240
|
+
if (vitem.optgroup && vitem.optgroup.parentNode == null) {
|
|
241
|
+
parent.appendChild(vitem.optgroup);
|
|
242
|
+
}
|
|
243
|
+
parent.appendChild(item);
|
|
244
|
+
// if (item.parentNode) {
|
|
245
|
+
// if (!item.parentNode.parentNode) {
|
|
246
|
+
// parent.appendChild(item.parentNode);
|
|
247
|
+
// }
|
|
248
|
+
// } else
|
|
249
|
+
// {
|
|
250
|
+
// parent.appendChild(item);
|
|
251
|
+
// }
|
|
252
|
+
// util.removeClass(item, "excluded");
|
|
253
|
+
if (!custom) {
|
|
254
|
+
// remove any <span> highlighting, without xss
|
|
255
|
+
item.textContent = item.textContent;
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Render the item list
|
|
260
|
+
* @return {Void}
|
|
261
|
+
*/
|
|
262
|
+
var render = function () {
|
|
263
|
+
if (this.items.length) {
|
|
264
|
+
var f = document.createDocumentFragment();
|
|
265
|
+
if (this.config.pagination) {
|
|
266
|
+
var pages = this.pages.slice(0, this.pageIndex);
|
|
267
|
+
util.each(pages, function (i, items) {
|
|
268
|
+
util.each(items, function (j, item) {
|
|
269
|
+
appendItem(item, f, this.customOption);
|
|
270
|
+
}, this);
|
|
271
|
+
}, this);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
util.each(this.items, function (i, item) {
|
|
275
|
+
appendItem(item, f, this.customOption);
|
|
276
|
+
}, this);
|
|
277
|
+
}
|
|
278
|
+
// highlight first selected option if any; first option otherwise
|
|
279
|
+
if (f.childElementCount || f.childNodes.length) {
|
|
280
|
+
removeActive(this.tree);
|
|
281
|
+
this.navIndex = (f.querySelector(".jsl-option.selected") ||
|
|
282
|
+
f.querySelector(".jsl-option")).idx;
|
|
283
|
+
if (!this.config.multiple) {
|
|
284
|
+
util.addClass(this.items[this.navIndex].dom, "active");
|
|
285
|
+
this.items[this.navIndex].active = true;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
this.tree.appendChild(f);
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
/**
|
|
292
|
+
* Dismiss / close the dropdown
|
|
293
|
+
* @param {obj} e
|
|
294
|
+
* @return {void}
|
|
295
|
+
*/
|
|
296
|
+
var dismiss = function (e) {
|
|
297
|
+
var target = e.target;
|
|
298
|
+
if (!this.container.contains(target) && (this.opened || util.hasClass(this.container, "notice"))) {
|
|
299
|
+
this.close();
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
/**
|
|
303
|
+
* Build a list item from the HTMLOptionElement
|
|
304
|
+
* @param {int} i HTMLOptionElement index
|
|
305
|
+
* @param {HTMLOptionElement} option
|
|
306
|
+
* @param {bool} group Has parent optgroup
|
|
307
|
+
* @return {void}
|
|
308
|
+
*/
|
|
309
|
+
var createItem = function (option, data) {
|
|
310
|
+
data = data || option;
|
|
311
|
+
var elementData = {
|
|
312
|
+
class: "jsl-option",
|
|
313
|
+
role: "treeitem",
|
|
314
|
+
"aria-selected": option.selected,
|
|
315
|
+
html: undefined,
|
|
316
|
+
textContent: undefined
|
|
317
|
+
};
|
|
318
|
+
if (this.customOption) {
|
|
319
|
+
elementData.html = this.config.renderOption(data); // asume xss prevention in custom render function
|
|
320
|
+
}
|
|
321
|
+
else {
|
|
322
|
+
elementData.textContent = option.textContent; // treat all as plain text
|
|
323
|
+
}
|
|
324
|
+
// var opt = util.createElement("li",elementData);
|
|
325
|
+
var opt = { idx: 0, disabled: option.disabled, html: elementData.html, text: elementData.textContent, parent: null, selected: option.selected };
|
|
326
|
+
opt.idx = option.idx;
|
|
327
|
+
opt.parent = option.parent;
|
|
328
|
+
this.items.push(opt);
|
|
329
|
+
if (option.defaultSelected) {
|
|
330
|
+
this.defaultSelected.push(option.idx);
|
|
331
|
+
}
|
|
332
|
+
// if (option.disabled) {
|
|
333
|
+
// opt.disabled = true;
|
|
334
|
+
// // util.addClass(opt, "disabled");
|
|
335
|
+
// }
|
|
336
|
+
// if (option.selected) {
|
|
337
|
+
// opt.selected = true;
|
|
338
|
+
// // util.addClass(opt, "selected");
|
|
339
|
+
// }
|
|
340
|
+
return opt;
|
|
341
|
+
};
|
|
342
|
+
var removeActive = function (container) {
|
|
343
|
+
var items = container.querySelectorAll("li.active");
|
|
344
|
+
for (let i = 0; i < items.length; i++) {
|
|
345
|
+
items[i].classList.remove("active");
|
|
346
|
+
}
|
|
347
|
+
};
|
|
348
|
+
/**
|
|
349
|
+
* Build the container
|
|
350
|
+
* @return {Void}
|
|
351
|
+
*/
|
|
352
|
+
var build = function () {
|
|
353
|
+
this.requiresPagination = this.config.pagination && this.config.pagination > 0;
|
|
354
|
+
// Set width
|
|
355
|
+
if (isset(this.config, "width")) {
|
|
356
|
+
if (util.isInt(this.config.width)) {
|
|
357
|
+
this.width = this.config.width + "px";
|
|
358
|
+
}
|
|
359
|
+
else {
|
|
360
|
+
if (this.config.width === "auto") {
|
|
361
|
+
this.width = "100%";
|
|
362
|
+
}
|
|
363
|
+
else { // if (util.includes(this.config.width, "%")) {
|
|
364
|
+
this.width = this.config.width;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
this.container = util.createElement("div", {
|
|
369
|
+
class: "jsl-container"
|
|
370
|
+
});
|
|
371
|
+
// Custom className
|
|
372
|
+
if (this.config.customClass) {
|
|
373
|
+
util.addClass(this.container, this.config.customClass);
|
|
374
|
+
}
|
|
375
|
+
// Mobile device
|
|
376
|
+
if (this.mobileDevice) {
|
|
377
|
+
util.addClass(this.container, "jsl-mobile");
|
|
378
|
+
}
|
|
379
|
+
else {
|
|
380
|
+
util.addClass(this.container, "jsl-desktop");
|
|
381
|
+
}
|
|
382
|
+
// Hide the HTMLSelectElement and prevent focus
|
|
383
|
+
this.el.tabIndex = -1;
|
|
384
|
+
// Native dropdown
|
|
385
|
+
if (this.config.nativeDropdown || this.mobileDevice) {
|
|
386
|
+
util.addClass(this.el, "jsl-visible");
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
util.addClass(this.el, "jsl-hidden");
|
|
390
|
+
}
|
|
391
|
+
this.selected = util.createElement("div", {
|
|
392
|
+
class: "jsl-selected",
|
|
393
|
+
disabled: this.disabled,
|
|
394
|
+
tabIndex: 0,
|
|
395
|
+
"aria-expanded": false
|
|
396
|
+
});
|
|
397
|
+
this.label = util.createElement(this.el.multiple ? "ul" : "span", {
|
|
398
|
+
class: "jsl-label"
|
|
399
|
+
});
|
|
400
|
+
var dropdown = util.createElement("div", {
|
|
401
|
+
class: "jsl-options-container"
|
|
402
|
+
});
|
|
403
|
+
this.tree = util.createElement("ul", {
|
|
404
|
+
class: "jsl-options",
|
|
405
|
+
role: "tree",
|
|
406
|
+
"aria-hidden": true,
|
|
407
|
+
"aria-expanded": false
|
|
408
|
+
});
|
|
409
|
+
this.notice = util.createElement("div", {
|
|
410
|
+
class: "jsl-notice"
|
|
411
|
+
});
|
|
412
|
+
this.el.setAttribute("aria-hidden", true);
|
|
413
|
+
if (this.disabled) {
|
|
414
|
+
this.el.disabled = true;
|
|
415
|
+
}
|
|
416
|
+
if (this.el.multiple) {
|
|
417
|
+
util.addClass(this.label, "jsl-tags");
|
|
418
|
+
util.addClass(this.container, "multiple");
|
|
419
|
+
// Collection of tags
|
|
420
|
+
this.tags = [];
|
|
421
|
+
// Collection of selected values
|
|
422
|
+
// #93 defaultSelected = false did not work as expected
|
|
423
|
+
this.selectedValues = (this.config.defaultSelected) ? this.getSelectedProperties('value') : [];
|
|
424
|
+
// Collection of selected indexes
|
|
425
|
+
this.selectedIndexes = this.getSelectedProperties('idx');
|
|
426
|
+
}
|
|
427
|
+
else {
|
|
428
|
+
// #93 defaultSelected = false did not work as expected
|
|
429
|
+
// these values were undefined
|
|
430
|
+
this.selectedValue = null;
|
|
431
|
+
this.selectedIndex = -1;
|
|
432
|
+
}
|
|
433
|
+
this.selected.appendChild(this.label);
|
|
434
|
+
if (this.config.clearable) {
|
|
435
|
+
this.selectClear = util.createElement("button", {
|
|
436
|
+
class: "jsl-clear",
|
|
437
|
+
type: "button"
|
|
438
|
+
});
|
|
439
|
+
this.container.appendChild(this.selectClear);
|
|
440
|
+
util.addClass(this.container, "clearable");
|
|
441
|
+
}
|
|
442
|
+
if (this.config.taggable) {
|
|
443
|
+
var li = util.createElement('li', {
|
|
444
|
+
class: 'input-tag'
|
|
445
|
+
});
|
|
446
|
+
this.input = util.createElement("input", {
|
|
447
|
+
class: "jsl-tag-input",
|
|
448
|
+
placeholder: this.config.tagPlaceholder,
|
|
449
|
+
tagIndex: 0,
|
|
450
|
+
autocomplete: "off",
|
|
451
|
+
autocorrect: "off",
|
|
452
|
+
autocapitalize: "off",
|
|
453
|
+
spellcheck: "false",
|
|
454
|
+
role: "textbox",
|
|
455
|
+
type: "search"
|
|
456
|
+
});
|
|
457
|
+
li.appendChild(this.input);
|
|
458
|
+
this.label.appendChild(li);
|
|
459
|
+
util.addClass(this.container, "taggable");
|
|
460
|
+
this.tagSeperators = [","];
|
|
461
|
+
if (this.config.tagSeperators) {
|
|
462
|
+
this.tagSeperators = this.tagSeperators.concat(this.config.tagSeperators);
|
|
463
|
+
var _aTempEscapedSeperators = [];
|
|
464
|
+
for (var _nTagSeperatorStepCount = 0; _nTagSeperatorStepCount < this.tagSeperators.length; _nTagSeperatorStepCount++) {
|
|
465
|
+
_aTempEscapedSeperators.push(util.escapeRegExp(this.tagSeperators[_nTagSeperatorStepCount]));
|
|
466
|
+
}
|
|
467
|
+
this.tagSeperatorsRegex = new RegExp(_aTempEscapedSeperators.join('|'), 'i');
|
|
468
|
+
}
|
|
469
|
+
else {
|
|
470
|
+
this.tagSeperatorsRegex = new RegExp(',', 'i');
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
if (this.config.searchable) {
|
|
474
|
+
this.input = util.createElement("input", {
|
|
475
|
+
class: "jsl-input",
|
|
476
|
+
tagIndex: -1,
|
|
477
|
+
autocomplete: "off",
|
|
478
|
+
autocorrect: "off",
|
|
479
|
+
autocapitalize: "off",
|
|
480
|
+
spellcheck: "false",
|
|
481
|
+
role: "textbox",
|
|
482
|
+
// type: "search",
|
|
483
|
+
type: "text",
|
|
484
|
+
placeholder: this.config.messages.searchPlaceholder
|
|
485
|
+
});
|
|
486
|
+
this.inputClear = util.createElement("button", {
|
|
487
|
+
class: "jsl-input-clear",
|
|
488
|
+
type: "button"
|
|
489
|
+
});
|
|
490
|
+
this.inputContainer = util.createElement("div", {
|
|
491
|
+
class: "jsl-input-container"
|
|
492
|
+
});
|
|
493
|
+
this.inputContainer.appendChild(this.input);
|
|
494
|
+
this.inputContainer.appendChild(this.inputClear);
|
|
495
|
+
dropdown.appendChild(this.inputContainer);
|
|
496
|
+
}
|
|
497
|
+
dropdown.appendChild(this.notice);
|
|
498
|
+
dropdown.appendChild(this.tree);
|
|
499
|
+
// List of items for the dropdown
|
|
500
|
+
this.items = [];
|
|
501
|
+
// Establish options
|
|
502
|
+
this.options = [];
|
|
503
|
+
// Check for options in the element
|
|
504
|
+
if (this.el.options.length) {
|
|
505
|
+
this.options = [].slice.call(this.el.options);
|
|
506
|
+
}
|
|
507
|
+
// Element may have optgroups so
|
|
508
|
+
// iterate element.children instead of element.options
|
|
509
|
+
var group = false, j = 0;
|
|
510
|
+
if (this.el.children.length) {
|
|
511
|
+
util.each(this.el.children, function (i, element) {
|
|
512
|
+
if (element.nodeName === "OPTGROUP") {
|
|
513
|
+
group = util.createElement("ul", {
|
|
514
|
+
class: "jsl-optgroup",
|
|
515
|
+
role: "group",
|
|
516
|
+
html: "<li class='jsl-optgroup--label'>" + element.label + "</li>"
|
|
517
|
+
});
|
|
518
|
+
util.each(element.children, function (x, el) {
|
|
519
|
+
el.idx = j;
|
|
520
|
+
var itm = createItem.call(this, el, group);
|
|
521
|
+
itm.optgroup = group;
|
|
522
|
+
// group.appendChild(util.toDom(itm));
|
|
523
|
+
j++;
|
|
524
|
+
}, this);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
element.idx = j;
|
|
528
|
+
createItem.call(this, element);
|
|
529
|
+
j++;
|
|
530
|
+
}
|
|
531
|
+
}, this);
|
|
532
|
+
}
|
|
533
|
+
// Options defined by the data option
|
|
534
|
+
if (this.config.data && Array.isArray(this.config.data)) {
|
|
535
|
+
this.data = [];
|
|
536
|
+
var optgroup = false, option;
|
|
537
|
+
group = false;
|
|
538
|
+
j = 0;
|
|
539
|
+
util.each(this.config.data, function (i, opt) {
|
|
540
|
+
// Check for group options
|
|
541
|
+
if (isset(opt, "children")) {
|
|
542
|
+
optgroup = util.createElement("optgroup", {
|
|
543
|
+
label: opt.text
|
|
544
|
+
});
|
|
545
|
+
group = util.createElement("ul", {
|
|
546
|
+
class: "jsl-optgroup",
|
|
547
|
+
role: "group",
|
|
548
|
+
html: "<li class='jsl-optgroup--label'>" + opt.text + "</li>"
|
|
549
|
+
});
|
|
550
|
+
util.each(opt.children, function (x, data) {
|
|
551
|
+
option = new Option(data.text, data.value, false, data.hasOwnProperty("selected") && data.selected === true);
|
|
552
|
+
option.disabled = isset(data, "disabled");
|
|
553
|
+
this.options.push(option);
|
|
554
|
+
optgroup.appendChild(option);
|
|
555
|
+
option.idx = j;
|
|
556
|
+
var itm = createItem.call(this, option, data);
|
|
557
|
+
itm.optgroup = group;
|
|
558
|
+
// group.appendChild(util.toDom(itm));
|
|
559
|
+
this.data[j] = data;
|
|
560
|
+
j++;
|
|
561
|
+
}, this);
|
|
562
|
+
this.el.appendChild(optgroup);
|
|
563
|
+
}
|
|
564
|
+
else {
|
|
565
|
+
option = new Option(opt.text, opt.value, false, opt.hasOwnProperty("selected") && opt.selected === true);
|
|
566
|
+
option.disabled = isset(opt, "disabled");
|
|
567
|
+
this.options.push(option);
|
|
568
|
+
option.idx = j;
|
|
569
|
+
createItem.call(this, option, opt);
|
|
570
|
+
this.data[j] = opt;
|
|
571
|
+
j++;
|
|
572
|
+
}
|
|
573
|
+
}, this);
|
|
574
|
+
}
|
|
575
|
+
this.setSelected(true);
|
|
576
|
+
var first;
|
|
577
|
+
this.navIndex = 0;
|
|
578
|
+
for (var i = 0; i < this.items.length; i++) {
|
|
579
|
+
first = this.items[i];
|
|
580
|
+
if (!first.disabled) {
|
|
581
|
+
first.active = true;
|
|
582
|
+
this.navIndex = i;
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
// if (!util.hasClass(first, "disabled")) {
|
|
586
|
+
// util.addClass(first, "active");
|
|
587
|
+
// this.navIndex = i;
|
|
588
|
+
// break;
|
|
589
|
+
// }
|
|
590
|
+
}
|
|
591
|
+
// Check for pagination / infinite scroll
|
|
592
|
+
this.pageIndex = 1;
|
|
593
|
+
// Create the pages
|
|
594
|
+
this.paginate();
|
|
595
|
+
this.container.appendChild(this.selected);
|
|
596
|
+
this.container.appendChild(dropdown);
|
|
597
|
+
this.placeEl = util.createElement("div", {
|
|
598
|
+
class: "jsl-placeholder"
|
|
599
|
+
});
|
|
600
|
+
// Set the placeholder
|
|
601
|
+
this.setPlaceholder();
|
|
602
|
+
this.selected.appendChild(this.placeEl);
|
|
603
|
+
// Disable if required
|
|
604
|
+
if (this.disabled) {
|
|
605
|
+
this.disable();
|
|
606
|
+
}
|
|
607
|
+
this.el.parentNode.insertBefore(this.container, this.el);
|
|
608
|
+
this.container.appendChild(this.el);
|
|
609
|
+
};
|
|
610
|
+
/**
|
|
611
|
+
* Navigate through the dropdown
|
|
612
|
+
* @param {obj} e
|
|
613
|
+
* @return {void}
|
|
614
|
+
*/
|
|
615
|
+
var navigate = function (e) {
|
|
616
|
+
e = e || window.event;
|
|
617
|
+
// Filter out the keys we don"t want
|
|
618
|
+
if (!this.items.length || !this.opened || !util.includes([13, 38, 40], e.which)) {
|
|
619
|
+
this.navigating = false;
|
|
620
|
+
return;
|
|
621
|
+
}
|
|
622
|
+
e.preventDefault();
|
|
623
|
+
if (e.which === 13) {
|
|
624
|
+
if (this.noResults || (this.config.taggable && this.input.value.length > 0)) {
|
|
625
|
+
return false;
|
|
626
|
+
}
|
|
627
|
+
return this.change(this.navIndex);
|
|
628
|
+
}
|
|
629
|
+
var direction, prevEl = this.items[this.navIndex];
|
|
630
|
+
var lastIndex = this.navIndex;
|
|
631
|
+
switch (e.which) {
|
|
632
|
+
case 38:
|
|
633
|
+
direction = 0;
|
|
634
|
+
if (this.navIndex > 0) {
|
|
635
|
+
this.navIndex--;
|
|
636
|
+
}
|
|
637
|
+
break;
|
|
638
|
+
case 40:
|
|
639
|
+
direction = 1;
|
|
640
|
+
if (this.navIndex < this.items.length - 1) {
|
|
641
|
+
this.navIndex++;
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
this.navigating = true;
|
|
645
|
+
function validForSelection(itm) {
|
|
646
|
+
if (itm == null || itm.dom == null) {
|
|
647
|
+
return false;
|
|
648
|
+
}
|
|
649
|
+
return !util.hasClass(itm.dom, "disabled") && !util.hasClass(itm.dom, "excluded");
|
|
650
|
+
}
|
|
651
|
+
// Instead of wasting memory holding a copy of this.items
|
|
652
|
+
// with disabled / excluded options omitted, skip them instead
|
|
653
|
+
// while (util.hasClass(this.items[this.navIndex], "disabled") || util.hasClass(this.items[this.navIndex], "excluded")) {
|
|
654
|
+
while (!validForSelection(this.items[this.navIndex])) {
|
|
655
|
+
if (this.navIndex > 0 && this.navIndex < this.items.length - 1) {
|
|
656
|
+
if (direction) {
|
|
657
|
+
this.navIndex++;
|
|
658
|
+
}
|
|
659
|
+
else {
|
|
660
|
+
this.navIndex--;
|
|
661
|
+
}
|
|
662
|
+
}
|
|
663
|
+
else {
|
|
664
|
+
this.navIndex = lastIndex;
|
|
665
|
+
break;
|
|
666
|
+
}
|
|
667
|
+
if (this.searching) {
|
|
668
|
+
if (this.navIndex > this.tree.lastElementChild.idx) {
|
|
669
|
+
this.navIndex = this.tree.lastElementChild.idx;
|
|
670
|
+
break;
|
|
671
|
+
}
|
|
672
|
+
else if (this.navIndex < this.tree.firstElementChild.idx) {
|
|
673
|
+
this.navIndex = this.tree.firstElementChild.idx;
|
|
674
|
+
break;
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
}
|
|
678
|
+
// Autoscroll the dropdown during navigation
|
|
679
|
+
var r = util.rect(this.items[this.navIndex].dom);
|
|
680
|
+
if (!direction) {
|
|
681
|
+
if (this.navIndex === 0) {
|
|
682
|
+
this.tree.scrollTop = 0;
|
|
683
|
+
}
|
|
684
|
+
else if (r.top - this.optsRect.top < 0) {
|
|
685
|
+
this.tree.scrollTop = this.tree.scrollTop + (r.top - this.optsRect.top);
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
else {
|
|
689
|
+
if (this.navIndex === 0) {
|
|
690
|
+
this.tree.scrollTop = 0;
|
|
691
|
+
}
|
|
692
|
+
else if ((r.top + r.height) > (this.optsRect.top + this.optsRect.height)) {
|
|
693
|
+
this.tree.scrollTop = this.tree.scrollTop + ((r.top + r.height) - (this.optsRect.top + this.optsRect.height));
|
|
694
|
+
}
|
|
695
|
+
// Load another page if needed
|
|
696
|
+
if (this.navIndex === this.tree.childElementCount - 1 && this.requiresPagination) {
|
|
697
|
+
load.call(this);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
if (prevEl) {
|
|
701
|
+
removeActive(this.tree);
|
|
702
|
+
prevEl.active = false;
|
|
703
|
+
}
|
|
704
|
+
util.addClass(this.items[this.navIndex].dom, "active");
|
|
705
|
+
this.items[this.navIndex].active = true;
|
|
706
|
+
};
|
|
707
|
+
/**
|
|
708
|
+
* Add a tag
|
|
709
|
+
* @param {HTMLElement} item
|
|
710
|
+
*/
|
|
711
|
+
var addTag = function (item) {
|
|
712
|
+
var that = this, r;
|
|
713
|
+
var docFrag = document.createDocumentFragment();
|
|
714
|
+
var option = this.options[item.idx];
|
|
715
|
+
var data = this.data ? this.data[item.idx] : option;
|
|
716
|
+
var elementData = { class: "jsl-tag", html: undefined, textContent: undefined };
|
|
717
|
+
if (this.customSelected) {
|
|
718
|
+
elementData.html = this.config.renderSelection(data); // asume xss prevention in custom render function
|
|
719
|
+
}
|
|
720
|
+
else {
|
|
721
|
+
elementData.textContent = option.textContent;
|
|
722
|
+
}
|
|
723
|
+
var tag = util.createElement("li", elementData);
|
|
724
|
+
var btn = util.createElement("button", {
|
|
725
|
+
class: "jsl-tag-remove",
|
|
726
|
+
type: "button"
|
|
727
|
+
});
|
|
728
|
+
tag.appendChild(btn);
|
|
729
|
+
// Set property to check against later
|
|
730
|
+
tag.idx = item.idx;
|
|
731
|
+
tag.tag = option.value;
|
|
732
|
+
this.tags.push(tag);
|
|
733
|
+
if (this.config.sortSelected) {
|
|
734
|
+
var tags = this.tags.slice();
|
|
735
|
+
// Deal with values that contain numbers
|
|
736
|
+
r = function (val, arr) {
|
|
737
|
+
val.replace(/(\d+)|(\D+)/g, function (that, $1, $2) {
|
|
738
|
+
arr.push([$1 || Infinity, $2 || ""]);
|
|
739
|
+
});
|
|
740
|
+
};
|
|
741
|
+
tags.sort(function (a, b) {
|
|
742
|
+
var x = [], y = [], ac, bc;
|
|
743
|
+
if (that.config.sortSelected === true) {
|
|
744
|
+
ac = a.tag;
|
|
745
|
+
bc = b.tag;
|
|
746
|
+
}
|
|
747
|
+
else if (that.config.sortSelected === 'text') {
|
|
748
|
+
ac = a.textContent;
|
|
749
|
+
bc = b.textContent;
|
|
750
|
+
}
|
|
751
|
+
r(ac, x);
|
|
752
|
+
r(bc, y);
|
|
753
|
+
while (x.length && y.length) {
|
|
754
|
+
var ax = x.shift();
|
|
755
|
+
var by = y.shift();
|
|
756
|
+
var nn = (ax[0] - by[0]) || ax[1].localeCompare(by[1]);
|
|
757
|
+
if (nn)
|
|
758
|
+
return nn;
|
|
759
|
+
}
|
|
760
|
+
return x.length - y.length;
|
|
761
|
+
});
|
|
762
|
+
util.each(tags, function (i, tg) {
|
|
763
|
+
docFrag.appendChild(tg);
|
|
764
|
+
});
|
|
765
|
+
this.label.innerHTML = "";
|
|
766
|
+
}
|
|
767
|
+
else {
|
|
768
|
+
docFrag.appendChild(tag);
|
|
769
|
+
}
|
|
770
|
+
if (this.config.taggable) {
|
|
771
|
+
this.label.insertBefore(docFrag, this.input.parentNode);
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
this.label.appendChild(docFrag);
|
|
775
|
+
}
|
|
776
|
+
};
|
|
777
|
+
/**
|
|
778
|
+
* Remove a tag
|
|
779
|
+
* @param {HTMLElement} item
|
|
780
|
+
* @return {void}
|
|
781
|
+
*/
|
|
782
|
+
var removeTag = function (item) {
|
|
783
|
+
var tag = false;
|
|
784
|
+
util.each(this.tags, function (i, t) {
|
|
785
|
+
if (t.idx === item.idx) {
|
|
786
|
+
tag = t;
|
|
787
|
+
}
|
|
788
|
+
}, this);
|
|
789
|
+
if (tag) {
|
|
790
|
+
this.label.removeChild(tag);
|
|
791
|
+
this.tags.splice(this.tags.indexOf(tag), 1);
|
|
792
|
+
}
|
|
793
|
+
};
|
|
794
|
+
/**
|
|
795
|
+
* Load the next page of items
|
|
796
|
+
* @return {void}
|
|
797
|
+
*/
|
|
798
|
+
var load = function () {
|
|
799
|
+
// sometimes atBottom = false when atBottom < 0.xxx pixel smaller than "this.pageIndex < this.pages.length"
|
|
800
|
+
var margin = 1; // solution: add a bit of margin
|
|
801
|
+
var tree = this.tree;
|
|
802
|
+
var scrollTop = tree.scrollTop;
|
|
803
|
+
var scrollHeight = tree.scrollHeight;
|
|
804
|
+
var offsetHeight = tree.offsetHeight;
|
|
805
|
+
var atBottom = scrollTop + margin >= (scrollHeight - offsetHeight);
|
|
806
|
+
if ((atBottom && this.pageIndex < this.pages.length)) {
|
|
807
|
+
var f = document.createDocumentFragment();
|
|
808
|
+
util.each(this.pages[this.pageIndex], function (i, item) {
|
|
809
|
+
appendItem(item, f, this.customOption);
|
|
810
|
+
}, this);
|
|
811
|
+
tree.appendChild(f);
|
|
812
|
+
this.pageIndex++;
|
|
813
|
+
this.emit("selectr.paginate", {
|
|
814
|
+
items: this.items.length,
|
|
815
|
+
total: this.data.length,
|
|
816
|
+
page: this.pageIndex,
|
|
817
|
+
pages: this.pages.length
|
|
818
|
+
});
|
|
819
|
+
}
|
|
820
|
+
};
|
|
821
|
+
/**
|
|
822
|
+
* Clear a search
|
|
823
|
+
* @return {void}
|
|
824
|
+
*/
|
|
825
|
+
var clearSearch = function () {
|
|
826
|
+
this.searchResults = null;
|
|
827
|
+
if (this.config.searchable || this.config.taggable) {
|
|
828
|
+
this.input.value = null;
|
|
829
|
+
this.searching = false;
|
|
830
|
+
if (this.config.searchable) {
|
|
831
|
+
util.removeClass(this.inputContainer, "active");
|
|
832
|
+
}
|
|
833
|
+
if (util.hasClass(this.container, "notice")) {
|
|
834
|
+
util.removeClass(this.container, "notice");
|
|
835
|
+
util.addClass(this.container, "open");
|
|
836
|
+
this.input.focus();
|
|
837
|
+
}
|
|
838
|
+
util.each(this.items, function (i, item) {
|
|
839
|
+
// Items that didn't match need the class
|
|
840
|
+
// removing to make them visible again
|
|
841
|
+
if (item.dom) {
|
|
842
|
+
util.removeClass(item.dom, "excluded");
|
|
843
|
+
}
|
|
844
|
+
// Remove the span element for underlining matched items
|
|
845
|
+
if (!this.customOption) {
|
|
846
|
+
// without xss
|
|
847
|
+
item.textContent = item.textContent;
|
|
848
|
+
}
|
|
849
|
+
}, this);
|
|
850
|
+
this.pageIndex = 1;
|
|
851
|
+
this.paginate();
|
|
852
|
+
}
|
|
853
|
+
};
|
|
854
|
+
// /**
|
|
855
|
+
// * Query matching for searches.
|
|
856
|
+
// * Wraps matching text in a span.jsl-match.
|
|
857
|
+
// *
|
|
858
|
+
// * @param {string} query
|
|
859
|
+
// * @param {HTMLOptionElement} option element
|
|
860
|
+
// * @return {bool} true if matched; false otherwise
|
|
861
|
+
// */
|
|
862
|
+
// var match = function (query, option) {
|
|
863
|
+
// var text = option.textContent;
|
|
864
|
+
// var RX = new RegExp(query, "ig");
|
|
865
|
+
// var result = RX.exec(text);
|
|
866
|
+
// if (result) {
|
|
867
|
+
// // #102 stop xss
|
|
868
|
+
// option.innerHTML = "";
|
|
869
|
+
// var span = document.createElement("span");
|
|
870
|
+
// span.classList.add("jsl-match");
|
|
871
|
+
// span.textContent = result[0];
|
|
872
|
+
// option.appendChild(document.createTextNode(text.substring(0, result.index)));
|
|
873
|
+
// option.appendChild(span);
|
|
874
|
+
// option.appendChild(document.createTextNode(text.substring(RX.lastIndex)));
|
|
875
|
+
// return true;
|
|
876
|
+
// }
|
|
877
|
+
// return false;
|
|
878
|
+
// };
|
|
879
|
+
// Main Lib
|
|
880
|
+
Selectr = function (el, config) {
|
|
881
|
+
if (!el) {
|
|
882
|
+
throw new Error("You must supply either a HTMLSelectElement or a CSS3 selector string.");
|
|
883
|
+
}
|
|
884
|
+
this.el = el;
|
|
885
|
+
// CSS3 selector string
|
|
886
|
+
if (typeof el === "string") {
|
|
887
|
+
this.el = document.querySelector(el);
|
|
888
|
+
}
|
|
889
|
+
if (this.el === null) {
|
|
890
|
+
throw new Error("The element you passed to Selectr can not be found.");
|
|
891
|
+
}
|
|
892
|
+
if (this.el.nodeName.toLowerCase() !== "select") {
|
|
893
|
+
throw new Error("The element you passed to Selectr is not a HTMLSelectElement.");
|
|
894
|
+
}
|
|
895
|
+
this.render(config);
|
|
896
|
+
};
|
|
897
|
+
/**
|
|
898
|
+
* Render the instance
|
|
899
|
+
* @param {object} config
|
|
900
|
+
* @return {void}
|
|
901
|
+
*/
|
|
902
|
+
Selectr.prototype.render = function (config) {
|
|
903
|
+
if (this.rendered)
|
|
904
|
+
return;
|
|
905
|
+
/**
|
|
906
|
+
* Default configuration options
|
|
907
|
+
* @type {Object}
|
|
908
|
+
*/
|
|
909
|
+
var defaultConfig = {
|
|
910
|
+
/**
|
|
911
|
+
* Emulates browser behaviour by selecting the first option by default
|
|
912
|
+
* @type {Boolean}
|
|
913
|
+
*/
|
|
914
|
+
defaultSelected: true,
|
|
915
|
+
/**
|
|
916
|
+
* Sets the width of the container
|
|
917
|
+
* @type {String}
|
|
918
|
+
*/
|
|
919
|
+
width: "auto",
|
|
920
|
+
/**
|
|
921
|
+
* Enables/ disables the container
|
|
922
|
+
* @type {Boolean}
|
|
923
|
+
*/
|
|
924
|
+
disabled: false,
|
|
925
|
+
/**
|
|
926
|
+
* Enables/ disables logic for mobile
|
|
927
|
+
* @type {Boolean}
|
|
928
|
+
*/
|
|
929
|
+
disabledMobile: true,
|
|
930
|
+
/**
|
|
931
|
+
* Enables / disables the search function
|
|
932
|
+
* @type {Boolean}
|
|
933
|
+
*/
|
|
934
|
+
searchable: true,
|
|
935
|
+
/**
|
|
936
|
+
* Enable disable the clear button
|
|
937
|
+
* @type {Boolean}
|
|
938
|
+
*/
|
|
939
|
+
clearable: false,
|
|
940
|
+
/**
|
|
941
|
+
* Sort the tags / multiselect options
|
|
942
|
+
* @type {Boolean}
|
|
943
|
+
*/
|
|
944
|
+
sortSelected: false,
|
|
945
|
+
/**
|
|
946
|
+
* Allow deselecting of select-one options
|
|
947
|
+
* @type {Boolean}
|
|
948
|
+
*/
|
|
949
|
+
allowDeselect: false,
|
|
950
|
+
/**
|
|
951
|
+
* Close the dropdown when scrolling (@AlexanderReiswich, #11)
|
|
952
|
+
* @type {Boolean}
|
|
953
|
+
*/
|
|
954
|
+
closeOnScroll: false,
|
|
955
|
+
/**
|
|
956
|
+
* Allow the use of the native dropdown (@jonnyscholes, #14)
|
|
957
|
+
* @type {Boolean}
|
|
958
|
+
*/
|
|
959
|
+
nativeDropdown: false,
|
|
960
|
+
/**
|
|
961
|
+
* Allow the use of native typing behavior for toggling, searching, selecting
|
|
962
|
+
* @type {boolean}
|
|
963
|
+
*/
|
|
964
|
+
nativeKeyboard: false,
|
|
965
|
+
/**
|
|
966
|
+
* Set the main placeholder
|
|
967
|
+
* @type {String}
|
|
968
|
+
*/
|
|
969
|
+
placeholder: UILang.dropDown.SelectOption,
|
|
970
|
+
/**
|
|
971
|
+
* Allow the tagging feature
|
|
972
|
+
* @type {Boolean}
|
|
973
|
+
*/
|
|
974
|
+
taggable: false,
|
|
975
|
+
/**
|
|
976
|
+
* Set the tag input placeholder (@labikmartin, #21, #22)
|
|
977
|
+
* @type {String}
|
|
978
|
+
*/
|
|
979
|
+
tagPlaceholder: UILang.dropDown.SelectOptionMultiple,
|
|
980
|
+
messages: {
|
|
981
|
+
noResults: UILang.dropDown.NoResults,
|
|
982
|
+
noOptions: UILang.dropDown.NoOptions,
|
|
983
|
+
maxSelections: UILang.dropDown.MaxSelection,
|
|
984
|
+
tagDuplicate: UILang.dropDown.AlreadyInUse,
|
|
985
|
+
searchPlaceholder: UILang.dropDown.Search
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
// add instance reference (#87)
|
|
989
|
+
this.el.selectr = this;
|
|
990
|
+
// Merge defaults with user set config
|
|
991
|
+
this.config = util.extend(defaultConfig, config);
|
|
992
|
+
// Store type
|
|
993
|
+
this.originalType = this.el.type;
|
|
994
|
+
// Store tabIndex
|
|
995
|
+
this.originalIndex = this.el.tabIndex;
|
|
996
|
+
// Store defaultSelected options for form reset
|
|
997
|
+
this.defaultSelected = [];
|
|
998
|
+
// Store the original option count
|
|
999
|
+
this.originalOptionCount = this.el.options.length;
|
|
1000
|
+
if (this.config.multiple || this.config.taggable) {
|
|
1001
|
+
this.el.multiple = true;
|
|
1002
|
+
}
|
|
1003
|
+
// Disabled?
|
|
1004
|
+
this.disabled = isset(this.config, "disabled");
|
|
1005
|
+
this.opened = false;
|
|
1006
|
+
if (this.config.taggable) {
|
|
1007
|
+
this.config.searchable = false;
|
|
1008
|
+
}
|
|
1009
|
+
this.navigating = false;
|
|
1010
|
+
this.mobileDevice = false;
|
|
1011
|
+
if (!this.config.disabledMobile && /Android|webOS|iPhone|iPad|BlackBerry|Windows Phone|Opera Mini|IEMobile|Mobile/i.test(navigator.userAgent)) {
|
|
1012
|
+
this.mobileDevice = true;
|
|
1013
|
+
}
|
|
1014
|
+
this.customOption = this.config.hasOwnProperty("renderOption") && typeof this.config.renderOption === "function";
|
|
1015
|
+
this.customSelected = this.config.hasOwnProperty("renderSelection") && typeof this.config.renderSelection === "function";
|
|
1016
|
+
this.supportsEventPassiveOption = this.detectEventPassiveOption();
|
|
1017
|
+
// Enable event emitter
|
|
1018
|
+
Events.mixin(this);
|
|
1019
|
+
build.call(this);
|
|
1020
|
+
this.bindEvents();
|
|
1021
|
+
this.update();
|
|
1022
|
+
this.optsRect = util.rect(this.tree);
|
|
1023
|
+
this.rendered = true;
|
|
1024
|
+
// Fixes macOS Safari bug #28
|
|
1025
|
+
if (!this.el.multiple) {
|
|
1026
|
+
this.el.selectedIndex = this.selectedIndex;
|
|
1027
|
+
}
|
|
1028
|
+
var that = this;
|
|
1029
|
+
setTimeout(function () {
|
|
1030
|
+
that.emit("selectr.init");
|
|
1031
|
+
}, 20);
|
|
1032
|
+
};
|
|
1033
|
+
Selectr.prototype.getSelected = function () {
|
|
1034
|
+
var selected = this.el.querySelectorAll('option:checked');
|
|
1035
|
+
return selected;
|
|
1036
|
+
};
|
|
1037
|
+
Selectr.prototype.getSelectedProperties = function (prop) {
|
|
1038
|
+
var selected = this.getSelected();
|
|
1039
|
+
var values = [].slice.call(selected)
|
|
1040
|
+
.map(function (option) { return option[prop]; })
|
|
1041
|
+
.filter(function (i) { return i !== null && i !== undefined; });
|
|
1042
|
+
return values;
|
|
1043
|
+
};
|
|
1044
|
+
/**
|
|
1045
|
+
* Feature detection: addEventListener passive option
|
|
1046
|
+
* https://dom.spec.whatwg.org/#dom-addeventlisteneroptions-passive
|
|
1047
|
+
* https://github.com/WICG/EventListenerOptions/blob/gh-pages/explainer.md
|
|
1048
|
+
*/
|
|
1049
|
+
Selectr.prototype.detectEventPassiveOption = function () {
|
|
1050
|
+
var supportsPassiveOption = false;
|
|
1051
|
+
try {
|
|
1052
|
+
var opts = Object.defineProperty({}, 'passive', {
|
|
1053
|
+
get: function () {
|
|
1054
|
+
supportsPassiveOption = true;
|
|
1055
|
+
}
|
|
1056
|
+
});
|
|
1057
|
+
window.addEventListener('test', null, opts);
|
|
1058
|
+
}
|
|
1059
|
+
catch (e) { }
|
|
1060
|
+
return supportsPassiveOption;
|
|
1061
|
+
};
|
|
1062
|
+
/**
|
|
1063
|
+
* Attach the required event listeners
|
|
1064
|
+
*/
|
|
1065
|
+
Selectr.prototype.bindEvents = function () {
|
|
1066
|
+
var that = this;
|
|
1067
|
+
this.events = {};
|
|
1068
|
+
this.events.dismiss = dismiss.bind(this);
|
|
1069
|
+
this.events.navigate = navigate.bind(this);
|
|
1070
|
+
this.events.reset = this.reset.bind(this);
|
|
1071
|
+
if (this.config.nativeDropdown || this.mobileDevice) {
|
|
1072
|
+
this.container.addEventListener("touchstart", function (e) {
|
|
1073
|
+
if (e.changedTouches[0].target === that.el) {
|
|
1074
|
+
that.toggle();
|
|
1075
|
+
}
|
|
1076
|
+
}, this.supportsEventPassiveOption ? { passive: true } : false);
|
|
1077
|
+
this.container.addEventListener("click", function (e) {
|
|
1078
|
+
if (e.target === that.el) {
|
|
1079
|
+
that.toggle();
|
|
1080
|
+
}
|
|
1081
|
+
});
|
|
1082
|
+
var getChangedOptions = function (last, current) {
|
|
1083
|
+
var added = [], removed = last.slice(0);
|
|
1084
|
+
var idx;
|
|
1085
|
+
for (var i = 0; i < current.length; i++) {
|
|
1086
|
+
idx = removed.indexOf(current[i]);
|
|
1087
|
+
if (idx > -1)
|
|
1088
|
+
removed.splice(idx, 1);
|
|
1089
|
+
else
|
|
1090
|
+
added.push(current[i]);
|
|
1091
|
+
}
|
|
1092
|
+
return [added, removed];
|
|
1093
|
+
};
|
|
1094
|
+
// Listen for the change on the native select
|
|
1095
|
+
// and update accordingly
|
|
1096
|
+
this.el.addEventListener("change", function (e) {
|
|
1097
|
+
if (e.__selfTriggered) {
|
|
1098
|
+
return;
|
|
1099
|
+
}
|
|
1100
|
+
if (that.el.multiple) {
|
|
1101
|
+
var indexes = that.getSelectedProperties('idx');
|
|
1102
|
+
var changes = getChangedOptions(that.selectedIndexes, indexes);
|
|
1103
|
+
util.each(changes[0], function (i, idx) {
|
|
1104
|
+
that.select(idx);
|
|
1105
|
+
}, that);
|
|
1106
|
+
util.each(changes[1], function (i, idx) {
|
|
1107
|
+
that.deselect(idx);
|
|
1108
|
+
}, that);
|
|
1109
|
+
}
|
|
1110
|
+
else {
|
|
1111
|
+
if (that.el.selectedIndex > -1) {
|
|
1112
|
+
that.select(that.el.selectedIndex);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
});
|
|
1116
|
+
}
|
|
1117
|
+
// Open the dropdown with Enter key if focused
|
|
1118
|
+
if (this.config.nativeDropdown) {
|
|
1119
|
+
this.container.addEventListener("keydown", function (e) {
|
|
1120
|
+
if (e.key === "Enter" && that.selected === document.activeElement) {
|
|
1121
|
+
// show native dropdown
|
|
1122
|
+
that.toggle();
|
|
1123
|
+
// focus on it
|
|
1124
|
+
setTimeout(function () {
|
|
1125
|
+
that.el.focus();
|
|
1126
|
+
}, 200);
|
|
1127
|
+
}
|
|
1128
|
+
});
|
|
1129
|
+
}
|
|
1130
|
+
// Non-native dropdown
|
|
1131
|
+
this.selected.addEventListener("click", function (e) {
|
|
1132
|
+
if (!that.disabled) {
|
|
1133
|
+
that.toggle();
|
|
1134
|
+
}
|
|
1135
|
+
e.preventDefault();
|
|
1136
|
+
});
|
|
1137
|
+
if (this.config.nativeKeyboard) {
|
|
1138
|
+
var typing = '';
|
|
1139
|
+
var typingTimeout = null;
|
|
1140
|
+
this.selected.addEventListener("keydown", function (e) {
|
|
1141
|
+
// Do nothing if disabled, not focused, or modifier keys are pressed
|
|
1142
|
+
if (that.disabled ||
|
|
1143
|
+
that.selected !== document.activeElement ||
|
|
1144
|
+
(e.altKey || e.ctrlKey || e.metaKey)) {
|
|
1145
|
+
return;
|
|
1146
|
+
}
|
|
1147
|
+
// Open the dropdown on [enter], [ ], [↓], and [↑] keys
|
|
1148
|
+
if (e.key === " " ||
|
|
1149
|
+
(!that.opened && ["Enter", "ArrowUp", "ArrowDown"].indexOf(e.key) > -1)) {
|
|
1150
|
+
that.toggle();
|
|
1151
|
+
e.preventDefault();
|
|
1152
|
+
e.stopPropagation();
|
|
1153
|
+
return;
|
|
1154
|
+
}
|
|
1155
|
+
// Type to search if multiple; type to select otherwise
|
|
1156
|
+
// make sure e.key is a single, printable character
|
|
1157
|
+
// .length check is a short-circut to skip checking keys like "ArrowDown", etc.
|
|
1158
|
+
// prefer "codePoint" methods; they work with the full range of unicode
|
|
1159
|
+
if (e.key.length <= 2 &&
|
|
1160
|
+
String[String.fromCodePoint ? "fromCodePoint" : "fromCharCode"](e.key[String.codePointAt ? "codePointAt" : "charCodeAt"](0)) === e.key) {
|
|
1161
|
+
if (that.config.multiple) {
|
|
1162
|
+
that.open();
|
|
1163
|
+
if (that.config.searchable) {
|
|
1164
|
+
that.input.value = e.key;
|
|
1165
|
+
that.input.focus();
|
|
1166
|
+
that.search(null, that.options.exactSearch);
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
else {
|
|
1170
|
+
if (typingTimeout) {
|
|
1171
|
+
clearTimeout(typingTimeout);
|
|
1172
|
+
}
|
|
1173
|
+
typing += e.key;
|
|
1174
|
+
var found = that.search(typing, that.options.exactSearch);
|
|
1175
|
+
if (found && found.length) {
|
|
1176
|
+
that.clear();
|
|
1177
|
+
that.setValue(found[0].value);
|
|
1178
|
+
}
|
|
1179
|
+
setTimeout(function () { typing = ''; }, 1000);
|
|
1180
|
+
}
|
|
1181
|
+
e.preventDefault();
|
|
1182
|
+
e.stopPropagation();
|
|
1183
|
+
return;
|
|
1184
|
+
}
|
|
1185
|
+
});
|
|
1186
|
+
// Close the dropdown on [esc] key
|
|
1187
|
+
this.container.addEventListener("keyup", function (e) {
|
|
1188
|
+
if (that.opened && e.key === "Escape") {
|
|
1189
|
+
that.close();
|
|
1190
|
+
e.stopPropagation();
|
|
1191
|
+
// keep focus so we can re-open easily if desired
|
|
1192
|
+
that.selected.focus();
|
|
1193
|
+
}
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
// Remove tag
|
|
1197
|
+
this.label.addEventListener("click", function (e) {
|
|
1198
|
+
if (!that.disabled && util.hasClass(e.target, "jsl-tag-remove")) {
|
|
1199
|
+
that.deselect(e.target.parentNode.idx);
|
|
1200
|
+
}
|
|
1201
|
+
});
|
|
1202
|
+
// Clear input
|
|
1203
|
+
if (this.selectClear) {
|
|
1204
|
+
this.selectClear.addEventListener("click", this.clear.bind(this));
|
|
1205
|
+
}
|
|
1206
|
+
// Prevent text selection
|
|
1207
|
+
this.tree.addEventListener("mousedown", function (e) {
|
|
1208
|
+
e.preventDefault();
|
|
1209
|
+
});
|
|
1210
|
+
// Select / deselect items
|
|
1211
|
+
this.tree.addEventListener("click", function (e) {
|
|
1212
|
+
var item = util.closest(e.target, function (el) {
|
|
1213
|
+
return el && util.hasClass(el, "jsl-option");
|
|
1214
|
+
});
|
|
1215
|
+
if (item) {
|
|
1216
|
+
if (!util.hasClass(item, "disabled")) {
|
|
1217
|
+
if (util.hasClass(item, "selected")) {
|
|
1218
|
+
if (that.el.multiple || !that.el.multiple && that.config.allowDeselect) {
|
|
1219
|
+
that.deselect(item.idx);
|
|
1220
|
+
}
|
|
1221
|
+
}
|
|
1222
|
+
else {
|
|
1223
|
+
that.select(item.idx);
|
|
1224
|
+
}
|
|
1225
|
+
if (that.opened && !that.el.multiple) {
|
|
1226
|
+
that.close();
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
e.preventDefault();
|
|
1231
|
+
e.stopPropagation();
|
|
1232
|
+
});
|
|
1233
|
+
// Mouseover list items
|
|
1234
|
+
this.tree.addEventListener("mouseover", function (e) {
|
|
1235
|
+
if (util.hasClass(e.target, "jsl-option")) {
|
|
1236
|
+
if (!util.hasClass(e.target, "disabled")) {
|
|
1237
|
+
removeActive(that.tree);
|
|
1238
|
+
util.addClass(e.target, "active");
|
|
1239
|
+
that.navIndex = -1;
|
|
1240
|
+
for (let idx = 0; idx < that.items.length; idx++) {
|
|
1241
|
+
if (that.items[idx].dom === e.target) {
|
|
1242
|
+
that.navIndex = idx;
|
|
1243
|
+
}
|
|
1244
|
+
}
|
|
1245
|
+
// that.navIndex = [].slice.call(that.items).indexOf(e.target);
|
|
1246
|
+
}
|
|
1247
|
+
}
|
|
1248
|
+
});
|
|
1249
|
+
// Searchable
|
|
1250
|
+
if (this.config.searchable) {
|
|
1251
|
+
// Show / hide the search input clear button
|
|
1252
|
+
this.input.addEventListener("focus", function (e) {
|
|
1253
|
+
that.searching = true;
|
|
1254
|
+
});
|
|
1255
|
+
this.input.addEventListener("blur", function (e) {
|
|
1256
|
+
that.searching = false;
|
|
1257
|
+
});
|
|
1258
|
+
this.input.addEventListener("input", function (e) {
|
|
1259
|
+
that.search();
|
|
1260
|
+
if (!that.config.taggable) {
|
|
1261
|
+
// Show / hide the search input clear button
|
|
1262
|
+
if (this.value.length) {
|
|
1263
|
+
util.addClass(this.parentNode, "active");
|
|
1264
|
+
}
|
|
1265
|
+
else {
|
|
1266
|
+
util.removeClass(this.parentNode, "active");
|
|
1267
|
+
}
|
|
1268
|
+
}
|
|
1269
|
+
});
|
|
1270
|
+
// Clear the search input
|
|
1271
|
+
this.inputClear.addEventListener("click", function (e) {
|
|
1272
|
+
that.input.value = null;
|
|
1273
|
+
clearSearch.call(that);
|
|
1274
|
+
if (!that.tree.childElementCount) {
|
|
1275
|
+
render.call(that);
|
|
1276
|
+
}
|
|
1277
|
+
});
|
|
1278
|
+
}
|
|
1279
|
+
if (this.config.taggable) {
|
|
1280
|
+
this.input.addEventListener("keyup", function (e) {
|
|
1281
|
+
that.search();
|
|
1282
|
+
if (that.config.taggable && this.value.length) {
|
|
1283
|
+
var _sVal = this.value.trim();
|
|
1284
|
+
if (_sVal.length && (e.which === 13 || that.tagSeperatorsRegex.test(_sVal))) {
|
|
1285
|
+
var _sGrabbedTagValue = _sVal.replace(that.tagSeperatorsRegex, '');
|
|
1286
|
+
_sGrabbedTagValue = util.escapeRegExp(_sGrabbedTagValue);
|
|
1287
|
+
_sGrabbedTagValue = _sGrabbedTagValue.trim();
|
|
1288
|
+
var _oOption;
|
|
1289
|
+
if (_sGrabbedTagValue.length) {
|
|
1290
|
+
_oOption = that.add({
|
|
1291
|
+
value: _sGrabbedTagValue,
|
|
1292
|
+
textContent: _sGrabbedTagValue,
|
|
1293
|
+
selected: true
|
|
1294
|
+
}, true);
|
|
1295
|
+
}
|
|
1296
|
+
if (_oOption) {
|
|
1297
|
+
that.close();
|
|
1298
|
+
clearSearch.call(that);
|
|
1299
|
+
}
|
|
1300
|
+
else {
|
|
1301
|
+
this.value = '';
|
|
1302
|
+
that.setMessage(that.config.messages.tagDuplicate);
|
|
1303
|
+
}
|
|
1304
|
+
}
|
|
1305
|
+
}
|
|
1306
|
+
});
|
|
1307
|
+
}
|
|
1308
|
+
this.update = util.debounce(function () {
|
|
1309
|
+
// Optionally close dropdown on scroll / resize (#11)
|
|
1310
|
+
if (that.opened && that.config.closeOnScroll) {
|
|
1311
|
+
that.close();
|
|
1312
|
+
}
|
|
1313
|
+
if (that.width) {
|
|
1314
|
+
that.container.style.width = that.width;
|
|
1315
|
+
}
|
|
1316
|
+
that.invert();
|
|
1317
|
+
}, 50);
|
|
1318
|
+
if (this.requiresPagination) {
|
|
1319
|
+
this.paginateItems = util.debounce(function () {
|
|
1320
|
+
load.call(this);
|
|
1321
|
+
}, 50);
|
|
1322
|
+
this.tree.addEventListener("scroll", this.paginateItems.bind(this));
|
|
1323
|
+
}
|
|
1324
|
+
// Dismiss when clicking outside the container
|
|
1325
|
+
document.addEventListener("click", this.events.dismiss);
|
|
1326
|
+
this.container.addEventListener("keydown", this.events.navigate);
|
|
1327
|
+
window.addEventListener("resize", this.update);
|
|
1328
|
+
window.addEventListener("scroll", this.update);
|
|
1329
|
+
// remove event listeners on destroy()
|
|
1330
|
+
this.on('selectr.destroy', function () {
|
|
1331
|
+
document.removeEventListener("click", this.events.dismiss);
|
|
1332
|
+
this.container.removeEventListener("keydown", this.events.navigate);
|
|
1333
|
+
window.removeEventListener("resize", this.update);
|
|
1334
|
+
window.removeEventListener("scroll", this.update);
|
|
1335
|
+
});
|
|
1336
|
+
// Listen for form.reset() (@ambrooks, #13)
|
|
1337
|
+
if (this.el.form) {
|
|
1338
|
+
this.el.form.addEventListener("reset", this.events.reset);
|
|
1339
|
+
// remove listener on destroy()
|
|
1340
|
+
this.on('selectr.destroy', function () {
|
|
1341
|
+
this.el.form.removeEventListener("reset", this.events.reset);
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
};
|
|
1345
|
+
/**
|
|
1346
|
+
* Check for selected options
|
|
1347
|
+
* @param {bool} reset
|
|
1348
|
+
*/
|
|
1349
|
+
Selectr.prototype.setSelected = function (reset) {
|
|
1350
|
+
// Select first option as with a native select-one element - #21, #24
|
|
1351
|
+
if (!this.config.data && !this.el.multiple && this.el.options.length) {
|
|
1352
|
+
// Browser has selected the first option by default
|
|
1353
|
+
if (this.el.selectedIndex === 0) {
|
|
1354
|
+
if (!this.el.options[0].defaultSelected && !this.config.defaultSelected) {
|
|
1355
|
+
this.el.selectedIndex = -1;
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
this.selectedIndex = this.el.selectedIndex;
|
|
1359
|
+
if (this.selectedIndex > -1) {
|
|
1360
|
+
this.select(this.selectedIndex);
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
// If we're changing a select-one to select-multiple via the config
|
|
1364
|
+
// and there are no selected options, the first option will be selected by the browser
|
|
1365
|
+
// Let's prevent that here.
|
|
1366
|
+
if (this.config.multiple && this.originalType === "select-one" && !this.config.data) {
|
|
1367
|
+
if (this.el.options[0].selected && !this.el.options[0].defaultSelected) {
|
|
1368
|
+
this.el.options[0].selected = false;
|
|
1369
|
+
}
|
|
1370
|
+
}
|
|
1371
|
+
util.each(this.options, function (i, option) {
|
|
1372
|
+
if (option.selected && option.defaultSelected) {
|
|
1373
|
+
this.select(option.idx);
|
|
1374
|
+
}
|
|
1375
|
+
}, this);
|
|
1376
|
+
if (this.config.selectedValue) {
|
|
1377
|
+
this.setValue(this.config.selectedValue);
|
|
1378
|
+
}
|
|
1379
|
+
if (this.config.data) {
|
|
1380
|
+
if (!this.el.multiple && this.config.defaultSelected && this.el.selectedIndex < 0 && this.config.data.length > 0) {
|
|
1381
|
+
this.select(0);
|
|
1382
|
+
}
|
|
1383
|
+
var j = 0;
|
|
1384
|
+
util.each(this.config.data, function (i, opt) {
|
|
1385
|
+
// Check for group options
|
|
1386
|
+
if (isset(opt, "children")) {
|
|
1387
|
+
util.each(opt.children, function (x, item) {
|
|
1388
|
+
if (item.hasOwnProperty("selected") && item.selected === true) {
|
|
1389
|
+
this.select(j);
|
|
1390
|
+
}
|
|
1391
|
+
j++;
|
|
1392
|
+
}, this);
|
|
1393
|
+
}
|
|
1394
|
+
else {
|
|
1395
|
+
if (opt.hasOwnProperty("selected") && opt.selected === true) {
|
|
1396
|
+
this.select(j);
|
|
1397
|
+
}
|
|
1398
|
+
j++;
|
|
1399
|
+
}
|
|
1400
|
+
}, this);
|
|
1401
|
+
}
|
|
1402
|
+
};
|
|
1403
|
+
/**
|
|
1404
|
+
* Destroy the instance
|
|
1405
|
+
* @return {void}
|
|
1406
|
+
*/
|
|
1407
|
+
Selectr.prototype.destroy = function () {
|
|
1408
|
+
if (!this.rendered)
|
|
1409
|
+
return;
|
|
1410
|
+
this.emit("selectr.destroy");
|
|
1411
|
+
// Revert to select-single if programtically set to multiple
|
|
1412
|
+
if (this.originalType === 'select-one') {
|
|
1413
|
+
this.el.multiple = false;
|
|
1414
|
+
}
|
|
1415
|
+
if (this.config.data) {
|
|
1416
|
+
this.el.innerHTML = "";
|
|
1417
|
+
}
|
|
1418
|
+
// Remove the className from select element
|
|
1419
|
+
util.removeClass(this.el, 'jsl-hidden');
|
|
1420
|
+
// Replace the container with the original select element
|
|
1421
|
+
this.container.parentNode.replaceChild(this.el, this.container);
|
|
1422
|
+
this.rendered = false;
|
|
1423
|
+
// remove reference
|
|
1424
|
+
delete this.el.selectr;
|
|
1425
|
+
};
|
|
1426
|
+
/**
|
|
1427
|
+
* Change an options state
|
|
1428
|
+
* @param {Number} index
|
|
1429
|
+
* @return {void}
|
|
1430
|
+
*/
|
|
1431
|
+
Selectr.prototype.change = function (index) {
|
|
1432
|
+
var item = this.items[index], option = this.options[index];
|
|
1433
|
+
if (option.disabled) {
|
|
1434
|
+
return;
|
|
1435
|
+
}
|
|
1436
|
+
if (option.selected && util.hasClass(item, "selected")) {
|
|
1437
|
+
this.deselect(index);
|
|
1438
|
+
}
|
|
1439
|
+
else {
|
|
1440
|
+
this.select(index);
|
|
1441
|
+
}
|
|
1442
|
+
if (this.opened && !this.el.multiple) {
|
|
1443
|
+
this.close();
|
|
1444
|
+
}
|
|
1445
|
+
};
|
|
1446
|
+
/**
|
|
1447
|
+
* Select an option
|
|
1448
|
+
* @param {Number} index
|
|
1449
|
+
* @return {void}
|
|
1450
|
+
*/
|
|
1451
|
+
Selectr.prototype.select = function (index) {
|
|
1452
|
+
var item = this.items[index], options = [].slice.call(this.el.options), option = this.options[index];
|
|
1453
|
+
if (this.el.multiple) {
|
|
1454
|
+
if (util.includes(this.selectedIndexes, index)) {
|
|
1455
|
+
return false;
|
|
1456
|
+
}
|
|
1457
|
+
if (this.config.maxSelections && this.tags.length === this.config.maxSelections) {
|
|
1458
|
+
this.setMessage(this.config.messages.maxSelections.replace("{max}", this.config.maxSelections), true);
|
|
1459
|
+
return false;
|
|
1460
|
+
}
|
|
1461
|
+
this.selectedValues.push(option.value);
|
|
1462
|
+
this.selectedIndexes.push(index);
|
|
1463
|
+
addTag.call(this, item);
|
|
1464
|
+
}
|
|
1465
|
+
else {
|
|
1466
|
+
var data = this.data ? this.data[index] : option;
|
|
1467
|
+
if (this.customSelected) {
|
|
1468
|
+
this.label.innerHTML = this.config.renderSelection(data);
|
|
1469
|
+
}
|
|
1470
|
+
else {
|
|
1471
|
+
// no xss
|
|
1472
|
+
this.label.textContent = option.textContent;
|
|
1473
|
+
}
|
|
1474
|
+
this.selectedValue = option.value;
|
|
1475
|
+
this.selectedIndex = index;
|
|
1476
|
+
util.each(this.options, function (i, o) {
|
|
1477
|
+
var opt = this.items[i];
|
|
1478
|
+
if (i !== index) {
|
|
1479
|
+
if (o) {
|
|
1480
|
+
util.removeClass(o, "selected");
|
|
1481
|
+
o.removeAttribute("selected");
|
|
1482
|
+
}
|
|
1483
|
+
opt.selected = false;
|
|
1484
|
+
opt.active = false;
|
|
1485
|
+
}
|
|
1486
|
+
}, this);
|
|
1487
|
+
}
|
|
1488
|
+
if (!util.includes(options, option)) {
|
|
1489
|
+
this.el.add(option);
|
|
1490
|
+
}
|
|
1491
|
+
item.selected = true;
|
|
1492
|
+
// item.dom.setAttribute("aria-selected", true);
|
|
1493
|
+
// util.addClass(item.dom, "selected");
|
|
1494
|
+
util.addClass(this.container, "has-selected");
|
|
1495
|
+
option.selected = true;
|
|
1496
|
+
option.setAttribute("selected", "");
|
|
1497
|
+
this.emit("selectr.change", option);
|
|
1498
|
+
this.emit("selectr.select", option);
|
|
1499
|
+
// fire native change event
|
|
1500
|
+
if ("createEvent" in document) {
|
|
1501
|
+
var evt = document.createEvent("HTMLEvents");
|
|
1502
|
+
evt.initEvent("change", true, true);
|
|
1503
|
+
evt.__selfTriggered = true;
|
|
1504
|
+
this.el.dispatchEvent(evt);
|
|
1505
|
+
}
|
|
1506
|
+
else {
|
|
1507
|
+
this.el.fireEvent("onchange");
|
|
1508
|
+
}
|
|
1509
|
+
};
|
|
1510
|
+
/**
|
|
1511
|
+
* Deselect an option
|
|
1512
|
+
* @param {Number} index
|
|
1513
|
+
* @return {void}
|
|
1514
|
+
*/
|
|
1515
|
+
Selectr.prototype.deselect = function (index, force) {
|
|
1516
|
+
var item = this.items[index], option = this.options[index];
|
|
1517
|
+
if (this.el.multiple) {
|
|
1518
|
+
var selIndex = this.selectedIndexes.indexOf(index);
|
|
1519
|
+
this.selectedIndexes.splice(selIndex, 1);
|
|
1520
|
+
var valIndex = this.selectedValues.indexOf(option.value);
|
|
1521
|
+
this.selectedValues.splice(valIndex, 1);
|
|
1522
|
+
removeTag.call(this, item);
|
|
1523
|
+
if (!this.tags.length) {
|
|
1524
|
+
util.removeClass(this.container, "has-selected");
|
|
1525
|
+
}
|
|
1526
|
+
}
|
|
1527
|
+
else {
|
|
1528
|
+
if (!force && !this.config.clearable && !this.config.allowDeselect) {
|
|
1529
|
+
return false;
|
|
1530
|
+
}
|
|
1531
|
+
this.label.innerHTML = "";
|
|
1532
|
+
this.selectedValue = null;
|
|
1533
|
+
this.el.selectedIndex = this.selectedIndex = -1;
|
|
1534
|
+
util.removeClass(this.container, "has-selected");
|
|
1535
|
+
}
|
|
1536
|
+
this.items[index].selected = false;
|
|
1537
|
+
if (this.items[index].dom) {
|
|
1538
|
+
this.items[index].dom.setAttribute("aria-selected", false);
|
|
1539
|
+
util.removeClass(this.items[index].dom, "selected");
|
|
1540
|
+
this.items[index].dom.selected = false;
|
|
1541
|
+
}
|
|
1542
|
+
option.selected = false;
|
|
1543
|
+
option.removeAttribute("selected");
|
|
1544
|
+
this.emit("selectr.change", null);
|
|
1545
|
+
this.emit("selectr.deselect", option);
|
|
1546
|
+
// fire native change event
|
|
1547
|
+
if ("createEvent" in document) {
|
|
1548
|
+
var evt = document.createEvent("HTMLEvents");
|
|
1549
|
+
evt.initEvent("change", true, true);
|
|
1550
|
+
evt.__selfTriggered = true;
|
|
1551
|
+
this.el.dispatchEvent(evt);
|
|
1552
|
+
}
|
|
1553
|
+
else {
|
|
1554
|
+
this.el.fireEvent("onchange");
|
|
1555
|
+
}
|
|
1556
|
+
};
|
|
1557
|
+
/**
|
|
1558
|
+
* Programmatically set selected values
|
|
1559
|
+
* @param {String|Array} value - A string or an array of strings
|
|
1560
|
+
*/
|
|
1561
|
+
Selectr.prototype.setValue = function (value) {
|
|
1562
|
+
var isArray = Array.isArray(value);
|
|
1563
|
+
if (!isArray) {
|
|
1564
|
+
value = value.toString().trim();
|
|
1565
|
+
}
|
|
1566
|
+
// Can't pass array to select-one
|
|
1567
|
+
if (!this.el.multiple && isArray) {
|
|
1568
|
+
return false;
|
|
1569
|
+
}
|
|
1570
|
+
util.each(this.options, function (i, option) {
|
|
1571
|
+
if (isArray && (value.indexOf(option.value) > -1) || option.value === value) {
|
|
1572
|
+
this.change(option.idx);
|
|
1573
|
+
}
|
|
1574
|
+
}, this);
|
|
1575
|
+
};
|
|
1576
|
+
/**
|
|
1577
|
+
* Set the selected value(s)
|
|
1578
|
+
* @param {bool} toObject Return only the raw values or an object
|
|
1579
|
+
* @param {bool} toJson Return the object as a JSON string
|
|
1580
|
+
* @return {mixed} Array or String
|
|
1581
|
+
*/
|
|
1582
|
+
Selectr.prototype.getValue = function (toObject, toJson) {
|
|
1583
|
+
var value;
|
|
1584
|
+
if (this.el.multiple) {
|
|
1585
|
+
if (toObject) {
|
|
1586
|
+
if (this.selectedIndexes.length) {
|
|
1587
|
+
value = {};
|
|
1588
|
+
value.values = [];
|
|
1589
|
+
util.each(this.selectedIndexes, function (i, index) {
|
|
1590
|
+
var option = this.options[index];
|
|
1591
|
+
value.values[i] = {
|
|
1592
|
+
value: option.value,
|
|
1593
|
+
text: option.textContent
|
|
1594
|
+
};
|
|
1595
|
+
}, this);
|
|
1596
|
+
}
|
|
1597
|
+
}
|
|
1598
|
+
else {
|
|
1599
|
+
value = this.selectedValues.slice();
|
|
1600
|
+
}
|
|
1601
|
+
}
|
|
1602
|
+
else {
|
|
1603
|
+
if (toObject) {
|
|
1604
|
+
var option = this.options[this.selectedIndex];
|
|
1605
|
+
value = {
|
|
1606
|
+
value: option.value,
|
|
1607
|
+
text: option.textContent
|
|
1608
|
+
};
|
|
1609
|
+
}
|
|
1610
|
+
else {
|
|
1611
|
+
value = this.selectedValue;
|
|
1612
|
+
}
|
|
1613
|
+
}
|
|
1614
|
+
if (toObject && toJson) {
|
|
1615
|
+
value = JSON.stringify(value);
|
|
1616
|
+
}
|
|
1617
|
+
return value;
|
|
1618
|
+
};
|
|
1619
|
+
/**
|
|
1620
|
+
* Add a new option or options
|
|
1621
|
+
* @param {object} data
|
|
1622
|
+
*/
|
|
1623
|
+
Selectr.prototype.add = function (data, checkDuplicate) {
|
|
1624
|
+
if (data) {
|
|
1625
|
+
this.data = this.data || [];
|
|
1626
|
+
this.items = this.items || [];
|
|
1627
|
+
this.options = this.options || [];
|
|
1628
|
+
if (Array.isArray(data)) {
|
|
1629
|
+
// We have an array on items
|
|
1630
|
+
util.each(data, function (i, obj) {
|
|
1631
|
+
this.add(obj, checkDuplicate);
|
|
1632
|
+
}, this);
|
|
1633
|
+
}
|
|
1634
|
+
// User passed a single object to the method
|
|
1635
|
+
// or Selectr passed an object from an array
|
|
1636
|
+
else if ("[object Object]" === Object.prototype.toString.call(data)) {
|
|
1637
|
+
if (checkDuplicate) {
|
|
1638
|
+
var dupe = false;
|
|
1639
|
+
util.each(this.options, function (i, option) {
|
|
1640
|
+
if (option.value.toLowerCase() === data.value.toLowerCase()) {
|
|
1641
|
+
dupe = true;
|
|
1642
|
+
}
|
|
1643
|
+
});
|
|
1644
|
+
if (dupe) {
|
|
1645
|
+
return false;
|
|
1646
|
+
}
|
|
1647
|
+
}
|
|
1648
|
+
var option = util.createElement('option', data);
|
|
1649
|
+
this.data.push(data);
|
|
1650
|
+
// Add the new option to the list
|
|
1651
|
+
this.options.push(option);
|
|
1652
|
+
// Add the index for later use
|
|
1653
|
+
option.idx = this.options.length > 0 ? this.options.length - 1 : 0;
|
|
1654
|
+
// Create a new item
|
|
1655
|
+
createItem.call(this, option);
|
|
1656
|
+
// Select the item if required
|
|
1657
|
+
if (data.selected) {
|
|
1658
|
+
this.select(option.idx);
|
|
1659
|
+
}
|
|
1660
|
+
// We may have had an empty select so update
|
|
1661
|
+
// the placeholder to reflect the changes.
|
|
1662
|
+
this.setPlaceholder();
|
|
1663
|
+
return option;
|
|
1664
|
+
}
|
|
1665
|
+
// Recount the pages
|
|
1666
|
+
if (this.config.pagination) {
|
|
1667
|
+
this.paginate();
|
|
1668
|
+
}
|
|
1669
|
+
return true;
|
|
1670
|
+
}
|
|
1671
|
+
};
|
|
1672
|
+
/**
|
|
1673
|
+
* Remove an option or options
|
|
1674
|
+
* @param {Mixed} o Array, integer (index) or string (value)
|
|
1675
|
+
* @return {Void}
|
|
1676
|
+
*/
|
|
1677
|
+
Selectr.prototype.remove = function (o) {
|
|
1678
|
+
var options = [];
|
|
1679
|
+
if (Array.isArray(o)) {
|
|
1680
|
+
util.each(o, function (i, opt) {
|
|
1681
|
+
if (util.isInt(opt)) {
|
|
1682
|
+
options.push(this.getOptionByIndex(opt));
|
|
1683
|
+
}
|
|
1684
|
+
else if (typeof opt === "string") {
|
|
1685
|
+
options.push(this.getOptionByValue(opt));
|
|
1686
|
+
}
|
|
1687
|
+
}, this);
|
|
1688
|
+
}
|
|
1689
|
+
else if (util.isInt(o)) {
|
|
1690
|
+
options.push(this.getOptionByIndex(o));
|
|
1691
|
+
}
|
|
1692
|
+
else if (typeof o === "string") {
|
|
1693
|
+
options.push(this.getOptionByValue(o));
|
|
1694
|
+
}
|
|
1695
|
+
if (options.length) {
|
|
1696
|
+
var index;
|
|
1697
|
+
util.each(options, function (i, option) {
|
|
1698
|
+
index = option.idx;
|
|
1699
|
+
// Remove the HTMLOptionElement
|
|
1700
|
+
this.el.remove(option);
|
|
1701
|
+
// Remove the reference from the option array
|
|
1702
|
+
this.options.splice(index, 1);
|
|
1703
|
+
// If the item has a parentNode (group element) it needs to be removed
|
|
1704
|
+
// otherwise the render function will still append it to the dropdown
|
|
1705
|
+
var parentNode = this.items[index].parentNode;
|
|
1706
|
+
if (parentNode) {
|
|
1707
|
+
parentNode.removeChild(this.items[index]);
|
|
1708
|
+
}
|
|
1709
|
+
// Remove reference from the items array
|
|
1710
|
+
this.items.splice(index, 1);
|
|
1711
|
+
// Reset the indexes
|
|
1712
|
+
util.each(this.options, function (i, opt) {
|
|
1713
|
+
opt.idx = i;
|
|
1714
|
+
this.items[i].idx = i;
|
|
1715
|
+
}, this);
|
|
1716
|
+
}, this);
|
|
1717
|
+
// We may have had an empty select now so update
|
|
1718
|
+
// the placeholder to reflect the changes.
|
|
1719
|
+
this.setPlaceholder();
|
|
1720
|
+
// Recount the pages
|
|
1721
|
+
if (this.config.pagination) {
|
|
1722
|
+
this.paginate();
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
};
|
|
1726
|
+
/**
|
|
1727
|
+
* Remove all options
|
|
1728
|
+
*/
|
|
1729
|
+
Selectr.prototype.removeAll = function () {
|
|
1730
|
+
// Clear any selected options
|
|
1731
|
+
this.clear(true);
|
|
1732
|
+
// Remove the HTMLOptionElements
|
|
1733
|
+
util.each(this.el.options, function (i, option) {
|
|
1734
|
+
this.el.remove(option);
|
|
1735
|
+
}, this);
|
|
1736
|
+
// Empty the dropdown
|
|
1737
|
+
util.truncate(this.tree);
|
|
1738
|
+
// Reset variables
|
|
1739
|
+
this.items = [];
|
|
1740
|
+
this.options = [];
|
|
1741
|
+
this.data = [];
|
|
1742
|
+
this.navIndex = 0;
|
|
1743
|
+
if (this.requiresPagination) {
|
|
1744
|
+
this.requiresPagination = false;
|
|
1745
|
+
this.pageIndex = 1;
|
|
1746
|
+
this.pages = [];
|
|
1747
|
+
}
|
|
1748
|
+
// Update the placeholder
|
|
1749
|
+
this.setPlaceholder();
|
|
1750
|
+
};
|
|
1751
|
+
/**
|
|
1752
|
+
* Perform a search
|
|
1753
|
+
* @param {string}|{null} query The query string (taken from user input if null)
|
|
1754
|
+
* @param {boolean} anchor Anchor search to beginning of strings (defaults to false)?
|
|
1755
|
+
* @return {Array} Search results, as an array of {text, value} objects
|
|
1756
|
+
*/
|
|
1757
|
+
Selectr.prototype.search = function (string, anchor) {
|
|
1758
|
+
if (this.navigating) {
|
|
1759
|
+
return;
|
|
1760
|
+
}
|
|
1761
|
+
// we're only going to alter the DOM for "live" searches
|
|
1762
|
+
var live = false;
|
|
1763
|
+
if (!string) {
|
|
1764
|
+
string = this.input.value;
|
|
1765
|
+
live = true;
|
|
1766
|
+
// Remove message and clear dropdown
|
|
1767
|
+
this.removeMessage();
|
|
1768
|
+
util.truncate(this.tree);
|
|
1769
|
+
}
|
|
1770
|
+
var results = [];
|
|
1771
|
+
var f = document.createDocumentFragment();
|
|
1772
|
+
string = string.trim().toLowerCase();
|
|
1773
|
+
if (string.length > 0) {
|
|
1774
|
+
var compare = anchor ? util.startsWith : util.includes;
|
|
1775
|
+
var counter = 0;
|
|
1776
|
+
this.searchResults = [];
|
|
1777
|
+
util.each(this.options, function (i, option) {
|
|
1778
|
+
var item = this.items[option.idx];
|
|
1779
|
+
var matches = compare(option.textContent.trim().toLowerCase(), string);
|
|
1780
|
+
if (matches && !option.disabled) {
|
|
1781
|
+
results.push({ text: option.textContent, value: option.value });
|
|
1782
|
+
if (live) {
|
|
1783
|
+
this.searchResults.push(item);
|
|
1784
|
+
if (counter < 50) {
|
|
1785
|
+
appendItem(item, f, this.customOption);
|
|
1786
|
+
counter++;
|
|
1787
|
+
util.removeClass(item.dom, "excluded");
|
|
1788
|
+
}
|
|
1789
|
+
// // Underline the matching results
|
|
1790
|
+
// if (!this.customOption) {
|
|
1791
|
+
// match(string, option);
|
|
1792
|
+
// }
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
else if (live && item.dom) {
|
|
1796
|
+
util.addClass(item.dom, "excluded");
|
|
1797
|
+
}
|
|
1798
|
+
}, this);
|
|
1799
|
+
if (live) {
|
|
1800
|
+
// Append results
|
|
1801
|
+
if (!f.childElementCount && !f.childNodes.length) {
|
|
1802
|
+
if (!this.config.taggable) {
|
|
1803
|
+
this.noResults = true;
|
|
1804
|
+
this.setMessage(this.config.messages.noResults);
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
else {
|
|
1808
|
+
// Highlight top result (@binary-koan #26)
|
|
1809
|
+
var prevEl = this.items[this.navIndex];
|
|
1810
|
+
var firstEl = f.querySelector(".jsl-option:not(.excluded)");
|
|
1811
|
+
this.noResults = false;
|
|
1812
|
+
removeActive(this.tree);
|
|
1813
|
+
// prevEl.active = false;
|
|
1814
|
+
this.navIndex = firstEl.idx;
|
|
1815
|
+
util.addClass(firstEl, "active");
|
|
1816
|
+
}
|
|
1817
|
+
this.tree.appendChild(f);
|
|
1818
|
+
}
|
|
1819
|
+
}
|
|
1820
|
+
else {
|
|
1821
|
+
clearSearch.call(this);
|
|
1822
|
+
render.call(this);
|
|
1823
|
+
return [];
|
|
1824
|
+
}
|
|
1825
|
+
this.pageIndex = 1;
|
|
1826
|
+
this.paginate();
|
|
1827
|
+
return results;
|
|
1828
|
+
};
|
|
1829
|
+
/**
|
|
1830
|
+
* Toggle the dropdown
|
|
1831
|
+
* @return {void}
|
|
1832
|
+
*/
|
|
1833
|
+
Selectr.prototype.toggle = function () {
|
|
1834
|
+
if (!this.disabled) {
|
|
1835
|
+
if (this.opened) {
|
|
1836
|
+
this.close();
|
|
1837
|
+
}
|
|
1838
|
+
else {
|
|
1839
|
+
this.open();
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
};
|
|
1843
|
+
/**
|
|
1844
|
+
* Open the dropdown
|
|
1845
|
+
* @return {void}
|
|
1846
|
+
*/
|
|
1847
|
+
Selectr.prototype.open = function () {
|
|
1848
|
+
var that = this;
|
|
1849
|
+
if (!this.options.length) {
|
|
1850
|
+
return false;
|
|
1851
|
+
}
|
|
1852
|
+
if (!this.opened) {
|
|
1853
|
+
this.emit("selectr.open");
|
|
1854
|
+
}
|
|
1855
|
+
this.opened = true;
|
|
1856
|
+
if (this.mobileDevice || this.config.nativeDropdown) {
|
|
1857
|
+
util.addClass(this.container, "native-open");
|
|
1858
|
+
if (this.config.data) {
|
|
1859
|
+
// Dump the options into the select
|
|
1860
|
+
// otherwise the native dropdown will be empty
|
|
1861
|
+
util.each(this.options, function (i, option) {
|
|
1862
|
+
this.el.add(option);
|
|
1863
|
+
}, this);
|
|
1864
|
+
}
|
|
1865
|
+
return;
|
|
1866
|
+
}
|
|
1867
|
+
util.addClass(this.container, "open");
|
|
1868
|
+
render.call(this);
|
|
1869
|
+
this.invert();
|
|
1870
|
+
this.tree.scrollTop = 0;
|
|
1871
|
+
util.removeClass(this.container, "notice");
|
|
1872
|
+
this.selected.setAttribute("aria-expanded", true);
|
|
1873
|
+
this.tree.setAttribute("aria-hidden", false);
|
|
1874
|
+
this.tree.setAttribute("aria-expanded", true);
|
|
1875
|
+
if (this.config.searchable && !this.config.taggable) {
|
|
1876
|
+
setTimeout(function () {
|
|
1877
|
+
that.input.focus();
|
|
1878
|
+
// Allow tab focus
|
|
1879
|
+
that.input.tabIndex = 0;
|
|
1880
|
+
}, 10);
|
|
1881
|
+
}
|
|
1882
|
+
};
|
|
1883
|
+
/**
|
|
1884
|
+
* Close the dropdown
|
|
1885
|
+
* @return {void}
|
|
1886
|
+
*/
|
|
1887
|
+
Selectr.prototype.close = function () {
|
|
1888
|
+
if (this.opened) {
|
|
1889
|
+
this.emit("selectr.close");
|
|
1890
|
+
}
|
|
1891
|
+
this.opened = false;
|
|
1892
|
+
this.navigating = false;
|
|
1893
|
+
if (this.mobileDevice || this.config.nativeDropdown) {
|
|
1894
|
+
util.removeClass(this.container, "native-open");
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
var notice = util.hasClass(this.container, "notice");
|
|
1898
|
+
if (this.config.searchable && !notice) {
|
|
1899
|
+
this.input.blur();
|
|
1900
|
+
// Disable tab focus
|
|
1901
|
+
this.input.tabIndex = -1;
|
|
1902
|
+
this.searching = false;
|
|
1903
|
+
}
|
|
1904
|
+
if (notice) {
|
|
1905
|
+
util.removeClass(this.container, "notice");
|
|
1906
|
+
this.notice.textContent = "";
|
|
1907
|
+
}
|
|
1908
|
+
util.removeClass(this.container, "open");
|
|
1909
|
+
util.removeClass(this.container, "native-open");
|
|
1910
|
+
this.selected.setAttribute("aria-expanded", false);
|
|
1911
|
+
this.tree.setAttribute("aria-hidden", true);
|
|
1912
|
+
this.tree.setAttribute("aria-expanded", false);
|
|
1913
|
+
util.truncate(this.tree);
|
|
1914
|
+
clearSearch.call(this);
|
|
1915
|
+
};
|
|
1916
|
+
/**
|
|
1917
|
+
* Enable the element
|
|
1918
|
+
* @return {void}
|
|
1919
|
+
*/
|
|
1920
|
+
Selectr.prototype.enable = function () {
|
|
1921
|
+
this.disabled = false;
|
|
1922
|
+
this.el.disabled = false;
|
|
1923
|
+
this.selected.tabIndex = this.originalIndex;
|
|
1924
|
+
if (this.el.multiple) {
|
|
1925
|
+
util.each(this.tags, function (i, t) {
|
|
1926
|
+
t.lastElementChild.tabIndex = 0;
|
|
1927
|
+
});
|
|
1928
|
+
}
|
|
1929
|
+
util.removeClass(this.container, "jsl-disabled");
|
|
1930
|
+
};
|
|
1931
|
+
/**
|
|
1932
|
+
* Disable the element
|
|
1933
|
+
* @param {boolean} container Disable the container only (allow value submit with form)
|
|
1934
|
+
* @return {void}
|
|
1935
|
+
*/
|
|
1936
|
+
Selectr.prototype.disable = function (container) {
|
|
1937
|
+
if (!container) {
|
|
1938
|
+
this.el.disabled = true;
|
|
1939
|
+
}
|
|
1940
|
+
this.selected.tabIndex = -1;
|
|
1941
|
+
if (this.el.multiple) {
|
|
1942
|
+
util.each(this.tags, function (i, t) {
|
|
1943
|
+
t.lastElementChild.tabIndex = -1;
|
|
1944
|
+
});
|
|
1945
|
+
}
|
|
1946
|
+
this.disabled = true;
|
|
1947
|
+
util.addClass(this.container, "jsl-disabled");
|
|
1948
|
+
};
|
|
1949
|
+
/**
|
|
1950
|
+
* Reset to initial state
|
|
1951
|
+
* @return {void}
|
|
1952
|
+
*/
|
|
1953
|
+
Selectr.prototype.reset = function () {
|
|
1954
|
+
if (!this.disabled) {
|
|
1955
|
+
this.clear();
|
|
1956
|
+
this.setSelected(true);
|
|
1957
|
+
util.each(this.defaultSelected, function (i, idx) {
|
|
1958
|
+
this.select(idx);
|
|
1959
|
+
}, this);
|
|
1960
|
+
this.emit("selectr.reset");
|
|
1961
|
+
}
|
|
1962
|
+
};
|
|
1963
|
+
/**
|
|
1964
|
+
* Clear all selections
|
|
1965
|
+
* @return {void}
|
|
1966
|
+
*/
|
|
1967
|
+
Selectr.prototype.clear = function (force, isClearLast) {
|
|
1968
|
+
if (this.el.multiple) {
|
|
1969
|
+
// Loop over the selectedIndexes so we don't have to loop over all the options
|
|
1970
|
+
// which can be costly if there are a lot of them
|
|
1971
|
+
if (this.selectedIndexes.length) {
|
|
1972
|
+
// Copy the array or we'll get an error
|
|
1973
|
+
var indexes = this.selectedIndexes.slice();
|
|
1974
|
+
if (isClearLast) {
|
|
1975
|
+
this.deselect(indexes.slice(-1)[0]);
|
|
1976
|
+
}
|
|
1977
|
+
else {
|
|
1978
|
+
util.each(indexes, function (i, idx) {
|
|
1979
|
+
this.deselect(idx);
|
|
1980
|
+
}, this);
|
|
1981
|
+
}
|
|
1982
|
+
}
|
|
1983
|
+
}
|
|
1984
|
+
else {
|
|
1985
|
+
if (this.selectedIndex > -1) {
|
|
1986
|
+
this.deselect(this.selectedIndex, force);
|
|
1987
|
+
}
|
|
1988
|
+
}
|
|
1989
|
+
this.emit("selectr.clear");
|
|
1990
|
+
};
|
|
1991
|
+
/**
|
|
1992
|
+
* Return serialised data
|
|
1993
|
+
* @param {boolean} toJson
|
|
1994
|
+
* @return {mixed} Returns either an object or JSON string
|
|
1995
|
+
*/
|
|
1996
|
+
Selectr.prototype.serialise = function (toJson) {
|
|
1997
|
+
var data = [];
|
|
1998
|
+
util.each(this.options, function (i, option) {
|
|
1999
|
+
var obj = {
|
|
2000
|
+
value: option.value,
|
|
2001
|
+
text: option.textContent,
|
|
2002
|
+
selected: undefined,
|
|
2003
|
+
disabled: undefined
|
|
2004
|
+
};
|
|
2005
|
+
if (option.selected) {
|
|
2006
|
+
obj.selected = true;
|
|
2007
|
+
}
|
|
2008
|
+
if (option.disabled) {
|
|
2009
|
+
obj.disabled = true;
|
|
2010
|
+
}
|
|
2011
|
+
data[i] = obj;
|
|
2012
|
+
});
|
|
2013
|
+
return toJson ? JSON.stringify(data) : data;
|
|
2014
|
+
};
|
|
2015
|
+
/**
|
|
2016
|
+
* Localised version of serialise() method
|
|
2017
|
+
*/
|
|
2018
|
+
Selectr.prototype.serialize = function (toJson) {
|
|
2019
|
+
return this.serialise(toJson);
|
|
2020
|
+
};
|
|
2021
|
+
/**
|
|
2022
|
+
* Sets the placeholder
|
|
2023
|
+
* @param {String} placeholder
|
|
2024
|
+
*/
|
|
2025
|
+
Selectr.prototype.setPlaceholder = function (placeholder) {
|
|
2026
|
+
// Set the placeholder
|
|
2027
|
+
placeholder = placeholder || this.config.placeholder || this.el.getAttribute("placeholder");
|
|
2028
|
+
if (!this.options.length) {
|
|
2029
|
+
placeholder = this.config.messages.noOptions;
|
|
2030
|
+
}
|
|
2031
|
+
this.placeEl.innerHTML = placeholder;
|
|
2032
|
+
};
|
|
2033
|
+
/**
|
|
2034
|
+
* Paginate the option list
|
|
2035
|
+
* @return {Array}
|
|
2036
|
+
*/
|
|
2037
|
+
Selectr.prototype.paginate = function () {
|
|
2038
|
+
if (!this.requiresPagination) {
|
|
2039
|
+
return;
|
|
2040
|
+
}
|
|
2041
|
+
var items = this.searching ? this.searchResults : this.items;
|
|
2042
|
+
if (items.length) {
|
|
2043
|
+
var that = this;
|
|
2044
|
+
this.pages = items.map(function (v, i) {
|
|
2045
|
+
return i % that.config.pagination === 0 ? items.slice(i, i + that.config.pagination) : null;
|
|
2046
|
+
}).filter(function (pages) {
|
|
2047
|
+
return pages;
|
|
2048
|
+
});
|
|
2049
|
+
return this.pages;
|
|
2050
|
+
}
|
|
2051
|
+
};
|
|
2052
|
+
/**
|
|
2053
|
+
* Display a message
|
|
2054
|
+
* @param {String} message The message
|
|
2055
|
+
*/
|
|
2056
|
+
Selectr.prototype.setMessage = function (message, close) {
|
|
2057
|
+
if (close) {
|
|
2058
|
+
this.close();
|
|
2059
|
+
}
|
|
2060
|
+
util.addClass(this.container, "notice");
|
|
2061
|
+
this.notice.textContent = message;
|
|
2062
|
+
};
|
|
2063
|
+
/**
|
|
2064
|
+
* Dismiss the current message
|
|
2065
|
+
*/
|
|
2066
|
+
Selectr.prototype.removeMessage = function () {
|
|
2067
|
+
util.removeClass(this.container, "notice");
|
|
2068
|
+
this.notice.innerHTML = "";
|
|
2069
|
+
};
|
|
2070
|
+
/**
|
|
2071
|
+
* Keep the dropdown within the window
|
|
2072
|
+
* @return {void}
|
|
2073
|
+
*/
|
|
2074
|
+
Selectr.prototype.invert = function () {
|
|
2075
|
+
var rt = util.rect(this.selected), oh = this.tree.parentNode.offsetHeight, wh = window.innerHeight, doInvert = rt.top + rt.height + oh > wh;
|
|
2076
|
+
if (doInvert) {
|
|
2077
|
+
util.addClass(this.container, "inverted");
|
|
2078
|
+
this.isInverted = true;
|
|
2079
|
+
}
|
|
2080
|
+
else {
|
|
2081
|
+
util.removeClass(this.container, "inverted");
|
|
2082
|
+
this.isInverted = false;
|
|
2083
|
+
}
|
|
2084
|
+
this.optsRect = util.rect(this.tree);
|
|
2085
|
+
};
|
|
2086
|
+
/**
|
|
2087
|
+
* Get an option via it's index
|
|
2088
|
+
* @param {Integer} index The index of the HTMLOptionElement required
|
|
2089
|
+
* @return {HTMLOptionElement}
|
|
2090
|
+
*/
|
|
2091
|
+
Selectr.prototype.getOptionByIndex = function (index) {
|
|
2092
|
+
return this.options[index];
|
|
2093
|
+
};
|
|
2094
|
+
/**
|
|
2095
|
+
* Get an option via it's value
|
|
2096
|
+
* @param {String} value The value of the HTMLOptionElement required
|
|
2097
|
+
* @return {HTMLOptionElement}
|
|
2098
|
+
*/
|
|
2099
|
+
Selectr.prototype.getOptionByValue = function (value) {
|
|
2100
|
+
var option = false;
|
|
2101
|
+
for (var i = 0, l = this.options.length; i < l; i++) {
|
|
2102
|
+
if (this.options[i].value.trim() === value.toString().trim()) {
|
|
2103
|
+
option = this.options[i];
|
|
2104
|
+
break;
|
|
2105
|
+
}
|
|
2106
|
+
}
|
|
2107
|
+
return option;
|
|
2108
|
+
};
|
|
2109
|
+
})();
|
|
2110
|
+
export let DropDown = function (input, config) {
|
|
2111
|
+
return new Selectr(input, config);
|
|
2112
|
+
};
|