1// jQuery TimePicker plugin - http://github.com/wvega/timepicker 2// 3// A jQuery plugin to enhance standard form input fields helping users to select 4// (or type) times. 5// 6// Copyright (c) 2010 Willington Vega <[email protected]> 7// Dual licensed under the MIT or GPL Version 2 licenses. 8 9 10// Define a cross-browser window.console.log method. 11// For IE and FF without Firebug, fallback to using an alert. 12//if (!window.console) { 13// var log = window.opera ? window.opera.postError : alert; 14// window.console = { log: function(str) { log(str) } }; 15//} 16 17if(typeof jQuery != 'undefined') { 18 (function($, undefined){ 19 20 function pad(str, ch, length) { 21 return Array(length + 1 - str.length).join(ch) + str; 22 } 23 24 function normalize() { 25 if (arguments.length == 1) { 26 return new Date(1988, 7, 24, arguments[0].getHours(), arguments[0].getMinutes(), 00); 27 } else if (arguments.length == 2) { 28 return new Date(1988, 7, 24, arguments[0], arguments[1], 00); 29 } else { 30 return new Date(1988, 7, 24); 31 } 32 } 33 34 $.TimePicker = function() { 35 var widget = this; 36 37 widget.ui = $('ul.ui-timepicker'); 38 if (widget.ui.length == 0) { 39 widget.ui = $('<ul></ul>').addClass('ui-timepicker ui-timepicker-hidden') 40 .addClass('ui-widget ui-widget-content ui-menu') 41 .addClass('ui-corner-all ui-helper-hidden') 42 .appendTo('body'); 43 44 if ($.fn.jquery >= '1.4.2') { 45 widget.ui.delegate('a', 'mouseenter.timepicker', function(event) { 46 // passing false instead of an instance object tells the function 47 // to use the current instance 48 widget.activate(false, $(this).parent()); 49 }).delegate('a', 'mouseleave.timepicker', function(event) { 50 widget.deactivate(false); 51 }).delegate('a', 'click.timepicker', function(event) { 52 event.preventDefault(); 53 widget.select(false, $(this).parent()); 54 }); 55 } 56 57 widget.ui.bind('click.timepicker, scroll.timepicker', function(event) { 58 clearTimeout(widget.closing); 59 }); 60 } 61 }; 62 63 $.TimePicker.count = 0; 64 $.TimePicker.instance = function() { 65 if (!$.TimePicker._instance) { 66 $.TimePicker._instance = new $.TimePicker(); 67 } 68 return $.TimePicker._instance; 69 } 70 71 $.TimePicker.prototype = { 72 // extracted from from jQuery UI Core 73 // http://github,com/jquery/jquery-ui/blob/master/ui/jquery.ui.core.js 74 keyCode: { 75 ALT: 18, 76 BLOQ_MAYUS: 20, 77 CTRL: 17, 78 DOWN: 40, 79 END: 35, 80 ENTER: 13, 81 HOME: 36, 82 LEFT: 37, 83 NUMPAD_ENTER: 108, 84 PAGE_DOWN: 34, 85 PAGE_UP: 33, 86 RIGHT: 39, 87 SHIFT: 16, 88 TAB: 9, 89 UP: 38 90 }, 91 92 _items: function(i, startTime) { 93 var widget = this, ul = $('<ul></ul>'), item = null, time, end; 94 95 if (startTime) { 96 time = normalize(startTime); 97 } else if (i.options.startTime) { 98 time = normalize(i.options.startTime); 99 } else { 100 time = normalize(i.options.startHour, i.options.startMinutes) 101 } 102 103 end = new Date(time.getTime() + 24 * 60 * 60 * 1000); 104 105 while(time < end) { 106 if (widget._isValidTime(i, time)) { 107 item = $('<li>').addClass('ui-menu-item').appendTo(ul); 108 $('<a>').addClass('ui-corner-all').text($.fn.timepicker.formatTime(i.options.timeFormat, time)).appendTo(item); 109 item.data('time-value', time); 110 } 111 time = new Date(time.getTime() + i.options.interval * 60 * 1000); 112 } 113 114 return ul.children(); 115 }, 116 117 _isValidTime: function(i, time) { 118 var min = null, max = null; 119 120 time = normalize(time); 121 122 if (i.options.minTime !== null) { 123 min = normalize(i.options.minTime); 124 } else if (i.options.minHour !== null || i.options.minMinutes !== null) { 125 min = normalize(i.options.minHour, i.options.minMinutes); 126 } 127 128 if (i.options.maxTime !== null) { 129 max = normalize(i.options.maxTime) 130 } else if (i.options.maxHour !== null || i.options.maxMinutes !== null) { 131 max = normalize(i.options.maxHour, i.options.maxMinutes); 132 } 133 134 if (min !== null && max !== null) { 135 return time >= min && time <= max; 136 } else if (min !== null) { 137 return time >= min; 138 } else if (max !== null) { 139 return time <= max; 140 } 141 142 return true; 143 }, 144 145 _hasScroll: function() { 146 return this.ui.height() < this.ui.attr('scrollHeight'); 147 }, 148 149 /** 150 * TODO: Write me! 151 * 152 * @param i 153 * @param direction 154 * @param edge 155 * */ 156 _move: function(i, direction, edge) { 157 var widget = this; 158 if (widget.closed()) { 159 widget.open(i); 160 } 161 if (!widget.active) { 162 widget.activate(i, widget.ui.children(edge)); 163 return; 164 } 165 var next = widget.active[direction + 'All']('.ui-menu-item').eq(0); 166 if (next.length) { 167 widget.activate(i, next); 168 } else { 169 widget.activate(i, widget.ui.children(edge)); 170 } 171 }, 172 173 // 174 // protected methods 175 // 176 177 register: function(node, options) { 178 var widget = this, i = {};// timepicker instance object 179 180 i.element = $(node); 181 182 if (i.element.data('TimePicker')) {return;} 183 184 185 i.element.data('TimePicker', i); 186 i.options = $.metadata ? $.extend({}, options, i.element.metadata()) : options; 187 i.widget = widget; 188 i.selectedTime = $.fn.timepicker.parseTime(i.element.val()); 189 190 // proxy functions for the exposed api methods 191 $.extend(i, { 192 next: function() {return widget.next(i);}, 193 previous: function() {return widget.previous(i);}, 194 first: function() {return widget.first(i);}, 195 last: function() {return widget.last(i);}, 196 selected: function() {return widget.selected(i);}, 197 open: function() {return widget.open(i);}, 198 close: function(force) {return widget.close(i, force);},
199 closed: function() {return widget.closed(i);}, 200 destroy: function() {return widget.destroy(i)}, 201 getTime: function() {return widget.getTime(i);}, 202 setTime: function(time) {return widget.setTime(i, time);} 203 }); 204 205 i.element.bind('keydown.timepicker', function(event) { 206 switch (event.which || event.keyCode) { 207 case widget.keyCode.ENTER: 208 case widget.keyCode.NUMPAD_ENTER: 209 event.preventDefault(); 210 if (widget.closed()) { 211 i.element.trigger('change.timepicker'); 212 } else { 213 widget.select(i, widget.active); 214 } 215 break; 216 case widget.keyCode.UP: 217 i.previous(); 218 break; 219 case widget.keyCode.DOWN: 220 i.next(); 221 break; 222 default: 223 if (!widget.closed()) { 224 i.close(true); 225 } 226 break; 227 } 228 }).bind('focus.timepicker', function(event) { 229 i.open(); 230 }).bind('blur.timepicker', function(event) { 231 i.close(); 232 }).bind('change.timepicker', function(event) { 233 if (i.closed()) { 234 i.setTime($.fn.timepicker.parseTime(i.element.val())); 235 } 236 }); 237 }, 238 239 select: function(i, item) { 240 var widget = this, instance = i === false ? widget.instance : i; 241 clearTimeout(widget.closing); 242 widget.setTime(instance, $.fn.timepicker.parseTime(item.children('a').text())); 243 widget.close(instance, true); 244 }, 245 246 activate: function(i, item) { 247 var widget = this, instance = i === false ? widget.instance : i; 248 249 if (instance !== widget.instance) { 250 return; 251 } else { 252 widget.deactivate(); 253 } 254 255 if (widget._hasScroll()) { 256 var offset = item.offset().top - widget.ui.offset().top, 257 scroll = widget.ui.scrollTop(), 258 height = widget.ui.height(); 259 if (offset < 0) { 260 widget.ui.scrollTop(scroll + offset); 261 } else if (offset >= height) { 262 widget.ui.scrollTop(scroll + offset - height + item.height()); 263 } 264 } 265 266 widget.active = item.eq(0).children('a').addClass('ui-state-hover') 267 .attr('id', 'ui-active-item') 268 .end(); 269 }, 270 271 deactivate: function() { 272 var widget = this; 273 if (!widget.active) {return;} 274 widget.active.children('a').removeClass('ui-state-hover').removeAttr('id'); 275 widget.active = null; 276 }, 277 278// option: function(key, value) { 279// var self = this; 280// // TODO: handle variable updates 281// if (arguments.length > 1) { 282// if (self.options.hasOwnProperty(key)) { 283// self.options[key] = value; 284// } 285// return self; 286// } 287// return self.options[key]; 288// }, 289 290 /** 291 * _activate, _deactivate, first, last, next, previous, _move and 292 * _hasScroll were extracted from jQuery UI Menu 293 * http://github,com/jquery/jquery-ui/blob/menu/ui/jquery.ui.menu.js 294 */ 295 296 // 297 // public methods 298 // 299 300 next: function(i) { 301 if (this.closed() || this.instance === i) { 302 this._move(i, 'next', '.ui-menu-item:first'); 303 } 304 }, 305 306 previous: function(i) { 307 if (this.closed() || this.instance === i) { 308 this._move(i, 'prev', '.ui-menu-item:last'); 309 } 310 }, 311 312 first: function(i) { 313 if (this.instance === i) { 314 return this.active && !this.active.prevAll('.ui-menu-item').length; 315 } 316 return false;
317 }, 318 319 last: function(i) { 320 if (this.instance === i) { 321 return this.active && !this.active.nextAll('.ui-menu-item').length; 322 } 323 return false; 324 }, 325 326 selected: function(i) { 327 if (this.instance === i) { 328 return this.active ? this.active : null; 329 } 330 return null; 331 }, 332 333 open: function(i) { 334 var widget = this, zindex; 335 336 // if a date is already selected and options.dynamic is true, 337 // arrange the items in the list so the first item is 338 // cronologically right after the selected date. 339 // TODO: set selectedTime 340 if (!i.items || (i.options.dynamic && i.selectedTime)) { 341 i.items = widget._items(i); 342 } 343 344 // remove old li elements but keep associated events, then append 345 // the new li elements to the ul 346 if (widget.instance !== i || (i.options.dynamic && i.selectedTime)) { 347 348 // handle menu events when using jQuery versions previous to 349 // 1.4.2 (thanks to Brian Link) 350 // http://github.com/wvega/timepicker/issues#issue/4 351 if ($.fn.jquery < '1.4.2') { 352 widget.ui.children().remove(); 353 widget.ui.append(i.items); 354 widget.ui.find('a').bind('mouseover.timepicker', function(event) { 355 widget.activate(i, $(this).parent()); 356 }).bind('mouseout.timepicker', function(event) { 357 widget.deactivate(i); 358 }).bind('click.timepicker', function(event) { 359 event.preventDefault(); 360 widget.select(i, $(this).parent()); 361 }); 362 } else { 363 widget.ui.children().detach(); 364 widget.ui.append(i.items); 365 } 366 } 367 368 // theme 369 widget.ui.removeClass('ui-helper-hidden ui-timepicker-hidden ui-timepicker-standard ui-timepicker-corners').show(); 370 371 switch (i.options.theme) { 372 case 'standard': 373 widget.ui.addClass('ui-timepicker-standard'); 374 break; 375 case 'standard-rounded-corners': 376 widget.ui.addClass('ui-timepicker-standard ui-timepicker-corners'); 377 break; 378 default: 379 break; 380 } 381 382 // position 383 // zindex = i.element.offsetParent().css('z-index'); zindex == 'auto' ? 'auto' : parseInt(zindex, 10) + 1 384 widget.ui.css($.extend(i.element.offset(), { 385 width: i.element.innerWidth(), 386 zIndex: i.options.zindex ? i.options.zindex : i.element.offsetParent().css('z-index') 387 })); 388 widget.ui.css('top', parseInt(widget.ui.css('top'), 10) + i.element.outerHeight()); 389 390 widget.instance = i; 391 392 // try to match input field's current value with an item in the 393 // dropdown 394 if (i.selectedTime) { 395 i.items.each(function() { 396 var item = $(this), time; 397 398 if ($.fn.jquery < '1.4.2') { 399 time = $.fn.timepicker.parseTime(item.find('a').text()); 400 } else { 401 time = item.data('time-value'); 402 } 403 404 if (time.getTime() == i.selectedTime.getTime()) { 405 widget.activate(i, item); 406 return false; 407 } 408 return true; 409 }); 410 } else { 411 widget.deactivate(i); 412 } 413 414 // don't break the chain 415 return i.element; 416 }, 417 418 close: function(i, force) { 419 var widget = this; 420 if (widget.closed() || force) { 421 clearTimeout(widget.closing); 422 if (widget.instance === i) { 423 widget.ui.scrollTop(0).addClass('ui-helper-hidden ui-timepicker-hidden').hide(); 424 widget.ui.children().removeClass('ui-state-hover'); 425 } 426 } else { 427 widget.closing = setTimeout(function() { 428 widget.close(i, true); 429 }, 150); 430 } 431 return i.element; 432 }, 433 434 closed: function() { 435 return this.ui.is(':hidden'); 436 }, 437 438 destroy: function(i) { 439 var widget = this; 440 widget.close(i, true); 441 return i.element.unbind('.timepicker').data('TimePicker', null); 442 }, 443 444 getTime: function(i) { 445 return i.selectedTime ? i.selectedTime : null; 446 }, 447 448 setTime: function(i, time) { 449 var widget = this; 450 if (time && time.getMinutes) { 451 i.selectedTime = time; 452 i.element.val($.fn.timepicker.formatTime(i.options.timeFormat, time)); 453 // custom change event and change callback 454 i.element.trigger('time-change', [time]); 455 if ($.isFunction(i.options.change)) { 456 i.options.change.apply(i.element, [time]); 457 } 458 } else { 459 i.selectedTime = null; 460 } 461 } 462 }; 463 464 $.TimePicker.defaults = { 465 timeFormat: 'hh:mm p', 466 minHour: null, 467 minMinutes: null, 468 minTime: null, 469 maxHour: null, 470 maxMinutes: null, 471 maxTime: null, 472 startHour: null, 473 startMinutes: null, 474 startTime: null, 475 interval: 30, 476 dynamic: true, 477 theme: 'standard', 478 zindex: null, 479 // callbacks 480 change: function(time) {} 481 }; 482 483 $.fn.timepicker = function(options) { 484 // TODO: see if it works with previous versions 485 if ($.fn.jquery < '1.3') { 486 return this; 487 } 488 489 // Calling the constructor again returns a reference to a TimePicker object. 490 if (this.length == 1 && this.data('TimePicker')) { 491 return this.data('TimePicker'); 492 } 493 494 var globals = $.extend({}, $.TimePicker.defaults, options); 495 496 return this.each(function() { 497 $.TimePicker.instance().register(this, globals); 498 }); 499 }; 500 501 /** 502 * TODO: documentation 503 */ 504 $.fn.timepicker.formatTime = function(format, time) { 505 var hours = time.getHours(), 506 hours12 = hours % 12, 507 minutes = time.getMinutes(), 508 seconds = time.getSeconds(), 509 replacements = { 510 hh: pad((hours12 === 0 ? 12 : hours12).toString(), '0', 2), 511 HH: pad(hours.toString(), '0', 2), 512 mm: pad(minutes.toString(), '0', 2), 513 ss: pad(seconds.toString(), '0', 2), 514 h: (hours12 === 0 ? 12 : hours12), 515 H: hours, 516 m: minutes, 517 s: seconds, 518 p: hours > 11 ? 'PM' : 'AM' 519 }, 520 str = format, k = ''; 521 for (k in replacements) { 522 if (replacements.hasOwnProperty(k)) { 523 str = str.replace(new RegExp(k,'g'), replacements[k]); 524 } 525 } 526 return str; 527 }; 528 529 /** 530 * Convert a string representing a given time into a Date object. 531 * 532 * The Date object will have attributes others than hours, minutes and 533 * seconds set to current local time values. The function will return 534 * false if given string can't be converted. 535 * 536 * If there is an 'a' in the string we set am to true, if there is a 'p' 537 * we set pm to true, if both are present only am is setted to true. 538 * 539 * All non-digit characters are removed from the string before trying to 540 * parse the time. 541 * 542 * '' can't be converted and the function returns false. 543 * '1' is converted to 01:00:00 am 544 * '11' is converted to 11:00:00 am 545 * '111' is converted to 01:11:00 am 546 * '1111' is converted to 11:11:00 am 547 * '11111' is converted to 01:11:11 am 548 * '111111' is converted to 11:11:11 am 549 * 550 * Only the first six (or less) characters are considered. 551 * 552 * Special case: 553 * 554 * When hours is greater than 24 and the last digit is less or equal than 6, and minutes 555 * and seconds are less or equal than 60, we append a trailing zero and 556 * start parsing process again. Examples: 557 * 558 * '95' is treated as '950' and converted to 09:50:00 am 559 * '46' is treated as '460' and converted to 05:00:00 am 560 * '57' can't be converted and the function returns false. 561 * 562 * For a detailed list of supported formats check the unit tests at 563 * http://github.com/wvega/timepicker/tree/master/tests/ 564 */ 565 $.fn.timepicker.parseTime = (function(str) { 566 var patterns = [ 567 // 1, 12, 123, 1234, 12345, 123456 568 [/^(\d+)$/, '$1'], 569 // :1, :2, :3, :4 ... :9 570 [/^:(\d)$/, '$10'], 571 // :1, :12, :123, :1234 ... 572 [/^:(\d+)/, '$1'], 573 // 6:06, 5:59, 5:8 574 [/^(\d):([7-9])$/, '0$10$2'], 575 [/^(\d):(\d\d)$/, '$1$2'], 576 [/^(\d):(\d{1,})$/, '0$1$20'], 577 // 10:8, 10:10, 10:34 578 [/^(\d\d):([7-9])$/, '$10$2'], 579 [/^(\d\d):(\d)$/, '$1$20'], 580 [/^(\d\d):(\d*)$/, '$1$2'], 581 // 123:4, 1234:456 582 [/^(\d{3,}):(\d)$/, '$10$2'], 583 [/^(\d{3,}):(\d{2,})/, '$1$2'], 584 // 585 [/^(\d):(\d):(\d)$/, '0$10$20$3'],
586 [/^(\d{1,2}):(\d):(\d\d)/, '$10$2$3']]; 587 588 return function(str) { 589 var time = normalize(new Date()), 590 am = false, pm = false, h = false, m = false, s = false, k = 0; 591 str = str.toLowerCase(); 592 am = /a/.test(str); 593 pm = am ? false : /p/.test(str); 594 str = str.replace(/[^0-9:]/g, '').replace(/:+/g, ':'); 595 596 for (k in patterns) { 597 if (patterns[k][0].test(str)) { 598 str = str.replace(patterns[k][0], patterns[k][1]); 599 break; 600 } 601 } 602 str = str.replace(/:/g, ''); 603 604 if (str.length == 1) { 605 h = str; 606 } else if (str.length == 2) { 607 h = str; 608 } else if (str.length == 3 || str.length == 5) { 609 h = str.substr(0, 1); 610 m = str.substr(1, 2); 611 s = str.substr(3, 2); 612 } else if (str.length == 4 || str.length > 5) { 613 h = str.substr(0, 2); 614 m = str.substr(2, 2); 615 s = str.substr(4, 2); 616 } 617 618 if (str.length > 0 && str.length < 5) { 619 if (str.length < 3) { 620 m = 0; 621 } 622 s = 0; 623 } 624 625 if (h === false || m === false || s === false) { 626 return false; 627 } 628 629 h = parseInt(h, 10);m = parseInt(m, 10);s = parseInt(s, 10); 630 631 if (am && h == 12) { 632 h = 0; 633 } else if (pm && h < 12) { 634 h = h + 12; 635 } 636 637 if (h > 24 && (h % 10) <= 6 && m <= 60 && s <= 60) { 638 return $.fn.timepicker.parseTime(str + '0' + (am ? 'a' : '') + (pm ? 'p' : '')); 639 } else if (h <= 24 && m <= 60 && s <= 60) { 640 time.setHours(h, m, s); 641 return time; 642 } else { 643 return false; 644 } 645 }; 646 })(); 647 })(jQuery); 648}
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.