Calendar.js 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /* Copyright (c) Business Objects 2006. All rights reserved. */
  2. if (typeof bobj == 'undefined') {
  3. bobj = {};
  4. }
  5. if (typeof bobj.crv == 'undefined') {
  6. bobj.crv = {};
  7. }
  8. if (typeof bobj.crv.Calendar == 'undefined') {
  9. bobj.crv.Calendar = {};
  10. }
  11. /*
  12. ================================================================================
  13. Calendar Widget
  14. ================================================================================
  15. */
  16. /**
  17. * Get a shared calendar instance
  18. */
  19. bobj.crv.Calendar.getInstance = function() {
  20. if (!bobj.crv.Calendar.__instance) {
  21. bobj.crv.Calendar.__instance = bobj.crv.newCalendar();
  22. }
  23. return bobj.crv.Calendar.__instance;
  24. };
  25. bobj.crv.Calendar.Signals = {
  26. OK_CLICK: 'okClick',
  27. CANCEL_CLICK: 'cancelClick',
  28. ON_HIDE: 'onHide'
  29. };
  30. bobj.crv.newCalendar = function(kwArgs) {
  31. var UPDATE = MochiKit.Base.update;
  32. kwArgs = UPDATE({
  33. id: bobj.uniqueId() + "_calendar",
  34. showTime: false,
  35. date: new Date(),
  36. // List of formats to match in order of preference. Once a format is
  37. // matched, the time field will be displayed in that format.
  38. timeFormats: ["HH:mm:ss", "H:mm:ss", "H:m:s", "HH:mm", "H:mm", "H:m",
  39. "h:mm:ss a", "h:m:s a", "h:mm:ssa", "h:m:sa", "h:mm a", "h:m a",
  40. "h:mma", "h:ma"]
  41. }, kwArgs);
  42. var o = newMenuWidget( );
  43. o.widgetType = 'Calendar';
  44. // Update instance with constructor arguments
  45. bobj.fillIn(o, kwArgs);
  46. // Update instance with member functions
  47. o._menuJustInTimeInit = o.justInTimeInit;
  48. UPDATE(o, bobj.crv.Calendar);
  49. o._curTimeFormat = o.timeFormats[0];
  50. o._cells = [];
  51. o._firstDay = 0;
  52. o._numDays = 0;
  53. return o;
  54. };
  55. bobj.crv.Calendar._createHeaderButtons = function() {
  56. var w = 8;
  57. var h = 4;
  58. var dx = 46;
  59. var dyUp = 0;
  60. var dyDown = 12;
  61. var bind = MochiKit.Base.bind;
  62. this._prevMonthBtn = newIconWidget(this.id+"_pm",_skin+'../lov.gif',bind(this._onPrevMonthClick, this),"",_calendarPrevMonthLab,w,h,dx,dyDown);
  63. this._prevYearBtn = newIconWidget(this.id+"_py",_skin+'../lov.gif',bind(this._onPrevYearClick, this),"",_calendarPrevYearLab,w,h,dx,dyDown);
  64. this._nextMonthBtn = newIconWidget(this.id+"_nm",_skin+'../lov.gif',bind(this._onNextMonthClick, this),"",_calendarNextMonthLab,w,h,dx,dyUp);
  65. this._nextYearBtn = newIconWidget(this.id+"_ny",_skin+'../lov.gif',bind(this._onNextYearClick, this),"",_calendarNextYearLab,w,h,dx,dyUp);
  66. this._prevMonthBtn.allowDblClick = true;
  67. this._prevYearBtn.allowDblClick = true;
  68. this._nextMonthBtn.allowDblClick = true;
  69. this._nextYearBtn.allowDblClick = true;
  70. };
  71. bobj.crv.Calendar._createTimeTextField = function() {
  72. var bind = MochiKit.Base.bind;
  73. this._timeField = newTextFieldWidget(
  74. this.id + '_time',
  75. bind(this._onTimeChange, this), //changeCB
  76. null, //maxChar
  77. null, //keyUpCB
  78. null, //enterCB
  79. true, //noMargin
  80. null, //tooltip
  81. null, //width
  82. null, //focusCB
  83. null); //blurCB
  84. };
  85. bobj.crv.Calendar._createOKCancelButtons = function() {
  86. var bind = MochiKit.Base.bind;
  87. this._okBtn = newButtonWidget(this.id + "_ok", L_bobj_crv_OK, bind(this._onOKClick, this));
  88. this._cancelBtn = newButtonWidget(this.id + "_cancel", L_bobj_crv_Cancel, bind(this._onCancelClick, this));
  89. };
  90. /**
  91. * Widget will auto-initialize the first time its show method is called.
  92. * Client code shoud not call this method.
  93. */
  94. bobj.crv.Calendar.justInTimeInit = function() {
  95. this._menuJustInTimeInit();
  96. this._prevMonthBtn.init();
  97. this._prevYearBtn.init();
  98. this._nextMonthBtn.init();
  99. this._nextYearBtn.init();
  100. this._okBtn.init();
  101. this._cancelBtn.init();
  102. this._timeField.init();
  103. this._timeField.layer.style.width = '100%';
  104. this._timeField.setValue(bobj.external.date.formatDate(this.date, this._curTimeFormat));
  105. this._timeRow = getLayer(this.id + '_timeRow');
  106. this._timeSep = getLayer(this.id + '_timeSep');
  107. this._month = getLayer(this.id + "_month");
  108. this._year = getLayer(this.id + "_year");
  109. var numCells = 6 * 7; // six rows in the calendar with 7 days each
  110. for (var i = 0; i < numCells; i++) {
  111. this._cells[i] = getLayer(this.id + '_c' + i);
  112. }
  113. this._update();
  114. };
  115. /**
  116. * Widget will be written into the document the first time its show method is called.
  117. * Client code shoud not call this method.
  118. */
  119. bobj.crv.Calendar.getHTML = function() {
  120. var h = bobj.html;
  121. var TABLE = h.TABLE;
  122. var TBODY = h.TBODY;
  123. var TR = h.TR;
  124. var TD = h.TD;
  125. var DIV = h.DIV;
  126. var SPAN = h.SPAN;
  127. var A = h.A;
  128. this._createHeaderButtons();
  129. this._createTimeTextField();
  130. this._createOKCancelButtons();
  131. var onkeydown = "MenuWidget_keyDown('" + this.id + "', event); return true";
  132. var onmousedown = "eventCancelBubble(event)";
  133. var onmouseup = "eventCancelBubble(event)";
  134. var onkeypress = "eventCancelBubble(event)";
  135. var dayHeaderAtt = {'class':"calendarTextPart"};
  136. var html = TABLE({dir: 'ltr', id: this.id, border:"0", cellpadding:"0", cellspacing:"0",
  137. onkeydown: onkeydown, onmousedown: onmousedown, onmouseup: onmouseup, onkeypress: onkeypress,
  138. 'class':"menuFrame", style:{cursor:"default", visibility:"hidden",'z-index': 10000}},
  139. TBODY(null,
  140. TR(null, TD(null, this._getMonthYearHTML())),
  141. TR(null, TD({align:"center"},
  142. TABLE({border:"0", cellspacing:"0", cellpadding:"0", style:{margin:"2px", 'margin-top': "6px"}},
  143. TR({align:"center"},
  144. TD(dayHeaderAtt, L_bobj_crv_SundayShort),
  145. TD(dayHeaderAtt, L_bobj_crv_MondayShort),
  146. TD(dayHeaderAtt, L_bobj_crv_TuesdayShort),
  147. TD(dayHeaderAtt, L_bobj_crv_WednesdayShort),
  148. TD(dayHeaderAtt, L_bobj_crv_ThursdayShort),
  149. TD(dayHeaderAtt, L_bobj_crv_FridayShort),
  150. TD(dayHeaderAtt, L_bobj_crv_SaturdayShort)),
  151. TR(null, TD({colspan:"7", style:{padding:"2px"}}, this._getSeparatorHTML())),
  152. this._getDaysHTML(),
  153. TR(null, TD({colspan:"7", style:{padding:"2px"}}, this._getSeparatorHTML())),
  154. TR({id:this.id + '_timeRow', style:{display:this.showTime ? '' : 'none'}},
  155. TD({colspan:"7", style:{'padding-top':"3px", 'padding-bottom':"3px", 'padding-right':"10px", 'padding-left':"10px"}},
  156. this._timeField.getHTML())),
  157. TR({id:this.id + '_timeSep',style:{display:this.showTime ? '' : 'none'}},
  158. TD({colspan:"7", style:{padding:"2px"}}, this._getSeparatorHTML())),
  159. TR(null, TD({colspan:"7", align:"right", style:{'padding-bottom':"3px", 'padding-top':"3px"}},
  160. TABLE(null, TBODY(null, TR(null,
  161. TD(null, this._okBtn.getHTML()),
  162. TD(null, this._cancelBtn.getHTML())))))))))));
  163. return this._getLinkHTML('startLink_' + this.id) + html + this._getLinkHTML('endLink_' + this.id);
  164. };
  165. bobj.crv.Calendar._getMonthYearHTML = function() {
  166. var h = bobj.html;
  167. var TABLE = h.TABLE;
  168. var TBODY = h.TBODY;
  169. var TR = h.TR;
  170. var TD = h.TD;
  171. var DIV = h.DIV;
  172. var SPAN = h.SPAN;
  173. return TABLE({'class':"dialogzone", width:"100%", cellpadding:"0", cellspacing:"0"},
  174. TBODY(null,
  175. TR(null,
  176. TD({style:{'padding-top':"1px"}}, this._nextMonthBtn.getHTML()),
  177. TD({rowspan:"2", width:"100%", align:"center", 'class':"dialogzone"},
  178. SPAN({id:this.id + "_month", tabIndex:"0"}, _month[this.date.getMonth()]),
  179. "&nbsp;&nbsp;",
  180. SPAN({id:this.id + "_year", tabIndex:"0"}, this.date.getFullYear())),
  181. TD({style:{'pading-top':"1px"}}, this._nextYearBtn.getHTML())),
  182. TR({valign:"top"},
  183. TD({style:{'padding-bottom':"1px"}}, this._prevMonthBtn.getHTML()),
  184. TD({style:{'padding-bottom':"1px"}}, this._prevYearBtn.getHTML()))));
  185. };
  186. bobj.crv.Calendar._getSeparatorHTML = function() {
  187. var h = bobj.html;
  188. var TABLE = h.TABLE;
  189. var TBODY = h.TBODY;
  190. var TR = h.TR;
  191. var TD = h.TD;
  192. return TABLE({width:"100%",
  193. height:"3",
  194. cellpadding:"0",
  195. cellspacing:"0",
  196. border:"0",
  197. style:backImgOffset(_skin+"menus.gif",0,80)},
  198. TBODY(null, TR(null, TD())));
  199. };
  200. bobj.crv.Calendar._getLinkHTML = function(id) {
  201. return bobj.html.A({
  202. id: id,
  203. href: "javascript:void(0)",
  204. onfocus: "MenuWidget_keepFocus('"+this.id+"')",
  205. style:{
  206. visibility:"hidden",
  207. position:"absolute"
  208. }});
  209. };
  210. bobj.crv.Calendar._getDaysHTML = function() {
  211. var TD = bobj.html.TD;
  212. var DIV = bobj.html.DIV;
  213. var html = '';
  214. for (i = 0; i < 6; ++i) {
  215. html += '<tr align="right">';
  216. for (j = 0; j < 7; ++j) {
  217. var cellNum = j + (i * 7);
  218. var eventArgs = "(this," + cellNum + "," + "event);";
  219. html += TD({id: this.id + '_c' + (i * 7 + j),
  220. 'class':"calendarTextPart",
  221. onmousedown: "bobj.crv.Calendar._onDayMouseDown" + eventArgs,
  222. onmouseover: "bobj.crv.Calendar._onDayMouseOver" + eventArgs,
  223. onmouseout: "bobj.crv.Calendar._onDayMouseOut" + eventArgs,
  224. ondblclick: "bobj.crv.Calendar._onDayDoubleClick" + eventArgs,
  225. onkeydown: "bobj.crv.Calendar._onDayKeyDown" + eventArgs},
  226. DIV({'class':"menuCalendar"}));
  227. }
  228. html += '</tr>';
  229. }
  230. return html;
  231. };
  232. /**
  233. * Update the calendar's display using the current date value
  234. */
  235. bobj.crv.Calendar._update = function() {
  236. var numCells = 6 * 7; // six rows in the calendar with 7 days each
  237. var curDate = this.date.getDate();
  238. var info = this._getMonthInfo(this.date.getMonth(), this.date.getFullYear());
  239. var firstCellInMonth = info.firstDay;
  240. this._firstDay = info.firstDay;
  241. this._numDays = info.numDays;
  242. var year = "" + this.date.getFullYear();
  243. while (year.length < 4) {
  244. year = "0" + year;
  245. }
  246. this._year.innerHTML = year;
  247. this._month.innerHTML = _month[this.date.getMonth()];
  248. this._selectedDate = null;
  249. for (var cellNum = 0; cellNum < numCells; cellNum++) {
  250. var cell = this._cells[cellNum].firstChild;
  251. var cssClass = "menuCalendar";
  252. var cellDate = this._getDateFromCellNum(cellNum);
  253. if (cellDate < 1 || cellDate > info.numDays) {
  254. cell.innerHTML = "";
  255. cell.tabIndex = "-1";
  256. }
  257. else {
  258. cell.innerHTML = "" + cellDate;
  259. cell.tabIndex = "0";
  260. if (cellDate == curDate) {
  261. cssClass = "menuCalendarSel";
  262. this._selectedDay = cell;
  263. }
  264. }
  265. cell.className = cssClass;
  266. }
  267. };
  268. bobj.crv.Calendar._getMonthInfo = function(month, year) {
  269. var date = new Date();
  270. date.setDate(1);
  271. date.setFullYear(year);
  272. date.setMonth(month);
  273. var firstDay = date.getDay(); // First day of the week in this month
  274. date.setDate(28);
  275. var lastDate = 28; // Last date in this month
  276. for (var i = 29; i < 32; i++) {
  277. date.setDate(i);
  278. if (date.getMonth() != month) {
  279. break;
  280. }
  281. lastDate = i;
  282. }
  283. return {firstDay: firstDay, numDays: lastDate};
  284. };
  285. bobj.crv.Calendar._setDayOfMonth = function(date) {
  286. if (date > 0 && date <= this._numDays) {
  287. var prevDate = this.date.getDate();
  288. if (date != prevDate) {
  289. var prevCell = this._getCellFromDate(prevDate);
  290. if (prevCell) {
  291. prevCell.firstChild.className = "menuCalendar";
  292. }
  293. this._getCellFromDate(date).firstChild.className = "menuCalendarSel";
  294. this.date.setDate(date);
  295. }
  296. }
  297. };
  298. bobj.crv.Calendar._getCellFromDate = function(date) {
  299. var cellNum = date + this._firstDay - 1;
  300. return this._cells[cellNum];
  301. };
  302. bobj.crv.Calendar._getDateFromCellNum = function(cellNum) {
  303. return cellNum - this._firstDay + 1;
  304. };
  305. bobj.crv.Calendar._onDayMouseOver = function(node, cellNum, event) {
  306. var o = getWidget(node);
  307. var div = node.firstChild;
  308. var date = cellNum - o._firstDay + 1;
  309. if (date < 1 || date > o._numDays) {
  310. div.className = "menuCalendar";
  311. }
  312. else {
  313. div.className = "menuCalendarSel";
  314. }
  315. };
  316. bobj.crv.Calendar._onDayMouseOut = function(node, cellNum, event) {
  317. var o = getWidget(node);
  318. var div = node.firstChild;
  319. var date = cellNum - o._firstDay + 1;
  320. if (date != o.date.getDate()) {
  321. div.className = "menuCalendar";
  322. }
  323. };
  324. bobj.crv.Calendar._onDayMouseDown = function(node, cellNum, event) {
  325. var o = getWidget(node);
  326. var date = cellNum - o._firstDay + 1;
  327. o._setDayOfMonth(date);
  328. };
  329. bobj.crv.Calendar._onDayDoubleClick = function(node, cellNum, event) {
  330. var o = getWidget(node);
  331. o._onOKClick();
  332. };
  333. bobj.crv.Calendar._onDayKeyDown = function(node, cellNum, event) {
  334. event = new MochiKit.Signal.Event(node, event);
  335. var key = event.key().string;
  336. if (key === "KEY_ENTER") {
  337. var o = getWidget(node);
  338. var date = cellNum - o._firstDay + 1;
  339. o._setDayOfMonth(date);
  340. }
  341. };
  342. bobj.crv.Calendar._onPrevMonthClick = function() {
  343. if(this.date.getMonth() === 0) {
  344. this.date.setYear(this.date.getFullYear() -1);
  345. this.date.setMonth(11);
  346. }
  347. else {
  348. this.date.setMonth(this.date.getMonth() - 1);
  349. }
  350. this._update();
  351. };
  352. bobj.crv.Calendar._onPrevYearClick = function() {
  353. this.date.setFullYear(this.date.getFullYear() - 1);
  354. this._update();
  355. };
  356. bobj.crv.Calendar._onNextMonthClick = function() {
  357. this.date.setMonth(this.date.getMonth() + 1);
  358. this._update();
  359. };
  360. bobj.crv.Calendar._onNextYearClick = function() {
  361. this.date.setFullYear(this.date.getFullYear() + 1);
  362. this._update();
  363. };
  364. bobj.crv.Calendar._onOKClick = function() {
  365. this.restoreFocus();
  366. MochiKit.Signal.signal(this, this.Signals.OK_CLICK, this._copyDate(this.date));
  367. this.show(false);
  368. };
  369. bobj.crv.Calendar._copyDate = function(date) {
  370. if (date) {
  371. return new Date(date.getFullYear(),
  372. date.getMonth(),
  373. date.getDate(),
  374. date.getHours(),
  375. date.getMinutes(),
  376. date.getSeconds(),
  377. date.getMilliseconds());
  378. }
  379. return new Date();
  380. };
  381. bobj.crv.Calendar._onCancelClick = function() {
  382. this.restoreFocus();
  383. this.show(false);
  384. MochiKit.Signal.signal(this, this.Signals.CANCEL_CLICK);
  385. };
  386. bobj.crv.Calendar._onTimeChange = function() {
  387. var text = this._timeField.getValue();
  388. var date = null;
  389. var format = null;
  390. for (var i = 0; i < this.timeFormats.length && date === null; ++i) {
  391. format = this.timeFormats[i];
  392. date = bobj.external.date.getDateFromFormat(text, format);
  393. }
  394. if (date) {
  395. this._curTimeFormat = format;
  396. this.date.setHours(date.getHours());
  397. this.date.setMinutes(date.getMinutes());
  398. this.date.setSeconds(date.getSeconds());
  399. this.date.setMilliseconds(date.getMilliseconds());
  400. }
  401. else {
  402. this._timeField.setValue(bobj.external.date.formatDate(this.date, this._curTimeFormat));
  403. }
  404. };
  405. bobj.crv.Calendar.setShowTime = function(isShow) {
  406. var disp = isShow ? '' : 'none';
  407. this.showTime = isShow;
  408. if (this.layer) {
  409. this._timeRow.style.display = disp;
  410. this._timeSep.style.display = disp;
  411. }
  412. };
  413. bobj.crv.Calendar.setDate = function(date) {
  414. this.date = date;
  415. if (this.layer) {
  416. this._timeField.setValue(bobj.external.date.formatDate(this.date, this._curTimeFormat));
  417. this._update();
  418. }
  419. };
  420. /**
  421. * Show the calendar. Will write out the HTML and init the widget also.
  422. *
  423. * @param isShow [bool] Show calendar if true. Hide it if false.
  424. * @param x [int] x coordinate for left of calendar
  425. * @param y [int] y coordinate for top of calendar
  426. * @param isAlignRight [bool, optional] When true, the x coordinate applies to
  427. * the right edge of the calendar
  428. * @param isAlignBottom [bool, optional] When true, the y coordinate applies to
  429. * the bottom edge of the calendar
  430. */
  431. bobj.crv.Calendar.show = function(isShow, x, y, isAlignRight, isAlignBottom) {
  432. ScrollMenuWidget_show.call(this, isShow, x, y);
  433. if(isShow) {
  434. this.focus();
  435. }
  436. else {
  437. MochiKit.Signal.signal(this, this.Signals.ON_HIDE);
  438. }
  439. };
  440. /**
  441. * Set focus on the Calendar. The currently selected day will receive focus.
  442. */
  443. bobj.crv.Calendar.focus = function() {
  444. if (this._selectedDay) {
  445. this._selectedDay.focus();
  446. }
  447. };