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