PageRenderTime 69ms CodeModel.GetById 29ms RepoModel.GetById 1ms app.codeStats 0ms

/framework/source/class/qx/ui/control/DateChooser.js

http://github.com/qooxdoo/qooxdoo
JavaScript | 771 lines | 453 code | 140 blank | 178 comment | 90 complexity | c2c2ff9e05f6a86db37c603a7a6c2bea MD5 | raw file
Possible License(s): CC-BY-SA-3.0, LGPL-3.0, MIT
  1. /* ************************************************************************
  2. qooxdoo - the new era of web development
  3. http://qooxdoo.org
  4. Copyright:
  5. 2006 STZ-IDA, Germany, http://www.stz-ida.de
  6. License:
  7. MIT: https://opensource.org/licenses/MIT
  8. See the LICENSE file in the project's top-level directory for details.
  9. Authors:
  10. * Til Schneider (til132)
  11. * Martin Wittemann (martinwittemann)
  12. ************************************************************************ */
  13. /**
  14. * A *date chooser* is a small calendar including a navigation bar to switch the shown
  15. * month. It includes a column for the calendar week and shows one month. Selecting
  16. * a date is as easy as tapping on it.
  17. *
  18. * To be conform with all form widgets, the {@link qx.ui.form.IForm} interface
  19. * is implemented.
  20. *
  21. * The following example creates and adds a date chooser to the root element.
  22. * A listener alerts the user if a new date is selected.
  23. *
  24. * <pre class='javascript'>
  25. * var chooser = new qx.ui.control.DateChooser();
  26. * this.getRoot().add(chooser, { left : 20, top: 20});
  27. *
  28. * chooser.addListener("changeValue", function(e) {
  29. * alert(e.getData());
  30. * });
  31. * </pre>
  32. *
  33. * Additionally to a selection event an execute event is available which is
  34. * fired by doubletap or tapping the space / enter key. With this event you
  35. * can for example save the selection and close the date chooser.
  36. *
  37. * @childControl navigation-bar {qx.ui.container.Composite} container for the navigation bar controls
  38. * @childControl last-year-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the last year button
  39. * @childControl last-year-button {qx.ui.form.Button} button to jump to the last year
  40. * @childControl last-month-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the last month button
  41. * @childControl last-month-button {qx.ui.form.Button} button to jump to the last month
  42. * @childControl next-month-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the next month button
  43. * @childControl next-month-button {qx.ui.form.Button} button to jump to the next month
  44. * @childControl next-year-button-tooltip {qx.ui.tooltip.ToolTip} tooltip for the next year button
  45. * @childControl next-year-button {qx.ui.form.Button} button to jump to the next year
  46. * @childControl month-year-label {qx.ui.basic.Label} shows the current month and year
  47. * @childControl week {qx.ui.basic.Label} week label (used multiple times)
  48. * @childControl weekday {qx.ui.basic.Label} weekday label (used multiple times)
  49. * @childControl day {qx.ui.basic.Label} day label (used multiple times)
  50. * @childControl date-pane {qx.ui.container.Composite} the pane used to position the week, weekday and day labels
  51. *
  52. */
  53. qx.Class.define("qx.ui.control.DateChooser",
  54. {
  55. extend : qx.ui.core.Widget,
  56. include : [
  57. qx.ui.core.MExecutable,
  58. qx.ui.form.MForm
  59. ],
  60. implement : [
  61. qx.ui.form.IExecutable,
  62. qx.ui.form.IForm,
  63. qx.ui.form.IDateForm
  64. ],
  65. /*
  66. *****************************************************************************
  67. CONSTRUCTOR
  68. *****************************************************************************
  69. */
  70. /**
  71. * @param date {Date ? null} The initial date to show. If <code>null</code>
  72. * the current day (today) is shown.
  73. */
  74. construct : function(date)
  75. {
  76. this.base(arguments);
  77. // set the layout
  78. var layout = new qx.ui.layout.VBox();
  79. this._setLayout(layout);
  80. // create the child controls
  81. this._createChildControl("navigation-bar");
  82. this._createChildControl("date-pane");
  83. // Support for key events
  84. this.addListener("keypress", this._onKeyPress);
  85. // initialize format - moved from statics{} to constructor due to [BUG #7149]
  86. var DateChooser = qx.ui.control.DateChooser;
  87. if (!DateChooser.MONTH_YEAR_FORMAT) {
  88. DateChooser.MONTH_YEAR_FORMAT = qx.locale.Date.getDateTimeFormat("yyyyMMMM", "MMMM yyyy");
  89. }
  90. // Show the right date
  91. var shownDate = (date != null) ? date : new Date();
  92. this.showMonth(shownDate.getMonth(), shownDate.getFullYear());
  93. // listen for locale changes
  94. if (qx.core.Environment.get("qx.dynlocale")) {
  95. qx.locale.Manager.getInstance().addListener("changeLocale", this._updateDatePane, this);
  96. }
  97. // register pointer up and down handler
  98. this.addListener("pointerdown", this._onPointerUpDown, this);
  99. this.addListener("pointerup", this._onPointerUpDown, this);
  100. },
  101. /*
  102. *****************************************************************************
  103. STATICS
  104. *****************************************************************************
  105. */
  106. statics :
  107. {
  108. /**
  109. * @type {string} The format for the date year label at the top center.
  110. */
  111. MONTH_YEAR_FORMAT : null,
  112. /**
  113. * @type {string} The format for the weekday labels (the headers of the date table).
  114. */
  115. WEEKDAY_FORMAT : "EE",
  116. /**
  117. * @type {string} The format for the week numbers (the labels of the left column).
  118. */
  119. WEEK_FORMAT : "ww"
  120. },
  121. /*
  122. *****************************************************************************
  123. PROPERTIES
  124. *****************************************************************************
  125. */
  126. properties :
  127. {
  128. // overridden
  129. appearance :
  130. {
  131. refine : true,
  132. init : "datechooser"
  133. },
  134. // overridden
  135. width :
  136. {
  137. refine : true,
  138. init : 200
  139. },
  140. // overridden
  141. height :
  142. {
  143. refine : true,
  144. init : 150
  145. },
  146. /** The currently shown month. 0 = january, 1 = february, and so on. */
  147. shownMonth :
  148. {
  149. check : "Integer",
  150. init : null,
  151. nullable : true,
  152. event : "changeShownMonth"
  153. },
  154. /** The currently shown year. */
  155. shownYear :
  156. {
  157. check : "Integer",
  158. init : null,
  159. nullable : true,
  160. event : "changeShownYear"
  161. },
  162. /** The date value of the widget. */
  163. value :
  164. {
  165. check : "Date",
  166. init : null,
  167. nullable : true,
  168. event : "changeValue",
  169. apply : "_applyValue"
  170. }
  171. },
  172. /*
  173. *****************************************************************************
  174. MEMBERS
  175. *****************************************************************************
  176. */
  177. members :
  178. {
  179. __weekdayLabelArr : null,
  180. __dayLabelArr : null,
  181. __weekLabelArr : null,
  182. // overridden
  183. /**
  184. * @lint ignoreReferenceField(_forwardStates)
  185. */
  186. _forwardStates :
  187. {
  188. invalid : true
  189. },
  190. /*
  191. ---------------------------------------------------------------------------
  192. WIDGET INTERNALS
  193. ---------------------------------------------------------------------------
  194. */
  195. // overridden
  196. _createChildControlImpl : function(id, hash)
  197. {
  198. var control;
  199. switch(id)
  200. {
  201. // NAVIGATION BAR STUFF
  202. case "navigation-bar":
  203. control = new qx.ui.container.Composite(new qx.ui.layout.HBox());
  204. // Add the navigation bar elements
  205. control.add(this.getChildControl("last-year-button"));
  206. control.add(this.getChildControl("last-month-button"));
  207. control.add(this.getChildControl("month-year-label"), {flex: 1});
  208. control.add(this.getChildControl("next-month-button"));
  209. control.add(this.getChildControl("next-year-button"));
  210. this._add(control);
  211. break;
  212. case "last-year-button-tooltip":
  213. control = new qx.ui.tooltip.ToolTip(this.tr("Last year"));
  214. break;
  215. case "last-year-button":
  216. control = new qx.ui.toolbar.Button();
  217. control.addState("lastYear");
  218. control.setFocusable(false);
  219. control.setToolTip(this.getChildControl("last-year-button-tooltip"));
  220. control.addListener("tap", this._onNavButtonTap, this);
  221. break;
  222. case "last-month-button-tooltip":
  223. control = new qx.ui.tooltip.ToolTip(this.tr("Last month"));
  224. break;
  225. case "last-month-button":
  226. control = new qx.ui.toolbar.Button();
  227. control.addState("lastMonth");
  228. control.setFocusable(false);
  229. control.setToolTip(this.getChildControl("last-month-button-tooltip"));
  230. control.addListener("tap", this._onNavButtonTap, this);
  231. break;
  232. case "next-month-button-tooltip":
  233. control = new qx.ui.tooltip.ToolTip(this.tr("Next month"));
  234. break;
  235. case "next-month-button":
  236. control = new qx.ui.toolbar.Button();
  237. control.addState("nextMonth");
  238. control.setFocusable(false);
  239. control.setToolTip(this.getChildControl("next-month-button-tooltip"));
  240. control.addListener("tap", this._onNavButtonTap, this);
  241. break;
  242. case "next-year-button-tooltip":
  243. control = new qx.ui.tooltip.ToolTip(this.tr("Next year"));
  244. break;
  245. case "next-year-button":
  246. control = new qx.ui.toolbar.Button();
  247. control.addState("nextYear");
  248. control.setFocusable(false);
  249. control.setToolTip(this.getChildControl("next-year-button-tooltip"));
  250. control.addListener("tap", this._onNavButtonTap, this);
  251. break;
  252. case "month-year-label":
  253. control = new qx.ui.basic.Label();
  254. control.setAllowGrowX(true);
  255. control.setAnonymous(true);
  256. break;
  257. case "week":
  258. control = new qx.ui.basic.Label();
  259. control.setAllowGrowX(true);
  260. control.setAllowGrowY(true);
  261. control.setSelectable(false);
  262. control.setAnonymous(true);
  263. control.setCursor("default");
  264. break;
  265. case "weekday":
  266. control = new qx.ui.basic.Label();
  267. control.setAllowGrowX(true);
  268. control.setAllowGrowY(true);
  269. control.setSelectable(false);
  270. control.setAnonymous(true);
  271. control.setCursor("default");
  272. break;
  273. case "day":
  274. control = new qx.ui.basic.Label();
  275. control.setAllowGrowX(true);
  276. control.setAllowGrowY(true);
  277. control.setCursor("default");
  278. control.addListener("pointerdown", this._onDayTap, this);
  279. control.addListener("dbltap", this._onDayDblTap, this);
  280. break;
  281. case "date-pane":
  282. var controlLayout = new qx.ui.layout.Grid();
  283. control = new qx.ui.container.Composite(controlLayout);
  284. for (var i = 0; i < 8; i++) {
  285. controlLayout.setColumnFlex(i, 1);
  286. }
  287. for (var i = 0; i < 7; i++) {
  288. controlLayout.setRowFlex(i, 1);
  289. }
  290. // Create the weekdays
  291. // Add an empty label as spacer for the week numbers
  292. var label = this.getChildControl("week#0");
  293. label.addState("header");
  294. control.add(label, {column: 0, row: 0});
  295. this.__weekdayLabelArr = [];
  296. for (var i=0; i<7; i++)
  297. {
  298. label = this.getChildControl("weekday#" + i);
  299. control.add(label, {column: i + 1, row: 0});
  300. this.__weekdayLabelArr.push(label);
  301. }
  302. // Add the days
  303. this.__dayLabelArr = [];
  304. this.__weekLabelArr = [];
  305. for (var y = 0; y < 6; y++)
  306. {
  307. // Add the week label
  308. var label = this.getChildControl("week#" + (y+1));
  309. control.add(label, {column: 0, row: y + 1});
  310. this.__weekLabelArr.push(label);
  311. // Add the day labels
  312. for (var x = 0; x < 7; x++)
  313. {
  314. var label = this.getChildControl("day#" + ((y*7)+x));
  315. control.add(label, {column:x + 1, row:y + 1});
  316. this.__dayLabelArr.push(label);
  317. }
  318. }
  319. this._add(control);
  320. break;
  321. }
  322. return control || this.base(arguments, id);
  323. },
  324. // apply methods
  325. _applyValue : function(value, old)
  326. {
  327. if ((value != null) && (this.getShownMonth() != value.getMonth() || this.getShownYear() != value.getFullYear()))
  328. {
  329. // The new date is in another month -> Show that month
  330. this.showMonth(value.getMonth(), value.getFullYear());
  331. }
  332. else
  333. {
  334. // The new date is in the current month -> Just change the states
  335. var newDay = (value == null) ? -1 : value.getDate();
  336. for (var i=0; i<6*7; i++)
  337. {
  338. var dayLabel = this.__dayLabelArr[i];
  339. if (dayLabel.hasState("otherMonth"))
  340. {
  341. if (dayLabel.hasState("selected")) {
  342. dayLabel.removeState("selected");
  343. }
  344. }
  345. else
  346. {
  347. var day = parseInt(dayLabel.getValue(), 10);
  348. if (day == newDay) {
  349. dayLabel.addState("selected");
  350. } else if (dayLabel.hasState("selected")) {
  351. dayLabel.removeState("selected");
  352. }
  353. }
  354. }
  355. }
  356. },
  357. /*
  358. ---------------------------------------------------------------------------
  359. EVENT HANDLER
  360. ---------------------------------------------------------------------------
  361. */
  362. /**
  363. * Handler which stops the propagation of the tap event if
  364. * the navigation bar or calendar headers will be tapped.
  365. *
  366. * @param e {qx.event.type.Pointer} The pointer up / down event
  367. */
  368. _onPointerUpDown : function(e) {
  369. var target = e.getTarget();
  370. if (target == this.getChildControl("navigation-bar") ||
  371. target == this.getChildControl("date-pane")) {
  372. e.stopPropagation();
  373. return;
  374. }
  375. },
  376. /**
  377. * Event handler. Called when a navigation button has been tapped.
  378. *
  379. * @param evt {qx.event.type.Data} The data event.
  380. */
  381. _onNavButtonTap : function(evt)
  382. {
  383. var year = this.getShownYear();
  384. var month = this.getShownMonth();
  385. switch(evt.getCurrentTarget())
  386. {
  387. case this.getChildControl("last-year-button"):
  388. year--;
  389. break;
  390. case this.getChildControl("last-month-button"):
  391. month--;
  392. if (month < 0)
  393. {
  394. month = 11;
  395. year--;
  396. }
  397. break;
  398. case this.getChildControl("next-month-button"):
  399. month++;
  400. if (month >= 12)
  401. {
  402. month = 0;
  403. year++;
  404. }
  405. break;
  406. case this.getChildControl("next-year-button"):
  407. year++;
  408. break;
  409. }
  410. this.showMonth(month, year);
  411. },
  412. /**
  413. * Event handler. Called when a day has been tapped.
  414. *
  415. * @param evt {qx.event.type.Data} The event.
  416. */
  417. _onDayTap : function(evt)
  418. {
  419. var time = evt.getCurrentTarget().dateTime;
  420. this.setValue(new Date(time));
  421. },
  422. /**
  423. * Event handler. Called when a day has been double-tapped.
  424. */
  425. _onDayDblTap : function() {
  426. this.execute();
  427. },
  428. /**
  429. * Event handler. Called when a key was pressed.
  430. *
  431. * @param evt {qx.event.type.Data} The event.
  432. */
  433. _onKeyPress : function(evt)
  434. {
  435. var dayIncrement = null;
  436. var monthIncrement = null;
  437. var yearIncrement = null;
  438. if (evt.getModifiers() == 0)
  439. {
  440. switch(evt.getKeyIdentifier())
  441. {
  442. case "Left":
  443. dayIncrement = -1;
  444. break;
  445. case "Right":
  446. dayIncrement = 1;
  447. break;
  448. case "Up":
  449. dayIncrement = -7;
  450. break;
  451. case "Down":
  452. dayIncrement = 7;
  453. break;
  454. case "PageUp":
  455. monthIncrement = -1;
  456. break;
  457. case "PageDown":
  458. monthIncrement = 1;
  459. break;
  460. case "Escape":
  461. if (this.getValue() != null)
  462. {
  463. this.setValue(null);
  464. return;
  465. }
  466. break;
  467. case "Enter":
  468. case "Space":
  469. if (this.getValue() != null) {
  470. this.execute();
  471. }
  472. return;
  473. }
  474. }
  475. else if (evt.isShiftPressed())
  476. {
  477. switch(evt.getKeyIdentifier())
  478. {
  479. case "PageUp":
  480. yearIncrement = -1;
  481. break;
  482. case "PageDown":
  483. yearIncrement = 1;
  484. break;
  485. }
  486. }
  487. if (dayIncrement != null || monthIncrement != null || yearIncrement != null)
  488. {
  489. var date = this.getValue();
  490. if (date != null) {
  491. date = new Date(date.getTime());
  492. }
  493. if (date == null) {
  494. date = new Date();
  495. }
  496. else
  497. {
  498. if (dayIncrement != null){date.setDate(date.getDate() + dayIncrement);}
  499. if (monthIncrement != null){date.setMonth(date.getMonth() + monthIncrement);}
  500. if (yearIncrement != null){date.setFullYear(date.getFullYear() + yearIncrement);}
  501. }
  502. this.setValue(date);
  503. }
  504. },
  505. /**
  506. * Shows a certain month.
  507. *
  508. * @param month {Integer ? null} the month to show (0 = january). If not set
  509. * the month will remain the same.
  510. * @param year {Integer ? null} the year to show. If not set the year will
  511. * remain the same.
  512. */
  513. showMonth : function(month, year)
  514. {
  515. if ((month != null && month != this.getShownMonth()) || (year != null && year != this.getShownYear()))
  516. {
  517. if (month != null) {
  518. this.setShownMonth(month);
  519. }
  520. if (year != null) {
  521. this.setShownYear(year);
  522. }
  523. this._updateDatePane();
  524. }
  525. },
  526. /**
  527. * Event handler. Used to handle the key events.
  528. *
  529. * @param e {qx.event.type.Data} The event.
  530. */
  531. handleKeyPress : function(e) {
  532. this._onKeyPress(e);
  533. },
  534. /**
  535. * Updates the date pane.
  536. */
  537. _updateDatePane : function()
  538. {
  539. var DateChooser = qx.ui.control.DateChooser;
  540. var today = new Date();
  541. var todayYear = today.getFullYear();
  542. var todayMonth = today.getMonth();
  543. var todayDayOfMonth = today.getDate();
  544. var selDate = this.getValue();
  545. var selYear = (selDate == null) ? -1 : selDate.getFullYear();
  546. var selMonth = (selDate == null) ? -1 : selDate.getMonth();
  547. var selDayOfMonth = (selDate == null) ? -1 : selDate.getDate();
  548. var shownMonth = this.getShownMonth();
  549. var shownYear = this.getShownYear();
  550. var startOfWeek = qx.locale.Date.getWeekStart();
  551. // Create a help date that points to the first of the current month
  552. var helpDate = new Date(this.getShownYear(), this.getShownMonth(), 1);
  553. var monthYearFormat = new qx.util.format.DateFormat(DateChooser.MONTH_YEAR_FORMAT);
  554. this.getChildControl("month-year-label").setValue(monthYearFormat.format(helpDate));
  555. // Show the day names
  556. var firstDayOfWeek = helpDate.getDay();
  557. var firstSundayInMonth = 1 + ((7 - firstDayOfWeek) % 7);
  558. var weekDayFormat = new qx.util.format.DateFormat(DateChooser.WEEKDAY_FORMAT);
  559. for (var i=0; i<7; i++)
  560. {
  561. var day = (i + startOfWeek) % 7;
  562. var dayLabel = this.__weekdayLabelArr[i];
  563. helpDate.setDate(firstSundayInMonth + day);
  564. dayLabel.setValue(weekDayFormat.format(helpDate));
  565. if (qx.locale.Date.isWeekend(day)) {
  566. dayLabel.addState("weekend");
  567. } else {
  568. dayLabel.removeState("weekend");
  569. }
  570. }
  571. // Show the days
  572. helpDate = new Date(shownYear, shownMonth, 1, 12, 0, 0);
  573. var nrDaysOfLastMonth = (7 + firstDayOfWeek - startOfWeek) % 7;
  574. helpDate.setDate(helpDate.getDate() - nrDaysOfLastMonth);
  575. var weekFormat = new qx.util.format.DateFormat(DateChooser.WEEK_FORMAT);
  576. for (var week=0; week<6; week++)
  577. {
  578. this.__weekLabelArr[week].setValue(weekFormat.format(helpDate));
  579. for (var i=0; i<7; i++)
  580. {
  581. var dayLabel = this.__dayLabelArr[week * 7 + i];
  582. var year = helpDate.getFullYear();
  583. var month = helpDate.getMonth();
  584. var dayOfMonth = helpDate.getDate();
  585. var isSelectedDate = (selYear == year && selMonth == month && selDayOfMonth == dayOfMonth);
  586. if (isSelectedDate) {
  587. dayLabel.addState("selected");
  588. } else {
  589. dayLabel.removeState("selected");
  590. }
  591. if (month != shownMonth) {
  592. dayLabel.addState("otherMonth");
  593. } else {
  594. dayLabel.removeState("otherMonth");
  595. }
  596. var isToday = (year == todayYear && month == todayMonth && dayOfMonth == todayDayOfMonth);
  597. if (isToday) {
  598. dayLabel.addState("today");
  599. } else {
  600. dayLabel.removeState("today");
  601. }
  602. dayLabel.setValue("" + dayOfMonth);
  603. dayLabel.dateTime = helpDate.getTime();
  604. // Go to the next day
  605. helpDate.setDate(helpDate.getDate() + 1);
  606. }
  607. }
  608. monthYearFormat.dispose();
  609. weekDayFormat.dispose();
  610. weekFormat.dispose();
  611. }
  612. },
  613. /*
  614. *****************************************************************************
  615. DESTRUCTOR
  616. *****************************************************************************
  617. */
  618. destruct : function()
  619. {
  620. if (qx.core.Environment.get("qx.dynlocale")) {
  621. qx.locale.Manager.getInstance().removeListener("changeLocale", this._updateDatePane, this);
  622. }
  623. this.__weekdayLabelArr = this.__dayLabelArr = this.__weekLabelArr = null;
  624. }
  625. });