Açıklama Yok

timePicker.js 65KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496
  1. /*
  2. * jQuery UI Timepicker
  3. *
  4. * Copyright 2010-2013, Francois Gelinas
  5. * Dual licensed under the MIT or GPL Version 2 licenses.
  6. * http://jquery.org/license
  7. *
  8. * http://fgelinas.com/code/timepicker
  9. *
  10. * Depends:
  11. * jquery.ui.core.js
  12. * jquery.ui.position.js (only if position settings are used)
  13. *
  14. * Change version 0.1.0 - moved the t-rex up here
  15. *
  16. ____
  17. ___ .-~. /_"-._
  18. `-._~-. / /_ "~o\ :Y
  19. \ \ / : \~x. ` ')
  20. ] Y / | Y< ~-.__j
  21. / ! _.--~T : l l< /.-~
  22. / / ____.--~ . ` l /~\ \<|Y
  23. / / .-~~" /| . ',-~\ \L|
  24. / / / .^ \ Y~Y \.^>/l_ "--'
  25. / Y .-"( . l__ j_j l_/ /~_.-~ .
  26. Y l / \ ) ~~~." / `/"~ / \.__/l_
  27. | \ _.-" ~-{__ l : l._Z~-.___.--~
  28. | ~---~ / ~~"---\_ ' __[>
  29. l . _.^ ___ _>-y~
  30. \ \ . .-~ .-~ ~>--" /
  31. \ ~---" / ./ _.-'
  32. "-.,_____.,_ _.--~\ _.-~
  33. ~~ ( _} -Row
  34. `. ~(
  35. ) \
  36. /,`--'~\--'~\
  37. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  38. ->T-Rex<-
  39. */
  40. (function ($) {
  41. $.extend($.ui, { timepicker: { version: "0.3.3"} });
  42. var PROP_NAME = 'timepicker',
  43. tpuuid = new Date().getTime();
  44. /* Time picker manager.
  45. Use the singleton instance of this class, $.timepicker, to interact with the time picker.
  46. Settings for (groups of) time pickers are maintained in an instance object,
  47. allowing multiple different settings on the same page. */
  48. function Timepicker() {
  49. this.debug = true; // Change this to true to start debugging
  50. this._curInst = null; // The current instance in use
  51. this._disabledInputs = []; // List of time picker inputs that have been disabled
  52. this._timepickerShowing = false; // True if the popup picker is showing , false if not
  53. this._inDialog = false; // True if showing within a "dialog", false if not
  54. this._dialogClass = 'ui-timepicker-dialog'; // The name of the dialog marker class
  55. this._mainDivId = 'ui-timepicker-div'; // The ID of the main timepicker division
  56. this._inlineClass = 'ui-timepicker-inline'; // The name of the inline marker class
  57. this._currentClass = 'ui-timepicker-current'; // The name of the current hour / minutes marker class
  58. this._dayOverClass = 'ui-timepicker-days-cell-over'; // The name of the day hover marker class
  59. this.regional = []; // Available regional settings, indexed by language code
  60. this.regional[''] = { // Default regional settings
  61. hourText: 'Hour', // Display text for hours section
  62. minuteText: 'Minute', // Display text for minutes link
  63. amPmText: ['AM', 'PM'], // Display text for AM PM
  64. closeButtonText: 'Done', // Text for the confirmation button (ok button)
  65. nowButtonText: 'Now', // Text for the now button
  66. deselectButtonText: 'Deselect' // Text for the deselect button
  67. };
  68. this._defaults = { // Global defaults for all the time picker instances
  69. showOn: 'focus', // 'focus' for popup on focus,
  70. // 'button' for trigger button, or 'both' for either (not yet implemented)
  71. button: null, // 'button' element that will trigger the timepicker
  72. showAnim: 'fadeIn', // Name of jQuery animation for popup
  73. showOptions: {}, // Options for enhanced animations
  74. appendText: '', // Display text following the input box, e.g. showing the format
  75. beforeShow: null, // Define a callback function executed before the timepicker is shown
  76. onSelect: null, // Define a callback function when a hour / minutes is selected
  77. onClose: null, // Define a callback function when the timepicker is closed
  78. timeSeparator: ':', // The character to use to separate hours and minutes.
  79. periodSeparator: ' ', // The character to use to separate the time from the time period.
  80. showPeriod: false, // Define whether or not to show AM/PM with selected time
  81. showPeriodLabels: true, // Show the AM/PM labels on the left of the time picker
  82. showLeadingZero: true, // Define whether or not to show a leading zero for hours < 10. [true/false]
  83. showMinutesLeadingZero: true, // Define whether or not to show a leading zero for minutes < 10.
  84. altField: '', // Selector for an alternate field to store selected time into
  85. defaultTime: 'now', // Used as default time when input field is empty or for inline timePicker
  86. // (set to 'now' for the current time, '' for no highlighted time)
  87. myPosition: 'left top', // Position of the dialog relative to the input.
  88. // see the position utility for more info : http://jqueryui.com/demos/position/
  89. atPosition: 'left bottom', // Position of the input element to match
  90. // Note : if the position utility is not loaded, the timepicker will attach left top to left bottom
  91. //NEW: 2011-02-03
  92. onHourShow: null, // callback for enabling / disabling on selectable hours ex : function(hour) { return true; }
  93. onMinuteShow: null, // callback for enabling / disabling on time selection ex : function(hour,minute) { return true; }
  94. hours: {
  95. starts: 0, // first displayed hour
  96. ends: 23 // last displayed hour
  97. },
  98. minutes: {
  99. starts: 0, // first displayed minute
  100. ends: 55, // last displayed minute
  101. interval: 5, // interval of displayed minutes
  102. manual: [] // optional extra manual entries for minutes
  103. },
  104. rows: 4, // number of rows for the input tables, minimum 2, makes more sense if you use multiple of 2
  105. // 2011-08-05 0.2.4
  106. showHours: true, // display the hours section of the dialog
  107. showMinutes: true, // display the minute section of the dialog
  108. optionalMinutes: false, // optionally parse inputs of whole hours with minutes omitted
  109. // buttons
  110. showCloseButton: false, // shows an OK button to confirm the edit
  111. showNowButton: false, // Shows the 'now' button
  112. showDeselectButton: false, // Shows the deselect time button
  113. maxTime: {
  114. hour: null,
  115. minute: null
  116. },
  117. minTime: {
  118. hour: null,
  119. minute: null
  120. }
  121. };
  122. $.extend(this._defaults, this.regional['']);
  123. this.tpDiv = $('<div id="' + this._mainDivId + '" class="ui-timepicker ui-widget ui-helper-clearfix ui-corner-all " style="display: none"></div>');
  124. }
  125. $.extend(Timepicker.prototype, {
  126. /* Class name added to elements to indicate already configured with a time picker. */
  127. markerClassName: 'hasTimepicker',
  128. /* Debug logging (if enabled). */
  129. log: function () {
  130. if (this.debug)
  131. console.log.apply('', arguments);
  132. },
  133. _widgetTimepicker: function () {
  134. return this.tpDiv;
  135. },
  136. /* Override the default settings for all instances of the time picker.
  137. @param settings object - the new settings to use as defaults (anonymous object)
  138. @return the manager object */
  139. setDefaults: function (settings) {
  140. extendRemove(this._defaults, settings || {});
  141. return this;
  142. },
  143. /* Attach the time picker to a jQuery selection.
  144. @param target element - the target input field or division or span
  145. @param settings object - the new settings to use for this time picker instance (anonymous) */
  146. _attachTimepicker: function (target, settings) {
  147. // check for settings on the control itself - in namespace 'time:'
  148. var inlineSettings = null;
  149. for (var attrName in this._defaults) {
  150. var attrValue = target.getAttribute('time:' + attrName);
  151. if (attrValue) {
  152. inlineSettings = inlineSettings || {};
  153. try {
  154. inlineSettings[attrName] = eval(attrValue);
  155. } catch (err) {
  156. inlineSettings[attrName] = attrValue;
  157. }
  158. }
  159. }
  160. var nodeName = target.nodeName.toLowerCase();
  161. var inline = (nodeName == 'div' || nodeName == 'span');
  162. if (!target.id) {
  163. this.uuid += 1;
  164. target.id = 'tp' + this.uuid;
  165. }
  166. var inst = this._newInst($(target), inline);
  167. inst.settings = $.extend({}, settings || {}, inlineSettings || {});
  168. if (nodeName == 'input') {
  169. this._connectTimepicker(target, inst);
  170. // init inst.hours and inst.minutes from the input value
  171. this._setTimeFromField(inst);
  172. } else if (inline) {
  173. this._inlineTimepicker(target, inst);
  174. }
  175. },
  176. /* Create a new instance object. */
  177. _newInst: function (target, inline) {
  178. var id = target[0].id.replace(/([^A-Za-z0-9_-])/g, '\\\\$1'); // escape jQuery meta chars
  179. return {
  180. id: id, input: target, // associated target
  181. inline: inline, // is timepicker inline or not :
  182. tpDiv: (!inline ? this.tpDiv : // presentation div
  183. $('<div class="' + this._inlineClass + ' ui-timepicker ui-widget ui-helper-clearfix"></div>'))
  184. };
  185. },
  186. /* Attach the time picker to an input field. */
  187. _connectTimepicker: function (target, inst) {
  188. var input = $(target);
  189. inst.append = $([]);
  190. inst.trigger = $([]);
  191. if (input.hasClass(this.markerClassName)) { return; }
  192. this._attachments(input, inst);
  193. input.addClass(this.markerClassName).
  194. keydown(this._doKeyDown).
  195. keyup(this._doKeyUp).
  196. bind("setData.timepicker", function (event, key, value) {
  197. inst.settings[key] = value;
  198. }).
  199. bind("getData.timepicker", function (event, key) {
  200. return this._get(inst, key);
  201. });
  202. $.data(target, PROP_NAME, inst);
  203. },
  204. /* Handle keystrokes. */
  205. _doKeyDown: function (event) {
  206. var inst = $.timepicker._getInst(event.target);
  207. var handled = true;
  208. inst._keyEvent = true;
  209. if ($.timepicker._timepickerShowing) {
  210. switch (event.keyCode) {
  211. case 9: $.timepicker._hideTimepicker();
  212. handled = false;
  213. break; // hide on tab out
  214. case 13:
  215. $.timepicker._updateSelectedValue(inst);
  216. $.timepicker._hideTimepicker();
  217. return false; // don't submit the form
  218. break; // select the value on enter
  219. case 27: $.timepicker._hideTimepicker();
  220. break; // hide on escape
  221. default: handled = false;
  222. }
  223. }
  224. else if (event.keyCode == 36 && event.ctrlKey) { // display the time picker on ctrl+home
  225. $.timepicker._showTimepicker(this);
  226. }
  227. else {
  228. handled = false;
  229. }
  230. if (handled) {
  231. event.preventDefault();
  232. event.stopPropagation();
  233. }
  234. },
  235. /* Update selected time on keyUp */
  236. /* Added verion 0.0.5 */
  237. _doKeyUp: function (event) {
  238. var inst = $.timepicker._getInst(event.target);
  239. $.timepicker._setTimeFromField(inst);
  240. $.timepicker._updateTimepicker(inst);
  241. },
  242. /* Make attachments based on settings. */
  243. _attachments: function (input, inst) {
  244. var appendText = this._get(inst, 'appendText');
  245. var isRTL = this._get(inst, 'isRTL');
  246. if (inst.append) { inst.append.remove(); }
  247. if (appendText) {
  248. inst.append = $('<span class="' + this._appendClass + '">' + appendText + '</span>');
  249. input[isRTL ? 'before' : 'after'](inst.append);
  250. }
  251. input.unbind('focus.timepicker', this._showTimepicker);
  252. input.unbind('click.timepicker', this._adjustZIndex);
  253. if (inst.trigger) { inst.trigger.remove(); }
  254. var showOn = this._get(inst, 'showOn');
  255. if (showOn == 'focus' || showOn == 'both') { // pop-up time picker when in the marked field
  256. input.bind("focus.timepicker", this._showTimepicker);
  257. input.bind("click.timepicker", this._adjustZIndex);
  258. }
  259. if (showOn == 'button' || showOn == 'both') { // pop-up time picker when 'button' element is clicked
  260. var button = this._get(inst, 'button');
  261. // Add button if button element is not set
  262. if(button == null) {
  263. button = $('<button class="ui-timepicker-trigger" type="button">...</button>');
  264. input.after(button);
  265. }
  266. $(button).bind("click.timepicker", function () {
  267. if ($.timepicker._timepickerShowing && $.timepicker._lastInput == input[0]) {
  268. $.timepicker._hideTimepicker();
  269. } else if (!inst.input.is(':disabled')) {
  270. $.timepicker._showTimepicker(input[0]);
  271. }
  272. return false;
  273. });
  274. }
  275. },
  276. /* Attach an inline time picker to a div. */
  277. _inlineTimepicker: function(target, inst) {
  278. var divSpan = $(target);
  279. if (divSpan.hasClass(this.markerClassName))
  280. return;
  281. divSpan.addClass(this.markerClassName).append(inst.tpDiv).
  282. bind("setData.timepicker", function(event, key, value){
  283. inst.settings[key] = value;
  284. }).bind("getData.timepicker", function(event, key){
  285. return this._get(inst, key);
  286. });
  287. $.data(target, PROP_NAME, inst);
  288. this._setTimeFromField(inst);
  289. this._updateTimepicker(inst);
  290. inst.tpDiv.show();
  291. },
  292. _adjustZIndex: function(input) {
  293. input = input.target || input;
  294. var inst = $.timepicker._getInst(input);
  295. inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
  296. },
  297. /* Pop-up the time picker for a given input field.
  298. @param input element - the input field attached to the time picker or
  299. event - if triggered by focus */
  300. _showTimepicker: function (input) {
  301. input = input.target || input;
  302. if (input.nodeName.toLowerCase() != 'input') { input = $('input', input.parentNode)[0]; } // find from button/image trigger
  303. if ($.timepicker._isDisabledTimepicker(input) || $.timepicker._lastInput == input) { return; } // already here
  304. // fix v 0.0.8 - close current timepicker before showing another one
  305. $.timepicker._hideTimepicker();
  306. var inst = $.timepicker._getInst(input);
  307. if ($.timepicker._curInst && $.timepicker._curInst != inst) {
  308. $.timepicker._curInst.tpDiv.stop(true, true);
  309. }
  310. var beforeShow = $.timepicker._get(inst, 'beforeShow');
  311. extendRemove(inst.settings, (beforeShow ? beforeShow.apply(input, [input, inst]) : {}));
  312. inst.lastVal = null;
  313. $.timepicker._lastInput = input;
  314. $.timepicker._setTimeFromField(inst);
  315. // calculate default position
  316. if ($.timepicker._inDialog) { input.value = ''; } // hide cursor
  317. if (!$.timepicker._pos) { // position below input
  318. $.timepicker._pos = $.timepicker._findPos(input);
  319. $.timepicker._pos[1] += input.offsetHeight; // add the height
  320. }
  321. var isFixed = false;
  322. $(input).parents().each(function () {
  323. isFixed |= $(this).css('position') == 'fixed';
  324. return !isFixed;
  325. });
  326. var offset = { left: $.timepicker._pos[0], top: $.timepicker._pos[1] };
  327. $.timepicker._pos = null;
  328. // determine sizing offscreen
  329. inst.tpDiv.css({ position: 'absolute', display: 'block', top: '-1000px' });
  330. $.timepicker._updateTimepicker(inst);
  331. // position with the ui position utility, if loaded
  332. if ( ( ! inst.inline ) && ( typeof $.ui.position == 'object' ) ) {
  333. inst.tpDiv.position({
  334. of: inst.input,
  335. my: $.timepicker._get( inst, 'myPosition' ),
  336. at: $.timepicker._get( inst, 'atPosition' ),
  337. // offset: $( "#offset" ).val(),
  338. // using: using,
  339. collision: 'flip'
  340. });
  341. var offset = inst.tpDiv.offset();
  342. $.timepicker._pos = [offset.top, offset.left];
  343. }
  344. // reset clicked state
  345. inst._hoursClicked = false;
  346. inst._minutesClicked = false;
  347. // fix width for dynamic number of time pickers
  348. // and adjust position before showing
  349. offset = $.timepicker._checkOffset(inst, offset, isFixed);
  350. inst.tpDiv.css({ position: ($.timepicker._inDialog && $.blockUI ?
  351. 'static' : (isFixed ? 'fixed' : 'absolute')), display: 'none',
  352. left: offset.left + 'px', top: offset.top + 'px'
  353. });
  354. if ( ! inst.inline ) {
  355. var showAnim = $.timepicker._get(inst, 'showAnim');
  356. var duration = $.timepicker._get(inst, 'duration');
  357. var postProcess = function () {
  358. $.timepicker._timepickerShowing = true;
  359. var borders = $.timepicker._getBorders(inst.tpDiv);
  360. inst.tpDiv.find('iframe.ui-timepicker-cover'). // IE6- only
  361. css({ left: -borders[0], top: -borders[1],
  362. width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
  363. });
  364. };
  365. // Fixed the zIndex problem for real (I hope) - FG - v 0.2.9
  366. $.timepicker._adjustZIndex(input);
  367. //inst.tpDiv.css('zIndex', $.timepicker._getZIndex(input) +1);
  368. if ($.effects && $.effects[showAnim]) {
  369. inst.tpDiv.show(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
  370. }
  371. else {
  372. inst.tpDiv.show((showAnim ? duration : null), postProcess);
  373. }
  374. if (!showAnim || !duration) { postProcess(); }
  375. if (inst.input.is(':visible') && !inst.input.is(':disabled')) { inst.input.focus(); }
  376. $.timepicker._curInst = inst;
  377. }
  378. },
  379. // This is an enhanced copy of the zIndex function of UI core 1.8.?? For backward compatibility.
  380. // Enhancement returns maximum zindex value discovered while traversing parent elements,
  381. // rather than the first zindex value found. Ensures the timepicker popup will be in front,
  382. // even in funky scenarios like non-jq dialog containers with large fixed zindex values and
  383. // nested zindex-influenced elements of their own.
  384. _getZIndex: function (target) {
  385. var elem = $(target);
  386. var maxValue = 0;
  387. var position, value;
  388. while (elem.length && elem[0] !== document) {
  389. position = elem.css("position");
  390. if (position === "absolute" || position === "relative" || position === "fixed") {
  391. value = parseInt(elem.css("zIndex"), 10);
  392. if (!isNaN(value) && value !== 0) {
  393. if (value > maxValue) { maxValue = value; }
  394. }
  395. }
  396. elem = elem.parent();
  397. }
  398. return maxValue;
  399. },
  400. /* Refresh the time picker
  401. @param target element - The target input field or inline container element. */
  402. _refreshTimepicker: function(target) {
  403. var inst = this._getInst(target);
  404. if (inst) {
  405. this._updateTimepicker(inst);
  406. }
  407. },
  408. /* Generate the time picker content. */
  409. _updateTimepicker: function (inst) {
  410. inst.tpDiv.empty().append(this._generateHTML(inst));
  411. this._rebindDialogEvents(inst);
  412. },
  413. _rebindDialogEvents: function (inst) {
  414. var borders = $.timepicker._getBorders(inst.tpDiv),
  415. self = this;
  416. inst.tpDiv
  417. .find('iframe.ui-timepicker-cover') // IE6- only
  418. .css({ left: -borders[0], top: -borders[1],
  419. width: inst.tpDiv.outerWidth(), height: inst.tpDiv.outerHeight()
  420. })
  421. .end()
  422. // after the picker html is appended bind the click & double click events (faster in IE this way
  423. // then letting the browser interpret the inline events)
  424. // the binding for the minute cells also exists in _updateMinuteDisplay
  425. .find('.ui-timepicker-minute-cell')
  426. .unbind()
  427. .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
  428. .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this))
  429. .end()
  430. .find('.ui-timepicker-hour-cell')
  431. .unbind()
  432. .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectHours, this))
  433. .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectHours, this))
  434. .end()
  435. .find('.ui-timepicker td a')
  436. .unbind()
  437. .bind('mouseout', function () {
  438. $(this).removeClass('ui-state-hover');
  439. if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).removeClass('ui-timepicker-prev-hover');
  440. if (this.className.indexOf('ui-timepicker-next') != -1) $(this).removeClass('ui-timepicker-next-hover');
  441. })
  442. .bind('mouseover', function () {
  443. if ( ! self._isDisabledTimepicker(inst.inline ? inst.tpDiv.parent()[0] : inst.input[0])) {
  444. $(this).parents('.ui-timepicker-calendar').find('a').removeClass('ui-state-hover');
  445. $(this).addClass('ui-state-hover');
  446. if (this.className.indexOf('ui-timepicker-prev') != -1) $(this).addClass('ui-timepicker-prev-hover');
  447. if (this.className.indexOf('ui-timepicker-next') != -1) $(this).addClass('ui-timepicker-next-hover');
  448. }
  449. })
  450. .end()
  451. .find('.' + this._dayOverClass + ' a')
  452. .trigger('mouseover')
  453. .end()
  454. .find('.ui-timepicker-now').bind("click", function(e) {
  455. $.timepicker.selectNow(e);
  456. }).end()
  457. .find('.ui-timepicker-deselect').bind("click",function(e) {
  458. $.timepicker.deselectTime(e);
  459. }).end()
  460. .find('.ui-timepicker-close').bind("click",function(e) {
  461. $.timepicker._hideTimepicker();
  462. }).end();
  463. },
  464. /* Generate the HTML for the current state of the time picker. */
  465. _generateHTML: function (inst) {
  466. var h, m, row, col, html, hoursHtml, minutesHtml = '',
  467. showPeriod = (this._get(inst, 'showPeriod') == true),
  468. showPeriodLabels = (this._get(inst, 'showPeriodLabels') == true),
  469. showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
  470. showHours = (this._get(inst, 'showHours') == true),
  471. showMinutes = (this._get(inst, 'showMinutes') == true),
  472. amPmText = this._get(inst, 'amPmText'),
  473. rows = this._get(inst, 'rows'),
  474. amRows = 0,
  475. pmRows = 0,
  476. amItems = 0,
  477. pmItems = 0,
  478. amFirstRow = 0,
  479. pmFirstRow = 0,
  480. hours = Array(),
  481. hours_options = this._get(inst, 'hours'),
  482. hoursPerRow = null,
  483. hourCounter = 0,
  484. hourLabel = this._get(inst, 'hourText'),
  485. showCloseButton = this._get(inst, 'showCloseButton'),
  486. closeButtonText = this._get(inst, 'closeButtonText'),
  487. showNowButton = this._get(inst, 'showNowButton'),
  488. nowButtonText = this._get(inst, 'nowButtonText'),
  489. showDeselectButton = this._get(inst, 'showDeselectButton'),
  490. deselectButtonText = this._get(inst, 'deselectButtonText'),
  491. showButtonPanel = showCloseButton || showNowButton || showDeselectButton;
  492. // prepare all hours and minutes, makes it easier to distribute by rows
  493. for (h = hours_options.starts; h <= hours_options.ends; h++) {
  494. hours.push (h);
  495. }
  496. hoursPerRow = Math.ceil(hours.length / rows); // always round up
  497. if (showPeriodLabels) {
  498. for (hourCounter = 0; hourCounter < hours.length; hourCounter++) {
  499. if (hours[hourCounter] < 12) {
  500. amItems++;
  501. }
  502. else {
  503. pmItems++;
  504. }
  505. }
  506. hourCounter = 0;
  507. amRows = Math.floor(amItems / hours.length * rows);
  508. pmRows = Math.floor(pmItems / hours.length * rows);
  509. // assign the extra row to the period that is more densely populated
  510. if (rows != amRows + pmRows) {
  511. // Make sure: AM Has Items and either PM Does Not, AM has no rows yet, or AM is more dense
  512. if (amItems && (!pmItems || !amRows || (pmRows && amItems / amRows >= pmItems / pmRows))) {
  513. amRows++;
  514. } else {
  515. pmRows++;
  516. }
  517. }
  518. amFirstRow = Math.min(amRows, 1);
  519. pmFirstRow = amRows + 1;
  520. if (amRows == 0) {
  521. hoursPerRow = Math.ceil(pmItems / pmRows);
  522. } else if (pmRows == 0) {
  523. hoursPerRow = Math.ceil(amItems / amRows);
  524. } else {
  525. hoursPerRow = Math.ceil(Math.max(amItems / amRows, pmItems / pmRows));
  526. }
  527. }
  528. html = '<table class="ui-timepicker-table ui-widget-content ui-corner-all"><tr>';
  529. if (showHours) {
  530. html += '<td class="ui-timepicker-hours">' +
  531. '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
  532. hourLabel +
  533. '</div>' +
  534. '<table class="ui-timepicker">';
  535. for (row = 1; row <= rows; row++) {
  536. html += '<tr>';
  537. // AM
  538. if (row == amFirstRow && showPeriodLabels) {
  539. html += '<th rowspan="' + amRows.toString() + '" class="periods" scope="row">' + amPmText[0] + '</th>';
  540. }
  541. // PM
  542. if (row == pmFirstRow && showPeriodLabels) {
  543. html += '<th rowspan="' + pmRows.toString() + '" class="periods" scope="row">' + amPmText[1] + '</th>';
  544. }
  545. for (col = 1; col <= hoursPerRow; col++) {
  546. if (showPeriodLabels && row < pmFirstRow && hours[hourCounter] >= 12) {
  547. html += this._generateHTMLHourCell(inst, undefined, showPeriod, showLeadingZero);
  548. } else {
  549. html += this._generateHTMLHourCell(inst, hours[hourCounter], showPeriod, showLeadingZero);
  550. hourCounter++;
  551. }
  552. }
  553. html += '</tr>';
  554. }
  555. html += '</table>' + // Close the hours cells table
  556. '</td>'; // Close the Hour td
  557. }
  558. if (showMinutes) {
  559. html += '<td class="ui-timepicker-minutes">';
  560. html += this._generateHTMLMinutes(inst);
  561. html += '</td>';
  562. }
  563. html += '</tr>';
  564. if (showButtonPanel) {
  565. var buttonPanel = '<tr><td colspan="3"><div class="ui-timepicker-buttonpane ui-widget-content">';
  566. if (showNowButton) {
  567. buttonPanel += '<button type="button" class="ui-timepicker-now ui-state-default ui-corner-all" '
  568. + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
  569. + nowButtonText + '</button>';
  570. }
  571. if (showDeselectButton) {
  572. buttonPanel += '<button type="button" class="ui-timepicker-deselect ui-state-default ui-corner-all" '
  573. + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
  574. + deselectButtonText + '</button>';
  575. }
  576. if (showCloseButton) {
  577. buttonPanel += '<button type="button" class="ui-timepicker-close ui-state-default ui-corner-all" '
  578. + ' data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" >'
  579. + closeButtonText + '</button>';
  580. }
  581. html += buttonPanel + '</div></td></tr>';
  582. }
  583. html += '</table>';
  584. return html;
  585. },
  586. /* Special function that update the minutes selection in currently visible timepicker
  587. * called on hour selection when onMinuteShow is defined */
  588. _updateMinuteDisplay: function (inst) {
  589. var newHtml = this._generateHTMLMinutes(inst);
  590. inst.tpDiv.find('td.ui-timepicker-minutes').html(newHtml);
  591. this._rebindDialogEvents(inst);
  592. // after the picker html is appended bind the click & double click events (faster in IE this way
  593. // then letting the browser interpret the inline events)
  594. // yes I know, duplicate code, sorry
  595. /* .find('.ui-timepicker-minute-cell')
  596. .bind("click", { fromDoubleClick:false }, $.proxy($.timepicker.selectMinutes, this))
  597. .bind("dblclick", { fromDoubleClick:true }, $.proxy($.timepicker.selectMinutes, this));
  598. */
  599. },
  600. /*
  601. * Generate the minutes table
  602. * This is separated from the _generateHTML function because is can be called separately (when hours changes)
  603. */
  604. _generateHTMLMinutes: function (inst) {
  605. var m, row, html = '',
  606. rows = this._get(inst, 'rows'),
  607. minutes = Array(),
  608. minutes_options = this._get(inst, 'minutes'),
  609. minutesPerRow = null,
  610. minuteCounter = 0,
  611. showMinutesLeadingZero = (this._get(inst, 'showMinutesLeadingZero') == true),
  612. onMinuteShow = this._get(inst, 'onMinuteShow'),
  613. minuteLabel = this._get(inst, 'minuteText');
  614. if ( ! minutes_options.starts) {
  615. minutes_options.starts = 0;
  616. }
  617. if ( ! minutes_options.ends) {
  618. minutes_options.ends = 59;
  619. }
  620. if ( ! minutes_options.manual) {
  621. minutes_options.manual = [];
  622. }
  623. for (m = minutes_options.starts; m <= minutes_options.ends; m += minutes_options.interval) {
  624. minutes.push(m);
  625. }
  626. for (i = 0; i < minutes_options.manual.length;i++) {
  627. var currMin = minutes_options.manual[i];
  628. // Validate & filter duplicates of manual minute input
  629. if (typeof currMin != 'number' || currMin < 0 || currMin > 59 || $.inArray(currMin, minutes) >= 0) {
  630. continue;
  631. }
  632. minutes.push(currMin);
  633. }
  634. // Sort to get correct order after adding manual minutes
  635. // Use compare function to sort by number, instead of string (default)
  636. minutes.sort(function(a, b) {
  637. return a-b;
  638. });
  639. minutesPerRow = Math.round(minutes.length / rows + 0.49); // always round up
  640. /*
  641. * The minutes table
  642. */
  643. // if currently selected minute is not enabled, we have a problem and need to select a new minute.
  644. if (onMinuteShow &&
  645. (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours , inst.minutes]) == false) ) {
  646. // loop minutes and select first available
  647. for (minuteCounter = 0; minuteCounter < minutes.length; minuteCounter += 1) {
  648. m = minutes[minuteCounter];
  649. if (onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours, m])) {
  650. inst.minutes = m;
  651. break;
  652. }
  653. }
  654. }
  655. html += '<div class="ui-timepicker-title ui-widget-header ui-helper-clearfix ui-corner-all">' +
  656. minuteLabel +
  657. '</div>' +
  658. '<table class="ui-timepicker">';
  659. minuteCounter = 0;
  660. for (row = 1; row <= rows; row++) {
  661. html += '<tr>';
  662. while (minuteCounter < row * minutesPerRow) {
  663. var m = minutes[minuteCounter];
  664. var displayText = '';
  665. if (m !== undefined ) {
  666. displayText = (m < 10) && showMinutesLeadingZero ? "0" + m.toString() : m.toString();
  667. }
  668. html += this._generateHTMLMinuteCell(inst, m, displayText);
  669. minuteCounter++;
  670. }
  671. html += '</tr>';
  672. }
  673. html += '</table>';
  674. return html;
  675. },
  676. /* Generate the content of a "Hour" cell */
  677. _generateHTMLHourCell: function (inst, hour, showPeriod, showLeadingZero) {
  678. var displayHour = hour;
  679. if ((hour > 12) && showPeriod) {
  680. displayHour = hour - 12;
  681. }
  682. if ((displayHour == 0) && showPeriod) {
  683. displayHour = 12;
  684. }
  685. if ((displayHour < 10) && showLeadingZero) {
  686. displayHour = '0' + displayHour;
  687. }
  688. var html = "";
  689. var enabled = true;
  690. var onHourShow = this._get(inst, 'onHourShow'); //custom callback
  691. var maxTime = this._get(inst, 'maxTime');
  692. var minTime = this._get(inst, 'minTime');
  693. if (hour == undefined) {
  694. html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
  695. return html;
  696. }
  697. if (onHourShow) {
  698. enabled = onHourShow.apply((inst.input ? inst.input[0] : null), [hour]);
  699. }
  700. if (enabled) {
  701. if ( !isNaN(parseInt(maxTime.hour)) && hour > maxTime.hour ) enabled = false;
  702. if ( !isNaN(parseInt(minTime.hour)) && hour < minTime.hour ) enabled = false;
  703. }
  704. if (enabled) {
  705. html = '<td class="ui-timepicker-hour-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-hour="' + hour.toString() + '">' +
  706. '<a class="ui-state-default ' +
  707. (hour == inst.hours ? 'ui-state-active' : '') +
  708. '">' +
  709. displayHour.toString() +
  710. '</a></td>';
  711. }
  712. else {
  713. html =
  714. '<td>' +
  715. '<span class="ui-state-default ui-state-disabled ' +
  716. (hour == inst.hours ? ' ui-state-active ' : ' ') +
  717. '">' +
  718. displayHour.toString() +
  719. '</span>' +
  720. '</td>';
  721. }
  722. return html;
  723. },
  724. /* Generate the content of a "Hour" cell */
  725. _generateHTMLMinuteCell: function (inst, minute, displayText) {
  726. var html = "";
  727. var enabled = true;
  728. var hour = inst.hours;
  729. var onMinuteShow = this._get(inst, 'onMinuteShow'); //custom callback
  730. var maxTime = this._get(inst, 'maxTime');
  731. var minTime = this._get(inst, 'minTime');
  732. if (onMinuteShow) {
  733. //NEW: 2011-02-03 we should give the hour as a parameter as well!
  734. enabled = onMinuteShow.apply((inst.input ? inst.input[0] : null), [inst.hours,minute]); //trigger callback
  735. }
  736. if (minute == undefined) {
  737. html = '<td><span class="ui-state-default ui-state-disabled">&nbsp;</span></td>';
  738. return html;
  739. }
  740. if (enabled && hour !== null) {
  741. if ( !isNaN(parseInt(maxTime.hour)) && !isNaN(parseInt(maxTime.minute)) && hour >= maxTime.hour && minute > maxTime.minute ) enabled = false;
  742. if ( !isNaN(parseInt(minTime.hour)) && !isNaN(parseInt(minTime.minute)) && hour <= minTime.hour && minute < minTime.minute ) enabled = false;
  743. }
  744. if (enabled) {
  745. html = '<td class="ui-timepicker-minute-cell" data-timepicker-instance-id="#' + inst.id.replace(/\\\\/g,"\\") + '" data-minute="' + minute.toString() + '" >' +
  746. '<a class="ui-state-default ' +
  747. (minute == inst.minutes ? 'ui-state-active' : '') +
  748. '" >' +
  749. displayText +
  750. '</a></td>';
  751. }
  752. else {
  753. html = '<td>' +
  754. '<span class="ui-state-default ui-state-disabled" >' +
  755. displayText +
  756. '</span>' +
  757. '</td>';
  758. }
  759. return html;
  760. },
  761. /* Detach a timepicker from its control.
  762. @param target element - the target input field or division or span */
  763. _destroyTimepicker: function(target) {
  764. var $target = $(target);
  765. var inst = $.data(target, PROP_NAME);
  766. if (!$target.hasClass(this.markerClassName)) {
  767. return;
  768. }
  769. var nodeName = target.nodeName.toLowerCase();
  770. $.removeData(target, PROP_NAME);
  771. if (nodeName == 'input') {
  772. inst.append.remove();
  773. inst.trigger.remove();
  774. $target.removeClass(this.markerClassName)
  775. .unbind('focus.timepicker', this._showTimepicker)
  776. .unbind('click.timepicker', this._adjustZIndex);
  777. } else if (nodeName == 'div' || nodeName == 'span')
  778. $target.removeClass(this.markerClassName).empty();
  779. },
  780. /* Enable the date picker to a jQuery selection.
  781. @param target element - the target input field or division or span */
  782. _enableTimepicker: function(target) {
  783. var $target = $(target),
  784. target_id = $target.attr('id'),
  785. inst = $.data(target, PROP_NAME);
  786. if (!$target.hasClass(this.markerClassName)) {
  787. return;
  788. }
  789. var nodeName = target.nodeName.toLowerCase();
  790. if (nodeName == 'input') {
  791. target.disabled = false;
  792. var button = this._get(inst, 'button');
  793. $(button).removeClass('ui-state-disabled').disabled = false;
  794. inst.trigger.filter('button').
  795. each(function() { this.disabled = false; }).end();
  796. }
  797. else if (nodeName == 'div' || nodeName == 'span') {
  798. var inline = $target.children('.' + this._inlineClass);
  799. inline.children().removeClass('ui-state-disabled');
  800. inline.find('button').each(
  801. function() { this.disabled = false }
  802. )
  803. }
  804. this._disabledInputs = $.map(this._disabledInputs,
  805. function(value) { return (value == target_id ? null : value); }); // delete entry
  806. },
  807. /* Disable the time picker to a jQuery selection.
  808. @param target element - the target input field or division or span */
  809. _disableTimepicker: function(target) {
  810. var $target = $(target);
  811. var inst = $.data(target, PROP_NAME);
  812. if (!$target.hasClass(this.markerClassName)) {
  813. return;
  814. }
  815. var nodeName = target.nodeName.toLowerCase();
  816. if (nodeName == 'input') {
  817. var button = this._get(inst, 'button');
  818. $(button).addClass('ui-state-disabled').disabled = true;
  819. target.disabled = true;
  820. inst.trigger.filter('button').
  821. each(function() { this.disabled = true; }).end();
  822. }
  823. else if (nodeName == 'div' || nodeName == 'span') {
  824. var inline = $target.children('.' + this._inlineClass);
  825. inline.children().addClass('ui-state-disabled');
  826. inline.find('button').each(
  827. function() { this.disabled = true }
  828. )
  829. }
  830. this._disabledInputs = $.map(this._disabledInputs,
  831. function(value) { return (value == target ? null : value); }); // delete entry
  832. this._disabledInputs[this._disabledInputs.length] = $target.attr('id');
  833. },
  834. /* Is the first field in a jQuery collection disabled as a timepicker?
  835. @param target_id element - the target input field or division or span
  836. @return boolean - true if disabled, false if enabled */
  837. _isDisabledTimepicker: function (target_id) {
  838. if ( ! target_id) { return false; }
  839. for (var i = 0; i < this._disabledInputs.length; i++) {
  840. if (this._disabledInputs[i] == target_id) { return true; }
  841. }
  842. return false;
  843. },
  844. /* Check positioning to remain on screen. */
  845. _checkOffset: function (inst, offset, isFixed) {
  846. var tpWidth = inst.tpDiv.outerWidth();
  847. var tpHeight = inst.tpDiv.outerHeight();
  848. var inputWidth = inst.input ? inst.input.outerWidth() : 0;
  849. var inputHeight = inst.input ? inst.input.outerHeight() : 0;
  850. var viewWidth = document.documentElement.clientWidth + $(document).scrollLeft();
  851. var viewHeight = document.documentElement.clientHeight + $(document).scrollTop();
  852. offset.left -= (this._get(inst, 'isRTL') ? (tpWidth - inputWidth) : 0);
  853. offset.left -= (isFixed && offset.left == inst.input.offset().left) ? $(document).scrollLeft() : 0;
  854. offset.top -= (isFixed && offset.top == (inst.input.offset().top + inputHeight)) ? $(document).scrollTop() : 0;
  855. // now check if timepicker is showing outside window viewport - move to a better place if so.
  856. offset.left -= Math.min(offset.left, (offset.left + tpWidth > viewWidth && viewWidth > tpWidth) ?
  857. Math.abs(offset.left + tpWidth - viewWidth) : 0);
  858. offset.top -= Math.min(offset.top, (offset.top + tpHeight > viewHeight && viewHeight > tpHeight) ?
  859. Math.abs(tpHeight + inputHeight) : 0);
  860. return offset;
  861. },
  862. /* Find an object's position on the screen. */
  863. _findPos: function (obj) {
  864. var inst = this._getInst(obj);
  865. var isRTL = this._get(inst, 'isRTL');
  866. while (obj && (obj.type == 'hidden' || obj.nodeType != 1)) {
  867. obj = obj[isRTL ? 'previousSibling' : 'nextSibling'];
  868. }
  869. var position = $(obj).offset();
  870. return [position.left, position.top];
  871. },
  872. /* Retrieve the size of left and top borders for an element.
  873. @param elem (jQuery object) the element of interest
  874. @return (number[2]) the left and top borders */
  875. _getBorders: function (elem) {
  876. var convert = function (value) {
  877. return { thin: 1, medium: 2, thick: 3}[value] || value;
  878. };
  879. return [parseFloat(convert(elem.css('border-left-width'))),
  880. parseFloat(convert(elem.css('border-top-width')))];
  881. },
  882. /* Close time picker if clicked elsewhere. */
  883. _checkExternalClick: function (event) {
  884. if (!$.timepicker._curInst) { return; }
  885. var $target = $(event.target);
  886. if ($target[0].id != $.timepicker._mainDivId &&
  887. $target.parents('#' + $.timepicker._mainDivId).length == 0 &&
  888. !$target.hasClass($.timepicker.markerClassName) &&
  889. !$target.hasClass($.timepicker._triggerClass) &&
  890. $.timepicker._timepickerShowing && !($.timepicker._inDialog && $.blockUI))
  891. $.timepicker._hideTimepicker();
  892. },
  893. /* Hide the time picker from view.
  894. @param input element - the input field attached to the time picker */
  895. _hideTimepicker: function (input) {
  896. var inst = this._curInst;
  897. if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
  898. if (this._timepickerShowing) {
  899. var showAnim = this._get(inst, 'showAnim');
  900. var duration = this._get(inst, 'duration');
  901. var postProcess = function () {
  902. $.timepicker._tidyDialog(inst);
  903. this._curInst = null;
  904. };
  905. if ($.effects && $.effects[showAnim]) {
  906. inst.tpDiv.hide(showAnim, $.timepicker._get(inst, 'showOptions'), duration, postProcess);
  907. }
  908. else {
  909. inst.tpDiv[(showAnim == 'slideDown' ? 'slideUp' :
  910. (showAnim == 'fadeIn' ? 'fadeOut' : 'hide'))]((showAnim ? duration : null), postProcess);
  911. }
  912. if (!showAnim) { postProcess(); }
  913. this._timepickerShowing = false;
  914. this._lastInput = null;
  915. if (this._inDialog) {
  916. this._dialogInput.css({ position: 'absolute', left: '0', top: '-100px' });
  917. if ($.blockUI) {
  918. $.unblockUI();
  919. $('body').append(this.tpDiv);
  920. }
  921. }
  922. this._inDialog = false;
  923. var onClose = this._get(inst, 'onClose');
  924. if (onClose) {
  925. onClose.apply(
  926. (inst.input ? inst.input[0] : null),
  927. [(inst.input ? inst.input.val() : ''), inst]); // trigger custom callback
  928. }
  929. }
  930. },
  931. /* Tidy up after a dialog display. */
  932. _tidyDialog: function (inst) {
  933. inst.tpDiv.removeClass(this._dialogClass).unbind('.ui-timepicker');
  934. },
  935. /* Retrieve the instance data for the target control.
  936. @param target element - the target input field or division or span
  937. @return object - the associated instance data
  938. @throws error if a jQuery problem getting data */
  939. _getInst: function (target) {
  940. try {
  941. return $.data(target, PROP_NAME);
  942. }
  943. catch (err) {
  944. throw 'Missing instance data for this timepicker';
  945. }
  946. },
  947. /* Get a setting value, defaulting if necessary. */
  948. _get: function (inst, name) {
  949. return inst.settings[name] !== undefined ?
  950. inst.settings[name] : this._defaults[name];
  951. },
  952. /* Parse existing time and initialise time picker. */
  953. _setTimeFromField: function (inst) {
  954. if (inst.input.val() == inst.lastVal) { return; }
  955. var defaultTime = this._get(inst, 'defaultTime');
  956. var timeToParse = defaultTime == 'now' ? this._getCurrentTimeRounded(inst) : defaultTime;
  957. if ((inst.inline == false) && (inst.input.val() != '')) { timeToParse = inst.input.val() }
  958. if (timeToParse instanceof Date) {
  959. inst.hours = timeToParse.getHours();
  960. inst.minutes = timeToParse.getMinutes();
  961. } else {
  962. var timeVal = inst.lastVal = timeToParse;
  963. if (timeToParse == '') {
  964. inst.hours = -1;
  965. inst.minutes = -1;
  966. } else {
  967. var time = this.parseTime(inst, timeVal);
  968. inst.hours = time.hours;
  969. inst.minutes = time.minutes;
  970. }
  971. }
  972. $.timepicker._updateTimepicker(inst);
  973. },
  974. /* Update or retrieve the settings for an existing time picker.
  975. @param target element - the target input field or division or span
  976. @param name object - the new settings to update or
  977. string - the name of the setting to change or retrieve,
  978. when retrieving also 'all' for all instance settings or
  979. 'defaults' for all global defaults
  980. @param value any - the new value for the setting
  981. (omit if above is an object or to retrieve a value) */
  982. _optionTimepicker: function(target, name, value) {
  983. var inst = this._getInst(target);
  984. if (arguments.length == 2 && typeof name == 'string') {
  985. return (name == 'defaults' ? $.extend({}, $.timepicker._defaults) :
  986. (inst ? (name == 'all' ? $.extend({}, inst.settings) :
  987. this._get(inst, name)) : null));
  988. }
  989. var settings = name || {};
  990. if (typeof name == 'string') {
  991. settings = {};
  992. settings[name] = value;
  993. }
  994. if (inst) {
  995. extendRemove(inst.settings, settings);
  996. if (this._curInst == inst) {
  997. this._hideTimepicker();
  998. this._updateTimepicker(inst);
  999. }
  1000. if (inst.inline) {
  1001. this._updateTimepicker(inst);
  1002. }
  1003. }
  1004. },
  1005. /* Set the time for a jQuery selection.
  1006. @param target element - the target input field or division or span
  1007. @param time String - the new time */
  1008. _setTimeTimepicker: function(target, time) {
  1009. var inst = this._getInst(target);
  1010. if (inst) {
  1011. this._setTime(inst, time);
  1012. this._updateTimepicker(inst);
  1013. this._updateAlternate(inst, time);
  1014. }
  1015. },
  1016. /* Set the time directly. */
  1017. _setTime: function(inst, time, noChange) {
  1018. var origHours = inst.hours;
  1019. var origMinutes = inst.minutes;
  1020. if (time instanceof Date) {
  1021. inst.hours = time.getHours();
  1022. inst.minutes = time.getMinutes();
  1023. } else {
  1024. var time = this.parseTime(inst, time);
  1025. inst.hours = time.hours;
  1026. inst.minutes = time.minutes;
  1027. }
  1028. if ((origHours != inst.hours || origMinutes != inst.minutes) && !noChange) {
  1029. inst.input.trigger('change');
  1030. }
  1031. this._updateTimepicker(inst);
  1032. this._updateSelectedValue(inst);
  1033. },
  1034. /* Return the current time, ready to be parsed, rounded to the closest minute by interval */
  1035. _getCurrentTimeRounded: function (inst) {
  1036. var currentTime = new Date(),
  1037. currentMinutes = currentTime.getMinutes(),
  1038. minutes_options = this._get(inst, 'minutes'),
  1039. // round to closest interval
  1040. adjustedMinutes = Math.round(currentMinutes / minutes_options.interval) * minutes_options.interval;
  1041. currentTime.setMinutes(adjustedMinutes);
  1042. return currentTime;
  1043. },
  1044. /*
  1045. * Parse a time string into hours and minutes
  1046. */
  1047. parseTime: function (inst, timeVal) {
  1048. var retVal = new Object();
  1049. retVal.hours = -1;
  1050. retVal.minutes = -1;
  1051. if(!timeVal)
  1052. return '';
  1053. var timeSeparator = this._get(inst, 'timeSeparator'),
  1054. amPmText = this._get(inst, 'amPmText'),
  1055. showHours = this._get(inst, 'showHours'),
  1056. showMinutes = this._get(inst, 'showMinutes'),
  1057. optionalMinutes = this._get(inst, 'optionalMinutes'),
  1058. showPeriod = (this._get(inst, 'showPeriod') == true),
  1059. p = timeVal.indexOf(timeSeparator);
  1060. // check if time separator found
  1061. if (p != -1) {
  1062. retVal.hours = parseInt(timeVal.substr(0, p), 10);
  1063. retVal.minutes = parseInt(timeVal.substr(p + 1), 10);
  1064. }
  1065. // check for hours only
  1066. else if ( (showHours) && ( !showMinutes || optionalMinutes ) ) {
  1067. retVal.hours = parseInt(timeVal, 10);
  1068. }
  1069. // check for minutes only
  1070. else if ( ( ! showHours) && (showMinutes) ) {
  1071. retVal.minutes = parseInt(timeVal, 10);
  1072. }
  1073. if (showHours) {
  1074. var timeValUpper = timeVal.toUpperCase();
  1075. if ((retVal.hours < 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[1].toUpperCase()) != -1)) {
  1076. retVal.hours += 12;
  1077. }
  1078. // fix for 12 AM
  1079. if ((retVal.hours == 12) && (showPeriod) && (timeValUpper.indexOf(amPmText[0].toUpperCase()) != -1)) {
  1080. retVal.hours = 0;
  1081. }
  1082. }
  1083. return retVal;
  1084. },
  1085. selectNow: function(event) {
  1086. var id = $(event.target).attr("data-timepicker-instance-id"),
  1087. $target = $(id),
  1088. inst = this._getInst($target[0]);
  1089. //if (!inst || (input && inst != $.data(input, PROP_NAME))) { return; }
  1090. var currentTime = new Date();
  1091. inst.hours = currentTime.getHours();
  1092. inst.minutes = currentTime.getMinutes();
  1093. this._updateSelectedValue(inst);
  1094. this._updateTimepicker(inst);
  1095. this._hideTimepicker();
  1096. },
  1097. deselectTime: function(event) {
  1098. var id = $(event.target).attr("data-timepicker-instance-id"),
  1099. $target = $(id),
  1100. inst = this._getInst($target[0]);
  1101. inst.hours = -1;
  1102. inst.minutes = -1;
  1103. this._updateSelectedValue(inst);
  1104. this._hideTimepicker();
  1105. },
  1106. selectHours: function (event) {
  1107. var $td = $(event.currentTarget),
  1108. id = $td.attr("data-timepicker-instance-id"),
  1109. newHours = parseInt($td.attr("data-hour")),
  1110. fromDoubleClick = event.data.fromDoubleClick,
  1111. $target = $(id),
  1112. inst = this._getInst($target[0]),
  1113. showMinutes = (this._get(inst, 'showMinutes') == true);
  1114. // don't select if disabled
  1115. if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
  1116. $td.parents('.ui-timepicker-hours:first').find('a').removeClass('ui-state-active');
  1117. $td.children('a').addClass('ui-state-active');
  1118. inst.hours = newHours;
  1119. // added for onMinuteShow callback
  1120. var onMinuteShow = this._get(inst, 'onMinuteShow'),
  1121. maxTime = this._get(inst, 'maxTime'),
  1122. minTime = this._get(inst, 'minTime');
  1123. if (onMinuteShow || maxTime.minute || minTime.minute) {
  1124. // this will trigger a callback on selected hour to make sure selected minute is allowed.
  1125. this._updateMinuteDisplay(inst);
  1126. }
  1127. this._updateSelectedValue(inst);
  1128. inst._hoursClicked = true;
  1129. if ((inst._minutesClicked) || (fromDoubleClick) || (showMinutes == false)) {
  1130. $.timepicker._hideTimepicker();
  1131. }
  1132. // return false because if used inline, prevent the url to change to a hashtag
  1133. return false;
  1134. },
  1135. selectMinutes: function (event) {
  1136. var $td = $(event.currentTarget),
  1137. id = $td.attr("data-timepicker-instance-id"),
  1138. newMinutes = parseInt($td.attr("data-minute")),
  1139. fromDoubleClick = event.data.fromDoubleClick,
  1140. $target = $(id),
  1141. inst = this._getInst($target[0]),
  1142. showHours = (this._get(inst, 'showHours') == true);
  1143. // don't select if disabled
  1144. if ( $.timepicker._isDisabledTimepicker($target.attr('id')) ) { return false }
  1145. $td.parents('.ui-timepicker-minutes:first').find('a').removeClass('ui-state-active');
  1146. $td.children('a').addClass('ui-state-active');
  1147. inst.minutes = newMinutes;
  1148. this._updateSelectedValue(inst);
  1149. inst._minutesClicked = true;
  1150. if ((inst._hoursClicked) || (fromDoubleClick) || (showHours == false)) {
  1151. $.timepicker._hideTimepicker();
  1152. // return false because if used inline, prevent the url to change to a hashtag
  1153. return false;
  1154. }
  1155. // return false because if used inline, prevent the url to change to a hashtag
  1156. return false;
  1157. },
  1158. _updateSelectedValue: function (inst) {
  1159. var newTime = this._getParsedTime(inst);
  1160. if (inst.input) {
  1161. inst.input.val(newTime);
  1162. inst.input.trigger('change');
  1163. }
  1164. var onSelect = this._get(inst, 'onSelect');
  1165. if (onSelect) { onSelect.apply((inst.input ? inst.input[0] : null), [newTime, inst]); } // trigger custom callback
  1166. this._updateAlternate(inst, newTime);
  1167. return newTime;
  1168. },
  1169. /* this function process selected time and return it parsed according to instance options */
  1170. _getParsedTime: function(inst) {
  1171. if (inst.hours == -1 && inst.minutes == -1) {
  1172. return '';
  1173. }
  1174. // default to 0 AM if hours is not valid
  1175. if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
  1176. // default to 0 minutes if minute is not valid
  1177. if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
  1178. var period = "",
  1179. showPeriod = (this._get(inst, 'showPeriod') == true),
  1180. showLeadingZero = (this._get(inst, 'showLeadingZero') == true),
  1181. showHours = (this._get(inst, 'showHours') == true),
  1182. showMinutes = (this._get(inst, 'showMinutes') == true),
  1183. optionalMinutes = (this._get(inst, 'optionalMinutes') == true),
  1184. amPmText = this._get(inst, 'amPmText'),
  1185. selectedHours = inst.hours ? inst.hours : 0,
  1186. selectedMinutes = inst.minutes ? inst.minutes : 0,
  1187. displayHours = selectedHours ? selectedHours : 0,
  1188. parsedTime = '';
  1189. // fix some display problem when hours or minutes are not selected yet
  1190. if (displayHours == -1) { displayHours = 0 }
  1191. if (selectedMinutes == -1) { selectedMinutes = 0 }
  1192. if (showPeriod) {
  1193. if (inst.hours == 0) {
  1194. displayHours = 12;
  1195. }
  1196. if (inst.hours < 12) {
  1197. period = amPmText[0];
  1198. }
  1199. else {
  1200. period = amPmText[1];
  1201. if (displayHours > 12) {
  1202. displayHours -= 12;
  1203. }
  1204. }
  1205. }
  1206. var h = displayHours.toString();
  1207. if (showLeadingZero && (displayHours < 10)) { h = '0' + h; }
  1208. var m = selectedMinutes.toString();
  1209. if (selectedMinutes < 10) { m = '0' + m; }
  1210. if (showHours) {
  1211. parsedTime += h;
  1212. }
  1213. if (showHours && showMinutes && (!optionalMinutes || m != 0)) {
  1214. parsedTime += this._get(inst, 'timeSeparator');
  1215. }
  1216. if (showMinutes && (!optionalMinutes || m != 0)) {
  1217. parsedTime += m;
  1218. }
  1219. if (showHours) {
  1220. if (period.length > 0) { parsedTime += this._get(inst, 'periodSeparator') + period; }
  1221. }
  1222. return parsedTime;
  1223. },
  1224. /* Update any alternate field to synchronise with the main field. */
  1225. _updateAlternate: function(inst, newTime) {
  1226. var altField = this._get(inst, 'altField');
  1227. if (altField) { // update alternate field too
  1228. $(altField).each(function(i,e) {
  1229. $(e).val(newTime);
  1230. });
  1231. }
  1232. },
  1233. _getTimeAsDateTimepicker: function(input) {
  1234. var inst = this._getInst(input);
  1235. if (inst.hours == -1 && inst.minutes == -1) {
  1236. return '';
  1237. }
  1238. // default to 0 AM if hours is not valid
  1239. if ((inst.hours < inst.hours.starts) || (inst.hours > inst.hours.ends )) { inst.hours = 0; }
  1240. // default to 0 minutes if minute is not valid
  1241. if ((inst.minutes < inst.minutes.starts) || (inst.minutes > inst.minutes.ends)) { inst.minutes = 0; }
  1242. return new Date(0, 0, 0, inst.hours, inst.minutes, 0);
  1243. },
  1244. /* This might look unused but it's called by the $.fn.timepicker function with param getTime */
  1245. /* added v 0.2.3 - gitHub issue #5 - Thanks edanuff */
  1246. _getTimeTimepicker : function(input) {
  1247. var inst = this._getInst(input);
  1248. return this._getParsedTime(inst);
  1249. },
  1250. _getHourTimepicker: function(input) {
  1251. var inst = this._getInst(input);
  1252. if ( inst == undefined) { return -1; }
  1253. return inst.hours;
  1254. },
  1255. _getMinuteTimepicker: function(input) {
  1256. var inst= this._getInst(input);
  1257. if ( inst == undefined) { return -1; }
  1258. return inst.minutes;
  1259. }
  1260. });
  1261. /* Invoke the timepicker functionality.
  1262. @param options string - a command, optionally followed by additional parameters or
  1263. Object - settings for attaching new timepicker functionality
  1264. @return jQuery object */
  1265. $.fn.timepicker = function (options) {
  1266. /* Initialise the time picker. */
  1267. if (!$.timepicker.initialized) {
  1268. $(document).mousedown($.timepicker._checkExternalClick);
  1269. $.timepicker.initialized = true;
  1270. }
  1271. /* Append timepicker main container to body if not exist. */
  1272. if ($("#"+$.timepicker._mainDivId).length === 0) {
  1273. $('body').append($.timepicker.tpDiv);
  1274. }
  1275. var otherArgs = Array.prototype.slice.call(arguments, 1);
  1276. if (typeof options == 'string' && (options == 'getTime' || options == 'getTimeAsDate' || options == 'getHour' || options == 'getMinute' ))
  1277. return $.timepicker['_' + options + 'Timepicker'].
  1278. apply($.timepicker, [this[0]].concat(otherArgs));
  1279. if (options == 'option' && arguments.length == 2 && typeof arguments[1] == 'string')
  1280. return $.timepicker['_' + options + 'Timepicker'].
  1281. apply($.timepicker, [this[0]].concat(otherArgs));
  1282. return this.each(function () {
  1283. typeof options == 'string' ?
  1284. $.timepicker['_' + options + 'Timepicker'].
  1285. apply($.timepicker, [this].concat(otherArgs)) :
  1286. $.timepicker._attachTimepicker(this, options);
  1287. });
  1288. };
  1289. /* jQuery extend now ignores nulls! */
  1290. function extendRemove(target, props) {
  1291. $.extend(target, props);
  1292. for (var name in props)
  1293. if (props[name] == null || props[name] == undefined)
  1294. target[name] = props[name];
  1295. return target;
  1296. };
  1297. $.timepicker = new Timepicker(); // singleton instance
  1298. $.timepicker.initialized = false;
  1299. $.timepicker.uuid = new Date().getTime();
  1300. $.timepicker.version = "0.3.3";
  1301. // Workaround for #4055
  1302. // Add another global to avoid noConflict issues with inline event handlers
  1303. window['TP_jQuery_' + tpuuid] = $;
  1304. })(jQuery);