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