@materializecss/materialize 1.2.0 → 1.2.1

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 (82) hide show
  1. package/Gruntfile.js +722 -712
  2. package/LICENSE +21 -21
  3. package/README.md +91 -91
  4. package/dist/css/materialize.css +68 -135
  5. package/dist/css/materialize.min.css +12 -12
  6. package/dist/js/materialize.js +1112 -1112
  7. package/dist/js/materialize.min.js +6 -6
  8. package/extras/noUiSlider/nouislider.css +403 -403
  9. package/extras/noUiSlider/nouislider.js +2147 -2147
  10. package/js/anime.min.js +34 -34
  11. package/js/autocomplete.js +479 -479
  12. package/js/buttons.js +354 -354
  13. package/js/cards.js +40 -40
  14. package/js/carousel.js +732 -732
  15. package/js/cash.js +960 -960
  16. package/js/characterCounter.js +136 -136
  17. package/js/chips.js +486 -486
  18. package/js/collapsible.js +275 -275
  19. package/js/component.js +44 -44
  20. package/js/datepicker.js +983 -983
  21. package/js/dropdown.js +669 -669
  22. package/js/forms.js +285 -285
  23. package/js/global.js +428 -428
  24. package/js/materialbox.js +453 -453
  25. package/js/modal.js +382 -382
  26. package/js/parallax.js +138 -138
  27. package/js/pushpin.js +148 -148
  28. package/js/range.js +263 -263
  29. package/js/scrollspy.js +295 -295
  30. package/js/select.js +391 -391
  31. package/js/sidenav.js +583 -583
  32. package/js/slider.js +359 -359
  33. package/js/tabs.js +402 -402
  34. package/js/tapTarget.js +315 -315
  35. package/js/timepicker.js +712 -712
  36. package/js/toasts.js +325 -325
  37. package/js/tooltip.js +320 -320
  38. package/js/waves.js +614 -614
  39. package/package.json +87 -84
  40. package/sass/_style.scss +929 -929
  41. package/sass/components/_badges.scss +55 -55
  42. package/sass/components/_buttons.scss +322 -322
  43. package/sass/components/_cards.scss +195 -195
  44. package/sass/components/_carousel.scss +90 -90
  45. package/sass/components/_chips.scss +96 -96
  46. package/sass/components/_collapsible.scss +91 -91
  47. package/sass/components/_collection.scss +106 -106
  48. package/sass/components/_color-classes.scss +32 -32
  49. package/sass/components/_color-variables.scss +370 -370
  50. package/sass/components/_datepicker.scss +191 -191
  51. package/sass/components/_dropdown.scss +84 -84
  52. package/sass/components/_global.scss +646 -646
  53. package/sass/components/_grid.scss +158 -158
  54. package/sass/components/_icons-material-design.scss +5 -5
  55. package/sass/components/_materialbox.scss +42 -42
  56. package/sass/components/_modal.scss +97 -97
  57. package/sass/components/_navbar.scss +208 -208
  58. package/sass/components/_normalize.scss +447 -447
  59. package/sass/components/_preloader.scss +334 -334
  60. package/sass/components/_pulse.scss +34 -34
  61. package/sass/components/_sidenav.scss +214 -214
  62. package/sass/components/_slider.scss +91 -91
  63. package/sass/components/_table_of_contents.scss +33 -33
  64. package/sass/components/_tabs.scss +99 -99
  65. package/sass/components/_tapTarget.scss +103 -103
  66. package/sass/components/_timepicker.scss +199 -199
  67. package/sass/components/_toast.scss +58 -58
  68. package/sass/components/_tooltip.scss +32 -32
  69. package/sass/components/_transitions.scss +12 -12
  70. package/sass/components/_typography.scss +62 -62
  71. package/sass/components/_variables.scss +352 -352
  72. package/sass/components/_waves.scss +187 -187
  73. package/sass/components/forms/_checkboxes.scss +200 -200
  74. package/sass/components/forms/_file-input.scss +44 -44
  75. package/sass/components/forms/_forms.scss +22 -22
  76. package/sass/components/forms/_input-fields.scss +388 -388
  77. package/sass/components/forms/_radio-buttons.scss +115 -115
  78. package/sass/components/forms/_range.scss +161 -161
  79. package/sass/components/forms/_select.scss +199 -199
  80. package/sass/components/forms/_switches.scss +91 -91
  81. package/sass/ghpages-materialize.scss +7 -7
  82. package/sass/materialize.scss +42 -42
