| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729 | /*! * ClockPicker v{package.version} (http://weareoutman.github.io/clockpicker/) * Copyright 2014 Wang Shenwei. * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE) */;(function(){	var $ = window.jQuery,		$win = $(window),		$doc = $(document),		$body;	// Can I use inline svg ?	var svgNS = 'http://www.w3.org/2000/svg',		svgSupported = 'SVGAngle' in window && (function(){			var supported,				el = document.createElement('div');			el.innerHTML = '<svg/>';			supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;			el.innerHTML = '';			return supported;		})();	// Can I use transition ?	var transitionSupported = (function(){		var style = document.createElement('div').style;		return 'transition' in style ||			'WebkitTransition' in style ||			'MozTransition' in style ||			'msTransition' in style ||			'OTransition' in style;	})();	// Listen touch events in touch screen device, instead of mouse events in desktop.	var touchSupported = 'ontouchstart' in window,		mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),		mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),		mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');	// Vibrate the device if supported	var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;	function createSvgElement(name) {		return document.createElementNS(svgNS, name);	}	function leadingZero(num) {		return (num < 10 ? '0' : '') + num;	}	// Get a unique id	var idCounter = 0;	function uniqueId(prefix) {		var id = ++idCounter + '';		return prefix ? prefix + id : id;	}	// Clock size	var dialRadius = 100,		outerRadius = 80,		// innerRadius = 80 on 12 hour clock		innerRadius = 54,		tickRadius = 13,		diameter = dialRadius * 2,		duration = transitionSupported ? 350 : 1;	// Popover template	var tpl = [		'<div class="popover clockpicker-popover">',			'<div class="arrow"></div>',			'<div class="popover-title">',				'<span class="clockpicker-span-hours text-primary"></span>',				' : ',				'<span class="clockpicker-span-minutes"></span>',				'<span class="clockpicker-span-am-pm"></span>',			'</div>',			'<div class="popover-content">',				'<div class="clockpicker-plate">',					'<div class="clockpicker-canvas"></div>',					'<div class="clockpicker-dial clockpicker-hours"></div>',					'<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',				'</div>',				'<span class="clockpicker-am-pm-block">',				'</span>',			'</div>',		'</div>'	].join('');	// ClockPicker	function ClockPicker(element, options) {		var popover = $(tpl),			plate = popover.find('.clockpicker-plate'),			hoursView = popover.find('.clockpicker-hours'),			minutesView = popover.find('.clockpicker-minutes'),			amPmBlock = popover.find('.clockpicker-am-pm-block'),			isInput = element.prop('tagName') === 'INPUT',			input = isInput ? element : element.find('input'),			addon = element.find('.input-group-addon'),			self = this,			timer;		this.id = uniqueId('cp');		this.element = element;		this.options = options;		this.isAppended = false;		this.isShown = false;		this.currentView = 'hours';		this.isInput = isInput;		this.input = input;		this.addon = addon;		this.popover = popover;		this.plate = plate;		this.hoursView = hoursView;		this.minutesView = minutesView;		this.amPmBlock = amPmBlock;		this.spanHours = popover.find('.clockpicker-span-hours');		this.spanMinutes = popover.find('.clockpicker-span-minutes');		this.spanAmPm = popover.find('.clockpicker-span-am-pm');		this.amOrPm = "PM";		// Setup for for 12 hour clock if option is selected		if (options.twelvehour) {			var  amPmButtonsTemplate = ['<div class="clockpicker-am-pm-block">',				'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-am-button">',				'AM</button>',				'<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-pm-button">',				'PM</button>',				'</div>'].join('');			var amPmButtons = $(amPmButtonsTemplate);			//amPmButtons.appendTo(plate);			////Not working b/c they are not shown when this runs			//$('clockpicker-am-button')			//    .on("click", function() {			//        self.amOrPm = "AM";			//        $('.clockpicker-span-am-pm').empty().append('AM');			//    });			//			//$('clockpicker-pm-button')			//    .on("click", function() {			//         self.amOrPm = "PM";			//        $('.clockpicker-span-am-pm').empty().append('PM');			//    });			$('<button type="button" class="btn btn-sm btn-default clockpicker-button am-button">' + "AM" + '</button>')				.on("click", function() {					self.amOrPm = "AM";					$('.clockpicker-span-am-pm').empty().append('AM');				}).appendTo(this.amPmBlock);			$('<button type="button" class="btn btn-sm btn-default clockpicker-button pm-button">' + "PM" + '</button>')				.on("click", function() {					self.amOrPm = 'PM';					$('.clockpicker-span-am-pm').empty().append('PM');				}).appendTo(this.amPmBlock);		}		if (! options.autoclose) {			// If autoclose is not setted, append a button			$('<button type="button" class="btn btn-sm btn-default btn-block clockpicker-button">' + options.donetext + '</button>')				.click($.proxy(this.done, this))				.appendTo(popover);		}		// Placement and arrow align - make sure they make sense.		if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';		if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';		popover.addClass(options.placement);		popover.addClass('clockpicker-align-' + options.align);		this.spanHours.click($.proxy(this.toggleView, this, 'hours'));		this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));		// Show or toggle		input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));		addon.on('click.clockpicker', $.proxy(this.toggle, this));		// Build ticks		var tickTpl = $('<div class="clockpicker-tick"></div>'),			i, tick, radian, radius;		// Hours view		if (options.twelvehour) {			for (i = 1; i < 13; i += 1) {				tick = tickTpl.clone();				radian = i / 6 * Math.PI;				radius = outerRadius;				tick.css('font-size', '120%');				tick.css({					left: dialRadius + Math.sin(radian) * radius - tickRadius,					top: dialRadius - Math.cos(radian) * radius - tickRadius				});				tick.html(i === 0 ? '00' : i);				hoursView.append(tick);				tick.on(mousedownEvent, mousedown);			}		} else {			for (i = 0; i < 24; i += 1) {				tick = tickTpl.clone();				radian = i / 6 * Math.PI;				var inner = i > 0 && i < 13;				radius = inner ? innerRadius : outerRadius;				tick.css({					left: dialRadius + Math.sin(radian) * radius - tickRadius,					top: dialRadius - Math.cos(radian) * radius - tickRadius				});				if (inner) {					tick.css('font-size', '120%');				}				tick.html(i === 0 ? '00' : i);				hoursView.append(tick);				tick.on(mousedownEvent, mousedown);			}		}		// Minutes view		for (i = 0; i < 60; i += 5) {			tick = tickTpl.clone();			radian = i / 30 * Math.PI;			tick.css({				left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,				top: dialRadius - Math.cos(radian) * outerRadius - tickRadius			});			tick.css('font-size', '120%');			tick.html(leadingZero(i));			minutesView.append(tick);			tick.on(mousedownEvent, mousedown);		}		// Clicking on minutes view space		plate.on(mousedownEvent, function(e){			if ($(e.target).closest('.clockpicker-tick').length === 0) {				mousedown(e, true);			}		});		// Mousedown or touchstart		function mousedown(e, space) {			var offset = plate.offset(),				isTouch = /^touch/.test(e.type),				x0 = offset.left + dialRadius,				y0 = offset.top + dialRadius,				dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,				dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,				z = Math.sqrt(dx * dx + dy * dy),				moved = false;			// When clicking on minutes view space, check the mouse position			if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {				return;			}			e.preventDefault();			// Set cursor style of body after 200ms			var movingTimer = setTimeout(function(){				$body.addClass('clockpicker-moving');			}, 200);			// Place the canvas to top			if (svgSupported) {				plate.append(self.canvas);			}			// Clock			self.setHand(dx, dy, ! space, true);			// Mousemove on document			$doc.off(mousemoveEvent).on(mousemoveEvent, function(e){				e.preventDefault();				var isTouch = /^touch/.test(e.type),					x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,					y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;				if (! moved && x === dx && y === dy) {					// Clicking in chrome on windows will trigger a mousemove event					return;				}				moved = true;				self.setHand(x, y, false, true);			});			// Mouseup on document			$doc.off(mouseupEvent).on(mouseupEvent, function(e){				$doc.off(mouseupEvent);				e.preventDefault();				var isTouch = /^touch/.test(e.type),					x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,					y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;				if ((space || moved) && x === dx && y === dy) {					self.setHand(x, y);				}				if (self.currentView === 'hours') {					self.toggleView('minutes', duration / 2);				} else {					if (options.autoclose) {						self.minutesView.addClass('clockpicker-dial-out');						setTimeout(function(){							self.done();						}, duration / 2);					}				}				plate.prepend(canvas);				// Reset cursor style of body				clearTimeout(movingTimer);				$body.removeClass('clockpicker-moving');				// Unbind mousemove event				$doc.off(mousemoveEvent);			});		}		if (svgSupported) {			// Draw clock hands and others			var canvas = popover.find('.clockpicker-canvas'),				svg = createSvgElement('svg');			svg.setAttribute('class', 'clockpicker-svg');			svg.setAttribute('width', diameter);			svg.setAttribute('height', diameter);			var g = createSvgElement('g');			g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');			var bearing = createSvgElement('circle');			bearing.setAttribute('class', 'clockpicker-canvas-bearing');			bearing.setAttribute('cx', 0);			bearing.setAttribute('cy', 0);			bearing.setAttribute('r', 2);			var hand = createSvgElement('line');			hand.setAttribute('x1', 0);			hand.setAttribute('y1', 0);			var bg = createSvgElement('circle');			bg.setAttribute('class', 'clockpicker-canvas-bg');			bg.setAttribute('r', tickRadius);			var fg = createSvgElement('circle');			fg.setAttribute('class', 'clockpicker-canvas-fg');			fg.setAttribute('r', 3.5);			g.appendChild(hand);			g.appendChild(bg);			g.appendChild(fg);			g.appendChild(bearing);			svg.appendChild(g);			canvas.append(svg);			this.hand = hand;			this.bg = bg;			this.fg = fg;			this.bearing = bearing;			this.g = g;			this.canvas = canvas;		}		raiseCallback(this.options.init);	}	function raiseCallback(callbackFunction) {		if (callbackFunction && typeof callbackFunction === "function") {			callbackFunction();		}	}	// Default options	ClockPicker.DEFAULTS = {		'default': '',       // default time, 'now' or '13:14' e.g.		fromnow: 0,          // set default time to * milliseconds from now (using with default = 'now')		placement: 'bottom', // clock popover placement		align: 'left',       // popover arrow align		donetext: '完成',    // done button text		autoclose: false,    // auto close when minute is selected		twelvehour: false, // change to 12 hour AM/PM clock from 24 hour		vibrate: true        // vibrate the device when dragging clock hand	};	// Show or hide popover	ClockPicker.prototype.toggle = function(){		this[this.isShown ? 'hide' : 'show']();	};	// Set popover position	ClockPicker.prototype.locate = function(){		var element = this.element,			popover = this.popover,			offset = element.offset(),			width = element.outerWidth(),			height = element.outerHeight(),			placement = this.options.placement,			align = this.options.align,			styles = {},			self = this;		popover.show();		// Place the popover		switch (placement) {			case 'bottom':				styles.top = offset.top + height;				break;			case 'right':				styles.left = offset.left + width;				break;			case 'top':				styles.top = offset.top - popover.outerHeight();				break;			case 'left':				styles.left = offset.left - popover.outerWidth();				break;		}		// Align the popover arrow		switch (align) {			case 'left':				styles.left = offset.left;				break;			case 'right':				styles.left = offset.left + width - popover.outerWidth();				break;			case 'top':				styles.top = offset.top;				break;			case 'bottom':				styles.top = offset.top + height - popover.outerHeight();				break;		}		popover.css(styles);	};	// Show popover	ClockPicker.prototype.show = function(e){		// Not show again		if (this.isShown) {			return;		}		raiseCallback(this.options.beforeShow);		var self = this;		// Initialize		if (! this.isAppended) {			// Append popover to body			$body = $(document.body).append(this.popover);			// Reset position when resize			$win.on('resize.clockpicker' + this.id, function(){				if (self.isShown) {					self.locate();				}			});			this.isAppended = true;		}		// Get the time		var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');		if (value[0] === 'now') {			var now = new Date(+ new Date() + this.options.fromnow);			value = [				now.getHours(),				now.getMinutes()			];		}		this.hours = + value[0] || 0;		this.minutes = + value[1] || 0;		this.spanHours.html(leadingZero(this.hours));		this.spanMinutes.html(leadingZero(this.minutes));		// Toggle to hours view		this.toggleView('hours');		// Set position		this.locate();		this.isShown = true;		// Hide when clicking or tabbing on any element except the clock, input and addon		$doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){			var target = $(e.target);			if (target.closest(self.popover).length === 0 &&					target.closest(self.addon).length === 0 &&					target.closest(self.input).length === 0) {				self.hide();			}		});		// Hide when ESC is pressed		$doc.on('keyup.clockpicker.' + this.id, function(e){			if (e.keyCode === 27) {				self.hide();			}		});		raiseCallback(this.options.afterShow);	};	// Hide popover	ClockPicker.prototype.hide = function(){		raiseCallback(this.options.beforeHide);		this.isShown = false;		// Unbinding events on document		$doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);		$doc.off('keyup.clockpicker.' + this.id);		this.popover.hide();		raiseCallback(this.options.afterHide);	};	// Toggle to hours or minutes view	ClockPicker.prototype.toggleView = function(view, delay){		var raiseAfterHourSelect = false;		if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {			raiseCallback(this.options.beforeHourSelect);			raiseAfterHourSelect = true;		}		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);		// Let's make transitions		hideView.addClass('clockpicker-dial-out');		nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');		// Reset clock hand		this.resetClock(delay);		// After transitions ended		clearTimeout(this.toggleViewTimer);		this.toggleViewTimer = setTimeout(function(){			hideView.css('visibility', 'hidden');		}, duration);		if (raiseAfterHourSelect) {			raiseCallback(this.options.afterHourSelect);		}	};	// Reset clock hand	ClockPicker.prototype.resetClock = function(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 ? innerRadius : outerRadius,			x = Math.sin(radian) * radius,			y = - Math.cos(radian) * radius,			self = this;		if (svgSupported && delay) {			self.canvas.addClass('clockpicker-canvas-out');			setTimeout(function(){				self.canvas.removeClass('clockpicker-canvas-out');				self.setHand(x, y);			}, delay);		} else {			this.setHand(x, y);		}	};	// Set clock hand to (x, y)	ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){		var radian = Math.atan2(x, - y),			isHours = this.currentView === 'hours',			unit = Math.PI / (isHours || roundBy5 ? 6 : 30),			z = Math.sqrt(x * x + y * y),			options = this.options,			inner = isHours && z < (outerRadius + innerRadius) / 2,			radius = inner ? innerRadius : outerRadius,			value;			if (options.twelvehour) {				radius = outerRadius;			}		// Radian should in range [0, 2PI]		if (radian < 0) {			radian = Math.PI * 2 + radian;		}		// Get the round value		value = Math.round(radian / unit);		// Get the round radian		radian = value * unit;		// Correct the hours or minutes		if (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 (vibrate && this.options.vibrate) {				// Do not vibrate too frequently				if (! this.vibrateTimer) {					navigator[vibrate](10);					this.vibrateTimer = setTimeout($.proxy(function(){						this.vibrateTimer = null;					}, this), 100);				}			}		}		this[this.currentView] = value;		this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));		// If svg is not supported, just add an active class to the tick		if (! svgSupported) {			this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){				var tick = $(this);				tick.toggleClass('active', value === + tick.html());			});			return;		}		// Place clock hand at the top when dragging		if (dragging || (! isHours && value % 5)) {			this.g.insertBefore(this.hand, this.bearing);			this.g.insertBefore(this.bg, this.fg);			this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');		} else {			// Or place it at the bottom			this.g.insertBefore(this.hand, this.bg);			this.g.insertBefore(this.fg, this.bg);			this.bg.setAttribute('class', 'clockpicker-canvas-bg');		}		// Set clock hand and others' position		var cx = Math.sin(radian) * radius,			cy = - Math.cos(radian) * radius;		this.hand.setAttribute('x2', cx);		this.hand.setAttribute('y2', cy);		this.bg.setAttribute('cx', cx);		this.bg.setAttribute('cy', cy);		this.fg.setAttribute('cx', cx);		this.fg.setAttribute('cy', cy);	};	// Hours and minutes are selected	ClockPicker.prototype.done = function() {		raiseCallback(this.options.beforeDone);		this.hide();		var last = this.input.prop('value'),			value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);		if  (this.options.twelvehour) {			value = value + this.amOrPm;		}		this.input.prop('value', value);		if (value !== last) {			this.input.triggerHandler('change');			if (! this.isInput) {				this.element.trigger('change');			}		}		if (this.options.autoclose) {			this.input.trigger('blur');		}		raiseCallback(this.options.afterDone);	};	// Remove clockpicker from input	ClockPicker.prototype.remove = function() {		this.element.removeData('clockpicker');		this.input.off('focus.clockpicker click.clockpicker');		this.addon.off('click.clockpicker');		if (this.isShown) {			this.hide();		}		if (this.isAppended) {			$win.off('resize.clockpicker' + this.id);			this.popover.remove();		}	};	// Extends $.fn.clockpicker	$.fn.clockpicker = function(option){		var args = Array.prototype.slice.call(arguments, 1);		return this.each(function(){			var $this = $(this),				data = $this.data('clockpicker');			if (! data) {				var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);				$this.data('clockpicker', new ClockPicker($this, options));			} else {				// Manual operatsions. show, hide, remove, e.g.				if (typeof data[option] === 'function') {					data[option].apply(data, args);				}			}		});	};}());
 |