kmaterialize 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,647 @@
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
+
120
+ this.el.addEventListener('click', this._handleInputClickBound);
121
+ this.el.addEventListener('keydown', this._handleInputKeydownBound);
122
+ this.plate.addEventListener('mousedown', this._handleClockClickStartBound);
123
+ this.plate.addEventListener('touchstart', this._handleClockClickStartBound);
124
+
125
+ $(this.spanHours).on('click', this.showView.bind(this, 'hours'));
126
+ $(this.spanMinutes).on('click', this.showView.bind(this, 'minutes'));
127
+ }
128
+
129
+ _removeEventHandlers() {
130
+ this.el.removeEventListener('click', this._handleInputClickBound);
131
+ this.el.removeEventListener('keydown', this._handleInputKeydownBound);
132
+ }
133
+
134
+ _handleInputClick() {
135
+ this.open();
136
+ }
137
+
138
+ _handleInputKeydown(e) {
139
+ if (e.which === M.keys.ENTER) {
140
+ e.preventDefault();
141
+ this.open();
142
+ }
143
+ }
144
+
145
+ _handleClockClickStart(e) {
146
+ e.preventDefault();
147
+ let clockPlateBR = this.plate.getBoundingClientRect();
148
+ let offset = { x: clockPlateBR.left, y: clockPlateBR.top };
149
+
150
+ this.x0 = offset.x + this.options.dialRadius;
151
+ this.y0 = offset.y + this.options.dialRadius;
152
+ this.moved = false;
153
+ let clickPos = Timepicker._Pos(e);
154
+ this.dx = clickPos.x - this.x0;
155
+ this.dy = clickPos.y - this.y0;
156
+
157
+ // Set clock hands
158
+ this.setHand(this.dx, this.dy, false);
159
+
160
+ // Mousemove on document
161
+ document.addEventListener('mousemove', this._handleDocumentClickMoveBound);
162
+ document.addEventListener('touchmove', this._handleDocumentClickMoveBound);
163
+
164
+ // Mouseup on document
165
+ document.addEventListener('mouseup', this._handleDocumentClickEndBound);
166
+ document.addEventListener('touchend', this._handleDocumentClickEndBound);
167
+ }
168
+
169
+ _handleDocumentClickMove(e) {
170
+ e.preventDefault();
171
+ let clickPos = Timepicker._Pos(e);
172
+ let x = clickPos.x - this.x0;
173
+ let y = clickPos.y - this.y0;
174
+ this.moved = true;
175
+ this.setHand(x, y, false, true);
176
+ }
177
+
178
+ _handleDocumentClickEnd(e) {
179
+ e.preventDefault();
180
+ document.removeEventListener('mouseup', this._handleDocumentClickEndBound);
181
+ document.removeEventListener('touchend', this._handleDocumentClickEndBound);
182
+ let clickPos = Timepicker._Pos(e);
183
+ let x = clickPos.x - this.x0;
184
+ let y = clickPos.y - this.y0;
185
+ if (this.moved && x === this.dx && y === this.dy) {
186
+ this.setHand(x, y);
187
+ }
188
+
189
+ if (this.currentView === 'hours') {
190
+ this.showView('minutes', this.options.duration / 2);
191
+ } else if (this.options.autoClose) {
192
+ $(this.minutesView).addClass('timepicker-dial-out');
193
+ setTimeout(() => {
194
+ this.done();
195
+ }, this.options.duration / 2);
196
+ }
197
+
198
+ if (typeof this.options.onSelect === 'function') {
199
+ this.options.onSelect.call(this, this.hours, this.minutes);
200
+ }
201
+
202
+ // Unbind mousemove event
203
+ document.removeEventListener('mousemove', this._handleDocumentClickMoveBound);
204
+ document.removeEventListener('touchmove', this._handleDocumentClickMoveBound);
205
+ }
206
+
207
+ _insertHTMLIntoDOM() {
208
+ this.$modalEl = $(Timepicker._template);
209
+ this.modalEl = this.$modalEl[0];
210
+ this.modalEl.id = 'modal-' + this.id;
211
+
212
+ // Append popover to input by default
213
+ let containerEl = document.querySelector(this.options.container);
214
+ if (this.options.container && !!containerEl) {
215
+ this.$modalEl.appendTo(containerEl);
216
+ } else {
217
+ this.$modalEl.insertBefore(this.el);
218
+ }
219
+ }
220
+
221
+ _setupModal() {
222
+ this.modal = M.Modal.init(this.modalEl, {
223
+ onOpenStart: this.options.onOpenStart,
224
+ onOpenEnd: this.options.onOpenEnd,
225
+ onCloseStart: this.options.onCloseStart,
226
+ onCloseEnd: () => {
227
+ if (typeof this.options.onCloseEnd === 'function') {
228
+ this.options.onCloseEnd.call(this);
229
+ }
230
+ this.isOpen = false;
231
+ }
232
+ });
233
+ }
234
+
235
+ _setupVariables() {
236
+ this.currentView = 'hours';
237
+ this.vibrate = navigator.vibrate
238
+ ? 'vibrate'
239
+ : navigator.webkitVibrate
240
+ ? 'webkitVibrate'
241
+ : null;
242
+
243
+ this._canvas = this.modalEl.querySelector('.timepicker-canvas');
244
+ this.plate = this.modalEl.querySelector('.timepicker-plate');
245
+
246
+ this.hoursView = this.modalEl.querySelector('.timepicker-hours');
247
+ this.minutesView = this.modalEl.querySelector('.timepicker-minutes');
248
+ this.spanHours = this.modalEl.querySelector('.timepicker-span-hours');
249
+ this.spanMinutes = this.modalEl.querySelector('.timepicker-span-minutes');
250
+ this.spanAmPm = this.modalEl.querySelector('.timepicker-span-am-pm');
251
+ this.footer = this.modalEl.querySelector('.timepicker-footer');
252
+ this.amOrPm = 'PM';
253
+ }
254
+
255
+ _pickerSetup() {
256
+ let $clearBtn = $(
257
+ `<button class="btn-flat timepicker-clear waves-effect" style="visibility: hidden;" type="button" tabindex="${
258
+ this.options.twelveHour ? '3' : '1'
259
+ }">${this.options.i18n.clear}</button>`
260
+ )
261
+ .appendTo(this.footer)
262
+ .on('click', this.clear.bind(this));
263
+ if (this.options.showClearBtn) {
264
+ $clearBtn.css({ visibility: '' });
265
+ }
266
+
267
+ let confirmationBtnsContainer = $('<div class="confirmation-btns"></div>');
268
+ $(
269
+ '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
270
+ (this.options.twelveHour ? '3' : '1') +
271
+ '">' +
272
+ this.options.i18n.cancel +
273
+ '</button>'
274
+ )
275
+ .appendTo(confirmationBtnsContainer)
276
+ .on('click', this.close.bind(this));
277
+ $(
278
+ '<button class="btn-flat timepicker-close waves-effect" type="button" tabindex="' +
279
+ (this.options.twelveHour ? '3' : '1') +
280
+ '">' +
281
+ this.options.i18n.done +
282
+ '</button>'
283
+ )
284
+ .appendTo(confirmationBtnsContainer)
285
+ .on('click', this.done.bind(this));
286
+ confirmationBtnsContainer.appendTo(this.footer);
287
+ }
288
+
289
+ _clockSetup() {
290
+ if (this.options.twelveHour) {
291
+ this.$amBtn = $('<div class="am-btn">AM</div>');
292
+ this.$pmBtn = $('<div class="pm-btn">PM</div>');
293
+ this.$amBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
294
+ this.$pmBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm);
295
+ }
296
+
297
+ this._buildHoursView();
298
+ this._buildMinutesView();
299
+ this._buildSVGClock();
300
+ }
301
+
302
+ _buildSVGClock() {
303
+ // Draw clock hands and others
304
+ let dialRadius = this.options.dialRadius;
305
+ let tickRadius = this.options.tickRadius;
306
+ let diameter = dialRadius * 2;
307
+
308
+ let svg = Timepicker._createSVGEl('svg');
309
+ svg.setAttribute('class', 'timepicker-svg');
310
+ svg.setAttribute('width', diameter);
311
+ svg.setAttribute('height', diameter);
312
+ let g = Timepicker._createSVGEl('g');
313
+ g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
314
+ let bearing = Timepicker._createSVGEl('circle');
315
+ bearing.setAttribute('class', 'timepicker-canvas-bearing');
316
+ bearing.setAttribute('cx', 0);
317
+ bearing.setAttribute('cy', 0);
318
+ bearing.setAttribute('r', 4);
319
+ let hand = Timepicker._createSVGEl('line');
320
+ hand.setAttribute('x1', 0);
321
+ hand.setAttribute('y1', 0);
322
+ let bg = Timepicker._createSVGEl('circle');
323
+ bg.setAttribute('class', 'timepicker-canvas-bg');
324
+ bg.setAttribute('r', tickRadius);
325
+ g.appendChild(hand);
326
+ g.appendChild(bg);
327
+ g.appendChild(bearing);
328
+ svg.appendChild(g);
329
+ this._canvas.appendChild(svg);
330
+
331
+ this.hand = hand;
332
+ this.bg = bg;
333
+ this.bearing = bearing;
334
+ this.g = g;
335
+ }
336
+
337
+ _buildHoursView() {
338
+ let $tick = $('<div class="timepicker-tick"></div>');
339
+ // Hours view
340
+ if (this.options.twelveHour) {
341
+ for (let i = 1; i < 13; i += 1) {
342
+ let tick = $tick.clone();
343
+ let radian = i / 6 * Math.PI;
344
+ let radius = this.options.outerRadius;
345
+ tick.css({
346
+ left:
347
+ this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
348
+ top:
349
+ this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
350
+ });
351
+ tick.html(i === 0 ? '00' : i);
352
+ this.hoursView.appendChild(tick[0]);
353
+ // tick.on(mousedownEvent, mousedown);
354
+ }
355
+ } else {
356
+ for (let i = 0; i < 24; i += 1) {
357
+ let tick = $tick.clone();
358
+ let radian = i / 6 * Math.PI;
359
+ let inner = i > 0 && i < 13;
360
+ let radius = inner ? this.options.innerRadius : this.options.outerRadius;
361
+ tick.css({
362
+ left:
363
+ this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px',
364
+ top:
365
+ this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px'
366
+ });
367
+ tick.html(i === 0 ? '00' : i);
368
+ this.hoursView.appendChild(tick[0]);
369
+ // tick.on(mousedownEvent, mousedown);
370
+ }
371
+ }
372
+ }
373
+
374
+ _buildMinutesView() {
375
+ let $tick = $('<div class="timepicker-tick"></div>');
376
+ // Minutes view
377
+ for (let i = 0; i < 60; i += 5) {
378
+ let tick = $tick.clone();
379
+ let radian = i / 30 * Math.PI;
380
+ tick.css({
381
+ left:
382
+ this.options.dialRadius +
383
+ Math.sin(radian) * this.options.outerRadius -
384
+ this.options.tickRadius +
385
+ 'px',
386
+ top:
387
+ this.options.dialRadius -
388
+ Math.cos(radian) * this.options.outerRadius -
389
+ this.options.tickRadius +
390
+ 'px'
391
+ });
392
+ tick.html(Timepicker._addLeadingZero(i));
393
+ this.minutesView.appendChild(tick[0]);
394
+ }
395
+ }
396
+
397
+ _handleAmPmClick(e) {
398
+ let $btnClicked = $(e.target);
399
+ this.amOrPm = $btnClicked.hasClass('am-btn') ? 'AM' : 'PM';
400
+ this._updateAmPmView();
401
+ }
402
+
403
+ _updateAmPmView() {
404
+ if (this.options.twelveHour) {
405
+ this.$amBtn.toggleClass('text-primary', this.amOrPm === 'AM');
406
+ this.$pmBtn.toggleClass('text-primary', this.amOrPm === 'PM');
407
+ }
408
+ }
409
+
410
+ _updateTimeFromInput() {
411
+ // Get the time
412
+ let value = ((this.el.value || this.options.defaultTime || '') + '').split(':');
413
+ if (this.options.twelveHour && !(typeof value[1] === 'undefined')) {
414
+ if (value[1].toUpperCase().indexOf('AM') > 0) {
415
+ this.amOrPm = 'AM';
416
+ } else {
417
+ this.amOrPm = 'PM';
418
+ }
419
+ value[1] = value[1].replace('AM', '').replace('PM', '');
420
+ }
421
+ if (value[0] === 'now') {
422
+ let now = new Date(+new Date() + this.options.fromNow);
423
+ value = [now.getHours(), now.getMinutes()];
424
+ if (this.options.twelveHour) {
425
+ this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM';
426
+ }
427
+ }
428
+ this.hours = +value[0] || 0;
429
+ this.minutes = +value[1] || 0;
430
+ this.spanHours.innerHTML = this.hours;
431
+ this.spanMinutes.innerHTML = Timepicker._addLeadingZero(this.minutes);
432
+
433
+ this._updateAmPmView();
434
+ }
435
+
436
+ showView(view, delay) {
437
+ if (view === 'minutes' && $(this.hoursView).css('visibility') === 'visible') {
438
+ // raiseCallback(this.options.beforeHourSelect);
439
+ }
440
+ let isHours = view === 'hours',
441
+ nextView = isHours ? this.hoursView : this.minutesView,
442
+ hideView = isHours ? this.minutesView : this.hoursView;
443
+ this.currentView = view;
444
+
445
+ $(this.spanHours).toggleClass('text-primary', isHours);
446
+ $(this.spanMinutes).toggleClass('text-primary', !isHours);
447
+
448
+ // Transition view
449
+ hideView.classList.add('timepicker-dial-out');
450
+ $(nextView)
451
+ .css('visibility', 'visible')
452
+ .removeClass('timepicker-dial-out');
453
+
454
+ // Reset clock hand
455
+ this.resetClock(delay);
456
+
457
+ // After transitions ended
458
+ clearTimeout(this.toggleViewTimer);
459
+ this.toggleViewTimer = setTimeout(() => {
460
+ $(hideView).css('visibility', 'hidden');
461
+ }, this.options.duration);
462
+ }
463
+
464
+ resetClock(delay) {
465
+ let view = this.currentView,
466
+ value = this[view],
467
+ isHours = view === 'hours',
468
+ unit = Math.PI / (isHours ? 6 : 30),
469
+ radian = value * unit,
470
+ radius =
471
+ isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius,
472
+ x = Math.sin(radian) * radius,
473
+ y = -Math.cos(radian) * radius,
474
+ self = this;
475
+
476
+ if (delay) {
477
+ $(this.canvas).addClass('timepicker-canvas-out');
478
+ setTimeout(() => {
479
+ $(self.canvas).removeClass('timepicker-canvas-out');
480
+ self.setHand(x, y);
481
+ }, delay);
482
+ } else {
483
+ this.setHand(x, y);
484
+ }
485
+ }
486
+
487
+ setHand(x, y, roundBy5) {
488
+ let radian = Math.atan2(x, -y),
489
+ isHours = this.currentView === 'hours',
490
+ unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
491
+ z = Math.sqrt(x * x + y * y),
492
+ inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2,
493
+ radius = inner ? this.options.innerRadius : this.options.outerRadius;
494
+
495
+ if (this.options.twelveHour) {
496
+ radius = this.options.outerRadius;
497
+ }
498
+
499
+ // Radian should in range [0, 2PI]
500
+ if (radian < 0) {
501
+ radian = Math.PI * 2 + radian;
502
+ }
503
+
504
+ // Get the round value
505
+ let value = Math.round(radian / unit);
506
+
507
+ // Get the round radian
508
+ radian = value * unit;
509
+
510
+ // Correct the hours or minutes
511
+ if (this.options.twelveHour) {
512
+ if (isHours) {
513
+ if (value === 0) value = 12;
514
+ } else {
515
+ if (roundBy5) value *= 5;
516
+ if (value === 60) value = 0;
517
+ }
518
+ } else {
519
+ if (isHours) {
520
+ if (value === 12) {
521
+ value = 0;
522
+ }
523
+ value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
524
+ } else {
525
+ if (roundBy5) {
526
+ value *= 5;
527
+ }
528
+ if (value === 60) {
529
+ value = 0;
530
+ }
531
+ }
532
+ }
533
+
534
+ // Once hours or minutes changed, vibrate the device
535
+ if (this[this.currentView] !== value) {
536
+ if (this.vibrate && this.options.vibrate) {
537
+ // Do not vibrate too frequently
538
+ if (!this.vibrateTimer) {
539
+ navigator[this.vibrate](10);
540
+ this.vibrateTimer = setTimeout(() => {
541
+ this.vibrateTimer = null;
542
+ }, 100);
543
+ }
544
+ }
545
+ }
546
+
547
+ this[this.currentView] = value;
548
+ if (isHours) {
549
+ this['spanHours'].innerHTML = value;
550
+ } else {
551
+ this['spanMinutes'].innerHTML = Timepicker._addLeadingZero(value);
552
+ }
553
+
554
+ // Set clock hand and others' position
555
+ let cx1 = Math.sin(radian) * (radius - this.options.tickRadius),
556
+ cy1 = -Math.cos(radian) * (radius - this.options.tickRadius),
557
+ cx2 = Math.sin(radian) * radius,
558
+ cy2 = -Math.cos(radian) * radius;
559
+ this.hand.setAttribute('x2', cx1);
560
+ this.hand.setAttribute('y2', cy1);
561
+ this.bg.setAttribute('cx', cx2);
562
+ this.bg.setAttribute('cy', cy2);
563
+ }
564
+
565
+ open() {
566
+ if (this.isOpen) {
567
+ return;
568
+ }
569
+
570
+ this.isOpen = true;
571
+ this._updateTimeFromInput();
572
+ this.showView('hours');
573
+
574
+ this.modal.open();
575
+ }
576
+
577
+ close() {
578
+ if (!this.isOpen) {
579
+ return;
580
+ }
581
+
582
+ this.isOpen = false;
583
+ this.modal.close();
584
+ }
585
+
586
+ /**
587
+ * Finish timepicker selection.
588
+ */
589
+ done(e, clearValue) {
590
+ // Set input value
591
+ let last = this.el.value;
592
+ let value = clearValue
593
+ ? ''
594
+ : Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes);
595
+ this.time = value;
596
+ if (!clearValue && this.options.twelveHour) {
597
+ value = `${value} ${this.amOrPm}`;
598
+ }
599
+ this.el.value = value;
600
+
601
+ // Trigger change event
602
+ if (value !== last) {
603
+ this.$el.trigger('change');
604
+ }
605
+
606
+ this.close();
607
+ this.el.focus();
608
+ }
609
+
610
+ clear() {
611
+ this.done(null, true);
612
+ }
613
+ }
614
+
615
+ Timepicker._template = [
616
+ '<div class= "modal timepicker-modal">',
617
+ '<div class="modal-content timepicker-container">',
618
+ '<div class="timepicker-digital-display">',
619
+ '<div class="timepicker-text-container">',
620
+ '<div class="timepicker-display-column">',
621
+ '<span class="timepicker-span-hours text-primary"></span>',
622
+ ':',
623
+ '<span class="timepicker-span-minutes"></span>',
624
+ '</div>',
625
+ '<div class="timepicker-display-column timepicker-display-am-pm">',
626
+ '<div class="timepicker-span-am-pm"></div>',
627
+ '</div>',
628
+ '</div>',
629
+ '</div>',
630
+ '<div class="timepicker-analog-display">',
631
+ '<div class="timepicker-plate">',
632
+ '<div class="timepicker-canvas"></div>',
633
+ '<div class="timepicker-dial timepicker-hours"></div>',
634
+ '<div class="timepicker-dial timepicker-minutes timepicker-dial-out"></div>',
635
+ '</div>',
636
+ '<div class="timepicker-footer"></div>',
637
+ '</div>',
638
+ '</div>',
639
+ '</div>'
640
+ ].join('');
641
+
642
+ M.Timepicker = Timepicker;
643
+
644
+ if (M.jQueryLoaded) {
645
+ M.initializeJqueryWrapper(Timepicker, 'timepicker', 'M_Timepicker');
646
+ }
647
+ })(cash);