var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } (function ($) { 'use strict'; var _defaults = { dialRadius: 135, outerRadius: 105, innerRadius: 70, tickRadius: 20, duration: 350, container: null, defaultTime: 'now', // default time, 'now' or '13:14' e.g. fromnow: 0, // Millisecond offset from the defaultTime doneText: 'Ok', // done button text clearText: 'Clear', cancelText: 'Cancel', autoClose: false, // auto close when minute is selected twelveHour: true, // change to 12 hour AM/PM clock from 24 hour vibrate: true // vibrate the device when dragging clock hand }; /** * @class * */ var Timepicker = function () { function Timepicker(el, options) { _classCallCheck(this, Timepicker); // If exists, destroy and reinitialize if (!!el.M_Timepicker) { el.M_Timepicker.destroy(); } this.el = el; this.$el = $(el); this.el.M_Timepicker = this; this.options = $.extend({}, Timepicker.defaults, options); this.id = M.guid(); this._insertHTMLIntoDOM(); this._setupModal(); this._setupVariables(); this._setupEventHandlers(); this._clockSetup(); this._pickerSetup(); } _createClass(Timepicker, [{ key: 'destroy', /** * Teardown component */ value: function destroy() { this._removeEventHandlers(); this.modal.destroy(); $(this.modalEl).remove(); this.el.M_Timepicker = undefined; } /** * Setup Event Handlers */ }, { key: '_setupEventHandlers', value: function _setupEventHandlers() { this._handleInputKeydownBound = this._handleInputKeydown.bind(this); this._handleInputClickBound = this._handleInputClick.bind(this); this._handleClockClickStartBound = this._handleClockClickStart.bind(this); this._handleDocumentClickMoveBound = this._handleDocumentClickMove.bind(this); this._handleDocumentClickEndBound = this._handleDocumentClickEnd.bind(this); this.el.addEventListener('click', this._handleInputClickBound); this.el.addEventListener('keydown', this._handleInputKeydownBound); this.plate.addEventListener('mousedown', this._handleClockClickStartBound); this.plate.addEventListener('touchstart', this._handleClockClickStartBound); $(this.spanHours).on('click', this.showView.bind(this, 'hours')); $(this.spanMinutes).on('click', this.showView.bind(this, 'minutes')); } }, { key: '_removeEventHandlers', value: function _removeEventHandlers() { this.el.removeEventListener('click', this._handleInputClickBound); this.el.removeEventListener('keydown', this._handleInputKeydownBound); } }, { key: '_handleInputClick', value: function _handleInputClick() { this.open(); } }, { key: '_handleInputKeydown', value: function _handleInputKeydown(e) { if (e.which === M.keys.ENTER) { e.preventDefault(); this.open(); } } }, { key: '_handleClockClickStart', value: function _handleClockClickStart(e) { e.preventDefault(); var clockPlateBR = this.plate.getBoundingClientRect(); var offset = { x: clockPlateBR.left, y: clockPlateBR.top }; this.x0 = offset.x + this.options.dialRadius; this.y0 = offset.y + this.options.dialRadius; this.moved = false; var clickPos = Timepicker._Pos(e); this.dx = clickPos.x - this.x0; this.dy = clickPos.y - this.y0; // Set clock hands this.setHand(this.dx, this.dy, false); // Mousemove on document document.addEventListener('mousemove', this._handleDocumentClickMoveBound); document.addEventListener('touchmove', this._handleDocumentClickMoveBound); // Mouseup on document document.addEventListener('mouseup', this._handleDocumentClickEndBound); document.addEventListener('touchend', this._handleDocumentClickEndBound); } }, { key: '_handleDocumentClickMove', value: function _handleDocumentClickMove(e) { e.preventDefault(); var clickPos = Timepicker._Pos(e); var x = clickPos.x - this.x0; var y = clickPos.y - this.y0; this.moved = true; this.setHand(x, y, false, true); } }, { key: '_handleDocumentClickEnd', value: function _handleDocumentClickEnd(e) { e.preventDefault(); document.removeEventListener('mouseup', this._handleDocumentClickEndBound); document.removeEventListener('touchend', this._handleDocumentClickEndBound); var clickPos = Timepicker._Pos(e); var x = clickPos.x - this.x0; var y = clickPos.y - this.y0; if (this.moved && x === this.dx && y === this.dy) { this.setHand(x, y); } if (this.currentView === 'hours') { this.showView('minutes', this.options.duration / 2); } else if (this.options.autoClose) { this.minutesView.addClass('timepicker-dial-out'); setTimeout(function () { this.done(); }, this.options.duration / 2); } // Unbind mousemove event document.removeEventListener('mousemove', this._handleDocumentClickMoveBound); document.removeEventListener('touchmove', this._handleDocumentClickMoveBound); } }, { key: '_insertHTMLIntoDOM', value: function _insertHTMLIntoDOM() { this.$modalEl = $(Timepicker._template); this.modalEl = this.$modalEl[0]; this.modalEl.id = 'modal-' + this.id; // Append popover to input by default var containerEl = document.querySelector(this.options.container); if (this.options.container && !!containerEl) { this.$modalEl.appendTo(containerEl); } else { this.$modalEl.insertBefore(this.el); } } }, { key: '_setupModal', value: function _setupModal() { var _this = this; this.modal = new M.Modal(this.modalEl, { complete: function () { _this.isOpen = false; } }); } }, { key: '_setupVariables', value: function _setupVariables() { this.currentView = 'hours'; this.vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null; this._canvas = this.modalEl.querySelector('.timepicker-canvas'); this.plate = this.modalEl.querySelector('.timepicker-plate'); this.hoursView = this.modalEl.querySelector('.timepicker-hours'); this.minutesView = this.modalEl.querySelector('.timepicker-minutes'); this.spanHours = this.modalEl.querySelector('.timepicker-span-hours'); this.spanMinutes = this.modalEl.querySelector('.timepicker-span-minutes'); this.spanAmPm = this.modalEl.querySelector('.timepicker-span-am-pm'); this.footer = this.modalEl.querySelector('.timepicker-footer'); this.amOrPm = 'PM'; } }, { key: '_pickerSetup', value: function _pickerSetup() { $('').appendTo(this.footer).on('click', this.clear.bind(this)); var confirmationBtnsContainer = $('
'); $('').appendTo(confirmationBtnsContainer).on('click', this.close.bind(this)); $('').appendTo(confirmationBtnsContainer).on('click', this.done.bind(this)); confirmationBtnsContainer.appendTo(this.footer); } }, { key: '_clockSetup', value: function _clockSetup() { if (this.options.twelveHour) { this.$amBtn = $('
AM
'); this.$pmBtn = $('
PM
'); this.$amBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm); this.$pmBtn.on('click', this._handleAmPmClick.bind(this)).appendTo(this.spanAmPm); } this._buildHoursView(); this._buildMinutesView(); this._buildSVGClock(); } }, { key: '_buildSVGClock', value: function _buildSVGClock() { // Draw clock hands and others var dialRadius = this.options.dialRadius; var tickRadius = this.options.tickRadius; var diameter = dialRadius * 2; var svg = Timepicker._createSVGEl('svg'); svg.setAttribute('class', 'timepicker-svg'); svg.setAttribute('width', diameter); svg.setAttribute('height', diameter); var g = Timepicker._createSVGEl('g'); g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')'); var bearing = Timepicker._createSVGEl('circle'); bearing.setAttribute('class', 'timepicker-canvas-bearing'); bearing.setAttribute('cx', 0); bearing.setAttribute('cy', 0); bearing.setAttribute('r', 4); var hand = Timepicker._createSVGEl('line'); hand.setAttribute('x1', 0); hand.setAttribute('y1', 0); var bg = Timepicker._createSVGEl('circle'); bg.setAttribute('class', 'timepicker-canvas-bg'); bg.setAttribute('r', tickRadius); g.appendChild(hand); g.appendChild(bg); g.appendChild(bearing); svg.appendChild(g); this._canvas.appendChild(svg); this.hand = hand; this.bg = bg; this.bearing = bearing; this.g = g; } }, { key: '_buildHoursView', value: function _buildHoursView() { var $tick = $('
'); // Hours view if (this.options.twelveHour) { for (var i = 1; i < 13; i += 1) { var tick = $tick.clone(); var radian = i / 6 * Math.PI; var radius = this.options.outerRadius; tick.css({ left: this.options.dialRadius + Math.sin(radian) * radius - this.options.tickRadius + 'px', top: this.options.dialRadius - Math.cos(radian) * radius - this.options.tickRadius + 'px' }); tick.html(i === 0 ? '00' : i); this.hoursView.appendChild(tick[0]); // tick.on(mousedownEvent, mousedown); } } else { for (var _i = 0; _i < 24; _i += 1) { var _tick = $tick.clone(); var _radian = _i / 6 * Math.PI; var inner = _i > 0 && _i < 13; var _radius = inner ? this.options.innerRadius : this.options.outerRadius; _tick.css({ left: this.options.dialRadius + Math.sin(_radian) * _radius - this.options.tickRadius + 'px', top: this.options.dialRadius - Math.cos(_radian) * _radius - this.options.tickRadius + 'px' }); _tick.html(_i === 0 ? '00' : _i); this.hoursView.appendChild(_tick[0]); // tick.on(mousedownEvent, mousedown); } } } }, { key: '_buildMinutesView', value: function _buildMinutesView() { var $tick = $('
'); // Minutes view for (var i = 0; i < 60; i += 5) { var tick = $tick.clone(); var radian = i / 30 * Math.PI; tick.css({ left: this.options.dialRadius + Math.sin(radian) * this.options.outerRadius - this.options.tickRadius + 'px', top: this.options.dialRadius - Math.cos(radian) * this.options.outerRadius - this.options.tickRadius + 'px' }); tick.html(Timepicker._addLeadingZero(i)); this.minutesView.appendChild(tick[0]); } } }, { key: '_handleAmPmClick', value: function _handleAmPmClick(e) { var $btnClicked = $(e.target); this.amOrPm = $btnClicked.hasClass('am-btn') ? 'AM' : 'PM'; this._updateAmPmView(); } }, { key: '_updateAmPmView', value: function _updateAmPmView() { if (this.options.twelveHour) { this.$amBtn.toggleClass('text-primary', this.amOrPm === 'AM'); this.$pmBtn.toggleClass('text-primary', this.amOrPm === 'PM'); } } }, { key: '_updateTimeFromInput', value: function _updateTimeFromInput() { // Get the time var value = ((this.el.value || this.options.defaultTime || '') + '').split(':'); if (this.options.twelveHour && !(typeof value[1] === 'undefined')) { if (value[1].indexOf("AM") > 0) { this.amOrPm = 'AM'; } else { this.amOrPm = 'PM'; } value[1] = value[1].replace("AM", "").replace("PM", ""); } if (value[0] === 'now') { var now = new Date(+new Date() + this.options.fromnow); value = [now.getHours(), now.getMinutes()]; if (this.options.twelveHour) { this.amOrPm = value[0] >= 12 && value[0] < 24 ? 'PM' : 'AM'; } } this.hours = +value[0] || 0; this.minutes = +value[1] || 0; this.spanHours.innerHTML = this.hours; this.spanMinutes.innerHTML = Timepicker._addLeadingZero(this.minutes); this._updateAmPmView(); } }, { key: 'showView', value: function showView(view, delay) { if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") { // raiseCallback(this.options.beforeHourSelect); } var isHours = view === 'hours', nextView = isHours ? this.hoursView : this.minutesView, hideView = isHours ? this.minutesView : this.hoursView; this.currentView = view; $(this.spanHours).toggleClass('text-primary', isHours); $(this.spanMinutes).toggleClass('text-primary', !isHours); // Transition view hideView.classList.add('timepicker-dial-out'); $(nextView).css('visibility', 'visible').removeClass('timepicker-dial-out'); // Reset clock hand this.resetClock(delay); // After transitions ended clearTimeout(this.toggleViewTimer); this.toggleViewTimer = setTimeout(function () { $(hideView).css('visibility', 'hidden'); }, this.options.duration); } }, { key: 'resetClock', value: function resetClock(delay) { var view = this.currentView, value = this[view], isHours = view === 'hours', unit = Math.PI / (isHours ? 6 : 30), radian = value * unit, radius = isHours && value > 0 && value < 13 ? this.options.innerRadius : this.options.outerRadius, x = Math.sin(radian) * radius, y = -Math.cos(radian) * radius, self = this; if (delay) { $(this.canvas).addClass('timepicker-canvas-out'); setTimeout(function () { $(self.canvas).removeClass('timepicker-canvas-out'); self.setHand(x, y); }, delay); } else { this.setHand(x, y); } } }, { key: 'setHand', value: function setHand(x, y, roundBy5) { var _this2 = this; var radian = Math.atan2(x, -y), isHours = this.currentView === 'hours', unit = Math.PI / (isHours || roundBy5 ? 6 : 30), z = Math.sqrt(x * x + y * y), inner = isHours && z < (this.options.outerRadius + this.options.innerRadius) / 2, radius = inner ? this.options.innerRadius : this.options.outerRadius; if (this.options.twelveHour) { radius = this.options.outerRadius; } // Radian should in range [0, 2PI] if (radian < 0) { radian = Math.PI * 2 + radian; } // Get the round value var value = Math.round(radian / unit); // Get the round radian radian = value * unit; // Correct the hours or minutes if (this.options.twelveHour) { if (isHours) { if (value === 0) value = 12; } else { if (roundBy5) value *= 5; if (value === 60) value = 0; } } else { if (isHours) { if (value === 12) { value = 0; } value = inner ? value === 0 ? 12 : value : value === 0 ? 0 : value + 12; } else { if (roundBy5) { value *= 5; } if (value === 60) { value = 0; } } } // Once hours or minutes changed, vibrate the device if (this[this.currentView] !== value) { if (this.vibrate && this.options.vibrate) { // Do not vibrate too frequently if (!this.vibrateTimer) { navigator[this.vibrate](10); this.vibrateTimer = setTimeout(function () { _this2.vibrateTimer = null; }, 100); } } } this[this.currentView] = value; if (isHours) { this['spanHours'].innerHTML = value; } else { this['spanMinutes'].innerHTML = Timepicker._addLeadingZero(value); } // Set clock hand and others' position var cx1 = Math.sin(radian) * (radius - this.options.tickRadius), cy1 = -Math.cos(radian) * (radius - this.options.tickRadius), cx2 = Math.sin(radian) * radius, cy2 = -Math.cos(radian) * radius; this.hand.setAttribute('x2', cx1); this.hand.setAttribute('y2', cy1); this.bg.setAttribute('cx', cx2); this.bg.setAttribute('cy', cy2); } }, { key: 'open', value: function open() { if (this.isOpen) { return; } this.isOpen = true; this._updateTimeFromInput(); this.showView('hours'); this.modal.open(); } }, { key: 'close', value: function close() { if (!this.isOpen) { return; } this.isOpen = false; this.modal.close(); } /** * Finish timepicker selection. */ }, { key: 'done', value: function done(e, clearValue) { // Set input value var last = this.el.value; var value = clearValue ? '' : Timepicker._addLeadingZero(this.hours) + ':' + Timepicker._addLeadingZero(this.minutes); this.time = value; if (!clearValue && this.options.twelveHour) { value = value + ' ' + this.amOrPm; } this.el.value = value; // Trigger change event if (value !== last) { this.$el.trigger('change'); } this.close(); this.el.focus(); } }, { key: 'clear', value: function clear() { this.done(null, true); } }], [{ key: 'init', value: function init($els, options) { var arr = []; $els.each(function () { arr.push(new Timepicker(this, options)); }); return arr; } }, { key: '_addLeadingZero', value: function _addLeadingZero(num) { return (num < 10 ? '0' : '') + num; } }, { key: '_createSVGEl', value: function _createSVGEl(name) { var svgNS = 'http://www.w3.org/2000/svg'; return document.createElementNS(svgNS, name); } /** * @typedef {Object} Point * @property {number} x The X Coordinate * @property {number} y The Y Coordinate */ /** * Get x position of mouse or touch event * @param {Event} e * @return {Point} x and y location */ }, { key: '_Pos', value: function _Pos(e) { if (e.targetTouches && e.targetTouches.length >= 1) { return { x: e.targetTouches[0].clientX, y: e.targetTouches[0].clientY }; } // mouse event return { x: e.clientX, y: e.clientY }; } /** * Get Instance */ }, { key: 'getInstance', value: function getInstance(el) { var domElem = !!el.jquery ? el[0] : el; return domElem.M_Timepicker; } }, { key: 'defaults', get: function () { return _defaults; } }]); return Timepicker; }(); Timepicker._template = [''].join(''); M.Timepicker = Timepicker; if (M.jQueryLoaded) { M.initializeJqueryWrapper(Timepicker, 'timepicker', 'M_Timepicker'); } })(cash);