clockpicker.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730
  1. /*!
  2. * ClockPicker v{package.version} (http://weareoutman.github.io/clockpicker/)
  3. * Copyright 2014 Wang Shenwei.
  4. * Licensed under MIT (https://github.com/weareoutman/clockpicker/blob/gh-pages/LICENSE)
  5. */
  6. ;(function(){
  7. var $ = window.jQuery,
  8. $win = $(window),
  9. $doc = $(document),
  10. $body;
  11. // Can I use inline svg ?
  12. var svgNS = 'http://www.w3.org/2000/svg',
  13. svgSupported = 'SVGAngle' in window && (function(){
  14. var supported,
  15. el = document.createElement('div');
  16. el.innerHTML = '<svg/>';
  17. supported = (el.firstChild && el.firstChild.namespaceURI) == svgNS;
  18. el.innerHTML = '';
  19. return supported;
  20. })();
  21. // Can I use transition ?
  22. var transitionSupported = (function(){
  23. var style = document.createElement('div').style;
  24. return 'transition' in style ||
  25. 'WebkitTransition' in style ||
  26. 'MozTransition' in style ||
  27. 'msTransition' in style ||
  28. 'OTransition' in style;
  29. })();
  30. // Listen touch events in touch screen device, instead of mouse events in desktop.
  31. var touchSupported = 'ontouchstart' in window,
  32. mousedownEvent = 'mousedown' + ( touchSupported ? ' touchstart' : ''),
  33. mousemoveEvent = 'mousemove.clockpicker' + ( touchSupported ? ' touchmove.clockpicker' : ''),
  34. mouseupEvent = 'mouseup.clockpicker' + ( touchSupported ? ' touchend.clockpicker' : '');
  35. // Vibrate the device if supported
  36. var vibrate = navigator.vibrate ? 'vibrate' : navigator.webkitVibrate ? 'webkitVibrate' : null;
  37. function createSvgElement(name) {
  38. return document.createElementNS(svgNS, name);
  39. }
  40. function leadingZero(num) {
  41. return (num < 10 ? '0' : '') + num;
  42. }
  43. // Get a unique id
  44. var idCounter = 0;
  45. function uniqueId(prefix) {
  46. var id = ++idCounter + '';
  47. return prefix ? prefix + id : id;
  48. }
  49. // Clock size
  50. var dialRadius = 100,
  51. outerRadius = 80,
  52. // innerRadius = 80 on 12 hour clock
  53. innerRadius = 54,
  54. tickRadius = 13,
  55. diameter = dialRadius * 2,
  56. duration = transitionSupported ? 350 : 1;
  57. // Popover template
  58. var tpl = [
  59. '<div class="popover clockpicker-popover">',
  60. '<div class="arrow"></div>',
  61. '<div class="popover-title">',
  62. '<span class="clockpicker-span-hours text-primary"></span>',
  63. ' : ',
  64. '<span class="clockpicker-span-minutes"></span>',
  65. '<span class="clockpicker-span-am-pm"></span>',
  66. '</div>',
  67. '<div class="popover-content">',
  68. '<div class="clockpicker-plate">',
  69. '<div class="clockpicker-canvas"></div>',
  70. '<div class="clockpicker-dial clockpicker-hours"></div>',
  71. '<div class="clockpicker-dial clockpicker-minutes clockpicker-dial-out"></div>',
  72. '</div>',
  73. '<span class="clockpicker-am-pm-block">',
  74. '</span>',
  75. '</div>',
  76. '</div>'
  77. ].join('');
  78. // ClockPicker
  79. function ClockPicker(element, options) {
  80. var popover = $(tpl),
  81. plate = popover.find('.clockpicker-plate'),
  82. hoursView = popover.find('.clockpicker-hours'),
  83. minutesView = popover.find('.clockpicker-minutes'),
  84. amPmBlock = popover.find('.clockpicker-am-pm-block'),
  85. isInput = element.prop('tagName') === 'INPUT',
  86. input = isInput ? element : element.find('input'),
  87. addon = element.find('.input-group-addon'),
  88. self = this,
  89. timer;
  90. this.id = uniqueId('cp');
  91. this.element = element;
  92. this.options = options;
  93. this.isAppended = false;
  94. this.isShown = false;
  95. this.currentView = 'hours';
  96. this.isInput = isInput;
  97. this.input = input;
  98. this.addon = addon;
  99. this.popover = popover;
  100. this.plate = plate;
  101. this.hoursView = hoursView;
  102. this.minutesView = minutesView;
  103. this.amPmBlock = amPmBlock;
  104. this.spanHours = popover.find('.clockpicker-span-hours');
  105. this.spanMinutes = popover.find('.clockpicker-span-minutes');
  106. this.spanAmPm = popover.find('.clockpicker-span-am-pm');
  107. this.amOrPm = "PM";
  108. // Setup for for 12 hour clock if option is selected
  109. if (options.twelvehour) {
  110. var amPmButtonsTemplate = ['<div class="clockpicker-am-pm-block">',
  111. '<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-am-button">',
  112. 'AM</button>',
  113. '<button type="button" class="btn btn-sm btn-default clockpicker-button clockpicker-pm-button">',
  114. 'PM</button>',
  115. '</div>'].join('');
  116. var amPmButtons = $(amPmButtonsTemplate);
  117. //amPmButtons.appendTo(plate);
  118. ////Not working b/c they are not shown when this runs
  119. //$('clockpicker-am-button')
  120. // .on("click", function() {
  121. // self.amOrPm = "AM";
  122. // $('.clockpicker-span-am-pm').empty().append('AM');
  123. // });
  124. //
  125. //$('clockpicker-pm-button')
  126. // .on("click", function() {
  127. // self.amOrPm = "PM";
  128. // $('.clockpicker-span-am-pm').empty().append('PM');
  129. // });
  130. $('<button type="button" class="btn btn-sm btn-default clockpicker-button am-button">' + "AM" + '</button>')
  131. .on("click", function() {
  132. self.amOrPm = "AM";
  133. $('.clockpicker-span-am-pm').empty().append('AM');
  134. }).appendTo(this.amPmBlock);
  135. $('<button type="button" class="btn btn-sm btn-default clockpicker-button pm-button">' + "PM" + '</button>')
  136. .on("click", function() {
  137. self.amOrPm = 'PM';
  138. $('.clockpicker-span-am-pm').empty().append('PM');
  139. }).appendTo(this.amPmBlock);
  140. }
  141. if (! options.autoclose) {
  142. // If autoclose is not setted, append a button
  143. $('<button type="button" class="btn btn-sm btn-default btn-block clockpicker-button">' + options.donetext + '</button>')
  144. .click($.proxy(this.done, this))
  145. .appendTo(popover);
  146. }
  147. // Placement and arrow align - make sure they make sense.
  148. if ((options.placement === 'top' || options.placement === 'bottom') && (options.align === 'top' || options.align === 'bottom')) options.align = 'left';
  149. if ((options.placement === 'left' || options.placement === 'right') && (options.align === 'left' || options.align === 'right')) options.align = 'top';
  150. popover.addClass(options.placement);
  151. popover.addClass('clockpicker-align-' + options.align);
  152. this.spanHours.click($.proxy(this.toggleView, this, 'hours'));
  153. this.spanMinutes.click($.proxy(this.toggleView, this, 'minutes'));
  154. // Show or toggle
  155. input.on('focus.clockpicker click.clockpicker', $.proxy(this.show, this));
  156. addon.on('click.clockpicker', $.proxy(this.toggle, this));
  157. // Build ticks
  158. var tickTpl = $('<div class="clockpicker-tick"></div>'),
  159. i, tick, radian, radius;
  160. // Hours view
  161. if (options.twelvehour) {
  162. for (i = 1; i < 13; i += 1) {
  163. tick = tickTpl.clone();
  164. radian = i / 6 * Math.PI;
  165. radius = outerRadius;
  166. tick.css('font-size', '120%');
  167. tick.css({
  168. left: dialRadius + Math.sin(radian) * radius - tickRadius,
  169. top: dialRadius - Math.cos(radian) * radius - tickRadius
  170. });
  171. tick.html(i === 0 ? '00' : i);
  172. hoursView.append(tick);
  173. tick.on(mousedownEvent, mousedown);
  174. }
  175. } else {
  176. for (i = 0; i < 24; i += 1) {
  177. tick = tickTpl.clone();
  178. radian = i / 6 * Math.PI;
  179. var inner = i > 0 && i < 13;
  180. radius = inner ? innerRadius : outerRadius;
  181. tick.css({
  182. left: dialRadius + Math.sin(radian) * radius - tickRadius,
  183. top: dialRadius - Math.cos(radian) * radius - tickRadius
  184. });
  185. if (inner) {
  186. tick.css('font-size', '120%');
  187. }
  188. tick.html(i === 0 ? '00' : i);
  189. hoursView.append(tick);
  190. tick.on(mousedownEvent, mousedown);
  191. }
  192. }
  193. // Minutes view
  194. for (i = 0; i < 60; i += 5) {
  195. tick = tickTpl.clone();
  196. radian = i / 30 * Math.PI;
  197. tick.css({
  198. left: dialRadius + Math.sin(radian) * outerRadius - tickRadius,
  199. top: dialRadius - Math.cos(radian) * outerRadius - tickRadius
  200. });
  201. tick.css('font-size', '120%');
  202. tick.html(leadingZero(i));
  203. minutesView.append(tick);
  204. tick.on(mousedownEvent, mousedown);
  205. }
  206. // Clicking on minutes view space
  207. plate.on(mousedownEvent, function(e){
  208. if ($(e.target).closest('.clockpicker-tick').length === 0) {
  209. mousedown(e, true);
  210. }
  211. });
  212. // Mousedown or touchstart
  213. function mousedown(e, space) {
  214. var offset = plate.offset(),
  215. isTouch = /^touch/.test(e.type),
  216. x0 = offset.left + dialRadius,
  217. y0 = offset.top + dialRadius,
  218. dx = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
  219. dy = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0,
  220. z = Math.sqrt(dx * dx + dy * dy),
  221. moved = false;
  222. // When clicking on minutes view space, check the mouse position
  223. if (space && (z < outerRadius - tickRadius || z > outerRadius + tickRadius)) {
  224. return;
  225. }
  226. e.preventDefault();
  227. // Set cursor style of body after 200ms
  228. var movingTimer = setTimeout(function(){
  229. $body.addClass('clockpicker-moving');
  230. }, 200);
  231. // Place the canvas to top
  232. if (svgSupported) {
  233. plate.append(self.canvas);
  234. }
  235. // Clock
  236. self.setHand(dx, dy, ! space, true);
  237. // Mousemove on document
  238. $doc.off(mousemoveEvent).on(mousemoveEvent, function(e){
  239. e.preventDefault();
  240. var isTouch = /^touch/.test(e.type),
  241. x = (isTouch ? e.originalEvent.touches[0] : e).pageX - x0,
  242. y = (isTouch ? e.originalEvent.touches[0] : e).pageY - y0;
  243. if (! moved && x === dx && y === dy) {
  244. // Clicking in chrome on windows will trigger a mousemove event
  245. return;
  246. }
  247. moved = true;
  248. self.setHand(x, y, false, true);
  249. });
  250. // Mouseup on document
  251. $doc.off(mouseupEvent).on(mouseupEvent, function(e){
  252. $doc.off(mouseupEvent);
  253. e.preventDefault();
  254. var isTouch = /^touch/.test(e.type),
  255. x = (isTouch ? e.originalEvent.changedTouches[0] : e).pageX - x0,
  256. y = (isTouch ? e.originalEvent.changedTouches[0] : e).pageY - y0;
  257. if ((space || moved) && x === dx && y === dy) {
  258. self.setHand(x, y);
  259. }
  260. if (self.currentView === 'hours') {
  261. self.toggleView('minutes', duration / 2);
  262. } else {
  263. if (options.autoclose) {
  264. self.minutesView.addClass('clockpicker-dial-out');
  265. setTimeout(function(){
  266. self.done();
  267. }, duration / 2);
  268. }
  269. }
  270. plate.prepend(canvas);
  271. // Reset cursor style of body
  272. clearTimeout(movingTimer);
  273. $body.removeClass('clockpicker-moving');
  274. // Unbind mousemove event
  275. $doc.off(mousemoveEvent);
  276. });
  277. }
  278. if (svgSupported) {
  279. // Draw clock hands and others
  280. var canvas = popover.find('.clockpicker-canvas'),
  281. svg = createSvgElement('svg');
  282. svg.setAttribute('class', 'clockpicker-svg');
  283. svg.setAttribute('width', diameter);
  284. svg.setAttribute('height', diameter);
  285. var g = createSvgElement('g');
  286. g.setAttribute('transform', 'translate(' + dialRadius + ',' + dialRadius + ')');
  287. var bearing = createSvgElement('circle');
  288. bearing.setAttribute('class', 'clockpicker-canvas-bearing');
  289. bearing.setAttribute('cx', 0);
  290. bearing.setAttribute('cy', 0);
  291. bearing.setAttribute('r', 2);
  292. var hand = createSvgElement('line');
  293. hand.setAttribute('x1', 0);
  294. hand.setAttribute('y1', 0);
  295. var bg = createSvgElement('circle');
  296. bg.setAttribute('class', 'clockpicker-canvas-bg');
  297. bg.setAttribute('r', tickRadius);
  298. var fg = createSvgElement('circle');
  299. fg.setAttribute('class', 'clockpicker-canvas-fg');
  300. fg.setAttribute('r', 3.5);
  301. g.appendChild(hand);
  302. g.appendChild(bg);
  303. g.appendChild(fg);
  304. g.appendChild(bearing);
  305. svg.appendChild(g);
  306. canvas.append(svg);
  307. this.hand = hand;
  308. this.bg = bg;
  309. this.fg = fg;
  310. this.bearing = bearing;
  311. this.g = g;
  312. this.canvas = canvas;
  313. }
  314. raiseCallback(this.options.init);
  315. }
  316. function raiseCallback(callbackFunction) {
  317. if (callbackFunction && typeof callbackFunction === "function") {
  318. callbackFunction();
  319. }
  320. }
  321. // Default options
  322. ClockPicker.DEFAULTS = {
  323. 'default': '', // default time, 'now' or '13:14' e.g.
  324. fromnow: 0, // set default time to * milliseconds from now (using with default = 'now')
  325. placement: 'bottom', // clock popover placement
  326. align: 'left', // popover arrow align
  327. donetext: '完成', // done button text
  328. autoclose: false, // auto close when minute is selected
  329. twelvehour: false, // change to 12 hour AM/PM clock from 24 hour
  330. vibrate: true // vibrate the device when dragging clock hand
  331. };
  332. // Show or hide popover
  333. ClockPicker.prototype.toggle = function(){
  334. this[this.isShown ? 'hide' : 'show']();
  335. };
  336. // Set popover position
  337. ClockPicker.prototype.locate = function(){
  338. var element = this.element,
  339. popover = this.popover,
  340. offset = element.offset(),
  341. width = element.outerWidth(),
  342. height = element.outerHeight(),
  343. placement = this.options.placement,
  344. align = this.options.align,
  345. styles = {},
  346. self = this;
  347. popover.show();
  348. // Place the popover
  349. switch (placement) {
  350. case 'bottom':
  351. styles.top = offset.top + height;
  352. break;
  353. case 'right':
  354. styles.left = offset.left + width;
  355. break;
  356. case 'top':
  357. styles.top = offset.top - popover.outerHeight();
  358. break;
  359. case 'left':
  360. styles.left = offset.left - popover.outerWidth();
  361. break;
  362. }
  363. // Align the popover arrow
  364. switch (align) {
  365. case 'left':
  366. styles.left = offset.left;
  367. break;
  368. case 'right':
  369. styles.left = offset.left + width - popover.outerWidth();
  370. break;
  371. case 'top':
  372. styles.top = offset.top;
  373. break;
  374. case 'bottom':
  375. styles.top = offset.top + height - popover.outerHeight();
  376. break;
  377. }
  378. popover.css(styles);
  379. };
  380. // Show popover
  381. ClockPicker.prototype.show = function(e){
  382. // Not show again
  383. if (this.isShown) {
  384. return;
  385. }
  386. raiseCallback(this.options.beforeShow);
  387. var self = this;
  388. // Initialize
  389. if (! this.isAppended) {
  390. // Append popover to body
  391. $body = $(document.body).append(this.popover);
  392. // Reset position when resize
  393. $win.on('resize.clockpicker' + this.id, function(){
  394. if (self.isShown) {
  395. self.locate();
  396. }
  397. });
  398. this.isAppended = true;
  399. }
  400. // Get the time
  401. var value = ((this.input.prop('value') || this.options['default'] || '') + '').split(':');
  402. if (value[0] === 'now') {
  403. var now = new Date(+ new Date() + this.options.fromnow);
  404. value = [
  405. now.getHours(),
  406. now.getMinutes()
  407. ];
  408. }
  409. this.hours = + value[0] || 0;
  410. this.minutes = + value[1] || 0;
  411. this.spanHours.html(leadingZero(this.hours));
  412. this.spanMinutes.html(leadingZero(this.minutes));
  413. // Toggle to hours view
  414. this.toggleView('hours');
  415. // Set position
  416. this.locate();
  417. this.isShown = true;
  418. // Hide when clicking or tabbing on any element except the clock, input and addon
  419. $doc.on('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id, function(e){
  420. var target = $(e.target);
  421. if (target.closest(self.popover).length === 0 &&
  422. target.closest(self.addon).length === 0 &&
  423. target.closest(self.input).length === 0) {
  424. self.hide();
  425. }
  426. });
  427. // Hide when ESC is pressed
  428. $doc.on('keyup.clockpicker.' + this.id, function(e){
  429. if (e.keyCode === 27) {
  430. self.hide();
  431. }
  432. });
  433. raiseCallback(this.options.afterShow);
  434. };
  435. // Hide popover
  436. ClockPicker.prototype.hide = function(){
  437. raiseCallback(this.options.beforeHide);
  438. this.isShown = false;
  439. // Unbinding events on document
  440. $doc.off('click.clockpicker.' + this.id + ' focusin.clockpicker.' + this.id);
  441. $doc.off('keyup.clockpicker.' + this.id);
  442. this.popover.hide();
  443. raiseCallback(this.options.afterHide);
  444. };
  445. // Toggle to hours or minutes view
  446. ClockPicker.prototype.toggleView = function(view, delay){
  447. var raiseAfterHourSelect = false;
  448. if (view === 'minutes' && $(this.hoursView).css("visibility") === "visible") {
  449. raiseCallback(this.options.beforeHourSelect);
  450. raiseAfterHourSelect = true;
  451. }
  452. var isHours = view === 'hours',
  453. nextView = isHours ? this.hoursView : this.minutesView,
  454. hideView = isHours ? this.minutesView : this.hoursView;
  455. this.currentView = view;
  456. this.spanHours.toggleClass('text-primary', isHours);
  457. this.spanMinutes.toggleClass('text-primary', ! isHours);
  458. // Let's make transitions
  459. hideView.addClass('clockpicker-dial-out');
  460. nextView.css('visibility', 'visible').removeClass('clockpicker-dial-out');
  461. // Reset clock hand
  462. this.resetClock(delay);
  463. // After transitions ended
  464. clearTimeout(this.toggleViewTimer);
  465. this.toggleViewTimer = setTimeout(function(){
  466. hideView.css('visibility', 'hidden');
  467. }, duration);
  468. if (raiseAfterHourSelect) {
  469. raiseCallback(this.options.afterHourSelect);
  470. }
  471. };
  472. // Reset clock hand
  473. ClockPicker.prototype.resetClock = function(delay){
  474. var view = this.currentView,
  475. value = this[view],
  476. isHours = view === 'hours',
  477. unit = Math.PI / (isHours ? 6 : 30),
  478. radian = value * unit,
  479. radius = isHours && value > 0 && value < 13 ? innerRadius : outerRadius,
  480. x = Math.sin(radian) * radius,
  481. y = - Math.cos(radian) * radius,
  482. self = this;
  483. if (svgSupported && delay) {
  484. self.canvas.addClass('clockpicker-canvas-out');
  485. setTimeout(function(){
  486. self.canvas.removeClass('clockpicker-canvas-out');
  487. self.setHand(x, y);
  488. }, delay);
  489. } else {
  490. this.setHand(x, y);
  491. }
  492. };
  493. // Set clock hand to (x, y)
  494. ClockPicker.prototype.setHand = function(x, y, roundBy5, dragging){
  495. var radian = Math.atan2(x, - y),
  496. isHours = this.currentView === 'hours',
  497. unit = Math.PI / (isHours || roundBy5 ? 6 : 30),
  498. z = Math.sqrt(x * x + y * y),
  499. options = this.options,
  500. inner = isHours && z < (outerRadius + innerRadius) / 2,
  501. radius = inner ? innerRadius : outerRadius,
  502. value;
  503. if (options.twelvehour) {
  504. radius = outerRadius;
  505. }
  506. // Radian should in range [0, 2PI]
  507. if (radian < 0) {
  508. radian = Math.PI * 2 + radian;
  509. }
  510. // Get the round value
  511. value = Math.round(radian / unit);
  512. // Get the round radian
  513. radian = value * unit;
  514. // Correct the hours or minutes
  515. if (options.twelvehour) {
  516. if (isHours) {
  517. if (value === 0) {
  518. value = 12;
  519. }
  520. } else {
  521. if (roundBy5) {
  522. value *= 5;
  523. }
  524. if (value === 60) {
  525. value = 0;
  526. }
  527. }
  528. } else {
  529. if (isHours) {
  530. if (value === 12) {
  531. value = 0;
  532. }
  533. value = inner ? (value === 0 ? 12 : value) : value === 0 ? 0 : value + 12;
  534. } else {
  535. if (roundBy5) {
  536. value *= 5;
  537. }
  538. if (value === 60) {
  539. value = 0;
  540. }
  541. }
  542. }
  543. // Once hours or minutes changed, vibrate the device
  544. if (this[this.currentView] !== value) {
  545. if (vibrate && this.options.vibrate) {
  546. // Do not vibrate too frequently
  547. if (! this.vibrateTimer) {
  548. navigator[vibrate](10);
  549. this.vibrateTimer = setTimeout($.proxy(function(){
  550. this.vibrateTimer = null;
  551. }, this), 100);
  552. }
  553. }
  554. }
  555. this[this.currentView] = value;
  556. this[isHours ? 'spanHours' : 'spanMinutes'].html(leadingZero(value));
  557. // If svg is not supported, just add an active class to the tick
  558. if (! svgSupported) {
  559. this[isHours ? 'hoursView' : 'minutesView'].find('.clockpicker-tick').each(function(){
  560. var tick = $(this);
  561. tick.toggleClass('active', value === + tick.html());
  562. });
  563. return;
  564. }
  565. // Place clock hand at the top when dragging
  566. if (dragging || (! isHours && value % 5)) {
  567. this.g.insertBefore(this.hand, this.bearing);
  568. this.g.insertBefore(this.bg, this.fg);
  569. this.bg.setAttribute('class', 'clockpicker-canvas-bg clockpicker-canvas-bg-trans');
  570. } else {
  571. // Or place it at the bottom
  572. this.g.insertBefore(this.hand, this.bg);
  573. this.g.insertBefore(this.fg, this.bg);
  574. this.bg.setAttribute('class', 'clockpicker-canvas-bg');
  575. }
  576. // Set clock hand and others' position
  577. var cx = Math.sin(radian) * radius,
  578. cy = - Math.cos(radian) * radius;
  579. this.hand.setAttribute('x2', cx);
  580. this.hand.setAttribute('y2', cy);
  581. this.bg.setAttribute('cx', cx);
  582. this.bg.setAttribute('cy', cy);
  583. this.fg.setAttribute('cx', cx);
  584. this.fg.setAttribute('cy', cy);
  585. };
  586. // Hours and minutes are selected
  587. ClockPicker.prototype.done = function() {
  588. raiseCallback(this.options.beforeDone);
  589. this.hide();
  590. var last = this.input.prop('value'),
  591. value = leadingZero(this.hours) + ':' + leadingZero(this.minutes);
  592. if (this.options.twelvehour) {
  593. value = value + this.amOrPm;
  594. }
  595. this.input.prop('value', value);
  596. if (value !== last) {
  597. this.input.triggerHandler('change');
  598. if (! this.isInput) {
  599. this.element.trigger('change');
  600. }
  601. }
  602. if (this.options.autoclose) {
  603. this.input.trigger('blur');
  604. }
  605. raiseCallback(this.options.afterDone);
  606. };
  607. // Remove clockpicker from input
  608. ClockPicker.prototype.remove = function() {
  609. this.element.removeData('clockpicker');
  610. this.input.off('focus.clockpicker click.clockpicker');
  611. this.addon.off('click.clockpicker');
  612. if (this.isShown) {
  613. this.hide();
  614. }
  615. if (this.isAppended) {
  616. $win.off('resize.clockpicker' + this.id);
  617. this.popover.remove();
  618. }
  619. };
  620. // Extends $.fn.clockpicker
  621. $.fn.clockpicker = function(option){
  622. var args = Array.prototype.slice.call(arguments, 1);
  623. return this.each(function(){
  624. var $this = $(this),
  625. data = $this.data('clockpicker');
  626. if (! data) {
  627. var options = $.extend({}, ClockPicker.DEFAULTS, $this.data(), typeof option == 'object' && option);
  628. $this.data('clockpicker', new ClockPicker($this, options));
  629. } else {
  630. // Manual operatsions. show, hide, remove, e.g.
  631. if (typeof data[option] === 'function') {
  632. data[option].apply(data, args);
  633. }
  634. }
  635. });
  636. };
  637. }());