Calendar.js 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943
  1. // wrapped by build app
  2. define("dojox/widget/Calendar", ["dijit","dojo","dojox","dojo/require!dijit/_Widget,dijit/_Templated,dijit/_Container,dijit/typematic,dojo/date,dojo/date/locale"], function(dijit,dojo,dojox){
  3. dojo.provide("dojox.widget.Calendar");
  4. dojo.experimental("dojox.widget.Calendar");
  5. dojo.require("dijit._Widget");
  6. dojo.require("dijit._Templated");
  7. dojo.require("dijit._Container");
  8. dojo.require("dijit.typematic");
  9. dojo.require("dojo.date");
  10. dojo.require("dojo.date.locale");
  11. dojo.declare("dojox.widget._CalendarBase", [dijit._Widget, dijit._Templated, dijit._Container], {
  12. // summary:
  13. // The Root class for all _Calendar extensions
  14. // templateString: String
  15. // The template to be used to construct the widget.
  16. templateString: dojo.cache("dojox.widget", "Calendar/Calendar.html", "<div class=\"dojoxCalendar\">\n <div tabindex=\"0\" class=\"dojoxCalendarContainer\" style=\"visibility: visible;\" dojoAttachPoint=\"container\">\n\t\t<div style=\"display:none\">\n\t\t\t<div dojoAttachPoint=\"previousYearLabelNode\"></div>\n\t\t\t<div dojoAttachPoint=\"nextYearLabelNode\"></div>\n\t\t\t<div dojoAttachPoint=\"monthLabelSpacer\"></div>\n\t\t</div>\n <div class=\"dojoxCalendarHeader\">\n <div>\n <div class=\"dojoxCalendarDecrease\" dojoAttachPoint=\"decrementMonth\"></div>\n </div>\n <div class=\"\">\n <div class=\"dojoxCalendarIncrease\" dojoAttachPoint=\"incrementMonth\"></div>\n </div>\n <div class=\"dojoxCalendarTitle\" dojoAttachPoint=\"header\" dojoAttachEvent=\"onclick: onHeaderClick\">\n </div>\n </div>\n <div class=\"dojoxCalendarBody\" dojoAttachPoint=\"containerNode\"></div>\n <div class=\"\">\n <div class=\"dojoxCalendarFooter\" dojoAttachPoint=\"footer\"> \n </div>\n </div>\n </div>\n</div>\n"),
  17. // _views: Array
  18. // The list of mixin views available on this calendar.
  19. _views: null,
  20. // useFx: Boolean
  21. // Specifies if visual effects should be applied to the widget.
  22. // The default behavior of the widget does not contain any effects.
  23. // The dojox.widget.CalendarFx package is needed for these.
  24. useFx: true,
  25. // widgetsInTemplate: Boolean
  26. // This widget is a container of other widgets, so this is true.
  27. widgetsInTemplate: true,
  28. // value: Date
  29. // The currently selected Date
  30. value: new Date(),
  31. constraints: null,
  32. // footerFormat: String
  33. // The date format of the date displayed in the footer. Can be
  34. // 'short', 'medium', and 'long'
  35. footerFormat: "medium",
  36. constructor: function(){
  37. this._views = [];
  38. this.value = new Date();
  39. },
  40. postMixInProperties: function(){
  41. var c = this.constraints;
  42. if(c){
  43. var fromISO = dojo.date.stamp.fromISOString;
  44. if(typeof c.min == "string"){
  45. c.min = fromISO(c.min);
  46. }
  47. if(typeof c.max == "string"){
  48. c.max = fromISO(c.max);
  49. }
  50. }
  51. this.value = this.parseInitialValue(this.value);
  52. },
  53. parseInitialValue: function(value){
  54. if (!value || value === -1){
  55. return new Date();
  56. }else if(value.getFullYear){
  57. return value;
  58. }else if (!isNaN(value)) {
  59. if (typeof this.value == "string") {
  60. value = parseInt(value);
  61. }
  62. value = this._makeDate(value);
  63. }
  64. return value;
  65. },
  66. _makeDate: function(value){
  67. return value;//new Date(value);
  68. },
  69. postCreate: function(){
  70. // summary:
  71. // Instantiates the mixin views
  72. this.displayMonth = new Date(this.get('value'));
  73. if(this._isInvalidDate(this.displayMonth)){
  74. this.displayMonth = new Date();
  75. }
  76. var mixin = {
  77. parent: this,
  78. _getValueAttr: dojo.hitch(this, function(){return new Date(this._internalValue || this.value);}),
  79. _getDisplayMonthAttr: dojo.hitch(this, function(){return new Date(this.displayMonth);}),
  80. _getConstraintsAttr: dojo.hitch(this, function(){return this.constraints;}),
  81. getLang: dojo.hitch(this, function(){return this.lang;}),
  82. isDisabledDate: dojo.hitch(this, this.isDisabledDate),
  83. getClassForDate: dojo.hitch(this, this.getClassForDate),
  84. addFx: this.useFx ? dojo.hitch(this, this.addFx) : function(){}
  85. };
  86. //Add the mixed in views.
  87. dojo.forEach(this._views, function(widgetType){
  88. var widget = new widgetType(mixin, dojo.create('div'));
  89. this.addChild(widget);
  90. var header = widget.getHeader();
  91. if(header){
  92. //place the views's header node in the header of the main widget
  93. this.header.appendChild(header);
  94. //hide the header node of the widget
  95. dojo.style(header, "display", "none");
  96. }
  97. //Hide all views
  98. dojo.style(widget.domNode, "visibility", "hidden");
  99. //Listen for the values in a view to be selected
  100. dojo.connect(widget, "onValueSelected", this, "_onDateSelected");
  101. widget.set("value", this.get('value'));
  102. }, this);
  103. if(this._views.length < 2){
  104. dojo.style(this.header, "cursor", "auto");
  105. }
  106. this.inherited(arguments);
  107. // Cache the list of children widgets.
  108. this._children = this.getChildren();
  109. this._currentChild = 0;
  110. //Populate the footer with today's date.
  111. var today = new Date();
  112. this.footer.innerHTML = "Today: "
  113. + dojo.date.locale.format(today, {
  114. formatLength:this.footerFormat,
  115. selector:'date',
  116. locale:this.lang});
  117. dojo.connect(this.footer, "onclick", this, "goToToday");
  118. var first = this._children[0];
  119. dojo.style(first.domNode, "top", "0px");
  120. dojo.style(first.domNode, "visibility", "visible");
  121. var header = first.getHeader();
  122. if(header){
  123. dojo.style(first.getHeader(), "display", "");
  124. }
  125. dojo[first.useHeader ? "removeClass" : "addClass"](this.container, "no-header");
  126. first.onDisplay();
  127. var _this = this;
  128. var typematic = function(nodeProp, dateProp, adj){
  129. dijit.typematic.addMouseListener(_this[nodeProp], _this, function(count){
  130. if(count >= 0){ _this._adjustDisplay(dateProp, adj);}
  131. }, 0.8, 500);
  132. };
  133. typematic("incrementMonth", "month", 1);
  134. typematic("decrementMonth", "month", -1);
  135. this._updateTitleStyle();
  136. },
  137. addFx: function(query, fromNode){
  138. // Stub function than can be overridden to add effects.
  139. },
  140. _isInvalidDate: function(/*Date*/ value){
  141. // summary:
  142. // Runs various tests on the value, checking for invalid conditions
  143. // tags:
  144. // private
  145. return !value || isNaN(value) || typeof value != "object" || value.toString() == this._invalidDate;
  146. },
  147. _setValueAttr: function(/*Date*/ value){
  148. // summary:
  149. // Set the current date and update the UI. If the date is disabled, the selection will
  150. // not change, but the display will change to the corresponding month.
  151. if(!value){
  152. value = new Date();
  153. }
  154. if(!value["getFullYear"]){
  155. value = dojo.date.stamp.fromISOString(value + "");
  156. }
  157. if(this._isInvalidDate(value)){
  158. return false;
  159. }
  160. if(!this.value || dojo.date.compare(value, this.value)){
  161. value = new Date(value);
  162. this.displayMonth = new Date(value);
  163. this._internalValue = value;
  164. if(!this.isDisabledDate(value, this.lang) && this._currentChild == 0){
  165. this.value = value;
  166. this.onChange(value);
  167. }
  168. if (this._children && this._children.length > 0) {
  169. this._children[this._currentChild].set("value", this.value);
  170. }
  171. return true;
  172. }
  173. return false;
  174. },
  175. isDisabledDate: function(/*Date*/date, /*String?*/locale){
  176. // summary:
  177. // May be overridden to disable certain dates in the calendar e.g. `isDisabledDate=dojo.date.locale.isWeekend`
  178. var c = this.constraints;
  179. var compare = dojo.date.compare;
  180. return c && (c.min && (compare(c.min, date, "date") > 0) ||
  181. (c.max && compare(c.max, date, "date") < 0));
  182. },
  183. onValueSelected: function(/*Date*/date){
  184. // summary:
  185. // A date cell was selected. It may be the same as the previous value.
  186. },
  187. _onDateSelected: function(date, formattedValue, force){
  188. this.displayMonth = date;
  189. this.set("value", date)
  190. //Only change the selected value if it was chosen from the
  191. //first child.
  192. if(!this._transitionVert(-1)){
  193. if(!formattedValue && formattedValue !== 0){
  194. formattedValue = this.get('value');
  195. }
  196. this.onValueSelected(formattedValue);
  197. }
  198. },
  199. onChange: function(/*Date*/date){
  200. // summary:
  201. // Called only when the selected date has changed
  202. },
  203. onHeaderClick: function(e){
  204. // summary:
  205. // Transitions to the next view.
  206. this._transitionVert(1);
  207. },
  208. goToToday: function(){
  209. this.set("value", new Date());
  210. this.onValueSelected(this.get('value'));
  211. },
  212. _transitionVert: function(/*Number*/direction){
  213. // summary:
  214. // Animates the views to show one and hide another, in a
  215. // vertical direction.
  216. // If 'direction' is 1, then the views slide upwards.
  217. // If 'direction' is -1, the views slide downwards.
  218. var curWidget = this._children[this._currentChild];
  219. var nextWidget = this._children[this._currentChild + direction];
  220. if(!nextWidget){return false;}
  221. dojo.style(nextWidget.domNode, "visibility", "visible");
  222. var height = dojo.style(this.containerNode, "height");
  223. nextWidget.set("value", this.displayMonth);
  224. if(curWidget.header){
  225. dojo.style(curWidget.header, "display", "none");
  226. }
  227. if(nextWidget.header){
  228. dojo.style(nextWidget.header, "display", "");
  229. }
  230. dojo.style(nextWidget.domNode, "top", (height * -1) + "px");
  231. dojo.style(nextWidget.domNode, "visibility", "visible");
  232. this._currentChild += direction;
  233. var height1 = height * direction;
  234. var height2 = 0;
  235. dojo.style(nextWidget.domNode, "top", (height1 * -1) + "px");
  236. // summary: Slides two nodes vertically.
  237. var anim1 = dojo.animateProperty({
  238. node: curWidget.domNode,
  239. properties: {top: height1},
  240. onEnd: function(){
  241. dojo.style(curWidget.domNode, "visibility", "hidden");
  242. }
  243. });
  244. var anim2 = dojo.animateProperty({
  245. node: nextWidget.domNode,
  246. properties: {top: height2},
  247. onEnd: function(){
  248. nextWidget.onDisplay();
  249. }
  250. });
  251. dojo[nextWidget.useHeader ? "removeClass" : "addClass"](this.container, "no-header");
  252. anim1.play();
  253. anim2.play();
  254. curWidget.onBeforeUnDisplay()
  255. nextWidget.onBeforeDisplay();
  256. this._updateTitleStyle();
  257. return true;
  258. },
  259. _updateTitleStyle: function(){
  260. dojo[this._currentChild < this._children.length -1 ? "addClass" : "removeClass"](this.header, "navToPanel");
  261. },
  262. _slideTable: function(/*String*/widget, /*Number*/direction, /*Function*/callback){
  263. // summary:
  264. // Animates the horizontal sliding of a table.
  265. var table = widget.domNode;
  266. //Clone the existing table
  267. var newTable = table.cloneNode(true);
  268. var left = dojo.style(table, "width");
  269. table.parentNode.appendChild(newTable);
  270. //Place the existing node either to the left or the right of the new node,
  271. //depending on which direction it is to slide.
  272. dojo.style(table, "left", (left * direction) + "px");
  273. //Call the function that generally populates the new cloned node with new data.
  274. //It may also attach event listeners.
  275. callback();
  276. //Animate the two nodes.
  277. var anim1 = dojo.animateProperty({node: newTable, properties:{left: left * direction * -1}, duration: 500, onEnd: function(){
  278. newTable.parentNode.removeChild(newTable);
  279. }});
  280. var anim2 = dojo.animateProperty({node: table, properties:{left: 0}, duration: 500});
  281. anim1.play();
  282. anim2.play();
  283. },
  284. _addView: function(view){
  285. //Insert the view at the start of the array.
  286. this._views.push(view);
  287. },
  288. getClassForDate: function(/*Date*/dateObject, /*String?*/locale){
  289. // summary:
  290. // May be overridden to return CSS classes to associate with the date entry for the given dateObject,
  291. // for example to indicate a holiday in specified locale.
  292. /*=====
  293. return ""; // String
  294. =====*/
  295. },
  296. _adjustDisplay: function(/*String*/part, /*int*/amount, noSlide){
  297. // summary:
  298. // This function overrides the base function defined in dijit.Calendar.
  299. // It changes the displayed years, months and days depending on the inputs.
  300. var child = this._children[this._currentChild];
  301. var month = this.displayMonth = child.adjustDate(this.displayMonth, amount);
  302. this._slideTable(child, amount, function(){
  303. child.set("value", month);
  304. });
  305. }
  306. });
  307. dojo.declare("dojox.widget._CalendarView", dijit._Widget, {
  308. // summary:
  309. // Base implementation for all view mixins.
  310. // All calendar views should extend this widget.
  311. headerClass: "",
  312. useHeader: true,
  313. cloneClass: function(clazz, n, before){
  314. // summary:
  315. // Clones all nodes with the class 'clazz' in a widget
  316. var template = dojo.query(clazz, this.domNode)[0];
  317. var i;
  318. if(!before){
  319. for(i = 0; i < n; i++){
  320. template.parentNode.appendChild(template.cloneNode(true));
  321. }
  322. }else{
  323. var bNode = dojo.query(clazz, this.domNode)[0];
  324. for(i = 0; i < n; i++){
  325. template.parentNode.insertBefore(template.cloneNode(true), bNode);
  326. }
  327. }
  328. },
  329. _setText: function(node, text){
  330. // summary:
  331. // Sets the text inside a node
  332. if(node.innerHTML != text){
  333. dojo.empty(node);
  334. node.appendChild(dojo.doc.createTextNode(text));
  335. }
  336. },
  337. getHeader: function(){
  338. // summary:
  339. // Returns the header node of a view. If none exists,
  340. // an empty DIV is created and returned.
  341. return this.header || (this.header = this.header = dojo.create("span", { "class":this.headerClass }));
  342. },
  343. onValueSelected: function(date){
  344. //Stub function called when a date is selected
  345. },
  346. adjustDate: function(date, amount){
  347. // summary:
  348. // Adds or subtracts values from a date.
  349. // The unit, e.g. "day", "month" or "year", is
  350. // specified in the "datePart" property of the
  351. // calendar view mixin.
  352. return dojo.date.add(date, this.datePart, amount);
  353. },
  354. onDisplay: function(){
  355. // summary:
  356. // Stub function that can be used to tell a view when it is shown.
  357. },
  358. onBeforeDisplay: function(){
  359. // summary:
  360. // Stub function that can be used to tell a view it is about to be shown.
  361. },
  362. onBeforeUnDisplay: function(){
  363. // summary:
  364. // Stub function that can be used to tell
  365. // a view when it is no longer shown.
  366. }
  367. });
  368. dojo.declare("dojox.widget._CalendarDay", null, {
  369. // summary:
  370. // Mixin for the dojox.widget.Calendar which provides
  371. // the standard day-view. A single month is shown at a time.
  372. parent: null,
  373. constructor: function(){
  374. this._addView(dojox.widget._CalendarDayView);
  375. }
  376. });
  377. dojo.declare("dojox.widget._CalendarDayView", [dojox.widget._CalendarView, dijit._Templated], {
  378. // summary: View class for the dojox.widget.Calendar.
  379. // Adds a view showing every day of a single month to the calendar.
  380. // This should not be mixed in directly with dojox.widget._CalendarBase.
  381. // Instead, use dojox.widget._CalendarDay
  382. // templateString: String
  383. // The template to be used to construct the widget.
  384. templateString: dojo.cache("dojox.widget", "Calendar/CalendarDay.html", "<div class=\"dijitCalendarDayLabels\" style=\"left: 0px;\" dojoAttachPoint=\"dayContainer\">\n\t<div dojoAttachPoint=\"header\">\n\t\t<div dojoAttachPoint=\"monthAndYearHeader\">\n\t\t\t<span dojoAttachPoint=\"monthLabelNode\" class=\"dojoxCalendarMonthLabelNode\"></span>\n\t\t\t<span dojoAttachPoint=\"headerComma\" class=\"dojoxCalendarComma\">,</span>\n\t\t\t<span dojoAttachPoint=\"yearLabelNode\" class=\"dojoxCalendarDayYearLabel\"></span>\n\t\t</div>\n\t</div>\n\t<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"margin: auto;\">\n\t\t<thead>\n\t\t\t<tr>\n\t\t\t\t<td class=\"dijitCalendarDayLabelTemplate\"><div class=\"dijitCalendarDayLabel\"></div></td>\n\t\t\t</tr>\n\t\t</thead>\n\t\t<tbody dojoAttachEvent=\"onclick: _onDayClick\">\n\t\t\t<tr class=\"dijitCalendarWeekTemplate\">\n\t\t\t\t<td class=\"dojoxCalendarNextMonth dijitCalendarDateTemplate\">\n\t\t\t\t\t<div class=\"dijitCalendarDateLabel\"></div>\n\t\t\t\t</td>\n\t\t\t</tr>\n\t\t</tbody>\n\t</table>\n</div>\n"),
  385. // datePart: String
  386. // Specifies how much to increment the displayed date when the user
  387. // clicks the array button to increment of decrement the view.
  388. datePart: "month",
  389. // dayWidth: String
  390. // Specifies the type of day name to display. "narrow" causes just one letter to be shown.
  391. dayWidth: "narrow",
  392. postCreate: function(){
  393. // summary:
  394. // Constructs the calendar view.
  395. this.cloneClass(".dijitCalendarDayLabelTemplate", 6);
  396. this.cloneClass(".dijitCalendarDateTemplate", 6);
  397. // now make 6 week rows
  398. this.cloneClass(".dijitCalendarWeekTemplate", 5);
  399. // insert localized day names in the header
  400. var dayNames = dojo.date.locale.getNames('days', this.dayWidth, 'standAlone', this.getLang());
  401. var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.getLang());
  402. // Set the text of the day labels.
  403. dojo.query(".dijitCalendarDayLabel", this.domNode).forEach(function(label, i){
  404. this._setText(label, dayNames[(i + dayOffset) % 7]);
  405. }, this);
  406. },
  407. onDisplay: function(){
  408. if(!this._addedFx){
  409. // Add visual effects to the view, if any has been specified.
  410. this._addedFx = true;
  411. this.addFx(".dijitCalendarDateTemplate div", this.domNode);
  412. }
  413. },
  414. _onDayClick: function(e){
  415. // summary:
  416. // Executed when a day value is clicked.
  417. // If the user somehow clicked the TR, rather than a
  418. // cell, ignore it.
  419. if(typeof(e.target._date) == "undefined"){return;}
  420. var date = new Date(this.get("displayMonth"));
  421. var p = e.target.parentNode;
  422. var c = "dijitCalendar";
  423. var d = dojo.hasClass(p, c + "PreviousMonth") ? -1 :
  424. (dojo.hasClass(p, c + "NextMonth") ? 1 : 0);
  425. if(d){date = dojo.date.add(date, "month", d)}
  426. date.setDate(e.target._date);
  427. // If the day is disabled, ignore it
  428. if(this.isDisabledDate(date)){
  429. dojo.stopEvent(e);
  430. return;
  431. }
  432. this.parent._onDateSelected(date);
  433. },
  434. _setValueAttr: function(value){
  435. //Change the day values
  436. this._populateDays();
  437. },
  438. _populateDays: function(){
  439. // summary:
  440. // Fills the days of the current month.
  441. var currentDate = new Date(this.get("displayMonth"));
  442. currentDate.setDate(1);
  443. var firstDay = currentDate.getDay();
  444. var daysInMonth = dojo.date.getDaysInMonth(currentDate);
  445. var daysInPreviousMonth = dojo.date.getDaysInMonth(dojo.date.add(currentDate, "month", -1));
  446. var today = new Date();
  447. var selected = this.get('value');
  448. var dayOffset = dojo.cldr.supplemental.getFirstDayOfWeek(this.getLang());
  449. if(dayOffset > firstDay){ dayOffset -= 7; }
  450. var compareDate = dojo.date.compare;
  451. var templateCls = ".dijitCalendarDateTemplate";
  452. var selectedCls = "dijitCalendarSelectedDate";
  453. var oldDate = this._lastDate;
  454. var redrawRequired = oldDate == null
  455. || oldDate.getMonth() != currentDate.getMonth()
  456. || oldDate.getFullYear() != currentDate.getFullYear();
  457. this._lastDate = currentDate;
  458. // If still showing the same month, it's much faster to not redraw,
  459. // and just change the selected date.
  460. if(!redrawRequired){
  461. dojo.query(templateCls, this.domNode)
  462. .removeClass(selectedCls)
  463. .filter(function(node){
  464. return node.className.indexOf("dijitCalendarCurrent") > -1
  465. && node._date == selected.getDate();
  466. })
  467. .addClass(selectedCls);
  468. return;
  469. }
  470. // Iterate through dates in the calendar and fill in date numbers and style info
  471. dojo.query(templateCls, this.domNode).forEach(function(template, i){
  472. i += dayOffset;
  473. var date = new Date(currentDate);
  474. var number, clazz = "dijitCalendar", adj = 0;
  475. if(i < firstDay){
  476. number = daysInPreviousMonth - firstDay + i + 1;
  477. adj = -1;
  478. clazz += "Previous";
  479. }else if(i >= (firstDay + daysInMonth)){
  480. number = i - firstDay - daysInMonth + 1;
  481. adj = 1;
  482. clazz += "Next";
  483. }else{
  484. number = i - firstDay + 1;
  485. clazz += "Current";
  486. }
  487. if(adj){
  488. date = dojo.date.add(date, "month", adj);
  489. }
  490. date.setDate(number);
  491. if(!compareDate(date, today, "date")){
  492. clazz = "dijitCalendarCurrentDate " + clazz;
  493. }
  494. if(!compareDate(date, selected, "date")
  495. && !compareDate(date, selected, "month")
  496. && !compareDate(date, selected, "year") ){
  497. clazz = selectedCls + " " + clazz;
  498. }
  499. if(this.isDisabledDate(date, this.getLang())){
  500. clazz = " dijitCalendarDisabledDate " + clazz;
  501. }
  502. var clazz2 = this.getClassForDate(date, this.getLang());
  503. if(clazz2){
  504. clazz = clazz2 + " " + clazz;
  505. }
  506. template.className = clazz + "Month dijitCalendarDateTemplate";
  507. template.dijitDateValue = date.valueOf();
  508. var label = dojo.query(".dijitCalendarDateLabel", template)[0];
  509. this._setText(label, date.getDate());
  510. label._date = label.parentNode._date = date.getDate();
  511. }, this);
  512. // Fill in localized month name
  513. var monthNames = dojo.date.locale.getNames('months', 'wide', 'standAlone', this.getLang());
  514. this._setText(this.monthLabelNode, monthNames[currentDate.getMonth()]);
  515. this._setText(this.yearLabelNode, currentDate.getFullYear());
  516. }
  517. });
  518. dojo.declare("dojox.widget._CalendarMonthYear", null, {
  519. // summary:
  520. // Mixin class for adding a view listing all 12
  521. // months of the year to the dojox.widget._CalendarBase
  522. constructor: function(){
  523. // summary:
  524. // Adds a dojox.widget._CalendarMonthView view to the calendar widget.
  525. this._addView(dojox.widget._CalendarMonthYearView);
  526. }
  527. });
  528. dojo.declare("dojox.widget._CalendarMonthYearView", [dojox.widget._CalendarView, dijit._Templated], {
  529. // summary:
  530. // A Calendar view listing the 12 months of the year
  531. // templateString: String
  532. // The template to be used to construct the widget.
  533. templateString: dojo.cache("dojox.widget", "Calendar/CalendarMonthYear.html", "<div class=\"dojoxCal-MY-labels\" style=\"left: 0px;\"\t\n\tdojoAttachPoint=\"myContainer\" dojoAttachEvent=\"onclick: onClick\">\n\t\t<table cellspacing=\"0\" cellpadding=\"0\" border=\"0\" style=\"margin: auto;\">\n\t\t\t\t<tbody>\n\t\t\t\t\t\t<tr class=\"dojoxCal-MY-G-Template\">\n\t\t\t\t\t\t\t\t<td class=\"dojoxCal-MY-M-Template\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"dojoxCalendarMonthLabel\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td class=\"dojoxCal-MY-M-Template\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"dojoxCalendarMonthLabel\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td class=\"dojoxCal-MY-Y-Template\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"dojoxCalendarYearLabel\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t\t\t<td class=\"dojoxCal-MY-Y-Template\">\n\t\t\t\t\t\t\t\t\t\t<div class=\"dojoxCalendarYearLabel\"></div>\n\t\t\t\t\t\t\t\t</td>\n\t\t\t\t\t\t </tr>\n\t\t\t\t\t\t <tr class=\"dojoxCal-MY-btns\">\n\t\t\t\t\t\t \t <td class=\"dojoxCal-MY-btns\" colspan=\"4\">\n\t\t\t\t\t\t \t\t <span class=\"dijitReset dijitInline dijitButtonNode ok-btn\" dojoAttachEvent=\"onclick: onOk\" dojoAttachPoint=\"okBtn\">\n\t\t\t\t\t\t \t \t \t <button\tclass=\"dijitReset dijitStretch dijitButtonContents\">OK</button>\n\t\t\t\t\t\t\t\t </span>\n\t\t\t\t\t\t\t\t <span class=\"dijitReset dijitInline dijitButtonNode cancel-btn\" dojoAttachEvent=\"onclick: onCancel\" dojoAttachPoint=\"cancelBtn\">\n\t\t\t\t\t\t \t \t\t <button\tclass=\"dijitReset dijitStretch dijitButtonContents\">Cancel</button>\n\t\t\t\t\t\t\t\t </span>\n\t\t\t\t\t\t \t </td>\n\t\t\t\t\t\t </tr>\n\t\t\t\t</tbody>\n\t\t</table>\n</div>\n"),
  534. // datePart: String
  535. // Specifies how much to increment the displayed date when the user
  536. // clicks the array button to increment of decrement the view.
  537. datePart: "year",
  538. // displayedYears: Number
  539. // The number of years to display at once.
  540. displayedYears: 10,
  541. useHeader: false,
  542. postCreate: function(){
  543. this.cloneClass(".dojoxCal-MY-G-Template", 5, ".dojoxCal-MY-btns");
  544. this.monthContainer = this.yearContainer = this.myContainer;
  545. var yClass = "dojoxCalendarYearLabel";
  546. var dClass = "dojoxCalendarDecrease";
  547. var iClass = "dojoxCalendarIncrease";
  548. dojo.query("." + yClass, this.myContainer).forEach(function(node, idx){
  549. var clazz = iClass;
  550. switch(idx){
  551. case 0:
  552. clazz = dClass;
  553. case 1:
  554. dojo.removeClass(node, yClass);
  555. dojo.addClass(node, clazz);
  556. break;
  557. }
  558. });
  559. // Get the year increment and decrement buttons.
  560. this._decBtn = dojo.query('.' + dClass, this.myContainer)[0];
  561. this._incBtn = dojo.query('.' + iClass, this.myContainer)[0];
  562. dojo.query(".dojoxCal-MY-M-Template", this.domNode)
  563. .filter(function(item){
  564. return item.cellIndex == 1;
  565. })
  566. .addClass("dojoxCal-MY-M-last");
  567. dojo.connect(this, "onBeforeDisplay", dojo.hitch(this, function(){
  568. this._cachedDate = new Date(this.get("value").getTime());
  569. this._populateYears(this._cachedDate.getFullYear());
  570. this._populateMonths();
  571. this._updateSelectedMonth();
  572. this._updateSelectedYear();
  573. }));
  574. dojo.connect(this, "_populateYears", dojo.hitch(this, function(){
  575. this._updateSelectedYear();
  576. }));
  577. dojo.connect(this, "_populateMonths", dojo.hitch(this, function(){
  578. this._updateSelectedMonth();
  579. }));
  580. this._cachedDate = this.get("value");
  581. this._populateYears();
  582. this._populateMonths();
  583. // Add visual effects to the view, if any have been mixed in
  584. this.addFx(".dojoxCalendarMonthLabel,.dojoxCalendarYearLabel ", this.myContainer);
  585. },
  586. _setValueAttr: function(value){
  587. if (value && value.getFullYear()) {
  588. this._populateYears(value.getFullYear());
  589. }
  590. },
  591. getHeader: function(){
  592. return null;
  593. },
  594. _getMonthNames: function(format){
  595. // summary:
  596. // Returns localized month names
  597. this._monthNames = this._monthNames || dojo.date.locale.getNames('months', format, 'standAlone', this.getLang());
  598. return this._monthNames;
  599. },
  600. _populateMonths: function(){
  601. // summary:
  602. // Populate the month names using the localized values.
  603. var monthNames = this._getMonthNames('abbr');
  604. dojo.query(".dojoxCalendarMonthLabel", this.monthContainer).forEach(dojo.hitch(this, function(node, cnt){
  605. this._setText(node, monthNames[cnt]);
  606. }));
  607. var constraints = this.get('constraints');
  608. if(constraints){
  609. var date = new Date();
  610. date.setFullYear(this._year);
  611. var min = -1, max = 12;
  612. if(constraints.min){
  613. var minY = constraints.min.getFullYear();
  614. if(minY > this._year){
  615. min = 12;
  616. }else if(minY == this._year){
  617. min = constraints.min.getMonth();
  618. }
  619. }
  620. if(constraints.max){
  621. var maxY = constraints.max.getFullYear();
  622. if(maxY < this._year){
  623. max = -1;
  624. }else if(maxY == this._year){
  625. max = constraints.max.getMonth();
  626. }
  627. }
  628. dojo.query(".dojoxCalendarMonthLabel", this.monthContainer)
  629. .forEach(dojo.hitch(this, function(node, cnt){
  630. dojo[(cnt < min || cnt > max) ? "addClass" : "removeClass"]
  631. (node, 'dijitCalendarDisabledDate');
  632. }));
  633. }
  634. var h = this.getHeader();
  635. if(h){
  636. this._setText(this.getHeader(), this.get("value").getFullYear());
  637. }
  638. },
  639. _populateYears: function(year){
  640. // summary:
  641. // Fills the list of years with a range of 12 numbers, with the current year
  642. // being the 6th number.
  643. var constraints = this.get('constraints');
  644. var dispYear = year || this.get("value").getFullYear();
  645. var firstYear = dispYear - Math.floor(this.displayedYears/2);
  646. var min = constraints && constraints.min ? constraints.min.getFullYear() : firstYear -10000;
  647. firstYear = Math.max(min, firstYear);
  648. // summary: Writes the years to display to the view
  649. this._displayedYear = dispYear;
  650. var yearLabels = dojo.query(".dojoxCalendarYearLabel", this.yearContainer);
  651. var max = constraints && constraints.max ? constraints.max.getFullYear() - firstYear : yearLabels.length;
  652. var disabledClass = 'dijitCalendarDisabledDate';
  653. yearLabels.forEach(dojo.hitch(this, function(node, cnt){
  654. if(cnt <= max){
  655. this._setText(node, firstYear + cnt);
  656. dojo.removeClass(node, disabledClass);
  657. }else{
  658. dojo.addClass(node, disabledClass);
  659. }
  660. }));
  661. if(this._incBtn){
  662. dojo[max < yearLabels.length ? "addClass" : "removeClass"](this._incBtn, disabledClass);
  663. }
  664. if(this._decBtn){
  665. dojo[min >= firstYear ? "addClass" : "removeClass"](this._decBtn, disabledClass);
  666. }
  667. var h = this.getHeader();
  668. if(h){
  669. this._setText(this.getHeader(), firstYear + " - " + (firstYear + 11));
  670. }
  671. },
  672. _updateSelectedYear: function(){
  673. this._year = String((this._cachedDate || this.get("value")).getFullYear());
  674. this._updateSelectedNode(".dojoxCalendarYearLabel", dojo.hitch(this, function(node, idx){
  675. return this._year !== null && node.innerHTML == this._year;
  676. }));
  677. },
  678. _updateSelectedMonth: function(){
  679. var month = (this._cachedDate || this.get("value")).getMonth();
  680. this._month = month;
  681. this._updateSelectedNode(".dojoxCalendarMonthLabel", function(node, idx){
  682. return idx == month;
  683. });
  684. },
  685. _updateSelectedNode: function(query, filter){
  686. var sel = "dijitCalendarSelectedDate";
  687. dojo.query(query, this.domNode)
  688. .forEach(function(node, idx, array){
  689. dojo[filter(node, idx, array) ? "addClass" : "removeClass"](node.parentNode, sel);
  690. });
  691. var selMonth = dojo.query('.dojoxCal-MY-M-Template div', this.myContainer)
  692. .filter(function(node){
  693. return dojo.hasClass(node.parentNode, sel);
  694. })[0];
  695. if(!selMonth){return;}
  696. var disabled = dojo.hasClass(selMonth, 'dijitCalendarDisabledDate');
  697. dojo[disabled ? 'addClass' : 'removeClass'](this.okBtn, "dijitDisabled");
  698. },
  699. onClick: function(evt){
  700. // summary:
  701. // Handles clicks on month names
  702. var clazz;
  703. var _this = this;
  704. var sel = "dijitCalendarSelectedDate";
  705. function hc(c){
  706. return dojo.hasClass(evt.target, c);
  707. }
  708. if(hc('dijitCalendarDisabledDate')){
  709. dojo.stopEvent(evt);
  710. return false;
  711. }
  712. if(hc("dojoxCalendarMonthLabel")){
  713. clazz = "dojoxCal-MY-M-Template";
  714. this._month = evt.target.parentNode.cellIndex + (evt.target.parentNode.parentNode.rowIndex * 2);
  715. this._cachedDate.setMonth(this._month);
  716. this._updateSelectedMonth();
  717. }else if(hc( "dojoxCalendarYearLabel")){
  718. clazz = "dojoxCal-MY-Y-Template";
  719. this._year = Number(evt.target.innerHTML);
  720. this._cachedDate.setYear(this._year);
  721. this._populateMonths();
  722. this._updateSelectedYear();
  723. }else if(hc("dojoxCalendarDecrease")){
  724. this._populateYears(this._displayedYear - 10);
  725. return true;
  726. }else if(hc("dojoxCalendarIncrease")){
  727. this._populateYears(this._displayedYear + 10);
  728. return true;
  729. }else{
  730. return true;
  731. }
  732. dojo.stopEvent(evt);
  733. return false;
  734. },
  735. onOk: function(evt){
  736. dojo.stopEvent(evt);
  737. if(dojo.hasClass(this.okBtn, "dijitDisabled")){
  738. return false;
  739. }
  740. this.onValueSelected(this._cachedDate);
  741. return false;
  742. },
  743. onCancel: function(evt){
  744. dojo.stopEvent(evt);
  745. this.onValueSelected(this.get("value"));
  746. return false;
  747. }
  748. });
  749. dojo.declare("dojox.widget.Calendar2Pane",
  750. [dojox.widget._CalendarBase,
  751. dojox.widget._CalendarDay,
  752. dojox.widget._CalendarMonthYear], {
  753. // summary: A Calendar withtwo panes, the second one
  754. // containing both month and year
  755. }
  756. );
  757. dojo.declare("dojox.widget.Calendar",
  758. [dojox.widget._CalendarBase,
  759. dojox.widget._CalendarDay,
  760. dojox.widget._CalendarMonthYear], {
  761. // summary: The standard Calendar. It includes day and month/year views.
  762. // No visual effects are included.
  763. }
  764. );
  765. dojo.declare("dojox.widget.DailyCalendar",
  766. [dojox.widget._CalendarBase,
  767. dojox.widget._CalendarDay], {
  768. // summary: A calendar withonly a daily view.
  769. _makeDate: function(value){
  770. var now = new Date();
  771. now.setDate(value);
  772. return now;
  773. }
  774. }
  775. );
  776. dojo.declare("dojox.widget.MonthAndYearlyCalendar",
  777. [dojox.widget._CalendarBase,
  778. dojox.widget._CalendarMonthYear], {
  779. // summary: A calendar withonly a daily view.
  780. }
  781. );
  782. });