/** * Time Picker module * @module timeanddat * @namespace ludo.calendar * @class ludo.calendar.TimePicker * @param {object} config Config object * @param {number} config.hours Initial hours * @param {number} config.minutes Initial minutes * @param {String} config.clockBackground Background color of "Watch face" * @param {String} config.hourColor Color of hours text, default: #555555 * @param {String} config.minuteColor Color for the minute text, default: #555555 * @param {String} config.handColor Color of Arrow Hand indicating selected hours and minutes. default: #669900 * @param {String} config.handTextColor Color of hour and minute text on the hour/minute hand. default: #FFFFFFF * @param {String} config.minuteDotColor Color of small dot inside the hour/minute hand., default: #FFFFFF * @fires ludo.calendar.TimePicker#mode Fired when switching from hours to minutes. Argument: {String} "hours" or "minutes" * @fires ludo.calendar.TimePicker#time Fired when either hours or minutes have been changed. * Example:<code> * var t = new ludo.calendar.TimePicker({ * layout:{width:500,height:500}, * renderTo:document.body, * listeners:{ * // Listener for the "time" event * 'time' : function(hour, minutes, timeAsString){ * // timeString in format HH:MM, example: 11:48 * console.log(timeAsString); * } * * }); *</code> */ ludo.calendar.TimePicker = new Class({ Extends: ludo.View, origo: undefined, hours: undefined, minutes: undefined, hourPrefixed: undefined, minutePrefixed: undefined, area: undefined, initialArea: undefined, hourGroupInner: undefined, hourGroup: undefined, frontGroup: undefined, hourElements: undefined, minuteEls: undefined, centerDot: undefined, needle: undefined, needleCircle: undefined, needleText: undefined, clipPath: undefined, mode: undefined, dragActive: false, drag: undefined, outerSize: 0.85, hourInnerSize: 0.65, handColor: '#669900', minuteDotColor:'#ffffff', clockBackground: '#DDDDDD', handTextColor: '#ffffff', hourColor:'#555555', minuteColor:'#555555', __construct: function (config) { this.parent(config); this.setConfigParams(config, ['hours', 'minutes','handColor', 'minuteDotColor', 'clockBackground','handTextColor', 'hourColor', 'minuteColor']); var d = new Date(); if (this.hours == undefined) { this.hours = d.getHours(); } if (this.minutes == undefined) { this.minutes = d.getMinutes(); } this.mode = 'hours'; }, __rendered: function () { this.parent(); this.renderClock(); this.notify(); }, setTime:function(hour, minutes){ this.hours = hour; this.minutes = minutes; this.setPrefixed(); this.restart(); this.notify(); this.updateNeedle(); }, /** * Show hours * @function restart * @memberof ludo.calendar.TimePicker.prototype */ restart: function () { this.showHours(); }, setPrefixed:function(){ this.hourPrefixed= this.hours < 10 ? "0" + this.hours : this.hours; this.minutePrefixed = this.minutes < 10 ? "0" + this.minutes : this.minutes; }, notify: function () { this.fireEvent('time', [this.hours, this.minutes, this.hourPrefixed + ":" + this.minutePrefixed]); }, mouseDown: function (e) { this.dragActive = true; var offset = this.getBody().offset(); this.drag = { elX: offset.left + parseInt(this.getBody().css("paddingLeft")) + parseInt(this.getBody().css("border-left-width")), elY: offset.top + parseInt(this.getBody().css("paddingLeft")) + parseInt(this.getBody().css("border-top-width")) }; this.updateTimeByEvent(e); }, mouseMove: function (e) { if (!this.dragActive)return; this.updateTimeByEvent(e); return false; }, mouseUp: function () { if (this.dragActive) { if (this.mode == 'hours') { this.showMinutes(); } } this.dragActive = false; }, updateTimeByEvent: function (e) { var p = e.touches && e.touches.length ? e.touches[0] : e; var posX = p.pageX - this.drag.elX; var posY = p.pageY - this.drag.elY; var angle = ludo.geometry.getAngleFrom(this.origo.x, this.origo.y, posX, posY); var distance = ludo.geometry.distanceBetweenPoints(this.origo.x, this.origo.y, posX, posY); var hour = this.hours; var minute = this.minutes; if (this.mode == "hours") { var inner = distance < (this.area.size / 2) * (this.hourInnerSize * 1.05); hour = angle / 360 * 12; if (inner)hour += 12; hour = Math.round(hour); if (!inner && hour == 0)hour = 12; if (inner && hour == 12)hour = 0; if (hour == 24) hour = 0; } else { minute = angle / 360 * 60; minute = Math.round(minute) % 60; } if (hour != this.hours || minute != this.minutes) { this.hours = hour; this.minutes = minute; this.setPrefixed(); this.notify(); this.updateNeedle(); } }, updateNeedle: function () { var pos, txt; if (this.mode == 'hours') { var angle = this.getHourAngle(this.hours); var length = this.getHourDistance(this.hours); pos = ludo.geometry.getPointDistanceFrom(this.origo.x, this.origo.y, angle, length); txt = this.hourPrefixed; this.needleText.text(txt); this.needleText.set('x', pos.x); this.needleText.set('y', pos.y); } else { var a = this.getMinuteAngle(this.minutes); var l = this.area.size * this.outerSize / 2; pos = ludo.geometry.getPointDistanceFrom(this.origo.x, this.origo.y, a, l); var minute = Math.round(this.minutes / 5) * 5; if (minute == 60)minute = 0; var m = minute < 10 ? "0" + minute : minute; var a2 = this.getMinuteAngle(minute); var pos2 = ludo.geometry.getPointDistanceFrom(this.origo.x, this.origo.y, a2, l); this.needleText.set('x', pos2.x); this.needleText.set('y', pos2.y); this.needleCircleInnerMinutes.set('cx', pos.x); this.needleCircleInnerMinutes.set('cy', pos.y); if(pos.x != pos2.x){ this.needleCircleInnerMinutes.show(); }else{ this.needleCircleInnerMinutes.hide(); } this.needleText.text(m); } this.clipCircle.set('cx', pos.x); this.clipCircle.set('cy', pos.y); this.needle.set('x2', pos.x); this.needle.set('y2', pos.y); this.needleCircle.set('cx', pos.x); this.needleCircle.set('cy', pos.y); }, renderClock: function () { var canvas = this.svg(); canvas.getNode().on(ludo.util.getDragStartEvent(), this.mouseDown.bind(this)); $(document.body).on(ludo.util.getDragMoveEvent(), this.mouseMove.bind(this)); $(document.body).on(ludo.util.getDragEndEvent(), this.mouseUp.bind(this)); var size = Math.min(canvas.width, canvas.height); this.hourElements = []; this.minuteEls = []; this.origo = { x: canvas.width / 2, y: canvas.height / 2 }; this.area = { x: (canvas.width - size) / 2, y: (canvas.height - size) / 2, size: size }; this.initialArea = { x: this.area.x, y: this.area.y, size: this.area.size }; this.clipPath = canvas.$('clipPath', {}); canvas.append(this.clipPath); this.clipCircle = new canvas.$('circle', { cx: this.origo.x, cy: this.origo.y, r: 5 }); this.clipPath.append(this.clipCircle); this.clockBackground = canvas.$('circle', { cx: this.origo.x, cy: this.origo.y, r: this.area.size / 2, css: { 'fill': this.clockBackground } }); canvas.append(this.clockBackground); this.hourGroup = new ludo.svg.Group({ css: { 'fill': this.hourColor, 'stroke-width': 0 } }); var styles = { '-webkit-user-select': 'none', '-moz-user-select': 'none', '-ms-user-select': 'none', 'user-select': 'none' }; canvas.append(this.hourGroup); this.hourGroupInner = new ludo.svg.Group({ css: { 'fill': this.hourColor, 'stroke-width': 0 } }); canvas.append(this.hourGroupInner); var i; for (i = 1; i <= 12; i++) { var text = new ludo.svg.Text("" + i, { css: styles }); text.set("text-anchor", "middle"); text.set("alignment-baseline", "middle"); this.hourGroup.append(text); this.hourElements.push({ hour: i, el: text }); } for (i = 13; i <= 24; i++) { var txt = i == 24 ? "00" : "" + i; text = new ludo.svg.Text(txt, { css: styles }); text.set("text-anchor", "middle"); text.set("alignment-baseline", "middle"); this.hourElements.push({ hour: i, el: text }); this.hourGroupInner.append(text); } this.centerDot = canvas.$('circle', { cx: this.origo.x, cy: this.origo.y, r: 2, css: { 'fill': this.handColor, 'stroke-width': 0 } }); this.needle = canvas.$('line', { x1: this.origo.x, y1: this.origo.y, x2: 100, y2: 100, css: { 'stroke-linecap': "round", 'stroke': this.handColor, 'stroke-width': 2 } } ); canvas.append(this.needle); this.minuteGroup = new ludo.svg.Group({ css: { 'fill': this.minuteColor, 'stroke-width': 0 } }); canvas.append(this.minuteGroup); for (i = 0; i < 60; i += 5) { var m = "" + i; if (m.length == "1")m = "0" + m; text = new ludo.svg.Text(m, { css: styles }); text.set("text-anchor", "middle"); text.set("alignment-baseline", "middle"); this.minuteEls.push({ minute: i, el: text }); this.minuteGroup.append(text); } this.needleCircle = canvas.$('circle', { cx: this.origo.x, cy: this.origo.y, r: 10, css: { 'fill': this.handColor } }); canvas.append(this.needleCircle); this.needleCircleInnerMinutes = canvas.$('circle', { cx: this.origo.x, cy: this.origo.y, r: 1, css: { fill: this.minuteDotColor } }); canvas.append(this.needleCircleInnerMinutes); this.needleText = new ludo.svg.Text("", { 'fill': this.handTextColor, css: styles }); this.needleText.set("text-anchor", "middle"); this.needleText.set("alignment-baseline", "middle"); canvas.append(this.needleText); this.needleText.clip(this.clipPath); this.showHours(); canvas.append(this.centerDot); this.resizeSVG(); }, positionHour: function (index) { var hour = this.hourElements[index].hour; var offset = this.getHourDistance(hour); var angle = this.getHourAngle(hour); var x = Math.cos(angle); var y = Math.sin(angle); this.hourElements[index].el.set('x', (x * offset) + this.origo.x); this.hourElements[index].el.set('y', (y * offset) + this.origo.y); }, getHourDistance: function (hour) { return hour <= 12 && hour > 0 ? this.area.size * this.outerSize / 2 : this.area.size * this.hourInnerSize / 2; }, getHourAngle: function (hour) { hour = hour % 12; var degrees = 360 / 12 * hour; degrees -= (360 * 3 / 12); return ludo.geometry.toRadians(degrees); }, getMinuteAngle: function (minute) { var degrees = 360 / 60 * minute; degrees -= 90; return ludo.geometry.toRadians(degrees); }, positionMinute: function (index) { var offset = this.area.size * this.outerSize / 2; var angle = this.getMinuteAngle(this.minuteEls[index].minute); var x = Math.cos(angle); var y = Math.sin(angle); this.minuteEls[index].el.set('x', (x * offset) + this.origo.x); this.minuteEls[index].el.set('y', (y * offset) + this.origo.y); }, resizeDOM: function () { this.parent(); if (!this.hourGroup)return; this.resizeSVG(); }, resizeSVG: function () { var canvas = this.svg(); canvas.fitParent(); var size = Math.min(canvas.width, canvas.height); this.area = { x: (canvas.width - size) / 2, y: (canvas.height - size) / 2, size: size }; this.hourGroup.css({ 'font-size': this.area.size / 20 }); this.hourGroupInner.css({ 'font-size': this.area.size / 25 }); this.needleCircle.set('r', this.area.size / 20); this.clipCircle.set('r', this.area.size / 20); this.origo = { x: canvas.width / 2, y: canvas.height / 2 }; for (i = 0; i < this.hourElements.length; i++) { this.positionHour(i); var s = i < 12 ? this.area.size / 20 : this.area.size / 25; this.hourElements[i].el.css("font-size", s); } for (i = 0; i < this.minuteEls.length; i++) { this.positionMinute(i); this.minuteEls[i].el.css("font-size", this.area.size / 20); } this.clockBackground.set('cx', this.origo.x); this.clockBackground.set('cy', this.origo.y); this.clockBackground.set('r', this.area.size / 2); this.centerDot.set('cx', this.origo.x); this.centerDot.set('cy', this.origo.y); this.centerDot.set('r', this.area.size / 70); this.needleCircleInnerMinutes.set('r', this.area.size / 150); this.needle.set('x1', this.origo.x); this.needle.set('y1', this.origo.y); this.needleText.css("font-size", this.area.size / 20); this.updateNeedle(); }, showMinutes: function () { this.mode = 'minutes'; this.hourGroup.hide(); this.hourGroupInner.hide(); this.minuteGroup.show(); this.needleCircleInnerMinutes.show(); this.updateNeedle(); this.fireEvent('mode', 'minutes'); }, showHours: function () { this.mode = 'hours'; this.hourGroup.show(); this.hourGroupInner.show(); this.minuteGroup.hide(); this.needleCircleInnerMinutes.hide(); this.updateNeedle(); this.fireEvent('mode', 'hours'); }, getTimeString:function(){ return this.hourPrefixed + ":" + this.minutePrefixed; }, val:function(val){ if(arguments.length > 0){ var tokens = val.split(/:/g); this.setTime(parseInt(tokens[0]),parseInt(tokens[1])); } return this.hourPrefixed + ":" + this.minutePrefixed; } });