j1-template 2022.0.17 → 2022.0.18

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.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/assets/error_pages/HTTP204.html +1 -1
  3. data/assets/error_pages/HTTP400.html +1 -1
  4. data/assets/error_pages/HTTP401.html +1 -1
  5. data/assets/error_pages/HTTP403.html +1 -1
  6. data/assets/error_pages/HTTP404.html +1 -1
  7. data/assets/error_pages/HTTP444.html +1 -1
  8. data/assets/error_pages/HTTP445.html +1 -1
  9. data/assets/error_pages/HTTP446.html +1 -1
  10. data/assets/error_pages/HTTP447.html +1 -1
  11. data/assets/error_pages/HTTP448.html +1 -1
  12. data/assets/error_pages/HTTP500.html +1 -1
  13. data/assets/error_pages/HTTP501.html +1 -1
  14. data/assets/error_pages/HTTP502.html +1 -1
  15. data/assets/error_pages/HTTP503.html +1 -1
  16. data/assets/themes/j1/adapter/js/customFunctions.js +221 -0
  17. data/assets/themes/j1/adapter/js/customModule.js +221 -0
  18. data/assets/themes/j1/adapter/js/dropdowns.js +319 -0
  19. data/assets/themes/j1/adapter/js/rtable.js +2 -6
  20. data/assets/themes/j1/modules/dropdowns/css/theme/uno/dropdowns.css +109 -0
  21. data/assets/themes/j1/modules/dropdowns/css/theme/uno/dropdowns.min.css +15 -0
  22. data/assets/themes/j1/modules/dropdowns/js/cash.js +978 -0
  23. data/assets/themes/j1/modules/dropdowns/js/dropdowns.js +864 -0
  24. data/lib/j1/commands/generate.rb +25 -14
  25. data/lib/j1/commands/rebuild.rb +1 -0
  26. data/lib/j1/commands/reset.rb +6 -3
  27. data/lib/j1/commands/setup.rb +18 -13
  28. data/lib/j1/commands/site.rb +2 -1
  29. data/lib/j1/utils/exec2.rb +33 -7
  30. data/lib/j1/version.rb +3 -3
  31. data/lib/starter_web/Gemfile +1 -1
  32. data/lib/starter_web/_config.yml +1 -1
  33. data/lib/starter_web/_data/apps/carousel.yml +138 -140
  34. data/lib/starter_web/_data/blocks/banner.yml +7 -7
  35. data/lib/starter_web/_data/modules/defaults/dropdowns.yml +164 -0
  36. data/lib/starter_web/_data/modules/dropdowns.yml +77 -0
  37. data/lib/starter_web/_data/resources.yml +73 -2
  38. data/lib/starter_web/_includes/attributes.asciidoc +1 -1
  39. data/lib/starter_web/_plugins/lunr_index.rb +2 -2
  40. data/lib/starter_web/package.json +2 -2
  41. data/lib/starter_web/pages/public/about/about_site.adoc +1 -1
  42. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/000_intro.adoc +1 -1
  43. data/lib/starter_web/pages/public/asciidoc_skeletons/documentation/documentation.adoc +1 -1
  44. data/lib/starter_web/pages/public/asciidoc_skeletons/multi-document/multi.adoc +1 -1
  45. data/lib/starter_web/pages/public/asciidoc_skeletons/simple-document/simple.adoc +1 -1
  46. data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.adoc +1 -1
  47. data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.ads.asciidoc +1 -1
  48. data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.base.asciidoc +1 -1
  49. data/lib/starter_web/pages/public/learn/roundtrip/100_present_images.comments.asciidoc +1 -1
  50. data/lib/starter_web/pages/public/learn/roundtrip/200_typography.adoc +1 -1
  51. data/lib/starter_web/pages/public/learn/roundtrip/300_icon_fonts.adoc +1 -1
  52. data/lib/starter_web/pages/public/learn/roundtrip/400_asciidoc_extensions.adoc +1 -1
  53. data/lib/starter_web/pages/public/learn/roundtrip/410_bs_modals_extentions.adoc +1 -1
  54. data/lib/starter_web/pages/public/learn/roundtrip/420_responsive_tables_extensions.adoc +1 -1
  55. data/lib/starter_web/pages/public/learn/roundtrip/500_themes.adoc +1 -1
  56. data/lib/starter_web/pages/public/learn/roundtrip/600_quicksearch.adoc +1 -1
  57. data/lib/starter_web/pages/public/learn/where_to_go.adoc +1 -1
  58. data/lib/starter_web/pages/public/legal/en/100_copyright.adoc +1 -1
  59. data/lib/starter_web/pages/public/legal/en/200_impress.adoc +1 -1
  60. data/lib/starter_web/pages/public/legal/en/400_comment_policy.adoc +1 -1
  61. data/lib/starter_web/pages/public/manuals/j1-dropdown.adoc +294 -0
  62. data/lib/starter_web/pages/public/manuals/{dropdown-help.adoc → msdropdown.adoc} +0 -0
  63. data/lib/starter_web/pages/public/panels/intro_panel/panel.adoc +1 -1
  64. data/lib/starter_web/pages/public/previewer/preview_bootstrap_theme.adoc +1 -1
  65. data/lib/starter_web/utilsrv/_defaults/package.json +1 -1
  66. data/lib/starter_web/utilsrv/package.json +1 -1
  67. metadata +14 -6
  68. data/assets/themes/j1/modules/fab/css/theme/uno/fab.css +0 -373
  69. data/assets/themes/j1/modules/fab/css/theme/uno/fab.min.css +0 -15
