pixl-xyapp 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/js/select.js ADDED
@@ -0,0 +1,845 @@
1
+ // Custom single- and multi-drop-down menus
2
+
3
+ var SingleSelect = {
4
+
5
+ maxMenuItems: 1000,
6
+
7
+ init: function(sel) {
8
+ // initialize all single-selects based on selector
9
+ $(sel).each( function() {
10
+ var self = this;
11
+ var $this = $(this);
12
+ $this.css('display', 'none');
13
+
14
+ var $ms = $('<div class="multiselect single"></div>');
15
+ if ($this.data('private')) $ms.attr('data-private', 1);
16
+ $this.after( $ms );
17
+
18
+ var redraw = function() {
19
+ // render contents of visible select div
20
+ $ms.empty();
21
+ $ms.append('<div class="select_chevron mdi mdi-chevron-down"></div>');
22
+
23
+ var opt = self.options[ self.selectedIndex ] || self.options[0] || { label: "(None)", value: "" };
24
+
25
+ var html = '<div class="single';
26
+ if (opt.value === "") html += ' novalue';
27
+ // if (opt.getAttribute && opt.getAttribute('data-class')) {
28
+ // html += ' ' + opt.getAttribute('data-class') + '';
29
+ // }
30
+ html += '">';
31
+
32
+ if (opt.getAttribute && opt.getAttribute('data-icon')) {
33
+ html += '<i class="mdi mdi-' + opt.getAttribute('data-icon') + '">&nbsp;</i>';
34
+ }
35
+
36
+ html += (opt.label || opt.value);
37
+ html += '</div>';
38
+ $ms.append(html);
39
+
40
+ if (opt.getAttribute && opt.getAttribute('data-class')) {
41
+ $ms.removeClass().addClass('multiselect single ' + opt.getAttribute('data-class'));
42
+ }
43
+ }; // redraw
44
+
45
+ redraw();
46
+
47
+ // also trigger a redraw if the underlying hidden select changes
48
+ $this.on('change', redraw);
49
+
50
+ $ms.on('mouseup', function() {
51
+ // create popover dialog for selecting and filtering
52
+ var html = '';
53
+ var is_private = $this.data('private');
54
+ if ($ms.hasClass('disabled')) return;
55
+
56
+ html += '<div class="sel_dialog_label">' + ($this.attr('title') || 'Select Item') + '</div>';
57
+
58
+ if (!$this.data('compact')) {
59
+ html += '<div class="sel_dialog_search_container">';
60
+ html += '<input type="text" id="fe_sel_dialog_search" class="sel_dialog_search" autocomplete="off" value=""/>';
61
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-magnify"></i></div>';
62
+ html += '</div>';
63
+ }
64
+
65
+ html += '<div id="d_sel_dialog_scrollarea" class="sel_dialog_scrollarea';
66
+ if ($this.data('nudgeheight')) html += ' nudgeheight';
67
+ html += '">';
68
+
69
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
70
+ var opt = self.options[idx];
71
+
72
+ if (opt.getAttribute('data-group')) {
73
+ html += '<div class="sel_dialog_group">' + opt.getAttribute('data-group') + '</div>';
74
+ }
75
+
76
+ html += '<div class="sel_dialog_item check ' + (opt.selected ? 'selected' : '');
77
+
78
+ if (opt.getAttribute && opt.getAttribute('data-class')) {
79
+ html += ' ' + opt.getAttribute('data-class') + '';
80
+ }
81
+ if ($this.data('shrinkwrap')) html += ' shrinkwrap';
82
+
83
+ html += '" ' + ((idx >= SingleSelect.maxMenuItems) ? 'style="display:none"' : '') + ' data-value="' + opt.value + '">';
84
+
85
+ if (opt.getAttribute && opt.getAttribute('data-icon')) {
86
+ html += '<i class="mdi mdi-' + opt.getAttribute('data-icon') + '">&nbsp;</i>';
87
+ }
88
+
89
+ html += '<span ' + (is_private ? 'data-private' : '') + '>' + (opt.label || opt.value) + '</span>';
90
+ html += '<div class="sel_dialog_item_check"><i class="mdi mdi-check"></i></div>';
91
+ html += '</div>';
92
+ }
93
+
94
+ html += '<div id="d_sel_dialog_more">';
95
+ if (self.options.length > SingleSelect.maxMenuItems) {
96
+ html += '(' + commify(self.options.length - SingleSelect.maxMenuItems) + ' more items not shown)';
97
+ }
98
+ html += '</div>';
99
+
100
+ html += '</div>';
101
+
102
+ Popover.attach( $ms, '<div style="padding:15px;">' + html + '</div>', $this.data('shrinkwrap') || false );
103
+
104
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').on('mouseup', function() {
105
+ // select item, close dialog and update multi-select
106
+ var $item = $(this);
107
+ $this.val( $item.data('value') );
108
+
109
+ Popover.detach();
110
+ // redraw();
111
+ $this.trigger('change');
112
+ });
113
+
114
+ if (!$this.data('compact')) {
115
+ var $input = $('#fe_sel_dialog_search');
116
+ $input.focus();
117
+
118
+ // setup keyboard handlers
119
+ $input.on('keyup', function(event) {
120
+ // refresh list on every keypress
121
+ var value = $input.val().toLowerCase();
122
+ var num_matched = 0;
123
+
124
+ if (value.length) $('#d_sel_dialog_scrollarea > div.sel_dialog_group').hide();
125
+ else $('#d_sel_dialog_scrollarea > div.sel_dialog_group').show();
126
+
127
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function() {
128
+ var $item = $(this);
129
+ var text = $item.find('> span').html().toLowerCase();
130
+
131
+ if (!value.length || (text.indexOf(value) > -1)) {
132
+ if (num_matched < SingleSelect.maxMenuItems) $item.addClass('match').show();
133
+ else $item.removeClass('match').hide();
134
+ num_matched++;
135
+ }
136
+ else {
137
+ $item.removeClass('match').hide();
138
+ }
139
+ } ); // each
140
+
141
+ if (num_matched > SingleSelect.maxMenuItems) {
142
+ $('#d_sel_dialog_more').html( '(' + commify(num_matched - SingleSelect.maxMenuItems) + ' more items not shown)' );
143
+ }
144
+ else $('#d_sel_dialog_more').html('');
145
+
146
+ Popover.reposition();
147
+ });
148
+ $input.on('keydown', function(event) {
149
+ // capture enter key
150
+ var value = $input.val().toLowerCase();
151
+ if ((event.keyCode == 13) && value.length) {
152
+ event.preventDefault();
153
+ event.stopPropagation();
154
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item.match').slice(0, 1).trigger('mouseup');
155
+ }
156
+ });
157
+ }
158
+
159
+ // highlight select field under us
160
+ $ms.addClass('selected');
161
+ Popover.onDetach = function() { $ms.removeClass('selected'); };
162
+ }); // mouseup
163
+
164
+ }); // forach elem
165
+ },
166
+
167
+ popupQuickMenu: function(opts) {
168
+ // show popup menu on custom element
169
+ // opts: { elem, title, items, value, callback, nocheck?, onCancel? }
170
+ // item: { id, title, icon }
171
+ var $elem = $(opts.elem);
172
+ var items = opts.items;
173
+ var callback = opts.callback;
174
+ var check = opts.nocheck ? 'nocheck' : 'check';
175
+ var html = '';
176
+
177
+ html += '<div class="sel_dialog_label">' + opts.title + '</div>';
178
+ if (opts.search) {
179
+ html += '<div class="sel_dialog_search_container">';
180
+ html += '<input type="text" id="fe_sel_dialog_search" class="sel_dialog_search" autocomplete="off" value=""/>';
181
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-magnify"></i></div>';
182
+ html += '</div>';
183
+ }
184
+ html += '<div id="d_sel_dialog_scrollarea" class="sel_dialog_scrollarea">';
185
+ for (var idy = 0, ley = items.length; idy < ley; idy++) {
186
+ var item = items[idy];
187
+ var sel = (item.id == opts.value);
188
+ if (item.group) html += '<div class="sel_dialog_group">' + item.group + '</div>';
189
+ html += '<div class="sel_dialog_item ' + check + ' ' + (sel ? 'selected' : '') + ' shrinkwrap" data-value="' + item.id + '">';
190
+ if (item.icon) html += '<i class="mdi mdi-' + item.icon + '">&nbsp;</i>';
191
+ html += '<span>' + item.title + '</span>';
192
+ if (!opts.nocheck) html += '<div class="sel_dialog_item_check"><i class="mdi mdi-check"></i></div>';
193
+ html += '</div>';
194
+ }
195
+ html += '</div>';
196
+
197
+ Popover.attach( $elem, '<div style="padding:15px;">' + html + '</div>', true );
198
+
199
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').on('mouseup', function() {
200
+ // select item, close dialog and update state
201
+ var $item = $(this);
202
+ var value = $item.data('value');
203
+
204
+ delete opts.onCancel;
205
+ Popover.detach();
206
+ callback(value);
207
+ }); // mouseup
208
+
209
+ Popover.onDetach = function() {
210
+ $elem.removeClass('popped');
211
+ if (opts.onCancel) opts.onCancel();
212
+ };
213
+
214
+ $elem.addClass('popped');
215
+
216
+ if (opts.search) {
217
+ // setup input field
218
+ var $input = $('#fe_sel_dialog_search');
219
+ $input.focus();
220
+
221
+ // setup keyboard handlers
222
+ $input.on('keyup', function(event) {
223
+ // refresh list on every keypress
224
+ var value = $input.val().toLowerCase();
225
+
226
+ if (value.length) $('#d_sel_dialog_scrollarea > div.sel_dialog_group').hide();
227
+ else $('#d_sel_dialog_scrollarea > div.sel_dialog_group').show();
228
+
229
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function() {
230
+ var $item = $(this);
231
+ var text = $item.find('> span').html().toLowerCase();
232
+ if (!value.length || (text.indexOf(value) > -1)) {
233
+ $item.addClass('match').show();
234
+ }
235
+ else {
236
+ $item.removeClass('match').hide();
237
+ }
238
+ } );
239
+ Popover.reposition();
240
+ });
241
+ $input.on('keydown', function(event) {
242
+ // capture enter key
243
+ var value = $input.val().toLowerCase();
244
+ if ((event.keyCode == 13) && value.length) {
245
+ // enter key with a value typed into the search box
246
+ event.preventDefault();
247
+ event.stopPropagation();
248
+
249
+ var mup = jQuery.Event( "mouseup" );
250
+ mup.metaKey = true; // bypass `hold` feature
251
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item.match').slice(0, 1).trigger(mup);
252
+ }
253
+ });
254
+ } // opts.search
255
+ }
256
+
257
+ }; // SingleSelect
258
+
259
+ var MultiSelect = {
260
+
261
+ init: function(sel) {
262
+ // initialize all multi-selects based on selector
263
+ $(sel).each( function() {
264
+ var self = this;
265
+ var $this = $(this);
266
+ $this.css('display', 'none');
267
+
268
+ var $ms = $('<div class="multiselect multi"></div>');
269
+ if ($this.data('compact')) $ms.addClass('compact');
270
+ if ($this.data('private')) $ms.attr('data-private', 1);
271
+ $this.after( $ms );
272
+
273
+ var redraw = function() {
274
+ // render contents of visible multiselect div
275
+ var num_sel = 0;
276
+ var inherited = $this.data('inherited') ? ($this.data('inherited') || '').split(/\,\s*/) : [];
277
+
278
+ $ms.empty();
279
+ $ms.append('<div class="select_chevron mdi mdi-chevron-double-down"></div>');
280
+
281
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
282
+ var opt = self.options[idx];
283
+ if (inherited.includes(opt.value)) {
284
+ // item is inherited -- show with lock icon
285
+ var html = '<i class="mdi mdi-lock-check">&nbsp;</i>';
286
+ if (opt.getAttribute && opt.getAttribute('data-icon')) {
287
+ html += '<i class="mdi mdi-' + opt.getAttribute('data-icon') + '">&nbsp;</i>';
288
+ }
289
+ html += opt.getAttribute('data-abbrev') || opt.label;
290
+ var tooltip = $this.data('itooltip') || '(Inherited Item)';
291
+ var $item = $('<div class="item inherited"></div>').attr('title', tooltip).data('value', opt.value).html(html);
292
+ $ms.append( $item );
293
+ num_sel++;
294
+ }
295
+ else if (opt.selected) {
296
+ // item is selected
297
+ var html = '<i class="mdi mdi-close">&nbsp;</i>';
298
+ if (opt.getAttribute && opt.getAttribute('data-icon')) {
299
+ html += '<i class="mdi mdi-' + opt.getAttribute('data-icon') + '">&nbsp;</i>';
300
+ }
301
+ html += opt.getAttribute('data-abbrev') || opt.label;
302
+ var $item = $('<div class="item"></div>').data('value', opt.value).html(html);
303
+ $ms.append( $item );
304
+ num_sel++;
305
+ }
306
+ }
307
+
308
+ if (num_sel) $ms.append( '<div class="clear"></div>' );
309
+ else $ms.append( '<div class="placeholder">' + ($this.attr('placeholder') || 'Click to select...') + '</div>' );
310
+
311
+ $ms.find('div.item > i.mdi-close').on('mouseup', function(e) {
312
+ // user clicked on the 'X' -- remove this item and redraw
313
+ var $item = $(this).parent();
314
+ var value = $item.data('value');
315
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
316
+ var opt = self.options[idx];
317
+ if (opt.selected && (opt.value == value)) {
318
+ opt.selected = false;
319
+ idx = len;
320
+ }
321
+ }
322
+ // redraw();
323
+ $this.trigger('change');
324
+ e.stopPropagation();
325
+ e.preventDefault();
326
+ return false;
327
+ });
328
+
329
+ if ($this.data('compact') && ($ms[0].offsetHeight < $ms[0].scrollHeight)) {
330
+ $ms.find('.select_chevron').removeClass().addClass('select_chevron mdi mdi-dots-horizontal');
331
+ }
332
+
333
+ if ($this.data('hold') && $this.data('volatile') && Popover.enabled && (Popover.$anchor.prop('id') == $this.prop('id'))) {
334
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function(idx) {
335
+ if (self.options[idx].selected) $(this).addClass('selected');
336
+ else $(this).removeClass('selected');
337
+ } );
338
+ }
339
+ }; // redraw
340
+
341
+ redraw();
342
+
343
+ // also trigger a redraw if the underlying hidden select changes
344
+ $this.on('change', redraw);
345
+
346
+ $ms.on('mouseup', function() {
347
+ // create popover dialog for selecting and filtering
348
+ var html = '';
349
+ var orig_sel_state = [];
350
+ var last_sel_idx = -1;
351
+ var inherited = $this.data('inherited') ? ($this.data('inherited') || '').split(/\,\s*/) : [];
352
+ var is_private = $this.data('private');
353
+ if ($ms.hasClass('disabled')) return;
354
+
355
+ html += '<div class="sel_dialog_label">' + ($this.attr('title') || 'Select Items');
356
+ if ($this.data('select-all')) {
357
+ var is_all_sel = !!(find_objects(self.options, { selected: true }).length == self.options.length);
358
+ html += '<div class="sel_all_none">' + (is_all_sel ? 'Select None' : 'Select All') + '</div>';
359
+ }
360
+ html += '</div>';
361
+
362
+ html += '<div class="sel_dialog_search_container">';
363
+ html += '<input type="text" id="fe_sel_dialog_search" class="sel_dialog_search" autocomplete="off" value=""/>';
364
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-magnify"></i></div>';
365
+ html += '</div>';
366
+ html += '<div id="d_sel_dialog_scrollarea" class="sel_dialog_scrollarea">';
367
+
368
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
369
+ var opt = self.options[idx];
370
+ var is_inherited = !!inherited.includes(opt.value);
371
+
372
+ if (opt.getAttribute('data-group')) {
373
+ html += '<div class="sel_dialog_group">' + opt.getAttribute('data-group') + '</div>';
374
+ }
375
+
376
+ html += '<div class="sel_dialog_item check ' + (opt.selected ? 'selected' : '');
377
+
378
+ if (opt.getAttribute && opt.getAttribute('data-class')) {
379
+ html += ' ' + opt.getAttribute('data-class') + '';
380
+ }
381
+ if ($this.data('shrinkwrap')) html += ' shrinkwrap';
382
+ if (is_inherited) html += ' inherited';
383
+
384
+ html += '" data-value="' + opt.value + '" title="' + (is_inherited ? ($this.data('itooltip') || '(Inherited Item)') : '') + '">';
385
+ if (opt.getAttribute && opt.getAttribute('data-icon')) {
386
+ html += '<i class="mdi mdi-' + opt.getAttribute('data-icon') + '">&nbsp;</i>';
387
+ }
388
+ html += '<span ' + (is_private ? 'data-private' : '') + '>' + (opt.label || opt.value) + '</span>';
389
+ html += '<div class="sel_dialog_item_check"><i class="mdi mdi-' + (is_inherited ? 'lock-check' : 'check') + '"></i></div>';
390
+ html += '</div>';
391
+ orig_sel_state.push( opt.selected );
392
+ if (opt.selected) last_sel_idx = idx;
393
+ }
394
+
395
+ html += '</div>';
396
+
397
+ if ($this.data('hold')) {
398
+ html += '<div class="sel_dialog_button_container">';
399
+ html += '<div class="button" id="btn_sel_dialog_cancel"><i class="mdi mdi-close-circle-outline">&nbsp;</i>Cancel</div>';
400
+ html += '<div class="button primary" id="btn_sel_dialog_add"><i class="mdi mdi-check-circle">&nbsp;</i>' + ($this.attr('confirm') || 'Accept') + '</div>';
401
+ html += '</div>';
402
+ } // hold
403
+
404
+ Popover.attach( $ms, '<div style="padding:15px;">' + html + '</div>', $this.data('shrinkwrap') || false );
405
+
406
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').on('mouseup', function(event) {
407
+ // select item, close dialog and update multi-select
408
+ var $item = $(this);
409
+ if ($item.hasClass('inherited')) return; // no clicky on inherited items
410
+
411
+ var value = $item.data('value');
412
+ var new_sel_state = !$item.hasClass('selected');
413
+ var new_sel_idx = -1;
414
+
415
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
416
+ var opt = self.options[idx];
417
+ if (opt.value == value) {
418
+ opt.selected = new_sel_state;
419
+ if (new_sel_state) $item.addClass('selected'); else $item.removeClass('selected');
420
+ if (new_sel_state) new_sel_idx = idx;
421
+ idx = len;
422
+ }
423
+ }
424
+
425
+ if (!$this.data('hold') || (self.options.length == 1) || event.metaKey) {
426
+ // close popover
427
+ Popover.detach();
428
+ }
429
+ else if ($this.data('hold') && event.shiftKey && (new_sel_idx > -1) && (last_sel_idx > -1) && (new_sel_idx != last_sel_idx)) {
430
+ // select range
431
+ if (last_sel_idx > new_sel_idx) { var temp = last_sel_idx; last_sel_idx = new_sel_idx; new_sel_idx = temp; }
432
+ for (var idx = last_sel_idx; idx <= new_sel_idx; idx++) { self.options[idx].selected = true; }
433
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').slice(last_sel_idx, new_sel_idx + 1).addClass('selected');
434
+ }
435
+ else if ($this.data('hold') && event.altKey && (new_sel_idx > -1) && (last_sel_idx > -1) && (new_sel_idx != last_sel_idx)) {
436
+ // select pattern
437
+ if (last_sel_idx > new_sel_idx) { var temp = last_sel_idx; last_sel_idx = new_sel_idx; new_sel_idx = temp; }
438
+ var dist = new_sel_idx - last_sel_idx;
439
+ for (var idx = last_sel_idx, len = self.options.length; idx < len; idx += dist) {
440
+ var opt = self.options[idx];
441
+ opt.selected = true;
442
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').slice(idx, idx + 1).addClass('selected');
443
+ }
444
+ }
445
+ else if ($this.data('hold') && !new_sel_state) {
446
+ // user has de-selected something, so re-evaluate last_sel_idx
447
+ last_sel_idx = find_object_idx( self.options, { selected: true } );
448
+ }
449
+
450
+ $this.trigger('change');
451
+ last_sel_idx = new_sel_idx;
452
+
453
+ // update select-all-none display
454
+ if ($this.data('select-all')) {
455
+ var is_all_sel = !!(find_objects(self.options, { selected: true }).length == self.options.length);
456
+ $('div.arrow_box div.sel_all_none').html( is_all_sel ? 'Select None' : 'Select All' );
457
+ }
458
+ }); // mouseup
459
+
460
+ if ($this.data('hold')) {
461
+ $('#btn_sel_dialog_cancel').on('mouseup', function() {
462
+ Popover.detach();
463
+
464
+ // restore original opts and redraw
465
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
466
+ var opt = self.options[idx];
467
+ opt.selected = orig_sel_state[idx];
468
+ }
469
+
470
+ redraw();
471
+ $this.trigger('change');
472
+ });
473
+ $('#btn_sel_dialog_add').on('mouseup', function() { Popover.detach(); });
474
+ } // hold
475
+
476
+ // attach click handler for select-all-none
477
+ $('div.arrow_box div.sel_all_none').on('mouseup', function(event) {
478
+ var is_all_sel = !!(find_objects(self.options, { selected: true }).length == self.options.length);
479
+ var new_sel_state = !is_all_sel;
480
+
481
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function(idx) {
482
+ var $item = $(this);
483
+ if ($item.hasClass('inherited')) return; // no clicky on inherited items
484
+
485
+ var opt = self.options[idx];
486
+ opt.selected = new_sel_state;
487
+ if (new_sel_state) $item.addClass('selected'); else $item.removeClass('selected');
488
+ } );
489
+
490
+ $this.trigger('change');
491
+ $(this).html( is_all_sel ? 'Select All' : 'Select None' );
492
+ });
493
+
494
+ // setup input field
495
+ var $input = $('#fe_sel_dialog_search');
496
+ $input.focus();
497
+
498
+ // setup keyboard handlers
499
+ $input.on('keyup', function(event) {
500
+ // refresh list on every keypress
501
+ var value = $input.val().toLowerCase();
502
+
503
+ if (value.length) $('#d_sel_dialog_scrollarea > div.sel_dialog_group').hide();
504
+ else $('#d_sel_dialog_scrollarea > div.sel_dialog_group').show();
505
+
506
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function() {
507
+ var $item = $(this);
508
+ if ($item.hasClass('inherited')) {
509
+ if (value.length) $item.hide();
510
+ else $item.show();
511
+ return;
512
+ }
513
+
514
+ var text = $item.find('> span').html().toLowerCase();
515
+ if (!value.length || (text.indexOf(value) > -1)) {
516
+ $item.addClass('match').show();
517
+ }
518
+ else {
519
+ $item.removeClass('match').hide();
520
+ }
521
+ } );
522
+ Popover.reposition();
523
+ });
524
+ $input.on('keydown', function(event) {
525
+ // capture enter key
526
+ var value = $input.val().toLowerCase();
527
+ if ((event.keyCode == 13) && value.length) {
528
+ // enter key with a value typed into the search box
529
+ event.preventDefault();
530
+ event.stopPropagation();
531
+
532
+ var mup = jQuery.Event( "mouseup" );
533
+ mup.metaKey = true; // bypass `hold` feature
534
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item.match').slice(0, 1).trigger(mup);
535
+ }
536
+ else if ((event.keyCode == 13) && $this.data('hold')) {
537
+ // enter key WITHOUT value typed into search box + hold mode
538
+ event.preventDefault();
539
+ event.stopPropagation();
540
+ $('#btn_sel_dialog_add').trigger( jQuery.Event("mouseup") );
541
+ }
542
+ else if ((event.keyCode == 27) && $this.data('hold')) {
543
+ // esc key WITHOUT value typed into search box + hold mode
544
+ event.preventDefault();
545
+ event.stopPropagation();
546
+ $('#btn_sel_dialog_cancel').trigger( jQuery.Event("mouseup") );
547
+ }
548
+ });
549
+
550
+ // handle enter/esc keys if search field is NOT focused
551
+ Popover.onKeyDown = function(event) {
552
+ if ((event.keyCode == 13) && !$input.is(':focus') && $this.data('hold')) {
553
+ // enter key
554
+ event.preventDefault();
555
+ event.stopPropagation();
556
+ $('#btn_sel_dialog_add').trigger( jQuery.Event("mouseup") );
557
+ }
558
+ else if ((event.keyCode == 27) && !$input.is(':focus') && $this.data('hold')) {
559
+ // esc key
560
+ event.preventDefault();
561
+ event.stopPropagation();
562
+ $('#btn_sel_dialog_cancel').trigger( jQuery.Event("mouseup") );
563
+ }
564
+ };
565
+
566
+ // highlight multiselect field under us
567
+ $ms.addClass('selected');
568
+ Popover.onDetach = function() {
569
+ $ms.removeClass('selected');
570
+ if (Dialog.active) Dialog.autoResize();
571
+ };
572
+ }); // mouseup
573
+
574
+ }); // foreach elem
575
+ },
576
+
577
+ popupQuickMenu: function(opts) {
578
+ // show popup menu on custom element
579
+ // opts: { elem, title, items, values, callback }
580
+ // item: { id, title, icon }
581
+ var $elem = $(opts.elem);
582
+ var items = opts.items;
583
+ var callback = opts.callback;
584
+ var html = '';
585
+
586
+ html += '<div class="sel_dialog_label">' + opts.title + '</div>';
587
+ if (opts.search) {
588
+ html += '<div class="sel_dialog_search_container">';
589
+ html += '<input type="text" id="fe_sel_dialog_search" class="sel_dialog_search" autocomplete="off" value=""/>';
590
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-magnify"></i></div>';
591
+ html += '</div>';
592
+ }
593
+ html += '<div id="d_sel_dialog_scrollarea" class="sel_dialog_scrollarea">';
594
+ for (var idy = 0, ley = items.length; idy < ley; idy++) {
595
+ var item = items[idy];
596
+ var sel = opts.values.includes(item.id);
597
+ html += '<div class="sel_dialog_item check ' + (sel ? 'selected' : '') + ' shrinkwrap" data-value="' + item.id + '">';
598
+ if (item.icon) html += '<i class="mdi mdi-' + item.icon + '">&nbsp;</i>';
599
+ html += '<span>' + item.title + '</span>';
600
+ html += '<div class="sel_dialog_item_check"><i class="mdi mdi-check"></i></div>';
601
+ html += '</div>';
602
+ }
603
+ html += '</div>';
604
+
605
+ Popover.attach( $elem, '<div style="padding:15px;">' + html + '</div>', true );
606
+
607
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').on('mouseup', function() {
608
+ // select item, close dialog and update state
609
+ var $item = $(this);
610
+ if ($item.hasClass('selected')) $item.removeClass('selected');
611
+ else $item.addClass('selected');
612
+
613
+ var values = [];
614
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item.selected').each( function() {
615
+ values.push( $(this).data('value') );
616
+ });
617
+
618
+ Popover.detach();
619
+ callback(values);
620
+ }); // mouseup
621
+
622
+ Popover.onDetach = function() {
623
+ $elem.removeClass('popped');
624
+ };
625
+
626
+ $elem.addClass('popped');
627
+
628
+ if (opts.search) {
629
+ // setup input field
630
+ var $input = $('#fe_sel_dialog_search');
631
+ $input.focus();
632
+
633
+ // setup keyboard handlers
634
+ $input.on('keyup', function(event) {
635
+ // refresh list on every keypress
636
+ var value = $input.val().toLowerCase();
637
+
638
+ if (value.length) $('#d_sel_dialog_scrollarea > div.sel_dialog_group').hide();
639
+ else $('#d_sel_dialog_scrollarea > div.sel_dialog_group').show();
640
+
641
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item').each( function() {
642
+ var $item = $(this);
643
+ var text = $item.find('> span').html().toLowerCase();
644
+ if (!value.length || (text.indexOf(value) > -1)) {
645
+ $item.addClass('match').show();
646
+ }
647
+ else {
648
+ $item.removeClass('match').hide();
649
+ }
650
+ } );
651
+ Popover.reposition();
652
+ });
653
+ $input.on('keydown', function(event) {
654
+ // capture enter key
655
+ var value = $input.val().toLowerCase();
656
+ if ((event.keyCode == 13) && value.length) {
657
+ // enter key with a value typed into the search box
658
+ event.preventDefault();
659
+ event.stopPropagation();
660
+
661
+ var mup = jQuery.Event( "mouseup" );
662
+ mup.metaKey = true; // bypass `hold` feature
663
+ $('#d_sel_dialog_scrollarea > div.sel_dialog_item.match').slice(0, 1).trigger(mup);
664
+ }
665
+ });
666
+ } // opts.search
667
+ }
668
+
669
+ }; // MultiSelect
670
+
671
+ var TextSelect = {
672
+
673
+ init: function(sel) {
674
+ // initialize all text-selects based on selector
675
+ $(sel).each( function() {
676
+ var self = this;
677
+ var $this = $(this);
678
+
679
+ var $ms = $('<div class="multiselect text"></div>');
680
+ $this.after( $ms );
681
+
682
+ var redraw = function() {
683
+ // render contents of visible multiselect div
684
+ var num_sel = 0;
685
+ $ms.empty();
686
+ $ms.append('<div class="select_chevron mdi mdi-plus"></div>');
687
+
688
+ for (var idx = 0, len = self.options.length; idx < len; idx++) {
689
+ var opt = self.options[idx];
690
+ var $item = $('<div class="item"></div>').data('value', opt.value).html(
691
+ '<i class="mdi mdi-close">&nbsp;</i>' + opt.label
692
+ );
693
+ $ms.append( $item );
694
+ num_sel++;
695
+ }
696
+
697
+ if (num_sel) $ms.append( '<div class="clear"></div>' );
698
+ else $ms.append( '<div class="placeholder">' + ($this.attr('placeholder') || 'Click to add...') + '</div>' );
699
+
700
+ $ms.find('div.item > i').on('mouseup', function(e) {
701
+ // user clicked on the 'X' -- remove this item and redraw
702
+ var $item = $(this).parent();
703
+ var value = $item.data('value');
704
+
705
+ var idx = find_object_idx( self.options, { value: value } );
706
+ self.options.remove( idx );
707
+
708
+ $this.trigger('change');
709
+ e.stopPropagation();
710
+ e.preventDefault();
711
+ return false;
712
+ });
713
+ }; // redraw
714
+
715
+ redraw();
716
+
717
+ // also trigger a redraw if the underlying hidden select changes
718
+ $this.on('change', redraw);
719
+
720
+ $ms.on('mouseup', function() {
721
+ // create popover dialog for adding new items
722
+ var html = '';
723
+ if ($ms.hasClass('disabled')) return;
724
+
725
+ html += '<div class="sel_dialog_label">' + ($this.attr('title') || 'Add New Item') + '</div>';
726
+ html += '<div class="sel_dialog_search_container">';
727
+ html += '<input type="text" id="fe_sel_dialog_text" class="sel_dialog_search" style="border-radius:2px;" autocomplete="off" value=""/>';
728
+ html += '<div class="sel_dialog_search_icon"><i class="mdi mdi-' + ($this.attr('icon') || 'plus') + '"></i></div>';
729
+ html += '</div>';
730
+
731
+ if ($this.attr('description')) {
732
+ html += '<div class="sel_dialog_caption">' + $this.attr('description') + '</div>';
733
+ }
734
+
735
+ html += '<div class="sel_dialog_button_container">';
736
+ html += '<div class="button" id="btn_sel_dialog_cancel">Cancel</div>';
737
+ html += '<div class="button primary" id="btn_sel_dialog_add">' + ($this.attr('confirm') || 'Add Item') + '</div>';
738
+ html += '</div>';
739
+
740
+ Popover.attach( $ms, '<div style="padding:15px;">' + html + '</div>', $this.data('shrinkwrap') || false );
741
+
742
+ var doAdd = function() {
743
+ app.clearError();
744
+
745
+ var value = $('#fe_sel_dialog_text').val().replace(/[\"\']+/g, '');
746
+ if ($this.attr('trim')) value = value.trim();
747
+ if ($this.attr('lower')) value = value.toLowerCase();
748
+ if ($this.data('validate') && !value.match( new RegExp($this.data('validate')) )) {
749
+ return app.badField('#fe_sel_dialog_text');
750
+ }
751
+
752
+ if (!value.length || find_object(self.options, { value: value })) {
753
+ Popover.detach();
754
+ return;
755
+ }
756
+
757
+ // add new item
758
+ var opt = new Option( value, value );
759
+ opt.selected = true;
760
+ self.options[ self.options.length ] = opt;
761
+
762
+ Popover.detach();
763
+ $this.trigger('change');
764
+ }; // doAdd
765
+
766
+ $('#btn_sel_dialog_cancel').on('mouseup', function() { Popover.detach(); });
767
+ $('#btn_sel_dialog_add').on('mouseup', function() { doAdd(); });
768
+
769
+ var $input = $('#fe_sel_dialog_text').focus().on('keydown', function(event) {
770
+ // capture enter key
771
+ if ((event.keyCode == 13) && this.value.length) {
772
+ event.preventDefault();
773
+ event.stopPropagation();
774
+ doAdd();
775
+ }
776
+ });
777
+
778
+ // highlight multiselect field under us
779
+ $ms.addClass('selected');
780
+ Popover.onDetach = function() { $ms.removeClass('selected'); };
781
+ }); // mouseup
782
+
783
+ }); // forach elem
784
+ },
785
+
786
+ popupQuickMenu: function(opts) {
787
+ // show popup menu on custom element
788
+ // opts: { elem, title, icon?, description?, confirm?, trim?, lower?, validate?, callback }
789
+ var $elem = $(opts.elem);
790
+ var callback = opts.callback;
791
+ var html = '';
792
+
793
+ html += '<div class="sel_dialog_label ' + (opts.danger ? 'danger' : '') +'">' + (opts.title || 'Add New Item') + '</div>';
794
+ html += '<div class="sel_dialog_search_container">';
795
+ html += '<input type="text" id="fe_sel_dialog_text" class="sel_dialog_search" style="border-radius:2px;" autocomplete="off" value=""/>';
796
+ html += '<div class="sel_dialog_search_icon ' + (opts.danger ? 'danger' : '') + '"><i class="mdi mdi-' + (opts.icon || 'plus') + '"></i></div>';
797
+ html += '</div>';
798
+
799
+ if (opts.description) {
800
+ html += '<div class="sel_dialog_caption">' + opts.description + '</div>';
801
+ }
802
+
803
+ html += '<div class="sel_dialog_button_container">';
804
+ html += '<div class="button" id="btn_sel_dialog_cancel">Cancel</div>';
805
+ html += '<div class="button primary ' + (opts.danger ? 'delete' : '') + '" id="btn_sel_dialog_add">' + (opts.confirm || 'Add Item') + '</div>';
806
+ html += '</div>';
807
+
808
+ Popover.attach( $elem, '<div style="padding:15px;">' + html + '</div>', true );
809
+
810
+ var doAdd = function() {
811
+ app.clearError();
812
+
813
+ var value = $('#fe_sel_dialog_text').val().replace(/[\"\']+/g, '');
814
+ if (opts.trim) value = value.trim();
815
+ if (opts.lower) value = value.toLowerCase();
816
+ if (opts.validate && !value.match( new RegExp(opts.validate) )) {
817
+ return app.badField('#fe_sel_dialog_text');
818
+ }
819
+
820
+ if (!value.length) {
821
+ Popover.detach();
822
+ return;
823
+ }
824
+
825
+ Popover.detach();
826
+ callback(value);
827
+ }; // doAdd
828
+
829
+ $('#btn_sel_dialog_cancel').on('mouseup', function() { Popover.detach(); });
830
+ $('#btn_sel_dialog_add').on('mouseup', function() { doAdd(); });
831
+
832
+ var $input = $('#fe_sel_dialog_text').focus().on('keydown', function(event) {
833
+ // capture enter key
834
+ if ((event.keyCode == 13) && this.value.length) {
835
+ event.preventDefault();
836
+ event.stopPropagation();
837
+ doAdd();
838
+ }
839
+ });
840
+
841
+ $elem.addClass('popped');
842
+ Popover.onDetach = function() { $elem.removeClass('popped'); };
843
+ }
844
+
845
+ }; // TextSelect