1 /* 2 * ***** BEGIN LICENSE BLOCK ***** 3 * Zimbra Collaboration Suite Web Client 4 * Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016 Synacor, Inc. 5 * 6 * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at: https://www.zimbra.com/license 9 * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15 10 * have been added to cover use of software over a computer network and provide for limited attribution 11 * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B. 12 * 13 * Software distributed under the License is distributed on an "AS IS" basis, 14 * WITHOUT WARRANTY OF ANY KIND, either express or implied. 15 * See the License for the specific language governing rights and limitations under the License. 16 * The Original Code is Zimbra Open Source Web Client. 17 * The Initial Developer of the Original Code is Zimbra, Inc. All rights to the Original Code were 18 * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015. 19 * 20 * All portions of the code are Copyright (C) 2010, 2011, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved. 21 * ***** END LICENSE BLOCK ***** 22 */ 23 24 /** 25 * Creates up to three separate DwtSelects for the time (hour, minute, am|pm) 26 * Showing the AM|PM select widget is dependent on the user's locale 27 * 28 * @author Parag Shah 29 * 30 * @param parent [DwtComposite] the parent widget 31 * @param id [string]* an ID that is propagated to component select objects 32 * 33 * @private 34 */ 35 DwtTimeSelect = function(parent, id) { 36 DwtComposite.call(this, {parent:parent, className: 'DwtTimeSelect'}); 37 38 this.id = id; 39 this._isLocale24Hour = true; 40 this._createSelects(); 41 }; 42 43 // IDs for types of time selects 44 DwtTimeSelect.START = 1; 45 DwtTimeSelect.END = 2; 46 47 // IDs for time select components 48 DwtTimeSelect.HOUR = 1; 49 DwtTimeSelect.MINUTE = 2; 50 DwtTimeSelect.AMPM = 3; 51 52 DwtTimeSelect.getDateFromFields = 53 function(hours, minutes, ampm, date) { 54 hours = Number(hours); 55 if (ampm) { 56 if (ampm == "AM" || ampm === 0) { 57 hours = (hours == 12) ? 0 : hours; 58 } else if (ampm == "PM" || ampm == 1) { 59 hours = (hours < 12) ? hours + 12 : hours; 60 } 61 } 62 date = date ? date : new Date(); 63 date.setHours(hours, Number(minutes), 0, 0); 64 return date; 65 }; 66 67 DwtTimeSelect.parse = 68 function(timeString) { 69 var date; 70 var lTimeString = timeString.toLowerCase(); 71 if (lTimeString === AjxMsg.midnight.toLowerCase() || lTimeString === AjxMsg.noon.toLowerCase()) { 72 date = new Date(); 73 date.setMinutes(0); 74 date.setSeconds(0); 75 date.setHours(lTimeString === AjxMsg.noon.toLowerCase() ? 12 : 0); 76 } else { 77 var timeFormatter = AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT); 78 date = timeFormatter.parse(timeString) || AjxDateFormat.parseTime(timeString); 79 } 80 return date; 81 }; 82 83 DwtTimeSelect.format = 84 function(date) { 85 if (date.getHours() == 0 && date.getMinutes() == 0) { 86 return AjxMsg.midnight; 87 } else if (date.getHours() == 12 && date.getMinutes() == 0) { 88 return AjxMsg.noon; 89 } else { 90 return AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT).format(date); 91 } 92 }; 93 94 /** 95 * Adjust an appt's start or end based on changes to the other one. If the user changes 96 * the start time, change the end time so that the appt duration is maintained. If the 97 * user changes the end time, we leave things alone. 98 * 99 * @param ev [Event] UI event from a DwtSelect 100 * @param startSelect [DwtTimeSelect] start time select 101 * @param endSelect [DwtTimeSelect] end time select 102 * @param startDateField [element] start date field 103 * @param endDateField [element] end date field 104 */ 105 DwtTimeSelect.adjustStartEnd = 106 function(ev, startSelect, endSelect, startDateField, endDateField) { 107 var select = ev._args.selectObj; 108 var startDate = AjxDateUtil.simpleParseDateStr(startDateField.value); 109 var endDate = AjxDateUtil.simpleParseDateStr(endDateField.value); 110 var startDateOrig = startDateField.value; 111 var endDateOrig = endDateField.value; 112 if (select.id == DwtTimeSelect.START) { 113 var hours = (select.compId == DwtTimeSelect.HOUR) ? ev._args.oldValue : startSelect.getHours(); 114 var minutes = (select.compId == DwtTimeSelect.MINUTE) ? ev._args.oldValue : startSelect.getMinutes(); 115 var ampm = (select.compId == DwtTimeSelect.AMPM) ? ev._args.oldValue : startSelect.getAmPm(); 116 var oldStartDateMs = DwtTimeSelect.getDateFromFields(hours, minutes, ampm, startDate).getTime(); 117 var newStartDateMs = DwtTimeSelect.getDateFromFields(startSelect.getHours(), startSelect.getMinutes(), startSelect.getAmPm(), startDate).getTime(); 118 var oldEndDateMs = DwtTimeSelect.getDateFromFields(endSelect.getHours(), endSelect.getMinutes(), endSelect.getAmPm(), endDate).getTime(); 119 var delta = oldEndDateMs - oldStartDateMs; 120 if (!delta) return null; 121 var newEndDateMs = newStartDateMs + delta; 122 var newEndDate = new Date(newEndDateMs); 123 endSelect.set(newEndDate); 124 endDateField.value = AjxDateUtil.simpleComputeDateStr(newEndDate); 125 if (endDateField.value != endDateOrig) { 126 return endDateField; 127 } 128 } else { 129 return null; 130 } 131 }; 132 133 /** 134 * Returns true if the start date/time is before the end date/time. 135 * 136 * @param ss [DwtTimeSelect] start time select 137 * @param es [DwtTimeSelect] end time select 138 * @param startDateField [element] start date field 139 * @param endDateField [element] end date field 140 */ 141 DwtTimeSelect.validStartEnd = 142 function(startDateField, endDateField, ss, es) { 143 var startDate = AjxDateUtil.simpleParseDateStr(startDateField.value); 144 var endDate = AjxDateUtil.simpleParseDateStr(endDateField.value); 145 146 if (startDate && endDate) { 147 if((startDate.valueOf() > endDate.valueOf())){ 148 return false; 149 } 150 // bug fix #11329 - dont allow year to be more than the earth will be around :] 151 if (startDate.getFullYear() > 9999 || endDate.getFullYear() > 9999) { 152 return false; 153 } 154 if(ss && es){ 155 var startDateMs = DwtTimeSelect.getDateFromFields(ss.getHours(), ss.getMinutes(), ss.getAmPm(), startDate).getTime(); 156 var endDateMs = DwtTimeSelect.getDateFromFields(es.getHours(), es.getMinutes(), es.getAmPm(), endDate).getTime(); 157 if (startDateMs > endDateMs) { 158 return false; 159 } 160 } 161 } else { 162 return false; 163 } 164 return true; 165 }; 166 167 DwtTimeSelect.prototype = new DwtComposite; 168 DwtTimeSelect.prototype.constructor = DwtTimeSelect; 169 DwtTimeSelect.prototype.isDwtTimeSelect = true; 170 171 DwtTimeSelect.prototype.toString = function() { 172 return 'DwtTimeSelect'; 173 }; 174 175 /** 176 * Sets the time select according to the given date. 177 * 178 * @param date [Date] a Date object 179 */ 180 DwtTimeSelect.prototype.set = 181 function(date) { 182 183 var hourIdx = 0, minuteIdx = 0, amPmIdx = 0; 184 var isLocale24Hour = this.isLocale24Hour(); 185 186 var hours = date.getHours(); 187 if (!isLocale24Hour && hours > 12) { 188 hourIdx = hours - 13; 189 } else if (!isLocale24Hour && hours == 0) { 190 hourIdx = this.getHourSelectSize() - 1; 191 } else { 192 hourIdx = isLocale24Hour ? hours : hours - 1; 193 } 194 195 minuteIdx = Math.floor(date.getMinutes() / 5); 196 197 if (!isLocale24Hour) { 198 amPmIdx = (date.getHours() >= 12) ? 1 : 0; 199 } 200 201 this.setSelected(hourIdx, minuteIdx, amPmIdx); 202 }; 203 204 205 /** 206 * Returns a date object with the hours and minutes set based on 207 * the values of this time select. 208 * 209 * @param date [Date] Optional. If specified, the hour and minute 210 * values will be set on the specified object; 211 * else, a new <code>Date</code> object is created. 212 */ 213 DwtTimeSelect.prototype.getValue = 214 function(date) { 215 return (DwtTimeSelect.getDateFromFields(this.getHours(), this.getMinutes(), this.getAmPm(), date)); 216 }; 217 218 DwtTimeSelect.prototype.getHours = 219 function() { 220 return this._hourSelect.getValue(); 221 }; 222 223 DwtTimeSelect.prototype.getMinutes = 224 function() { 225 return this._minuteSelect.getValue(); 226 }; 227 228 DwtTimeSelect.prototype.getAmPm = 229 function() { 230 return this._amPmSelect ? this._amPmSelect.getValue() : null; 231 }; 232 233 DwtTimeSelect.prototype.setSelected = 234 function(hourIdx, minuteIdx, amPmIdx) { 235 this._hourSelect.setSelected(hourIdx); 236 this._minuteSelect.setSelected(minuteIdx); 237 if (!this._isLocale24Hour) { 238 this._amPmSelect.setSelected(amPmIdx); 239 } 240 }; 241 242 DwtTimeSelect.prototype.addChangeListener = 243 function(listener) { 244 this._hourSelect.addChangeListener(listener); 245 this._minuteSelect.addChangeListener(listener); 246 if (this._amPmSelect) 247 this._amPmSelect.addChangeListener(listener); 248 }; 249 250 DwtTimeSelect.prototype.isLocale24Hour = 251 function() { 252 return this._isLocale24Hour; 253 }; 254 255 DwtTimeSelect.prototype.getHourSelectSize = 256 function() { 257 return this._hourSelect.size(); 258 }; 259 260 DwtTimeSelect.prototype.getMinuteSelectSize = 261 function() { 262 return this._minuteSelect.size(); 263 }; 264 265 DwtTimeSelect.prototype.getSelectedHourIdx = 266 function() { 267 return this._hourSelect.getSelectedIndex(); 268 }; 269 270 DwtTimeSelect.prototype.getSelectedMinuteIdx = 271 function() { 272 return this._minuteSelect.getSelectedIndex(); 273 }; 274 275 DwtTimeSelect.prototype.getSelectedAmPmIdx = 276 function() { 277 return this._amPmSelect ? this._amPmSelect.getSelectedIndex() : 0; 278 }; 279 280 DwtTimeSelect.prototype.setEnabled = 281 function(enabled) { 282 DwtComposite.prototype.setEnabled.call(this, enabled); 283 284 this._hourSelect.setEnabled(enabled); 285 this._minuteSelect.setEnabled(enabled); 286 if (this._amPmSelect) this._amPmSelect.setEnabled(enabled); 287 }; 288 289 DwtTimeSelect.prototype._createSelects = 290 function() { 291 this._hourSelectId = Dwt.getNextId(); 292 this._minuteSelectId = Dwt.getNextId(); 293 this._amPmSelectId = Dwt.getNextId(); 294 295 // get the time formatter for the user's locale 296 var timeFormatter = AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT); 297 var hourSegmentIdx = 0; 298 var minuteSegmentIdx = 0; 299 300 var html = []; 301 var i = 0; 302 303 html[i++] = "<table border=0 cellpadding=0 cellspacing=0><tr>"; 304 305 // walk time formatter's segments array to render each segment part in the right order 306 for (var j = 0; j < timeFormatter._segments.length; j++) { 307 var segmentStr = timeFormatter._segments[j]._s; 308 309 if (timeFormatter._segments[j] instanceof AjxFormat.TextSegment) { 310 var trimStr = AjxStringUtil.trim(segmentStr); 311 if (trimStr.length) { 312 html[i++] = "<td class='TextPadding ZmFieldLabel'>" 313 html[i++] = segmentStr; 314 html[i++] = "</td>"; 315 } 316 } else if (segmentStr.charAt(0) == "h" || segmentStr.charAt(0) == "H") { 317 hourSegmentIdx = j; 318 html[i++] = "<td width=42 id='" 319 html[i++] = this._hourSelectId; 320 html[i++] = "'></td>"; 321 } else if (segmentStr.charAt(0) == "m") { 322 minuteSegmentIdx = j; 323 html[i++] = "<td width=42 id='" 324 html[i++] = this._minuteSelectId; 325 html[i++] = "'></td>"; 326 } else if (segmentStr == "a") { 327 this._isLocale24Hour = false; 328 html[i++] = "<td width=42 id='" 329 html[i++] = this._amPmSelectId; 330 html[i++] = "'></td>"; 331 } 332 } 333 334 html[i++] = "</tr></table>"; 335 336 // append html template to DOM 337 this.getHtmlElement().innerHTML = html.join(""); 338 339 // init vars for adding hour DwtSelect 340 var now = new Date(); 341 var start = this._isLocale24Hour ? 0 : 1; 342 var limit = this._isLocale24Hour ? 24 : 13; 343 344 // create new DwtSelect for hour slot 345 this._hourSelect = new DwtSelect({parent:this}); 346 this._hourSelect.id = this.id; 347 this._hourSelect.compId = DwtTimeSelect.HOUR; 348 for (var i = start; i < limit; i++) { 349 now.setHours(i); 350 var label = timeFormatter._segments[hourSegmentIdx].format(now); 351 this._hourSelect.addOption(label, false, i); 352 } 353 this._hourSelect.reparentHtmlElement(this._hourSelectId); 354 delete this._hourSelectId; 355 356 // create new DwtSelect for minute slot 357 this._minuteSelect = new DwtSelect({parent:this}); 358 this._minuteSelect.id = this.id; 359 this._minuteSelect.compId = DwtTimeSelect.MINUTE; 360 for (var i = 0; i < 60; i = i + 5) { 361 now.setMinutes(i); 362 var label = timeFormatter._segments[minuteSegmentIdx].format(now); 363 this._minuteSelect.addOption(label, false, i); 364 } 365 this._minuteSelect.reparentHtmlElement(this._minuteSelectId); 366 delete this._minuteSelectId; 367 368 // if locale is 12-hour time, add AM|PM DwtSelect 369 if (!this._isLocale24Hour) { 370 this._amPmSelect = new DwtSelect({parent:this}); 371 this._amPmSelect.id = this.id; 372 this._amPmSelect.compId = DwtTimeSelect.AMPM; 373 this._amPmSelect.addOption(I18nMsg["periodAm"], false, "AM"); 374 this._amPmSelect.addOption(I18nMsg["periodPm"], false, "PM"); 375 this._amPmSelect.reparentHtmlElement(this._amPmSelectId); 376 delete this._amPmSelectId; 377 } 378 }; 379 380 /** 381 * Creates up to three separate DwtSelects for the time (hour, minute, am|pm) 382 * Showing the AM|PM select widget is dependent on the user's locale 383 * 384 * @author Parag Shah 385 * 386 * @param parent [DwtComposite] the parent widget 387 * @param id [string]* an ID that is propagated to component select objects 388 * 389 * @private 390 */ 391 DwtTimeInput = function(parent, id, parentElement, interval) { 392 var params = {parent:parent, id: "DwtTimeInput", className: 'DwtTimeInput'}; 393 if(parentElement) { 394 params.parentElement = parentElement; 395 } 396 DwtComposite.call(this, params); 397 398 this._interval = interval || DwtTimeInput.FIFTEEN_MIN_INTERVAL; 399 this.id = id; 400 this._isLocale24Hour = true; 401 this._createSelects(); 402 this._useTextInput = true; 403 }; 404 405 DwtTimeInput.THIRTY_MIN_INTERVAL = 30; 406 DwtTimeInput.FIFTEEN_MIN_INTERVAL = 15; 407 408 // IDs for types of time selects 409 DwtTimeInput.START = 1; 410 DwtTimeInput.END = 2; 411 412 // IDs for time select components 413 DwtTimeInput.HOUR = 1; 414 DwtTimeInput.MINUTE = 2; 415 DwtTimeInput.AMPM = 3; 416 417 DwtTimeInput.ROWS = 8; // Show 8 rows at a time 418 DwtTimeInput.DEFAULT_TOP_ROW = 8; // Make row 8 (8 AM) the initial topmost visible row unless overridden 419 420 DwtTimeInput.getDateFromFields = 421 function(timeStr, date) { 422 var formattedDate = DwtTimeSelect.parse(timeStr); 423 date = date || new Date(); 424 date.setHours(formattedDate.getHours(), formattedDate.getMinutes(), 0, 0); 425 return date; 426 }; 427 428 /** 429 * Adjust an appt's start or end based on changes to the other one. If the user changes 430 * the start time, change the end time so that the appt duration is maintained. If the 431 * user changes the end time, we leave things alone. 432 * 433 * @param ev [Event] UI event from a DwtSelect 434 * @param startSelect [DwtTimeInput] start time select 435 * @param endSelect [DwtTimeInput] end time select 436 * @param startDateField [element] start date field 437 * @param endDateField [element] end date field 438 * @param dateInfo [object] date info used to calculate the old time before changing this 439 * @param id [string] an ID which got changed 440 */ 441 DwtTimeInput.adjustStartEnd = 442 function(ev, startSelect, endSelect, startDateField, endDateField, dateInfo, id) { 443 var startDate = AjxDateUtil.simpleParseDateStr(startDateField.value); 444 var endDate = AjxDateUtil.simpleParseDateStr(endDateField.value); 445 var startDateOrig = startDateField.value; 446 var endDateOrig = endDateField.value; 447 if (id == DwtTimeInput.START) { 448 var timeStr = dateInfo ? dateInfo.startTimeStr : startSelect.getTimeString(); 449 var oldStartDateMs = DwtTimeInput.getDateFromFields(timeStr, startDate).getTime(); 450 var newStartDateMs = DwtTimeInput.getDateFromFields(startSelect.getTimeString(), startDate).getTime(); 451 var oldEndDateMs = DwtTimeInput.getDateFromFields(endSelect.getTimeString(), endDate).getTime(); 452 453 var delta = oldEndDateMs - oldStartDateMs; 454 if (!delta) return null; 455 456 var newEndDateMs = newStartDateMs + delta; 457 var newEndDate = new Date(newEndDateMs); 458 459 startSelect.set(new Date(newStartDateMs)); 460 endSelect.set(newEndDate); 461 endDateField.value = AjxDateUtil.simpleComputeDateStr(newEndDate); 462 463 if (endDateField.value != endDateOrig) { 464 return endDateField; 465 } 466 } else if (id == DwtTimeInput.END){ 467 var timeStr = dateInfo ? dateInfo.endTimeStr : endSelect.getTimeString(); 468 var oldEndDateMs = DwtTimeInput.getDateFromFields(timeStr, endDate).getTime(); 469 var newEndDateMs = DwtTimeInput.getDateFromFields(endSelect.getTimeString(), endDate).getTime(); 470 var oldStartDateMs = DwtTimeInput.getDateFromFields(startSelect.getTimeString(), startDate).getTime(); 471 472 var delta = oldEndDateMs - oldStartDateMs; 473 if (!delta) return null; 474 475 //adjust start date only when the end date falls earlier than start date 476 if(newEndDateMs < oldStartDateMs) { 477 var newStartDateMs = newEndDateMs - delta; 478 var newStartDate = new Date(newStartDateMs); 479 480 startSelect.set(newStartDate); 481 endSelect.set(new Date(newEndDateMs)); 482 startDateField.value = AjxDateUtil.simpleComputeDateStr(newStartDate); 483 endDateField.value = AjxDateUtil.simpleComputeDateStr(new Date(newEndDateMs)); 484 } 485 486 if (startDateField.value != startDateOrig) { 487 return startDateField; 488 } 489 490 } else { 491 return null; 492 } 493 }; 494 495 /** 496 * Returns true if the start date/time is before the end date/time. 497 * 498 * @param ss [DwtTimeInput] start time select 499 * @param es [DwtTimeInput] end time select 500 * @param startDateField [element] start date field 501 * @param endDateField [element] end date field 502 */ 503 DwtTimeInput.validStartEnd = 504 function(startDateField, endDateField, ss, es) { 505 var startDate = AjxDateUtil.simpleParseDateStr(startDateField.value); 506 var endDate = AjxDateUtil.simpleParseDateStr(endDateField.value); 507 508 if (startDate && endDate) { 509 if((startDate.valueOf() > endDate.valueOf())) { 510 return false; 511 } 512 // bug fix #11329 - dont allow year to be more than the earth will be around :] 513 if (startDate.getFullYear() > 9999 || endDate.getFullYear() > 9999) { 514 return false; 515 } 516 if (ss && es) { 517 var startTime = ss.getTimeString(); 518 var endTime = es.getTimeString(); 519 if (startTime && endTime) { 520 var startDateMs = DwtTimeInput.getDateFromFields(startTime, startDate).getTime(); 521 var endDateMs = DwtTimeInput.getDateFromFields(endTime, endDate).getTime(); 522 if (startDateMs > endDateMs) { 523 return false; 524 } 525 } 526 } 527 } else { 528 return false; 529 } 530 return true; 531 }; 532 533 DwtTimeInput.prototype = new DwtComposite; 534 DwtTimeInput.prototype.constructor = DwtTimeInput; 535 DwtTimeInput.prototype.isDwtTimeInput = true; 536 537 DwtTimeInput.prototype.toString = function() { 538 return 'DwtTimeInput'; 539 }; 540 541 /** 542 * Sets the time select according to the given date. 543 * 544 * @param date [Date] a Date object 545 */ 546 DwtTimeInput.prototype.set = 547 function(date) { 548 var timeStr = DwtTimeSelect.format(date); 549 this._originalTimeStr = timeStr; 550 this._timeSelectInput.setValue(timeStr); 551 this._scrollToValue(timeStr); 552 }; 553 554 /** 555 * Sets the time string after validating it 556 * 557 * @param date [Date] a Date object 558 */ 559 DwtTimeInput.prototype.setValue = 560 function(str) { 561 //sets only if the date is valid 562 var date = DwtTimeSelect.parse(str); 563 if (!date) str = ""; 564 this._originalTimeStr = str; 565 this._timeSelectInput.setValue(str); 566 this._scrollToValue(str); 567 }; 568 569 DwtTimeInput.prototype._scrollToValue = 570 function(str) { 571 var index = this.getTimeIndex(str); 572 if (index !== null) 573 this._hoursSelectMenu.setSelectedItem(index); 574 }; 575 576 /** 577 * Returns a date object with the hours and minutes set based on 578 * the values of this time picker. 579 * 580 * @param date [Date] Optional. If specified, the hour and minute 581 * values will be set on the specified object; 582 * else, a new <code>Date</code> object is created. 583 */ 584 DwtTimeInput.prototype.getValue = 585 function(date) { 586 //return (DwtTimeInput.getDateFromFields(this.getHours(), this.getMinutes(), this.getAmPm(), date)); 587 var d = DwtTimeSelect.parse(this._timeSelectInput.getValue()); 588 if(!d) { 589 d = new Date(); 590 } 591 date = date || new Date(); 592 //daylight saving time 593 if(AjxDateUtil.isDayShifted(date)) { 594 AjxDateUtil.rollToNextDay(date); 595 } 596 597 date.setHours(d.getHours(), d.getMinutes(), 0, 0); 598 return date; 599 }; 600 601 DwtTimeInput.prototype.getHours = 602 function() { 603 var d = this.getValue(); 604 return d ? d.getHours() : null; 605 }; 606 607 DwtTimeInput.prototype.getMinutes = 608 function() { 609 var d = this.getValue(); 610 return d ? d.getMinutes() : null; 611 }; 612 613 DwtTimeInput.prototype.addChangeListener = 614 function(listener) { 615 this._changeListener = listener; 616 var callback = AjxCallback.simpleClosure(this.handleTimeChange, this, listener); 617 this._timeSelectInput.setHandler(DwtEvent.ONBLUR, callback); 618 }; 619 620 DwtTimeInput.prototype.handleTimeChange = 621 function(listener, ev) { 622 //restore old value if the new time is not in correct format 623 var str = this._timeSelectInput.getValue(); 624 var d = DwtTimeSelect.parse(str); 625 if(!d) { 626 //TODO: Try to guess the time 627 /*var newDate = this.correctTimeString(str, DwtTimeSelect.parse(this._originalTimeStr)); 628 this.setValue(DwtTimeSelect.format(newDate) || "");*/ 629 this.setValue(this._originalTimeStr); 630 } else { 631 this._scrollToValue(str); 632 } 633 634 listener.run(ev, this.id); 635 }; 636 637 DwtTimeInput.prototype.correctTimeString = 638 function(val, originalDate) { 639 640 var segments = val.split(":"); 641 642 if(!segments) return originalDate; 643 644 var hrs = (segments.length && segments[0] != null) ? parseInt(segments[0].replace(/\D/g, "")) : null; 645 var mins = (segments.length > 1 && segments[1]!= null) ? parseInt(segments[1].replace(/\D/g, "")) : 0; 646 647 if(!hrs) hrs = (hrs == 0) ? 0 : originalDate.getHours(); 648 if(!mins) mins = 0; 649 650 originalDate.setHours(hrs, mins, 0, 0); 651 652 return originalDate; 653 654 }; 655 656 DwtTimeInput.prototype.isLocale24Hour = 657 function() { 658 return this._isLocale24Hour; 659 }; 660 661 DwtTimeInput.prototype.setEnabled = 662 function(enabled) { 663 DwtComposite.prototype.setEnabled.call(this, enabled); 664 this._timeSelectInput.setEnabled(enabled); 665 this._timeSelectBtn.setEnabled(enabled); 666 }; 667 668 669 DwtTimeInput.prototype._timeButtonListener = 670 function(ev) { 671 var timeFormatter = AjxDateFormat.getTimeInstance(AjxDateFormat.SHORT); 672 var defaultTopMenuItem; 673 if(!this._menuItemsAdded) { 674 var j, 675 k, 676 mi, 677 smi, 678 text, 679 maxMinutesItem, 680 minutesSelectMenu, 681 now = new Date(), 682 timeSelectButton = this._timeSelectBtn, 683 menuSelectionListener = new AjxListener(this, this._timeSelectionListener); 684 685 for (j = 0; j < 24; j++) { 686 now.setHours(j); 687 now.setMinutes(0); 688 689 mi = new DwtMenuItem({parent: this._hoursSelectMenu, style: DwtMenuItem.NO_STYLE}); 690 text = timeFormatter.format(now); // Regular formatter, returns the I18nMsg formatted time 691 this.putTimeIndex(text, j); 692 693 if (j==0 || j==12) { 694 text = DwtTimeSelect.format(now); // Specialized formatter, returns AjxMsg.midnight for midnight and AjxMsg.noon for noon 695 this.putTimeIndex(text, j); // Both should go in the indexer 696 } 697 698 mi.setText(text); 699 mi.setData("value", j*60); 700 if (menuSelectionListener) mi.addSelectionListener(menuSelectionListener); 701 if (j == DwtTimeInput.DEFAULT_TOP_ROW) defaultTopMenuItem = mi; 702 703 maxMinutesItem = 60 / this._interval; 704 minutesSelectMenu = new DwtMenu({parent:mi, style:DwtMenu.DROPDOWN_CENTERV_STYLE, layout:DwtMenu.LAYOUT_CASCADE, maxRows:maxMinutesItem, congruent: true}); 705 mi.setMenu(minutesSelectMenu, true); 706 mi.setSelectableWithSubmenu(true); 707 minutesSelectMenu.dontStealFocus(true); 708 for (k = 1; k < maxMinutesItem; k++) { 709 now.setMinutes(k*this._interval); 710 smi = new DwtMenuItem({parent: minutesSelectMenu, style: DwtMenuItem.NO_STYLE}); 711 smi.setText(timeFormatter.format(now)); 712 smi.setData("value", j*60 + k*this._interval); 713 if (menuSelectionListener) smi.addSelectionListener(menuSelectionListener); 714 } 715 } 716 this._hoursSelectMenu.setWidth(timeSelectButton.getW() + this._timeSelectInput.getW()); 717 718 this._menuItemsAdded = true; 719 } 720 ev.item.popup(); 721 if (defaultTopMenuItem) { 722 this._hoursSelectMenu.scrollToItem(defaultTopMenuItem); 723 } 724 this._scrollToValue(timeFormatter.format(this.getValue())); 725 }; 726 727 DwtTimeInput.prototype._timeSelectionListener = 728 function(ev) { 729 if(ev.item && ev.item instanceof DwtMenuItem){ 730 this._timeSelectInput.setValue(ev.item.getText()); 731 this._timeSelectValue = ev.item.getData("value"); 732 if(this._changeListener) this._changeListener.run(ev, this.id); 733 return; 734 } 735 }; 736 737 DwtTimeInput.prototype.getTimeString = 738 function() { 739 //validate and returns only valid time string 740 var date = DwtTimeSelect.parse(this._timeSelectInput.getValue()); 741 return date ? this._timeSelectInput.getValue() : ""; 742 }; 743 744 DwtTimeInput.prototype.getInputField = 745 function() { 746 return this._timeSelectInput; 747 }; 748 749 DwtTimeInput.prototype.getTabGroupMember = 750 function() { 751 return this._tabGroup; 752 }; 753 754 DwtTimeInput.prototype.putTimeIndex = 755 function(text, value) { 756 this._timeIndex[text.replace(/\:\d\d/, ":00").replace(/\s/,"").toLowerCase()] = value; 757 }; 758 759 DwtTimeInput.prototype.getTimeIndex = 760 function(text) { 761 if (!text) return null; 762 var index = this._timeIndex[text.replace(/\:\d\d/, ":00").replace(/\s/,"").toLowerCase()]; 763 return (index || index===0) ? index : null; 764 }; 765 766 DwtTimeInput.prototype._createSelects = 767 function() { 768 var label = (this.id === DwtTimeSelect.START ? ZmMsg.startTime : 769 this.id === DwtTimeSelect.END ? ZmMsg.endTime : 770 ZmMsg.time); 771 772 // get the time formatter for the user's locale 773 774 this.getHtmlElement().innerHTML = AjxTemplate.expand("calendar.Appointment#ApptTimeInput", {id: this._htmlElId}); 775 776 var inputId = Dwt.getNextId("DwtTimeInputSelect_"); 777 if (this.id && this.id == DwtTimeSelect.START) { 778 inputId += "_startTimeInput"; 779 } 780 else if (this.id && this.id == DwtTimeSelect.END) { 781 inputId += "_endTimeInput"; 782 } 783 //create time select input field 784 var params = { 785 parent: this, 786 parentElement: (this._htmlElId + "_timeSelectInput"), 787 type: DwtInputField.STRING, 788 label: label, 789 errorIconStyle: DwtInputField.ERROR_ICON_NONE, 790 validationStyle: DwtInputField.CONTINUAL_VALIDATION, 791 inputId: inputId, 792 id: Dwt.getNextId("DwtTimeInputField_") 793 }; 794 795 this._timeSelectInput = new DwtInputField(params); 796 var timeInputEl = this._timeSelectInput.getInputElement(); 797 Dwt.setSize(timeInputEl, "80px", "2rem"); 798 timeInputEl.typeId = this.id; 799 //listeners 800 var buttonListener = new AjxListener(this, this._timeButtonListener); 801 var buttonId = this._htmlElId + "_timeSelectBtn"; 802 //create time select drop down button 803 var timeSelectButton = this._timeSelectBtn = new DwtButton({parent:this}); 804 timeSelectButton.addDropDownSelectionListener(buttonListener); 805 806 timeSelectButton.setData(Dwt.KEY_ID, buttonId); 807 timeSelectButton.setSize("20"); 808 timeSelectButton.setAttribute('aria-label', label); 809 810 this._timeIndex = {}; 811 // create menu for button 812 this._hoursSelectMenu = new DwtMenu({parent:timeSelectButton, style:DwtMenu.DROPDOWN_STYLE, layout:DwtMenu.LAYOUT_SCROLL, maxRows:DwtTimeInput.ROWS}); 813 timeSelectButton.setMenu(this._hoursSelectMenu, true, false, false, true); 814 this._menuItemsAdded = false; 815 timeSelectButton.reparentHtmlElement(buttonId); 816 817 this._tabGroup = new DwtTabGroup(this.getHTMLElId()); 818 this._tabGroup.addMember(this._timeSelectInput); 819 this._tabGroup.addMember(timeSelectButton); 820 }; 821