@@ -0,0 +1,864 @@
1
+ /*
2
+ # -----------------------------------------------------------------------------
3
+ # ~/assets/themes/j1/modules/dropdowns/js/dropdowns.js
4
+ # MODIFIED DROPDOWN JS from project Materialize
5
+ # Provides JS Core funtions for the J1 DROPDOWNS Module
6
+ #
7
+ # Product/Info:
8
+ # https://jekyll.one
9
+ # https://github.com/Dogfalo/materialize
10
+ #
11
+ # Copyright (C) 2021 Juergen Adams
12
+ #
13
+ # J1 Template is licensed under the MIT License.
14
+ # See: https://github.com/jekyll-one-org/J1 Template/blob/master/LICENSE
15
+ # -----------------------------------------------------------------------------
16
+ # TODO:
17
+ # jadams, 2020-10-11: module needs to be rewitten to PURE jQuery
18
+ # -----------------------------------------------------------------------------
19
+ */
20
+ 'use strict';
21
+
22
+ // -----------------------------------------------------------------------------
23
+ // ESLint shimming
24
+ // -----------------------------------------------------------------------------
25
+ /* eslint indent: "off" */
26
+ /* eslint no-unused-vars: "off" */
27
+ /* eslint no-undef: "off" */
28
+ /* eslint no-redeclare: "off" */
29
+ /* eslint indent: "off" */
30
+ // -----------------------------------------------------------------------------
31
+
32
+ (function($, anim) {
33
+
34
+ var _defaults = {
35
+ direction: 'top',
36
+ hoverEnabled: true,
37
+ toolbarEnabled: false
38
+ };
39
+
40
+ $.fn.reverse = [].reverse;
41
+
42
+ // jadams, 2020-10-10, added class from main (materialize.js)
43
+ //
44
+ class Component {
45
+
46
+ constructor(classDef, el, options) {
47
+ // Display error if el is valid HTML Element
48
+ if (!(el instanceof Element)) {
49
+ console.error(Error(el + ' is not an HTML Element'));
50
+ }
51
+
52
+ // If exists, destroy and reinitialize in child
53
+ var ins = classDef.getInstance(el);
54
+ if (!!ins) {
55
+ ins.destroy();
56
+ }
57
+
58
+ this.el = el;
59
+ this.$el = cash(el);
60
+ }
61
+
62
+ /**
63
+ * Initializes components
64
+ * @param {class} classDef
65
+ * @param {Element | NodeList | jQuery} els
66
+ * @param {Object} options
67
+ */
68
+ static init(classDef, els, options) {
69
+ var instances = null;
70
+ if (els instanceof Element) {
71
+ instances = new classDef(els, options);
72
+ } else if (!!els && (els.jquery || els.cash || els instanceof NodeList)) {
73
+ var instancesArr = [];
74
+ for (var i = 0; i < els.length; i++) {
75
+ instancesArr.push(new classDef(els[i], options));
76
+ }
77
+ instances = instancesArr;
78
+ }
79
+ return instances;
80
+ }
81
+ }
82
+
83
+ /**
84
+ * Initialize jQuery wrapper for plugin
85
+ * @param {Class} plugin javascript class
86
+ * @param {string} pluginName jQuery plugin name
87
+ * @param {string} classRef Class reference name
88
+ */
89
+ j1.initializeJqueryWrapper = function (plugin, pluginName, classRef) {
90
+ jQuery.fn[pluginName] = function (methodOrOptions) {
91
+ // Call plugin method if valid method name is passed in
92
+ if (plugin.prototype[methodOrOptions]) {
93
+ var params = Array.prototype.slice.call(arguments, 1);
94
+
95
+ // Getter methods
96
+ if (methodOrOptions.slice(0, 3) === 'get') {
97
+ var instance = this.first()[0][classRef];
98
+ return instance[methodOrOptions].apply(instance, params);
99
+ }
100
+
101
+ // Void methods
102
+ return this.each(function () {
103
+ var instance = this[classRef];
104
+ instance[methodOrOptions].apply(instance, params);
105
+ });
106
+
107
+ // Initialize plugin if options or no argument is passed in
108
+ } else if (typeof methodOrOptions === 'object' || !methodOrOptions) {
109
+ plugin.init(this, arguments[0]);
110
+ return this;
111
+ }
112
+
113
+ // Return error if an unrecognized method name is passed in
114
+ jQuery.error("Method " + methodOrOptions + " does not exist on jQuery." + pluginName);
115
+ };
116
+ };
117
+
118
+ /**
119
+ * @class
120
+ */
121
+ class Dropdowns extends Component {
122
+ constructor(el, options) {
123
+ super(Dropdowns, el, options);
124
+
125
+ this.el.M_Dropdown = this;
126
+ Dropdowns._dropdowns.push(this);
127
+
128
+ this.id = this.getIdFromTrigger(el);
129
+ this.dropdownEl = document.getElementById(this.id);
130
+ this.$dropdownEl = $(this.dropdownEl);
131
+
132
+ /**
133
+ * Options for the dropdown
134
+ * @member Dropdowns#options
135
+ * @prop {String} [alignment='left'] - Edge which the dropdown is aligned to
136
+ * @prop {Boolean} [autoFocus=true] - Automatically focus dropdown el for keyboard
137
+ * @prop {Boolean} [constrainWidth=true] - Constrain width to width of the button
138
+ * @prop {Element} container - Container element to attach dropdown to (optional)
139
+ * @prop {Boolean} [coverTrigger=true] - Place dropdown over trigger
140
+ * @prop {Boolean} [closeOnClick=true] - Close on click of dropdown item
141
+ * @prop {Boolean} [hover=false] - Open dropdown on hover
142
+ * @prop {Number} [inDuration=150] - Duration of open animation in ms
143
+ * @prop {Number} [outDuration=250] - Duration of close animation in ms
144
+ * @prop {Function} onOpenStart - Function called when dropdown starts opening
145
+ * @prop {Function} onOpenEnd - Function called when dropdown finishes opening
146
+ * @prop {Function} onCloseStart - Function called when dropdown starts closing
147
+ * @prop {Function} onCloseEnd - Function called when dropdown finishes closing
148
+ */
149
+ this.options = $.extend({}, Dropdowns.defaults, options);
150
+
151
+ /**
152
+ * Describes open/close state of dropdown
153
+ * @type {Boolean}
154
+ */
155
+ this.isOpen = false;
156
+
157
+ /**
158
+ * Describes if dropdown content is scrollable
159
+ * @type {Boolean}
160
+ */
161
+ this.isScrollable = false;
162
+
163
+ /**
164
+ * Describes if touch moving on dropdown content
165
+ * @type {Boolean}
166
+ */
167
+ this.isTouchMoving = false;
168
+
169
+ this.focusedIndex = -1;
170
+ this.filterQuery = [];
171
+
172
+ this.keys = {
173
+ TAB: 9,
174
+ ENTER: 13,
175
+ ESC: 27,
176
+ ARROW_UP: 38,
177
+ ARROW_DOWN: 40
178
+ };
179
+
180
+ // Move dropdown-content after dropdown-trigger
181
+ if (!!this.options.container) {
182
+ $(this.options.container).append(this.dropdownEl);
183
+ } else {
184
+ this.$el.after(this.dropdownEl);
185
+ }
186
+
187
+ this._makeDropdownFocusable();
188
+ this._resetFilterQueryBound = this._resetFilterQuery.bind(this);
189
+ this._handleDocumentClickBound = this._handleDocumentClick.bind(this);
190
+ this._handleDocumentTouchmoveBound = this._handleDocumentTouchmove.bind(this);
191
+ this._handleDropdownClickBound = this._handleDropdownClick.bind(this);
192
+ this._handleDropdownKeydownBound = this._handleDropdownKeydown.bind(this);
193
+ this._handleTriggerKeydownBound = this._handleTriggerKeydown.bind(this);
194
+ this._setupEventHandlers();
195
+ }
196
+
197
+ static get defaults() {
198
+ return _defaults;
199
+ }
200
+
201
+ static init(els, options) {
202
+ return super.init(this, els, options);
203
+ }
204
+
205
+ /**
206
+ * Get Instance
207
+ */
208
+ static getInstance(el) {
209
+ let domElem = !!el.jquery ? el[0] : el;
210
+ return domElem.M_Dropdown;
211
+ }
212
+
213
+ /**
214
+ * Gets id of component from a trigger
215
+ * @param {Element} trigger trigger
216
+ * @returns {string}
217
+ */
218
+ getIdFromTrigger (trigger) {
219
+ var id = trigger.getAttribute('data-target');
220
+ if (!id) {
221
+ id = trigger.getAttribute('href');
222
+ if (id) {
223
+ id = id.slice(1);
224
+ } else {
225
+ id = '';
226
+ }
227
+ }
228
+ return id;
229
+ }
230
+
231
+ // ---------------------------------------------------------------------------
232
+ // executeFunctionByName()
233
+ // execute a function by NAME (functionName) in a browser context
234
+ // (e.g. window) the function is published
235
+ // ---------------------------------------------------------------------------
236
+ executeFunctionByName (functionName, context /*, args */) {
237
+ var args = Array.prototype.slice.call(arguments, 2);
238
+ var namespaces = functionName.split('.');
239
+ var func = namespaces.pop();
240
+ for(var i = 0; i < namespaces.length; i++) {
241
+ context = context[namespaces[i]];
242
+ }
243
+ return context[func].apply(context, args);
244
+ }
245
+
246
+ checkPossibleAlignments (el, container, bounding, offset) {
247
+ var canAlign = {
248
+ top: true,
249
+ right: true,
250
+ bottom: true,
251
+ left: true,
252
+ spaceOnTop: null,
253
+ spaceOnRight: null,
254
+ spaceOnBottom: null,
255
+ spaceOnLeft: null
256
+ };
257
+
258
+ var containerAllowsOverflow = getComputedStyle(container).overflow === 'visible';
259
+ var containerRect = container.getBoundingClientRect();
260
+ var containerHeight = Math.min(containerRect.height, window.innerHeight);
261
+ var containerWidth = Math.min(containerRect.width, window.innerWidth);
262
+ var elOffsetRect = el.getBoundingClientRect();
263
+
264
+ var scrollLeft = container.scrollLeft;
265
+ var scrollTop = container.scrollTop;
266
+
267
+ var scrolledX = bounding.left - scrollLeft;
268
+ var scrolledYTopEdge = bounding.top - scrollTop;
269
+ var scrolledYBottomEdge = bounding.top + elOffsetRect.height - scrollTop;
270
+
271
+ // Check for container and viewport for left
272
+ canAlign.spaceOnRight = !containerAllowsOverflow ? containerWidth - (scrolledX + bounding.width) : window.innerWidth - (elOffsetRect.left + bounding.width);
273
+ if (canAlign.spaceOnRight < 0) {
274
+ canAlign.left = false;
275
+ }
276
+
277
+ // Check for container and viewport for Right
278
+ canAlign.spaceOnLeft = !containerAllowsOverflow ? scrolledX - bounding.width + elOffsetRect.width : elOffsetRect.right - bounding.width;
279
+ if (canAlign.spaceOnLeft < 0) {
280
+ canAlign.right = false;
281
+ }
282
+
283
+ // Check for container and viewport for Top
284
+ canAlign.spaceOnBottom = !containerAllowsOverflow ? containerHeight - (scrolledYTopEdge + bounding.height + offset) : window.innerHeight - (elOffsetRect.top + bounding.height + offset);
285
+ if (canAlign.spaceOnBottom < 0) {
286
+ canAlign.top = false;
287
+ }
288
+
289
+ // Check for container and viewport for Bottom
290
+ canAlign.spaceOnTop = !containerAllowsOverflow ? scrolledYBottomEdge - (bounding.height - offset) : elOffsetRect.bottom - (bounding.height + offset);
291
+ if (canAlign.spaceOnTop < 0) {
292
+ canAlign.bottom = false;
293
+ }
294
+
295
+ return canAlign;
296
+ }
297
+
298
+ /**
299
+ * Teardown component
300
+ */
301
+ destroy() {
302
+ this._resetDropdownStyles();
303
+ this._removeEventHandlers();
304
+ Dropdowns._dropdowns.splice(Dropdowns._dropdowns.indexOf(this), 1);
305
+ this.el.M_Dropdown = undefined;
306
+ }
307
+
308
+ /**
309
+ * Setup Event Handlers
310
+ */
311
+ _setupEventHandlers() {
312
+ // Trigger keydown handler
313
+ this.el.addEventListener('keydown', this._handleTriggerKeydownBound);
314
+
315
+ // Item click handler
316
+ this.dropdownEl.addEventListener('click', this._handleDropdownClickBound);
317
+
318
+ // Hover event handlers
319
+ if (this.options.hover) {
320
+ this._handleMouseEnterBound = this._handleMouseEnter.bind(this);
321
+ this.el.addEventListener('mouseenter', this._handleMouseEnterBound);
322
+ this._handleMouseLeaveBound = this._handleMouseLeave.bind(this);
323
+ this.el.addEventListener('mouseleave', this._handleMouseLeaveBound);
324
+ this.dropdownEl.addEventListener('mouseleave', this._handleMouseLeaveBound);
325
+
326
+ // Click event handlers
327
+ } else {
328
+ this._handleClickBound = this._handleClick.bind(this);
329
+ this.el.addEventListener('click', this._handleClickBound);
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Remove Event Handlers
335
+ */
336
+ _removeEventHandlers() {
337
+ this.el.removeEventListener('keydown', this._handleTriggerKeydownBound);
338
+ this.dropdownEl.removeEventListener('click', this._handleDropdownClickBound);
339
+
340
+ if (this.options.hover) {
341
+ this.el.removeEventListener('mouseenter', this._handleMouseEnterBound);
342
+ this.el.removeEventListener('mouseleave', this._handleMouseLeaveBound);
343
+ this.dropdownEl.removeEventListener('mouseleave', this._handleMouseLeaveBound);
344
+ } else {
345
+ this.el.removeEventListener('click', this._handleClickBound);
346
+ }
347
+ }
348
+
349
+ _setupTemporaryEventHandlers() {
350
+ // Use capture phase event handler to prevent click
351
+ document.body.addEventListener('click', this._handleDocumentClickBound, true);
352
+ document.body.addEventListener('touchend', this._handleDocumentClickBound);
353
+ document.body.addEventListener('touchmove', this._handleDocumentTouchmoveBound);
354
+ this.dropdownEl.addEventListener('keydown', this._handleDropdownKeydownBound);
355
+ }
356
+
357
+ _removeTemporaryEventHandlers() {
358
+ // Use capture phase event handler to prevent click
359
+ document.body.removeEventListener('click', this._handleDocumentClickBound, true);
360
+ document.body.removeEventListener('touchend', this._handleDocumentClickBound);
361
+ document.body.removeEventListener('touchmove', this._handleDocumentTouchmoveBound);
362
+ this.dropdownEl.removeEventListener('keydown', this._handleDropdownKeydownBound);
363
+ }
364
+
365
+ _handleClick(e) {
366
+ e.preventDefault();
367
+ this.open();
368
+ }
369
+
370
+ _handleMouseEnter() {
371
+ this.open();
372
+ }
373
+
374
+ _handleMouseLeave(e) {
375
+ let toEl = e.toElement || e.relatedTarget;
376
+ let leaveToDropdownContent = !!$(toEl).closest('.dropdown-content').length;
377
+ let leaveToActiveDropdownTrigger = false;
378
+
379
+ let $closestTrigger = $(toEl).closest('.dropdowns');
380
+ if (
381
+ $closestTrigger.length &&
382
+ !!$closestTrigger[0].M_Dropdown &&
383
+ $closestTrigger[0].M_Dropdown.isOpen
384
+ ) {
385
+ leaveToActiveDropdownTrigger = true;
386
+ }
387
+
388
+ // Close hover dropdown if mouse did not leave to either active dropdown-trigger or dropdown-content
389
+ if (!leaveToActiveDropdownTrigger && !leaveToDropdownContent) {
390
+ this.close();
391
+ }
392
+ }
393
+
394
+ _handleDocumentClick(e) {
395
+ let $target = $(e.target);
396
+ if (
397
+ this.options.closeOnClick &&
398
+ $target.closest('.dropdown-content').length &&
399
+ !this.isTouchMoving
400
+ ) {
401
+ // isTouchMoving to check if scrolling on mobile.
402
+ setTimeout(() => {
403
+ this.close();
404
+ }, 0);
405
+ } else if (
406
+ $target.closest('.dropdowns').length ||
407
+ !$target.closest('.dropdown-content').length
408
+ ) {
409
+ setTimeout(() => {
410
+ this.close();
411
+ }, 0);
412
+ }
413
+ this.isTouchMoving = false;
414
+ }
415
+
416
+ _handleTriggerKeydown(e) {
417
+ // ARROW DOWN OR ENTER WHEN SELECT IS CLOSED - open Dropdowns
418
+ if ((e.which === this.keys.ARROW_DOWN || e.which === this.keys.ENTER) && !this.isOpen) {
419
+ e.preventDefault();
420
+ this.open();
421
+ }
422
+ }
423
+
424
+ /**
425
+ * Handle Document Touchmove
426
+ * @param {Event} e
427
+ */
428
+ _handleDocumentTouchmove(e) {
429
+ let $target = $(e.target);
430
+ if ($target.closest('.dropdown-content').length) {
431
+ this.isTouchMoving = true;
432
+ }
433
+ }
434
+
435
+ /**
436
+ * Handle Dropdowns Click
437
+ * @param {Event} e
438
+ */
439
+ _handleDropdownClick(e) {
440
+
441
+ // onItemClick callback (by reference)
442
+ // if (typeof this.options.onItemClick === 'function') {
443
+ // let itemEl = $(e.target).closest('li')[0];
444
+ // this.options.onItemClick.call(this, itemEl);
445
+ // }
446
+
447
+ // onItemClick callback (by name)
448
+ if (this.options.onItemClick !== 'false') {
449
+ var _this = this;
450
+ // var itemEl = $(e.target).closest('li')[0];
451
+ this.executeFunctionByName(this.options.onItemClick, window, _this);
452
+ }
453
+ }
454
+
455
+ /**
456
+ * Handle Dropdowns Keydown
457
+ * @param {Event} e
458
+ */
459
+ _handleDropdownKeydown(e) {
460
+ if (e.which === this.keys.TAB) {
461
+ e.preventDefault();
462
+ this.close();
463
+
464
+ // Navigate down dropdown list
465
+ } else if ((e.which === this.keys.ARROW_DOWN || e.which === this.keys.ARROW_UP) && this.isOpen) {
466
+ e.preventDefault();
467
+ let direction = e.which === this.keys.ARROW_DOWN ? 1 : -1;
468
+ let newFocusedIndex = this.focusedIndex;
469
+ let foundNewIndex = false;
470
+ do {
471
+ newFocusedIndex = newFocusedIndex + direction;
472
+
473
+ if (
474
+ !!this.dropdownEl.children[newFocusedIndex] &&
475
+ this.dropdownEl.children[newFocusedIndex].tabIndex !== -1
476
+ ) {
477
+ foundNewIndex = true;
478
+ break;
479
+ }
480
+ } while (newFocusedIndex < this.dropdownEl.children.length && newFocusedIndex >= 0);
481
+
482
+ if (foundNewIndex) {
483
+ this.focusedIndex = newFocusedIndex;
484
+ this._focusFocusedItem();
485
+ }
486
+
487
+ // ENTER selects choice on focused item
488
+ } else if (e.which === this.keys.ENTER && this.isOpen) {
489
+ // Search for <a> and <button>
490
+ let focusedElement = this.dropdownEl.children[this.focusedIndex];
491
+ let $activatableElement = $(focusedElement)
492
+ .find('a, button')
493
+ .first();
494
+
495
+ // Click a or button tag if exists, otherwise click li tag
496
+ if (!!$activatableElement.length) {
497
+ $activatableElement[0].click();
498
+ } else if (!!focusedElement) {
499
+ focusedElement.click();
500
+ }
501
+
502
+ // Close dropdown on ESC
503
+ } else if (e.which === this.keys.ESC && this.isOpen) {
504
+ e.preventDefault();
505
+ this.close();
506
+ }
507
+
508
+ // CASE WHEN USER TYPE LETTERS
509
+ let letter = String.fromCharCode(e.which).toLowerCase(),
510
+ nonLetters = [9, 13, 27, 38, 40];
511
+ if (letter && nonLetters.indexOf(e.which) === -1) {
512
+ this.filterQuery.push(letter);
513
+
514
+ let string = this.filterQuery.join(''),
515
+ newOptionEl = $(this.dropdownEl)
516
+ .find('li')
517
+ .filter((el) => {
518
+ return (
519
+ $(el)
520
+ .text()
521
+ .toLowerCase()
522
+ .indexOf(string) === 0
523
+ );
524
+ })[0];
525
+
526
+ if (newOptionEl) {
527
+ this.focusedIndex = $(newOptionEl).index();
528
+ this._focusFocusedItem();
529
+ }
530
+ }
531
+
532
+ this.filterTimeout = setTimeout(this._resetFilterQueryBound, 1000);
533
+ }
534
+
535
+ /**
536
+ * Setup dropdown
537
+ */
538
+ _resetFilterQuery() {
539
+ this.filterQuery = [];
540
+ }
541
+
542
+ _resetDropdownStyles() {
543
+ this.$dropdownEl.css({
544
+ display: '',
545
+ width: '',
546
+ height: '',
547
+ left: '',
548
+ top: '',
549
+ 'transform-origin': '',
550
+ transform: '',
551
+ opacity: ''
552
+ });
553
+ }
554
+
555
+ _makeDropdownFocusable() {
556
+ // Needed for arrow key navigation
557
+ this.dropdownEl.tabIndex = 0;
558
+
559
+ // Only set tabindex if it hasn't been set by user
560
+ $(this.dropdownEl)
561
+ .children()
562
+ .each(function(el) {
563
+ if (!el.getAttribute('tabindex')) {
564
+ el.setAttribute('tabindex', 0);
565
+ }
566
+ });
567
+ }
568
+
569
+ _focusFocusedItem() {
570
+ if (
571
+ this.focusedIndex >= 0 &&
572
+ this.focusedIndex < this.dropdownEl.children.length &&
573
+ this.options.autoFocus
574
+ ) {
575
+ this.dropdownEl.children[this.focusedIndex].focus();
576
+ }
577
+ }
578
+
579
+ _getDropdownPosition() {
580
+ let offsetParentBRect = this.el.offsetParent.getBoundingClientRect();
581
+ let triggerBRect = this.el.getBoundingClientRect();
582
+ let dropdownBRect = this.dropdownEl.getBoundingClientRect();
583
+
584
+ let idealHeight = dropdownBRect.height;
585
+ let idealWidth = dropdownBRect.width;
586
+ let idealXPos = triggerBRect.left - dropdownBRect.left;
587
+ let idealYPos = triggerBRect.top - dropdownBRect.top;
588
+
589
+ let dropdownBounds = {
590
+ left: idealXPos,
591
+ top: idealYPos,
592
+ height: idealHeight,
593
+ width: idealWidth
594
+ };
595
+
596
+ // Countainer here will be closest ancestor with overflow: hidden
597
+ let closestOverflowParent = !!this.dropdownEl.offsetParent
598
+ ? this.dropdownEl.offsetParent
599
+ : this.dropdownEl.parentNode;
600
+
601
+ let alignments = this.checkPossibleAlignments(
602
+ this.el,
603
+ closestOverflowParent,
604
+ dropdownBounds,
605
+ this.options.coverTrigger ? 0 : triggerBRect.height
606
+ );
607
+
608
+ let verticalAlignment = 'top';
609
+ let horizontalAlignment = this.options.alignment;
610
+ idealYPos += this.options.coverTrigger ? 0 : triggerBRect.height;
611
+
612
+ // Reset isScrollable
613
+ this.isScrollable = false;
614
+
615
+ if (!alignments.top) {
616
+ if (alignments.bottom) {
617
+ verticalAlignment = 'bottom';
618
+ } else {
619
+ this.isScrollable = true;
620
+
621
+ // Determine which side has most space and cutoff at correct height
622
+ if (alignments.spaceOnTop > alignments.spaceOnBottom) {
623
+ verticalAlignment = 'bottom';
624
+ idealHeight += alignments.spaceOnTop;
625
+ idealYPos -= alignments.spaceOnTop;
626
+ } else {
627
+ idealHeight += alignments.spaceOnBottom;
628
+ }
629
+ }
630
+ }
631
+
632
+ // If preferred horizontal alignment is possible
633
+ if (!alignments[horizontalAlignment]) {
634
+ let oppositeAlignment = horizontalAlignment === 'left' ? 'right' : 'left';
635
+ if (alignments[oppositeAlignment]) {
636
+ horizontalAlignment = oppositeAlignment;
637
+ } else {
638
+ // Determine which side has most space and cutoff at correct height
639
+ if (alignments.spaceOnLeft > alignments.spaceOnRight) {
640
+ horizontalAlignment = 'right';
641
+ idealWidth += alignments.spaceOnLeft;
642
+ idealXPos -= alignments.spaceOnLeft;
643
+ } else {
644
+ horizontalAlignment = 'left';
645
+ idealWidth += alignments.spaceOnRight;
646
+ }
647
+ }
648
+ }
649
+
650
+ if (verticalAlignment === 'bottom') {
651
+ idealYPos =
652
+ idealYPos - dropdownBRect.height + (this.options.coverTrigger ? triggerBRect.height : 0);
653
+ }
654
+ if (horizontalAlignment === 'right') {
655
+ idealXPos = idealXPos - dropdownBRect.width + triggerBRect.width;
656
+ }
657
+ return {
658
+ x: idealXPos,
659
+ y: idealYPos,
660
+ verticalAlignment: verticalAlignment,
661
+ horizontalAlignment: horizontalAlignment,
662
+ height: idealHeight,
663
+ width: idealWidth
664
+ };
665
+ }
666
+
667
+ /**
668
+ * Animate in dropdown
669
+ */
670
+ _animateIn() {
671
+ anim.remove(this.dropdownEl);
672
+ anim({
673
+ targets: this.dropdownEl,
674
+ opacity: {
675
+ value: [0, 1],
676
+ easing: 'easeOutQuad'
677
+ },
678
+ scaleX: [0.3, 1],
679
+ scaleY: [0.3, 1],
680
+ duration: this.options.inDuration,
681
+ easing: 'easeOutQuint',
682
+ complete: (anim) => {
683
+ if (this.options.autoFocus) {
684
+ this.dropdownEl.focus();
685
+ }
686
+
687
+ // onOpenEnd callback (by reference)
688
+ if (typeof this.options.onOpenEnd === 'function') {
689
+ this.options.onOpenEnd.call(this, this.el);
690
+ }
691
+ }
692
+ });
693
+ }
694
+
695
+ /**
696
+ * Animate out dropdown
697
+ */
698
+ _animateOut() {
699
+ anim.remove(this.dropdownEl);
700
+ anim({
701
+ targets: this.dropdownEl,
702
+ opacity: {
703
+ value: 0,
704
+ easing: 'easeOutQuint'
705
+ },
706
+ scaleX: 0.3,
707
+ scaleY: 0.3,
708
+ duration: this.options.outDuration,
709
+ easing: 'easeOutQuint',
710
+ complete: (anim) => {
711
+ this._resetDropdownStyles();
712
+
713
+ // onCloseEnd callback (by reference)
714
+ if (typeof this.options.onCloseEnd === 'function') {
715
+ this.options.onCloseEnd.call(this, this.el);
716
+ }
717
+ }
718
+ });
719
+ }
720
+
721
+ /**
722
+ * Place dropdown
723
+ */
724
+ _placeDropdown() {
725
+ // Set width before calculating positionInfo
726
+ let idealWidth = this.options.constrainWidth
727
+ ? this.el.getBoundingClientRect().width
728
+ : this.dropdownEl.getBoundingClientRect().width;
729
+ this.dropdownEl.style.width = idealWidth + 'px';
730
+
731
+ let positionInfo = this._getDropdownPosition();
732
+ this.dropdownEl.style.left = positionInfo.x + 'px';
733
+ this.dropdownEl.style.top = positionInfo.y + 'px';
734
+ this.dropdownEl.style.height = positionInfo.height + 'px';
735
+ this.dropdownEl.style.width = positionInfo.width + 'px';
736
+ this.dropdownEl.style.transformOrigin = `${
737
+ positionInfo.horizontalAlignment === 'left' ? '0' : '100%'
738
+ } ${positionInfo.verticalAlignment === 'top' ? '0' : '100%'}`;
739
+ }
740
+
741
+ /**
742
+ * Open Dropdowns
743
+ */
744
+ open() {
745
+ if (this.isOpen) {
746
+ return;
747
+ }
748
+ this.isOpen = true;
749
+
750
+ // onOpen callback (by reference)
751
+ // if (typeof this.options.onOpen === 'function') {
752
+ // this.options.onOpen.call(this, this.el);
753
+ // }
754
+
755
+ var _this = this;
756
+ var listItems = '#' + _this.id + " li";
757
+ var menuItems = document.querySelectorAll(listItems);
758
+
759
+ // Loop through each <li> element
760
+ for (var i=0; i < menuItems.length; i++) {
761
+ // adding a event listener, mark selected menuItem by class active
762
+ menuItems[i].addEventListener('click', function(event) {
763
+ event.preventDefault();
764
+ for(var i=0; i < menuItems.length; i++) {
765
+ if (menuItems[i].classList.contains('active')) {
766
+ menuItems[i].classList.remove('active');
767
+ }
768
+ }
769
+ // add `active` to current clicked element
770
+ this.classList.add('active');
771
+ }, false);
772
+ }
773
+
774
+ // onOpen callback (ny name)
775
+ var _this = this;
776
+ if (this.options.onOpen) {
777
+ this.executeFunctionByName(this.options.onOpen, window, _this);
778
+ }
779
+
780
+ // Reset styles
781
+ this._resetDropdownStyles();
782
+ this.dropdownEl.style.display = 'block';
783
+
784
+ this._placeDropdown();
785
+ this._animateIn();
786
+ this._setupTemporaryEventHandlers();
787
+ }
788
+
789
+ /**
790
+ * Close Dropdowns
791
+ */
792
+ close() {
793
+ if (!this.isOpen) {
794
+ return;
795
+ }
796
+ this.isOpen = false;
797
+ this.focusedIndex = -1;
798
+
799
+ // onClose callback (by reference)
800
+ // if (typeof this.options.onClose === 'function') {
801
+ // this.options.onCloseStart.call(this, this.el);
802
+ // }
803
+
804
+ // onClose callback (by name)
805
+ var _this = this;
806
+ if (this.options.onClose) {
807
+ this.executeFunctionByName(this.options.onClose, window, _this);
808
+ }
809
+
810
+ this._animateOut();
811
+ this._removeTemporaryEventHandlers();
812
+
813
+ if (this.options.autoFocus) {
814
+ this.el.focus();
815
+ }
816
+ }
817
+
818
+ /**
819
+ * Recalculate dimensions
820
+ */
821
+ recalculateDimensions() {
822
+ if (this.isOpen) {
823
+ this.$dropdownEl.css({
824
+ width: '',
825
+ height: '',
826
+ left: '',
827
+ top: '',
828
+ 'transform-origin': ''
829
+ });
830
+ this._placeDropdown();
831
+ }
832
+ }
833
+ }
834
+
835
+ /**
836
+ * @static
837
+ * @memberof Dropdowns
838
+ */
839
+ Dropdowns._dropdowns = [];
840
+
841
+ // jadams, 2020-10-10: moved to j1 name space
842
+ //
843
+ // M.FloatingActionButton = FloatingActionButton;
844
+ j1.dropdowns = Dropdowns;
845
+
846
+ // jadams, 2020-10-10: check how to transform to jQuery
847
+ //
848
+
849
+ // Check for jQuery
850
+ //
851
+ j1.jQueryLoaded = !!window.jQuery;
852
+
853
+ if (j1.jQueryLoaded) {
854
+ j1.initializeJqueryWrapper (
855
+ Dropdowns,
856
+ 'dropdown',
857
+ 'M_Dropdown'
858
+ );
859
+ }
860
+
861
+ // jadams, 2020-10-10: TODO: check if anime could be a replacement
862
+ // for (huge) animate.css
863
+ // })($, j1.anime);
864
+ })(cash, j1.anime);