5c4807545182aa6af99860a9faeff8fe38006480
[moodle.git] / lib / form / yui / dateselector / dateselector.js
1 YUI.add('moodle-form-dateselector', function(Y) {
3     /**
4      * Add some custom methods to the node class to make our lives a little
5      * easier within this module.
6      */
7     Y.mix(Y.Node.prototype, {
8         /**
9          * Gets the value of the first option in the select box
10          */
11         firstOptionValue : function() {
12             if (this.get('nodeName').toLowerCase() != 'select') {
13                 return false;
14             }
15             return this.one('option').get('value');
16         },
17         /**
18          * Gets the value of the last option in the select box
19          */
20         lastOptionValue : function() {
21             if (this.get('nodeName').toLowerCase() != 'select') {
22                 return false;
23             }
24             return this.all('option').item(this.optionSize()-1).get('value');
25         },
26         /**
27          * Gets the number of options in the select box
28          */
29         optionSize : function() {
30             if (this.get('nodeName').toLowerCase() != 'select') {
31                 return false;
32             }
33             return parseInt(this.all('option').size());
34         },
35         /**
36          * Gets the value of the selected option in the select box
37          */
38         selectedOptionValue : function() {
39             if (this.get('nodeName').toLowerCase() != 'select') {
40                 return false;
41             }
42             return this.all('option').item(this.get('selectedIndex')).get('value');
43         }
44     });
46     /**
47      * Calendar class
48      *
49      * This is our main class
50      */
51     var CALENDAR = function(config) {
52         CALENDAR.superclass.constructor.apply(this, arguments);
53     };
54     CALENDAR.prototype = {
55         panel : null,
56         yearselect : null,
57         monthselect : null,
58         dayselect : null,
59         calendarimage : null,
60         enablecheckbox : null,
61         closepopup : true,
62         initializer : function(config) {
63             var controls = this.get('node').all('select');
64             controls.each(function(node){
65                 if (node.get('name').match(/\[year]/)) {
66                     this.yearselect = node;
67                 } else if (node.get('name').match(/\[month\]/)) {
68                     this.monthselect = node;
69                 } else if (node.get('name').match(/\[day]/)) {
70                     this.dayselect = node;
71                 }
72                 node.after('change', this.handle_select_change, this);
73             }, this);
75             // Loop through the input fields.
76             var inputs = this.get('node').all('input');
77             inputs.each(function(node) {
78                 // Check if the current node is a calendar image field.
79                 if (node.get('name').match(/\[calendar]/)) {
80                     // Set it so that when the image is clicked the pop-up displays.
81                     node.on('click', this.focus_event, this);
82                     // Set the node to the calendarimage variable.
83                     this.calendarimage = node;
84                 } else { // Must be the enabled checkbox field.
85                     // If the enable checkbox is clicked we want to either disable/enable the calendar image.
86                     node.on('click', this.toggle_calendar_image, this);
87                     // Set the node to the enablecheckbox variable.
88                     this.enablecheckbox = node;
89                 }
90                 // Ensure that the calendarimage and enablecheckbox values have been set.
91                 if (this.calendarimage && this.enablecheckbox) {
92                     // Set the calendar icon status depending on the value of the checkbox.
93                     this.toggle_calendar_image();
94                 }
95             }, this);
96         },
97         focus_event : function(e) {
98             M.form.dateselector.cancel_any_timeout();
99             // If the current owner is set, then the pop-up is currently being displayed, so hide it.
100             if (M.form.dateselector.currentowner == this) {
101                 this.release_calendar();
102             } else if ((this.enablecheckbox == null)
103                 || (this.enablecheckbox.get('checked'))) { // Must be hidden. If the field is enabled display the pop-up.
104                 this.claim_calendar();
105             }
106             // Stop the input image field from submitting the form.
107             e.preventDefault();
108         },
109         handle_select_change : function(e) {
110             // It may seem as if the following variable is not used, however any call to set_date_from_selects will trigger a
111             // call to set_selects_from_date if the calendar is open as the date has changed. Whenever the calendar is displayed
112             // the set_selects_from_date function is set to trigger on any date change (see function connect_handlers).
113             this.closepopup = false;
114             this.set_date_from_selects();
115             this.closepopup = true;
116         },
117         claim_calendar : function() {
118             M.form.dateselector.cancel_any_timeout();
119             if (M.form.dateselector.currentowner == this) {
120                 return;
121             }
122             if (M.form.dateselector.currentowner) {
123                 M.form.dateselector.currentowner.release_calendar();
124             }
125             if (M.form.dateselector.currentowner != this) {
126                 this.connect_handlers();
127                 this.set_date_from_selects();
128             }
129             M.form.dateselector.currentowner = this;
130             M.form.dateselector.calendar.cfg.setProperty('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1));
131             M.form.dateselector.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31));
132             M.form.dateselector.panel.show();
133             M.form.dateselector.fix_position();
134             setTimeout(function(){M.form.dateselector.cancel_any_timeout()}, 100);
135         },
136         set_date_from_selects : function() {
137             var year = parseInt(this.yearselect.get('value'));
138             var month = parseInt(this.monthselect.get('value')) - 1;
139             var day = parseInt(this.dayselect.get('value'));
140             M.form.dateselector.calendar.select(new Date(year, month, day));
141             M.form.dateselector.calendar.setMonth(month);
142             M.form.dateselector.calendar.setYear(year);
143             M.form.dateselector.calendar.render();
144         },
145         set_selects_from_date : function(eventtype, args) {
146             var date = args[0][0];
147             var newyear = date[0];
148             var newindex = newyear - this.yearselect.firstOptionValue();
149             this.yearselect.set('selectedIndex', newindex);
150             this.monthselect.set('selectedIndex', date[1] - this.monthselect.firstOptionValue());
151             this.dayselect.set('selectedIndex', date[2] - this.dayselect.firstOptionValue());
152             if (M.form.dateselector.currentowner && this.closepopup) {
153                 this.release_calendar();
154             }
155         },
156         connect_handlers : function() {
157             M.form.dateselector.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
158         },
159         release_calendar : function() {
160             M.form.dateselector.panel.hide();
161             M.form.dateselector.currentowner = null;
162             M.form.dateselector.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
163         },
164         toggle_calendar_image : function() {
165             // If the enable checkbox is not checked, disable the image.
166             if (!this.enablecheckbox.get('checked')) {
167                 this.calendarimage.set('disabled', 'disabled');
168                 this.calendarimage.setStyle('cursor', 'default');
169             } else {
170                 this.calendarimage.set('disabled', false);
171                 this.calendarimage.setStyle('cursor', 'auto');
172             }
173         }
174     };
175     Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, {
176         NAME : 'Date Selector',
177         ATTRS : {
178             firstdayofweek  : {
179                 validator : Y.Lang.isString
180             },
181             node : {
182                 setter : function(node) {
183                     return Y.one(node);
184                 }
185             }
186         }
187     });
189     M.form = M.form || {};
190     M.form.dateselector = {
191         panel : null,
192         calendar : null,
193         currentowner : null,
194         hidetimeout : null,
195         repositiontimeout : null,
196         init_date_selectors : function(config) {
197             if (this.panel === null) {
198                 this.initPanel(config);
199             }
200             Y.all('.fdate_time_selector').each(function() {
201                 config.node = this;
202                 new CALENDAR(config);
203             });
204             Y.all('.fdate_selector').each(function() {
205                 config.node = this;
206                 new CALENDAR(config);
207             });
208         },
209         initPanel : function(config) {
210             this.panel = new Y.Overlay({
211                 visible : false,
212                 bodyContent : Y.Node.create('<div id="dateselector-calendar-content"></div>'),
213                 id : 'dateselector-calendar-panel'
214             });
215             this.panel.render(document.body);
216             // zIndex is added by panel.render() and is set to 0.
217             // Remove zIndex from panel, as this should be set by CSS. This can be done by removeAttr but
218             // ie8 fails and there is know issue for it.
219             Y.one('#dateselector-calendar-panel').setStyle('zIndex', null);
220             this.panel.on('heightChange', this.fix_position, this);
222             Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();});
223             Y.one(document.body).on('click', this.document_click, this);
225             this.calendar = new Y.YUI2.widget.Calendar(document.getElementById('dateselector-calendar-content'), {
226                 iframe: false,
227                 hide_blank_weeks: true,
228                 start_weekday: config.firstdayofweek,
229                 locale_weekdays: 'medium',
230                 locale_months: 'long',
231                 WEEKDAYS_MEDIUM: [
232                     config.sun,
233                     config.mon,
234                     config.tue,
235                     config.wed,
236                     config.thu,
237                     config.fri,
238                     config.sat ],
239                 MONTHS_LONG: [
240                     config.january,
241                     config.february,
242                     config.march,
243                     config.april,
244                     config.may,
245                     config.june,
246                     config.july,
247                     config.august,
248                     config.september,
249                     config.october,
250                     config.november,
251                     config.december ]
252             });
253             this.calendar.changePageEvent.subscribe(function(){
254                 this.fix_position();
255             }, this);
256         },
257         cancel_any_timeout : function() {
258             if (this.hidetimeout) {
259                 clearTimeout(this.hidetimeout);
260                 this.hidetimeout = null;
261             }
262             if (this.repositiontimeout) {
263                 clearTimeout(this.repositiontimeout);
264                 this.repositiontimeout = null;
265             }
266         },
267         delayed_reposition : function() {
268             if (this.repositiontimeout) {
269                 clearTimeout(this.repositiontimeout);
270                 this.repositiontimeout = null;
271             }
272             this.repositiontimeout = setTimeout(this.fix_position, 500);
273         },
274         fix_position : function() {
275             if (this.currentowner) {
276                 this.panel.set('align', {
277                     node:this.currentowner.get('node').one('select'),
278                     points:[Y.WidgetPositionAlign.BL, Y.WidgetPositionAlign.TL]
279                 });
280             }
281         },
282         document_click : function(e) {
283             if (this.currentowner) {
284                 if (this.currentowner.get('node').ancestor('div').contains(e.target)) {
285                     setTimeout(function() {M.form.dateselector.cancel_any_timeout()}, 100);
286                 } else {
287                     this.currentowner.release_calendar();
288                 }
289             }
290         }
291     }
293 }, '@VERSION@', {requires:['base','node','overlay', 'yui2-calendar', 'moodle-form-dateselector-skin']});