package/js/timepicker.js CHANGED
@@ -1,712 +1,712 @@
1
- (function($) {
2
- 'use strict';
3
-
4
- let _defaults = {
5
- dialRadius: 135,
6
- outerRadius: 105,
7
- innerRadius: 70,
8
- tickRadius: 20,
9
- duration: 350,
10
- container: null,
11
- defaultTime: 'now', // default time, 'now' or '13:14' e.g.
12
- fromNow: 0, // Millisecond offset from the defaultTime
13
- showClearBtn: false,
14
-
15
- // internationalization
16
- i18n: {
17
- cancel: 'Cancel',
18
- clear: 'Clear',
19
- done: 'Ok'
20
- },
21
-
22
- autoClose: false, // auto close when minute is selected
23
- twelveHour: true, // change to 12 hour AM/PM clock from 24 hour
24
- vibrate: true, // vibrate the device when dragging clock hand
25
-
26
- // Callbacks
27
- onOpenStart: null,
28
- onOpenEnd: null,
29
- onCloseStart: null,
30
- onCloseEnd: null,
31
- onSelect: null
32
- };
33
-
34
- /**
35
- * @class
36
- *
37
- */
38
- class Timepicker extends Component {
39
- constructor(el, options) {
40
- super(Timepicker, el, options);
41
-
42
- this.el.M_Timepicker = this;
43
-
44
- this.options = $.extend({}, Timepicker.defaults, options);
45
-
46
- this.id = M.guid();
47
- this._insertHTMLIntoDOM();
48
- this._setupModal();
49
- this._setupVariables();
50
- this._setupEventHandlers();
51
-
52
- this._clockSetup();
53
- this._pickerSetup();
54
- }
55
-
56
- static get defaults() {
57
- return _defaults;
58
- }
59
-
60
- static init(els, options) {
61
- return super.init(this, els, options);
62
- }
63
-
64
- static _addLeadingZero(num) {
65
- return (num < 10 ? '0' : '') + num;
66
- }
67
-
68
- static _createSVGEl(name) {
69
- let svgNS = 'http://www.w3.org/2000/svg';
70
- return document.createElementNS(svgNS, name);
71
- }
72
-
73
- /**
74
- * @typedef {Object} Point
75
- * @property {number} x The X Coordinate
76
- * @property {number} y The Y Coordinate
77
- */
78
-
79
- /**
80
- * Get x position of mouse or touch event
81
- * @param {Event} e
82
- * @return {Point} x and y location
83
- */
84
- static _Pos(e) {
85
- if (e.targetTouches && e.targetTouches.length >= 1) {
86
- return { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY };
87
- }
88
- // mouse event
89
- return { x: e.clientX, y: e.clientY };
90
- }
91
-
92
- /**
93
- * Get Instance
94
- */
95
- static getInstance(el) {
96
- let domElem = !!el.jquery ? el[0] : el;
97
- return domElem.M_Timepicker;
98
- }
99
-
100
- /**
101
- * Teardown component
102
- */
103
- destroy() {
104
- this._removeEventHandlers();
105
- this.modal.destroy();
106
- $(this.modalEl).remove();
107
- this.el.M_Timepicker = undefined;
108
- }
109
-
110
- /**
111
- * Setup Event Handlers
112
- */
113
- _setupEventHandlers() {
114
- this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
115
- this._handleInputClickBound = this._handleInputClick.bind(this);
116
- this._handleClockClickStartBound = this._handleClockClickStart.bind(this);
117
- this._handleDocumentClickMoveBound = this._handleDocumentClickMove.bind(this);
118
- this._handleDocumentClickEndBound = this._handleDocumentClickEnd.bind(this);
119
- this._inputFromTextFieldBound = this._handleTimeInputEnterKey.bind(this);
120
-
121
- this.el.addEventListener('click', this._handleInputClickBound);
122
- this.el.addEventListener('keydown', this._handleInputKeydownBound);
123
- this.plate.addEventListener('mousedown', this._handleClockClickStartBound);
124
- this.plate.addEventListener('touchstart', this._handleClockClickStartBound);
125
- this.digitalClock.addEventListener('keyup', this._inputFromTextFieldBound);
126
-
127
- $(this.inputHours).on('click', this.showView.bind(this, 'hours'));
128
- $(this.inputMinutes).on('click', this.showView.bind(this, 'minutes'));
129
- }
130
-
131
- _removeEventHandlers() {
132
- this.el.removeEventListener('click', this._handleInputClickBound);
133
- this.el.removeEventListener('keydown', this._handleInputKeydownBound);
134
- }
135
-
136
- _handleInputClick() {
137
- this.open();
138
- }
139
-
140
- _handleInputKeydown(e) {
141
- if (e.which === M.keys.ENTER) {
142
- e.preventDefault();
143
- this.open();
144
- }
145
- }
146
-
147
- _handleTimeInputEnterKey(e) {
148
- if (e.which === M.keys.ENTER) {
149
- e.preventDefault();
150
- this._inputFromTextField();
151
- }
152
- }
153
-
154
- _handleClockClickStart(e) {
155
- e.preventDefault();
156
- let clockPlateBR = this.plate.getBoundingClientRect();
157
- let offset = { x: clockPlateBR.left, y: clockPlateBR.top };
158
-
159
- this.x0 = offset.x + this.options.dialRadius;
160
- this.y0 = offset.y + this.options.dialRadius;
161
- this.moved = false;
162
- let clickPos = Timepicker._Pos(e);
163
- this.dx = clickPos.x - this.x0;
164
- this.dy = clickPos.y - this.y0;
165
-
166
- // Set clock hands
167
- this.setHand(this.dx, this.dy, false);
168
-
169
- // Mousemove on document
170
- document.addEventListener('mousemove', this._handleDocumentClickMoveBound);
171
- document.addEventListener('touchmove', this._handleDocumentClickMoveBound);
172
-
173
- // Mouseup on document
174
- document.addEventListener('mouseup', this._handleDocumentClickEndBound);
175
- document.addEventListener('touchend', this._handleDocumentClickEndBound);
176
- }
177
-
178
- _handleDocumentClickMove(e) {
179
- e.preventDefault();
180
- let clickPos = Timepicker._Pos(e);
181
- let x = clickPos.x - this.x0;
182
- let y = clickPos.y - this.y0;
183
- this.moved = true;
184
- this.setHand(x, y, false, true);
185
- }
186
-
187
- _handleDocumentClickEnd(e) {
188
- e.preventDefault();
189
- document.removeEventListener('mouseup', this._handleDocumentClickEndBound);
190
- document.removeEventListener('touchend', this._handleDocumentClickEndBound);
191
- let clickPos = Timepicker._Pos(e);
192
- let x = clickPos.x - this.x0;
193
- let y = clickPos.y - this.y0;
194
- if (this.moved && x === this.dx && y === this.dy) {
195
- this.setHand(x, y);
196
- }
197
-
198
- if (this.currentView === 'hours') {
199
- this.showView('minutes', this.options.duration / 2);
200
- } else if (this.options.autoClose) {
201
- $(this.minutesView).addClass('timepicker-dial-out');
202
- setTimeout(() => {
203
- this.done();
204
- }, this.options.duration / 2);
205
- }
206
-
207
- if (typeof this.options.onSelect === 'function') {
208
- this.options.onSelect.call(this, this.hours, this.minutes);
209
- }
210
-
211
- // Unbind mousemove event
212
- document.removeEventListener('mousemove', this._handleDocumentClickMoveBound);
213
- document.removeEventListener('touchmove', this._handleDocumentClickMoveBound);
214
- }
215
-
216
- _insertHTMLIntoDOM() {
217
- this.$modalEl = $(Timepicker._template);
218
- this.modalEl = this.$modalEl[0];
219
- this.modalEl.id = 'modal-' + this.id;
220
-
221
- // Append popover to input by default
222
- const optEl = this.options.container;
223
- let containerEl = optEl instanceof HTMLElement ? optEl : document.querySelector(optEl);
224
- if (this.options.container && !!containerEl) {
225
- this.$modalEl.appendTo(containerEl);
226
- } else {
227
- this.$modalEl.insertBefore(this.el);
228
- }
229
- }
230
-
231
- _setupModal() {
232
- this.modal = M.Modal.init(this.modalEl, {
233
- onOpenStart: this.options.onOpenStart,
234
- onOpenEnd: this.options.onOpenEnd,
235
- onCloseStart: this.options.onCloseStart,
236
- onCloseEnd: () => {
237
- if (typeof this.options.onCloseEnd === 'function') {
238
- this.options.onCloseEnd.call(this);
239
- }
240
- this.isOpen = false;
241
- }
242
- });
243
- }
244
-
245
- _setupVariables() {
246
- this.currentView = 'hours';
247
- this.vibrate = navigator.vibrate
248
- ? 'vibrate'
249
- : navigator.webkitVibrate
250
- ? 'webkitVibrate'
251
- : null;
252
-
253
- this._canvas = this.modalEl.querySelector('.timepicker-canvas');
254
- this.plate = this.modalEl.querySelector('.timepicker-plate');
255
- this.digitalClock = this.modalEl.querySelector('.timepicker-display-column');
256
-
257
- this.hoursView = this.modalEl.querySelector('.timepicker-hours');
258
- this.minutesView = this.modalEl.querySelector('.timepicker-minutes');
259
- this.inputHours = this.modalEl.querySelector('.timepicker-input-hours');
260
- this.inputMinutes = this.modalEl.querySelector('.timepicker-input-minutes');
261
- this.spanAmPm = this.modalEl.querySelector('.timepicker-span-am-pm');
262
- this.footer = this.modalEl.querySelector('.timepicker-footer');
263
- this.amOrPm = 'PM';
264
- }
265
-
266
- _pickerSetup() {
267
- let $clearBtn = $(
268
- `<button class="btn-flat timepicker-clear waves-effect" style="visibility: hidden;" type="button" tabindex="${
269
- this.options.twelveHour ? '3' : '1'
270
- }">${this.options.i18n.clear}</button>`
271
- )
272
- .appendTo(this.footer)
273
- .on('click', this.clear.bind(this));
274
- if (this.options.showClearBtn) {
275
- $clearBtn.css({ visibility: '' });
276
- }
277
-
278
- let confirmationBtnsContainer = $('<div class="confirmation-btns"></div>');
279
- $(
280
- '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
281
- (this.options.twelveHour ? '3' : '1') +
282
- '">' +
283
- this.options.i18n.cancel +
284
- '</button>'
285
- )
286
- .appendTo(confirmationBtnsContainer)
287
- .on('click', this.close.bind(this));
288
- $(
289
- '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
290
- (this.options.twelveHour ? '3' : '1') +
291
- '">' +
292
- this.options.i18n.done +
293
- '</button>'
294
- )
295
- .appendTo(confirmationBtnsContainer)
296
- .on('click', this.done.bind(this));
297
- confirmationBtnsContainer.appendTo(this.footer);
298
- }
299
-
300
- _clockSetup() {
301
- if (this.options.twelveHour) {
302
- this.$amBtn = $('<div class="am-btn">AM</div>');
303
- this.$pmBtn = $('<div class="pm-btn">PM</div>');
304
- this.$amBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
305
- this.$pmBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
306
- }
307
-
308
- this._buildHoursView();
309
- this._buildMinutesView();
310
- this._buildSVGClock();
311
- }
312
-
313
- _buildSVGClock() {
314
- // Draw clock hands and others
315
- let dialRadius = this.options.dialRadius;
316
- let tickRadius = this.options.tickRadius;
317
- let diameter = dialRadius * 2;
318
-
319
- let svg = Timepicker._createSVGEl('svg');
320
- svg.setAttribute('class', 'timepicker-svg');
321
- svg.setAttribute('width', diameter);
322
- svg.setAttribute('height', diameter);
323
- let g = Timepicker._createSVGEl('g');
324
- g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
325
- let bearing = Timepicker._createSVGEl('circle');
326
- bearing.setAttribute('class', 'timepicker-canvas-bearing');
327
- bearing.setAttribute('cx', 0);
328
- bearing.setAttribute('cy', 0);
329
- bearing.setAttribute('r', 4);
330
- let hand = Timepicker._createSVGEl('line');
331
- hand.setAttribute('x1', 0);
332
- hand.setAttribute('y1', 0);
333
- let bg = Timepicker._createSVGEl('circle');
334
- bg.setAttribute('class', 'timepicker-canvas-bg');
335
- bg.setAttribute('r', tickRadius);
336
- g.appendChild(hand);
337
- g.appendChild(bg);
338
- g.appendChild(bearing);
339
- svg.appendChild(g);
340
- this._canvas.appendChild(svg);
341
-
342
- this.hand = hand;
343
- this.bg = bg;
344
- this.bearing = bearing;
345
- this.g = g;
346
- }
347
-
348
- _buildHoursView() {
349
- let $tick = $('<div class="timepicker-tick"></div>');
350
- // Hours view
351
- if (this.options.twelveHour) {
352
- for (let i = 1; i < 13; i += 1) {
353
- let tick = $tick.clone();
354
- let radian = (i / 6) * Math.PI;
355
- let radius = this.options.outerRadius;
356
- tick.css({
357
- left:
358
- this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
359
- top:
360
- this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
361
- });
362
- tick.html(i === 0 ? '00' : i);
363
- this.hoursView.appendChild(tick[0]);
364
- // tick.on(mousedownEvent, mousedown);
365
- }
366
- } else {
367
- for (let i = 0; i < 24; i += 1) {
368
- let tick = $tick.clone();
369
- let radian = (i / 6) * Math.PI;
370
- let inner = i > 0 && i < 13;
371
- let radius = inner ? this.options.innerRadius : this.options.outerRadius;
372
- tick.css({
373
- left:
374
- this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
375
- top:
376
- this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
377
- });
378
- tick.html(i === 0 ? '00' : i);
379
- this.hoursView.appendChild(tick[0]);
380
- // tick.on(mousedownEvent, mousedown);
381
- }
382
- }
383
- }
384
-
385
- _buildMinutesView() {
386
- let $tick = $('<div class="timepicker-tick"></div>');
387
- // Minutes view
388
- for (let i = 0; i < 60; i += 5) {
389
- let tick = $tick.clone();
390
- let radian = (i / 30) * Math.PI;
391
- tick.css({
392
- left:
393
- this.options.dialRadius +
394
- Math.sin(radian) * this.options.outerRadius -
395
- this.options.tickRadius +
396
- 'px',
397
- top:
398
- this.options.dialRadius -
399
- Math.cos(radian) * this.options.outerRadius -
400
- this.options.tickRadius +
401
- 'px'
402
- });
403
- tick.html(Timepicker._addLeadingZero(i));
404
- this.minutesView.appendChild(tick[0]);
405
- }
406
- }
407
-
408
- _handleAmPmClick(e) {
409
- let $btnClicked = $(e.target);
410
- this.amOrPm = $btnClicked.hasClass('am-btn') ? 'AM' : 'PM';
411
- this._updateAmPmView();
412
- }
413
-
414
- _updateAmPmView() {
415
- if (this.options.twelveHour) {
416
- this.$amBtn.toggleClass('text-primary', this.amOrPm === 'AM');
417
- this.$pmBtn.toggleClass('text-primary', this.amOrPm === 'PM');
418
- }
419
- }
420
-
421
- _updateTimeFromInput() {
422
- // Get the time
423
- let value = ((this.el.value || this.options.defaultTime || '') + '').split(':');
424
- if (this.options.twelveHour && !(typeof value[1] === 'undefined')) {
425
- if (value[1].toUpperCase().indexOf('AM') > 0) {
426
- this.amOrPm = 'AM';
427
- } else {
428
- this.amOrPm = 'PM';
429
- }
430
- value[1] = value[1].replace('AM', '').replace('PM', '');
431
- }
432
- if (value[0] === 'now') {
433
- let now = new Date(+new Date() + this.options.fromNow);
434
- value = [now.getHours(), now.getMinutes()];
435
- if (this.options.twelveHour) {
436
- this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
437
- }
438
- }
439
- this.hours = +value[0] || 0;
440
- this.minutes = +value[1] || 0;
441
- this.inputHours.value = this.hours;
442
- this.inputMinutes.value = Timepicker._addLeadingZero(this.minutes);
443
-
444
- this._updateAmPmView();
445
- }
446
-
447
- showView(view, delay) {
448
- if (view === 'minutes' && $(this.hoursView).css('visibility') === 'visible') {
449
- // raiseCallback(this.options.beforeHourSelect);
450
- }
451
- let isHours = view === 'hours',
452
- nextView = isHours ? this.hoursView : this.minutesView,
453
- hideView = isHours ? this.minutesView : this.hoursView;
454
- this.currentView = view;
455
-
456
- $(this.inputHours).toggleClass('text-primary', isHours);
457
- $(this.inputMinutes).toggleClass('text-primary', !isHours);
458
-
459
- // Transition view
460
- hideView.classList.add('timepicker-dial-out');
461
- $(nextView)
462
- .css('visibility', 'visible')
463
- .removeClass('timepicker-dial-out');
464
-
465
- // Reset clock hand
466
- this.resetClock(delay);
467
-
468
- // After transitions ended
469
- clearTimeout(this.toggleViewTimer);
470
- this.toggleViewTimer = setTimeout(() => {
471
- $(hideView).css('visibility', 'hidden');
472
- }, this.options.duration);
473
- }
474
-
475
- resetClock(delay) {
476
- let view = this.currentView,
477
- value = this[view],
478
- isHours = view === 'hours',
479
- unit = Math.PI / (isHours ? 6 : 30),
480
- radian = value * unit,
481
- radius =
482
- isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius,
483
- x = Math.sin(radian) * radius,
484
- y = -Math.cos(radian) * radius,
485
- self = this;
486
-
487
- if (delay) {
488
- $(this.canvas).addClass('timepicker-canvas-out');
489
- setTimeout(() => {
490
- $(self.canvas).removeClass('timepicker-canvas-out');
491
- self.setHand(x, y);
492
- }, delay);
493
- } else {
494
- this.setHand(x, y);
495
- }
496
- }
497
-
498
- _inputFromTextField() {
499
- const isHours = this.currentView === 'hours';
500
-
501
- if (isHours) {
502
- const value = this['inputHours'].value;
503
-
504
- if (value > 0 && value < 13) {
505
- this.drawClockFromTimeInput(value, isHours);
506
-
507
- this.showView('minutes', this.options.duration / 2);
508
-
509
- this.hours = value;
510
- this.inputMinutes.focus();
511
- } else {
512
- const hour = new Date().getHours();
513
- this['inputHours'].value = hour % 12;
514
- }
515
- } else {
516
- const value = this['inputMinutes'].value;
517
-
518
- if (value >= 0 && value < 60) {
519
- this['inputMinutes'].value = Timepicker._addLeadingZero(value);
520
-
521
- this.drawClockFromTimeInput(value, isHours);
522
-
523
- this.minutes = value;
524
- this.modalEl.querySelector('.confirmation-btns :nth-child(2)').focus();
525
- } else {
526
- const minutes = new Date().getMinutes();
527
- this['inputMinutes'].value = Timepicker._addLeadingZero(minutes);
528
- }
529
- }
530
- }
531
-
532
- drawClockFromTimeInput(value, isHours) {
533
- const unit = Math.PI / (isHours ? 6 : 30);
534
- const radian = value * unit;
535
- let radius;
536
-
537
- if (this.options.twelveHour) {
538
- radius = this.options.outerRadius;
539
- }
540
-
541
- let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
542
- cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
543
- cx2 = Math.sin(radian) * radius,
544
- cy2 = -Math.cos(radian) * radius;
545
-
546
- this.hand.setAttribute('x2', cx1);
547
- this.hand.setAttribute('y2', cy1);
548
- this.bg.setAttribute('cx', cx2);
549
- this.bg.setAttribute('cy', cy2);
550
- }
551
-
552
- setHand(x, y, roundBy5) {
553
- let radian = Math.atan2(x, -y),
554
- isHours = this.currentView === 'hours',
555
- unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
556
- z = Math.sqrt(x * x + y * y),
557
- inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2,
558
- radius = inner ? this.options.innerRadius : this.options.outerRadius;
559
-
560
- if (this.options.twelveHour) {
561
- radius = this.options.outerRadius;
562
- }
563
-
564
- // Radian should in range [0, 2PI]
565
- if (radian < 0) {
566
- radian = Math.PI * 2 + radian;
567
- }
568
-
569
- // Get the round value
570
- let value = Math.round(radian / unit);
571
-
572
- // Get the round radian
573
- radian = value * unit;
574
-
575
- // Correct the hours or minutes
576
- if (this.options.twelveHour) {
577
- if (isHours) {
578
- if (value === 0) value = 12;
579
- } else {
580
- if (roundBy5) value *= 5;
581
- if (value === 60) value = 0;
582
- }
583
- } else {
584
- if (isHours) {
585
- if (value === 12) {
586
- value = 0;
587
- }
588
- value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
589
- } else {
590
- if (roundBy5) {
591
- value *= 5;
592
- }
593
- if (value === 60) {
594
- value = 0;
595
- }
596
- }
597
- }
598
-
599
- // Once hours or minutes changed, vibrate the device
600
- if (this[this.currentView] !== value) {
601
- if (this.vibrate && this.options.vibrate) {
602
- // Do not vibrate too frequently
603
- if (!this.vibrateTimer) {
604
- navigator[this.vibrate](10);
605
- this.vibrateTimer = setTimeout(() => {
606
- this.vibrateTimer = null;
607
- }, 100);
608
- }
609
- }
610
- }
611
-
612
- this[this.currentView] = value;
613
- if (isHours) {
614
- this['inputHours'].value = value;
615
- } else {
616
- this['inputMinutes'].value = Timepicker._addLeadingZero(value);
617
- }
618
-
619
- // Set clock hand and others' position
620
- let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
621
- cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
622
- cx2 = Math.sin(radian) * radius,
623
- cy2 = -Math.cos(radian) * radius;
624
- this.hand.setAttribute('x2', cx1);
625
- this.hand.setAttribute('y2', cy1);
626
- this.bg.setAttribute('cx', cx2);
627
- this.bg.setAttribute('cy', cy2);
628
- }
629
-
630
- open() {
631
- if (this.isOpen) {
632
- return;
633
- }
634
-
635
- this.isOpen = true;
636
- this._updateTimeFromInput();
637
- this.showView('hours');
638
-
639
- this.modal.open();
640
- }
641
-
642
- close() {
643
- if (!this.isOpen) {
644
- return;
645
- }
646
-
647
- this.isOpen = false;
648
- this.modal.close();
649
- }
650
-
651
- /**
652
- * Finish timepicker selection.
653
- */
654
- done(e, clearValue) {
655
- // Set input value
656
- let last = this.el.value;
657
- let value = clearValue
658
- ? ''
659
- : Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes);
660
- this.time = value;
661
- if (!clearValue && this.options.twelveHour) {
662
- value = `${value} ${this.amOrPm}`;
663
- }
664
- this.el.value = value;
665
-
666
- // Trigger change event
667
- if (value !== last) {
668
- this.$el.trigger('change');
669
- }
670
-
671
- this.close();
672
- this.el.focus();
673
- }
674
-
675
- clear() {
676
- this.done(null, true);
677
- }
678
- }
679
-
680
- Timepicker._template = [
681
- '<div class= "modal timepicker-modal">',
682
- '<div class="modal-content timepicker-container">',
683
- '<div class="timepicker-digital-display">',
684
- '<div class="timepicker-text-container">',
685
- '<div class="timepicker-display-column">',
686
- '<input type="text" maxlength="2" autofocus class="timepicker-input-hours text-primary" />',
687
- ':',
688
- '<input type="text" maxlength="2" class="timepicker-input-minutes" />',
689
- '</div>',
690
- '<div class="timepicker-display-column timepicker-display-am-pm">',
691
- '<div class="timepicker-span-am-pm"></div>',
692
- '</div>',
693
- '</div>',
694
- '</div>',
695
- '<div class="timepicker-analog-display">',
696
- '<div class="timepicker-plate">',
697
- '<div class="timepicker-canvas"></div>',
698
- '<div class="timepicker-dial timepicker-hours"></div>',
699
- '<div class="timepicker-dial timepicker-minutes timepicker-dial-out"></div>',
700
- '</div>',
701
- '<div class="timepicker-footer"></div>',
702
- '</div>',
703
- '</div>',
704
- '</div>'
705
- ].join('');
706
-
707
- M.Timepicker = Timepicker;
708
-
709
- if (M.jQueryLoaded) {
710
- M.initializeJqueryWrapper(Timepicker, 'timepicker', 'M_Timepicker');
711
- }
712
- })(cash);
1
+ (function($) {
2
+ 'use strict';
3
+
4
+ let _defaults = {
5
+ dialRadius: 135,
6
+ outerRadius: 105,
7
+ innerRadius: 70,
8
+ tickRadius: 20,
9
+ duration: 350,
10
+ container: null,
11
+ defaultTime: 'now', // default time, 'now' or '13:14' e.g.
12
+ fromNow: 0, // Millisecond offset from the defaultTime
13
+ showClearBtn: false,
14
+
15
+ // internationalization
16
+ i18n: {
17
+ cancel: 'Cancel',
18
+ clear: 'Clear',
19
+ done: 'Ok'
20
+ },
21
+
22
+ autoClose: false, // auto close when minute is selected
23
+ twelveHour: true, // change to 12 hour AM/PM clock from 24 hour
24
+ vibrate: true, // vibrate the device when dragging clock hand
25
+
26
+ // Callbacks
27
+ onOpenStart: null,
28
+ onOpenEnd: null,
29
+ onCloseStart: null,
30
+ onCloseEnd: null,
31
+ onSelect: null
32
+ };
33
+
34
+ /**
35
+ * @class
36
+ *
37
+ */
38
+ class Timepicker extends Component {
39
+ constructor(el, options) {
40
+ super(Timepicker, el, options);
41
+
42
+ this.el.M_Timepicker = this;
43
+
44
+ this.options = $.extend({}, Timepicker.defaults, options);
45
+
46
+ this.id = M.guid();
47
+ this._insertHTMLIntoDOM();
48
+ this._setupModal();
49
+ this._setupVariables();
50
+ this._setupEventHandlers();
51
+
52
+ this._clockSetup();
53
+ this._pickerSetup();
54
+ }
55
+
56
+ static get defaults() {
57
+ return _defaults;
58
+ }
59
+
60
+ static init(els, options) {
61
+ return super.init(this, els, options);
62
+ }
63
+
64
+ static _addLeadingZero(num) {
65
+ return (num < 10 ? '0' : '') + num;
66
+ }
67
+
68
+ static _createSVGEl(name) {
69
+ let svgNS = 'http://www.w3.org/2000/svg';
70
+ return document.createElementNS(svgNS, name);
71
+ }
72
+
73
+ /**
74
+ * @typedef {Object} Point
75
+ * @property {number} x The X Coordinate
76
+ * @property {number} y The Y Coordinate
77
+ */
78
+
79
+ /**
80
+ * Get x position of mouse or touch event
81
+ * @param {Event} e
82
+ * @return {Point} x and y location
83
+ */
84
+ static _Pos(e) {
85
+ if (e.targetTouches && e.targetTouches.length >= 1) {
86
+ return { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY };
87
+ }
88
+ // mouse event
89
+ return { x: e.clientX, y: e.clientY };
90
+ }
91
+
92
+ /**
93
+ * Get Instance
94
+ */
95
+ static getInstance(el) {
96
+ let domElem = !!el.jquery ? el[0] : el;
97
+ return domElem.M_Timepicker;
98
+ }
99
+
100
+ /**
101
+ * Teardown component
102
+ */
103
+ destroy() {
104
+ this._removeEventHandlers();
105
+ this.modal.destroy();
106
+ $(this.modalEl).remove();
107
+ this.el.M_Timepicker = undefined;
108
+ }
109
+
110
+ /**
111
+ * Setup Event Handlers
112
+ */
113
+ _setupEventHandlers() {
114
+ this._handleInputKeydownBound = this._handleInputKeydown.bind(this);
115
+ this._handleInputClickBound = this._handleInputClick.bind(this);
116
+ this._handleClockClickStartBound = this._handleClockClickStart.bind(this);
117
+ this._handleDocumentClickMoveBound = this._handleDocumentClickMove.bind(this);
118
+ this._handleDocumentClickEndBound = this._handleDocumentClickEnd.bind(this);
119
+ this._inputFromTextFieldBound = this._handleTimeInputEnterKey.bind(this);
120
+
121
+ this.el.addEventListener('click', this._handleInputClickBound);
122
+ this.el.addEventListener('keydown', this._handleInputKeydownBound);
123
+ this.plate.addEventListener('mousedown', this._handleClockClickStartBound);
124
+ this.plate.addEventListener('touchstart', this._handleClockClickStartBound);
125
+ this.digitalClock.addEventListener('keyup', this._inputFromTextFieldBound);
126
+
127
+ $(this.inputHours).on('click', this.showView.bind(this, 'hours'));
128
+ $(this.inputMinutes).on('click', this.showView.bind(this, 'minutes'));
129
+ }
130
+
131
+ _removeEventHandlers() {
132
+ this.el.removeEventListener('click', this._handleInputClickBound);
133
+ this.el.removeEventListener('keydown', this._handleInputKeydownBound);
134
+ }
135
+
136
+ _handleInputClick() {
137
+ this.open();
138
+ }
139
+
140
+ _handleInputKeydown(e) {
141
+ if (e.which === M.keys.ENTER) {
142
+ e.preventDefault();
143
+ this.open();
144
+ }
145
+ }
146
+
147
+ _handleTimeInputEnterKey(e) {
148
+ if (e.which === M.keys.ENTER) {
149
+ e.preventDefault();
150
+ this._inputFromTextField();
151
+ }
152
+ }
153
+
154
+ _handleClockClickStart(e) {
155
+ e.preventDefault();
156
+ let clockPlateBR = this.plate.getBoundingClientRect();
157
+ let offset = { x: clockPlateBR.left, y: clockPlateBR.top };
158
+
159
+ this.x0 = offset.x + this.options.dialRadius;
160
+ this.y0 = offset.y + this.options.dialRadius;
161
+ this.moved = false;
162
+ let clickPos = Timepicker._Pos(e);
163
+ this.dx = clickPos.x - this.x0;
164
+ this.dy = clickPos.y - this.y0;
165
+
166
+ // Set clock hands
167
+ this.setHand(this.dx, this.dy, false);
168
+
169
+ // Mousemove on document
170
+ document.addEventListener('mousemove', this._handleDocumentClickMoveBound);
171
+ document.addEventListener('touchmove', this._handleDocumentClickMoveBound);
172
+
173
+ // Mouseup on document
174
+ document.addEventListener('mouseup', this._handleDocumentClickEndBound);
175
+ document.addEventListener('touchend', this._handleDocumentClickEndBound);
176
+ }
177
+
178
+ _handleDocumentClickMove(e) {
179
+ e.preventDefault();
180
+ let clickPos = Timepicker._Pos(e);
181
+ let x = clickPos.x - this.x0;
182
+ let y = clickPos.y - this.y0;
183
+ this.moved = true;
184
+ this.setHand(x, y, false, true);
185
+ }
186
+
187
+ _handleDocumentClickEnd(e) {
188
+ e.preventDefault();
189
+ document.removeEventListener('mouseup', this._handleDocumentClickEndBound);
190
+ document.removeEventListener('touchend', this._handleDocumentClickEndBound);
191
+ let clickPos = Timepicker._Pos(e);
192
+ let x = clickPos.x - this.x0;
193
+ let y = clickPos.y - this.y0;
194
+ if (this.moved && x === this.dx && y === this.dy) {
195
+ this.setHand(x, y);
196
+ }
197
+
198
+ if (this.currentView === 'hours') {
199
+ this.showView('minutes', this.options.duration / 2);
200
+ } else if (this.options.autoClose) {
201
+ $(this.minutesView).addClass('timepicker-dial-out');
202
+ setTimeout(() => {
203
+ this.done();
204
+ }, this.options.duration / 2);
205
+ }
206
+
207
+ if (typeof this.options.onSelect === 'function') {
208
+ this.options.onSelect.call(this, this.hours, this.minutes);
209
+ }
210
+
211
+ // Unbind mousemove event
212
+ document.removeEventListener('mousemove', this._handleDocumentClickMoveBound);
213
+ document.removeEventListener('touchmove', this._handleDocumentClickMoveBound);
214
+ }
215
+
216
+ _insertHTMLIntoDOM() {
217
+ this.$modalEl = $(Timepicker._template);
218
+ this.modalEl = this.$modalEl[0];
219
+ this.modalEl.id = 'modal-' + this.id;
220
+
221
+ // Append popover to input by default
222
+ const optEl = this.options.container;
223
+ let containerEl = optEl instanceof HTMLElement ? optEl : document.querySelector(optEl);
224
+ if (this.options.container && !!containerEl) {
225
+ this.$modalEl.appendTo(containerEl);
226
+ } else {
227
+ this.$modalEl.insertBefore(this.el);
228
+ }
229
+ }
230
+
231
+ _setupModal() {
232
+ this.modal = M.Modal.init(this.modalEl, {
233
+ onOpenStart: this.options.onOpenStart,
234
+ onOpenEnd: this.options.onOpenEnd,
235
+ onCloseStart: this.options.onCloseStart,
236
+ onCloseEnd: () => {
237
+ if (typeof this.options.onCloseEnd === 'function') {
238
+ this.options.onCloseEnd.call(this);
239
+ }
240
+ this.isOpen = false;
241
+ }
242
+ });
243
+ }
244
+
245
+ _setupVariables() {
246
+ this.currentView = 'hours';
247
+ this.vibrate = navigator.vibrate
248
+ ? 'vibrate'
249
+ : navigator.webkitVibrate
250
+ ? 'webkitVibrate'
251
+ : null;
252
+
253
+ this._canvas = this.modalEl.querySelector('.timepicker-canvas');
254
+ this.plate = this.modalEl.querySelector('.timepicker-plate');
255
+ this.digitalClock = this.modalEl.querySelector('.timepicker-display-column');
256
+
257
+ this.hoursView = this.modalEl.querySelector('.timepicker-hours');
258
+ this.minutesView = this.modalEl.querySelector('.timepicker-minutes');
259
+ this.inputHours = this.modalEl.querySelector('.timepicker-input-hours');
260
+ this.inputMinutes = this.modalEl.querySelector('.timepicker-input-minutes');
261
+ this.spanAmPm = this.modalEl.querySelector('.timepicker-span-am-pm');
262
+ this.footer = this.modalEl.querySelector('.timepicker-footer');
263
+ this.amOrPm = 'PM';
264
+ }
265
+
266
+ _pickerSetup() {
267
+ let $clearBtn = $(
268
+ `<button class="btn-flat timepicker-clear waves-effect" style="visibility: hidden;" type="button" tabindex="${
269
+ this.options.twelveHour ? '3' : '1'
270
+ }">${this.options.i18n.clear}</button>`
271
+ )
272
+ .appendTo(this.footer)
273
+ .on('click', this.clear.bind(this));
274
+ if (this.options.showClearBtn) {
275
+ $clearBtn.css({ visibility: '' });
276
+ }
277
+
278
+ let confirmationBtnsContainer = $('<div class="confirmation-btns"></div>');
279
+ $(
280
+ '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
281
+ (this.options.twelveHour ? '3' : '1') +
282
+ '">' +
283
+ this.options.i18n.cancel +
284
+ '</button>'
285
+ )
286
+ .appendTo(confirmationBtnsContainer)
287
+ .on('click', this.close.bind(this));
288
+ $(
289
+ '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
290
+ (this.options.twelveHour ? '3' : '1') +
291
+ '">' +
292
+ this.options.i18n.done +
293
+ '</button>'
294
+ )
295
+ .appendTo(confirmationBtnsContainer)
296
+ .on('click', this.done.bind(this));
297
+ confirmationBtnsContainer.appendTo(this.footer);
298
+ }
299
+
300
+ _clockSetup() {
301
+ if (this.options.twelveHour) {
302
+ this.$amBtn = $('<div class="am-btn">AM</div>');
303
+ this.$pmBtn = $('<div class="pm-btn">PM</div>');
304
+ this.$amBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
305
+ this.$pmBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
306
+ }
307
+
308
+ this._buildHoursView();
309
+ this._buildMinutesView();
310
+ this._buildSVGClock();
311
+ }
312
+
313
+ _buildSVGClock() {
314
+ // Draw clock hands and others
315
+ let dialRadius = this.options.dialRadius;
316
+ let tickRadius = this.options.tickRadius;
317
+ let diameter = dialRadius * 2;
318
+
319
+ let svg = Timepicker._createSVGEl('svg');
320
+ svg.setAttribute('class', 'timepicker-svg');
321
+ svg.setAttribute('width', diameter);
322
+ svg.setAttribute('height', diameter);
323
+ let g = Timepicker._createSVGEl('g');
324
+ g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
325
+ let bearing = Timepicker._createSVGEl('circle');
326
+ bearing.setAttribute('class', 'timepicker-canvas-bearing');
327
+ bearing.setAttribute('cx', 0);
328
+ bearing.setAttribute('cy', 0);
329
+ bearing.setAttribute('r', 4);
330
+ let hand = Timepicker._createSVGEl('line');
331
+ hand.setAttribute('x1', 0);
332
+ hand.setAttribute('y1', 0);
333
+ let bg = Timepicker._createSVGEl('circle');
334
+ bg.setAttribute('class', 'timepicker-canvas-bg');
335
+ bg.setAttribute('r', tickRadius);
336
+ g.appendChild(hand);
337
+ g.appendChild(bg);
338
+ g.appendChild(bearing);
339
+ svg.appendChild(g);
340
+ this._canvas.appendChild(svg);
341
+
342
+ this.hand = hand;
343
+ this.bg = bg;
344
+ this.bearing = bearing;
345
+ this.g = g;
346
+ }
347
+
348
+ _buildHoursView() {
349
+ let $tick = $('<div class="timepicker-tick"></div>');
350
+ // Hours view
351
+ if (this.options.twelveHour) {
352
+ for (let i = 1; i < 13; i += 1) {
353
+ let tick = $tick.clone();
354
+ let radian = (i / 6) * Math.PI;
355
+ let radius = this.options.outerRadius;
356
+ tick.css({
357
+ left:
358
+ this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
359
+ top:
360
+ this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
361
+ });
362
+ tick.html(i === 0 ? '00' : i);
363
+ this.hoursView.appendChild(tick[0]);
364
+ // tick.on(mousedownEvent, mousedown);
365
+ }
366
+ } else {
367
+ for (let i = 0; i < 24; i += 1) {
368
+ let tick = $tick.clone();
369
+ let radian = (i / 6) * Math.PI;
370
+ let inner = i > 0 && i < 13;
371
+ let radius = inner ? this.options.innerRadius : this.options.outerRadius;
372
+ tick.css({
373
+ left:
374
+ this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
375
+ top:
376
+ this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
377
+ });
378
+ tick.html(i === 0 ? '00' : i);
379
+ this.hoursView.appendChild(tick[0]);
380
+ // tick.on(mousedownEvent, mousedown);
381
+ }
382
+ }
383
+ }
384
+
385
+ _buildMinutesView() {
386
+ let $tick = $('<div class="timepicker-tick"></div>');
387
+ // Minutes view
388
+ for (let i = 0; i < 60; i += 5) {
389
+ let tick = $tick.clone();
390
+ let radian = (i / 30) * Math.PI;
391
+ tick.css({
392
+ left:
393
+ this.options.dialRadius +
394
+ Math.sin(radian) * this.options.outerRadius -
395
+ this.options.tickRadius +
396
+ 'px',
397
+ top:
398
+ this.options.dialRadius -
399
+ Math.cos(radian) * this.options.outerRadius -
400
+ this.options.tickRadius +
401
+ 'px'
402
+ });
403
+ tick.html(Timepicker._addLeadingZero(i));
404
+ this.minutesView.appendChild(tick[0]);
405
+ }
406
+ }
407
+
408
+ _handleAmPmClick(e) {
409
+ let $btnClicked = $(e.target);
410
+ this.amOrPm = $btnClicked.hasClass('am-btn') ? 'AM' : 'PM';
411
+ this._updateAmPmView();
412
+ }
413
+
414
+ _updateAmPmView() {
415
+ if (this.options.twelveHour) {
416
+ this.$amBtn.toggleClass('text-primary', this.amOrPm === 'AM');
417
+ this.$pmBtn.toggleClass('text-primary', this.amOrPm === 'PM');
418
+ }
419
+ }
420
+
421
+ _updateTimeFromInput() {
422
+ // Get the time
423
+ let value = ((this.el.value || this.options.defaultTime || '') + '').split(':');
424
+ if (this.options.twelveHour && !(typeof value[1] === 'undefined')) {
425
+ if (value[1].toUpperCase().indexOf('AM') > 0) {
426
+ this.amOrPm = 'AM';
427
+ } else {
428
+ this.amOrPm = 'PM';
429
+ }
430
+ value[1] = value[1].replace('AM', '').replace('PM', '');
431
+ }
432
+ if (value[0] === 'now') {
433
+ let now = new Date(+new Date() + this.options.fromNow);
434
+ value = [now.getHours(), now.getMinutes()];
435
+ if (this.options.twelveHour) {
436
+ this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
437
+ }
438
+ }
439
+ this.hours = +value[0] || 0;
440
+ this.minutes = +value[1] || 0;
441
+ this.inputHours.value = this.hours;
442
+ this.inputMinutes.value = Timepicker._addLeadingZero(this.minutes);
443
+
444
+ this._updateAmPmView();
445
+ }
446
+
447
+ showView(view, delay) {
448
+ if (view === 'minutes' && $(this.hoursView).css('visibility') === 'visible') {
449
+ // raiseCallback(this.options.beforeHourSelect);
450
+ }
451
+ let isHours = view === 'hours',
452
+ nextView = isHours ? this.hoursView : this.minutesView,
453
+ hideView = isHours ? this.minutesView : this.hoursView;
454
+ this.currentView = view;
455
+
456
+ $(this.inputHours).toggleClass('text-primary', isHours);
457
+ $(this.inputMinutes).toggleClass('text-primary', !isHours);
458
+
459
+ // Transition view
460
+ hideView.classList.add('timepicker-dial-out');
461
+ $(nextView)
462
+ .css('visibility', 'visible')
463
+ .removeClass('timepicker-dial-out');
464
+
465
+ // Reset clock hand
466
+ this.resetClock(delay);
467
+
468
+ // After transitions ended
469
+ clearTimeout(this.toggleViewTimer);
470
+ this.toggleViewTimer = setTimeout(() => {
471
+ $(hideView).css('visibility', 'hidden');
472
+ }, this.options.duration);
473
+ }
474
+
475
+ resetClock(delay) {
476
+ let view = this.currentView,
477
+ value = this[view],
478
+ isHours = view === 'hours',
479
+ unit = Math.PI / (isHours ? 6 : 30),
480
+ radian = value * unit,
481
+ radius =
482
+ isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius,
483
+ x = Math.sin(radian) * radius,
484
+ y = -Math.cos(radian) * radius,
485
+ self = this;
486
+
487
+ if (delay) {
488
+ $(this.canvas).addClass('timepicker-canvas-out');
489
+ setTimeout(() => {
490
+ $(self.canvas).removeClass('timepicker-canvas-out');
491
+ self.setHand(x, y);
492
+ }, delay);
493
+ } else {
494
+ this.setHand(x, y);
495
+ }
496
+ }
497
+
498
+ _inputFromTextField() {
499
+ const isHours = this.currentView === 'hours';
500
+
501
+ if (isHours) {
502
+ const value = this['inputHours'].value;
503
+
504
+ if (value > 0 && value < 13) {
505
+ this.drawClockFromTimeInput(value, isHours);
506
+
507
+ this.showView('minutes', this.options.duration / 2);
508
+
509
+ this.hours = value;
510
+ this.inputMinutes.focus();
511
+ } else {
512
+ const hour = new Date().getHours();
513
+ this['inputHours'].value = hour % 12;
514
+ }
515
+ } else {
516
+ const value = this['inputMinutes'].value;
517
+
518
+ if (value >= 0 && value < 60) {
519
+ this['inputMinutes'].value = Timepicker._addLeadingZero(value);
520
+
521
+ this.drawClockFromTimeInput(value, isHours);
522
+
523
+ this.minutes = value;
524
+ this.modalEl.querySelector('.confirmation-btns :nth-child(2)').focus();
525
+ } else {
526
+ const minutes = new Date().getMinutes();
527
+ this['inputMinutes'].value = Timepicker._addLeadingZero(minutes);
528
+ }
529
+ }
530
+ }
531
+
532
+ drawClockFromTimeInput(value, isHours) {
533
+ const unit = Math.PI / (isHours ? 6 : 30);
534
+ const radian = value * unit;
535
+ let radius;
536
+
537
+ if (this.options.twelveHour) {
538
+ radius = this.options.outerRadius;
539
+ }
540
+
541
+ let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
542
+ cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
543
+ cx2 = Math.sin(radian) * radius,
544
+ cy2 = -Math.cos(radian) * radius;
545
+
546
+ this.hand.setAttribute('x2', cx1);
547
+ this.hand.setAttribute('y2', cy1);
548
+ this.bg.setAttribute('cx', cx2);
549
+ this.bg.setAttribute('cy', cy2);
550
+ }
551
+
552
+ setHand(x, y, roundBy5) {
553
+ let radian = Math.atan2(x, -y),
554
+ isHours = this.currentView === 'hours',
555
+ unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
556
+ z = Math.sqrt(x * x + y * y),
557
+ inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2,
558
+ radius = inner ? this.options.innerRadius : this.options.outerRadius;
559
+
560
+ if (this.options.twelveHour) {
561
+ radius = this.options.outerRadius;
562
+ }
563
+
564
+ // Radian should in range [0, 2PI]
565
+ if (radian < 0) {
566
+ radian = Math.PI * 2 + radian;
567
+ }
568
+
569
+ // Get the round value
570
+ let value = Math.round(radian / unit);
571
+
572
+ // Get the round radian
573
+ radian = value * unit;
574
+
575
+ // Correct the hours or minutes
576
+ if (this.options.twelveHour) {
577
+ if (isHours) {
578
+ if (value === 0) value = 12;
579
+ } else {
580
+ if (roundBy5) value *= 5;
581
+ if (value === 60) value = 0;
582
+ }
583
+ } else {
584
+ if (isHours) {
585
+ if (value === 12) {
586
+ value = 0;
587
+ }
588
+ value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
589
+ } else {
590
+ if (roundBy5) {
591
+ value *= 5;
592
+ }
593
+ if (value === 60) {
594
+ value = 0;
595
+ }
596
+ }
597
+ }
598
+
599
+ // Once hours or minutes changed, vibrate the device
600
+ if (this[this.currentView] !== value) {
601
+ if (this.vibrate && this.options.vibrate) {
602
+ // Do not vibrate too frequently
603
+ if (!this.vibrateTimer) {
604
+ navigator[this.vibrate](10);
605
+ this.vibrateTimer = setTimeout(() => {
606
+ this.vibrateTimer = null;
607
+ }, 100);
608
+ }
609
+ }
610
+ }
611
+
612
+ this[this.currentView] = value;
613
+ if (isHours) {
614
+ this['inputHours'].value = value;
615
+ } else {
616
+ this['inputMinutes'].value = Timepicker._addLeadingZero(value);
617
+ }
618
+
619
+ // Set clock hand and others' position
620
+ let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
621
+ cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
622
+ cx2 = Math.sin(radian) * radius,
623
+ cy2 = -Math.cos(radian) * radius;
624
+ this.hand.setAttribute('x2', cx1);
625
+ this.hand.setAttribute('y2', cy1);
626
+ this.bg.setAttribute('cx', cx2);
627
+ this.bg.setAttribute('cy', cy2);
628
+ }
629
+
630
+ open() {
631
+ if (this.isOpen) {
632
+ return;
633
+ }
634
+
635
+ this.isOpen = true;
636
+ this._updateTimeFromInput();
637
+ this.showView('hours');
638
+
639
+ this.modal.open();
640
+ }
641
+
642
+ close() {
643
+ if (!this.isOpen) {
644
+ return;
645
+ }
646
+
647
+ this.isOpen = false;
648
+ this.modal.close();
649
+ }
650
+
651
+ /**
652
+ * Finish timepicker selection.
653
+ */
654
+ done(e, clearValue) {
655
+ // Set input value
656
+ let last = this.el.value;
657
+ let value = clearValue
658
+ ? ''
659
+ : Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes);
660
+ this.time = value;
661
+ if (!clearValue && this.options.twelveHour) {
662
+ value = `${value} ${this.amOrPm}`;
663
+ }
664
+ this.el.value = value;
665
+
666
+ // Trigger change event
667
+ if (value !== last) {
668
+ this.$el.trigger('change');
669
+ }
670
+
671
+ this.close();
672
+ this.el.focus();
673
+ }
674
+
675
+ clear() {
676
+ this.done(null, true);
677
+ }
678
+ }
679
+
680
+ Timepicker._template = [
681
+ '<div class= "modal timepicker-modal">',
682
+ '<div class="modal-content timepicker-container">',
683
+ '<div class="timepicker-digital-display">',
684
+ '<div class="timepicker-text-container">',
685
+ '<div class="timepicker-display-column">',
686
+ '<input type="text" maxlength="2" autofocus class="timepicker-input-hours text-primary" />',
687
+ ':',
688
+ '<input type="text" maxlength="2" class="timepicker-input-minutes" />',
689
+ '</div>',
690
+ '<div class="timepicker-display-column timepicker-display-am-pm">',
691
+ '<div class="timepicker-span-am-pm"></div>',
692
+ '</div>',
693
+ '</div>',
694
+ '</div>',
695
+ '<div class="timepicker-analog-display">',
696
+ '<div class="timepicker-plate">',
697
+ '<div class="timepicker-canvas"></div>',
698
+ '<div class="timepicker-dial timepicker-hours"></div>',
699
+ '<div class="timepicker-dial timepicker-minutes timepicker-dial-out"></div>',
700
+ '</div>',
701
+ '<div class="timepicker-footer"></div>',
702
+ '</div>',
703
+ '</div>',
704
+ '</div>'
705
+ ].join('');
706
+
707
+ M.Timepicker = Timepicker;
708
+
709
+ if (M.jQueryLoaded) {
710
+ M.initializeJqueryWrapper(Timepicker, 'timepicker', 'M_Timepicker');
711
+ }
712
+ })(cash);