form-dateselector MDL-23096 Converted the date selector to a YUI-Moodle module
[moodle.git] / lib / form / yui / dateselector / dateselector.js
CommitLineData
e5473854
SH
1YUI.add('moodle-form-dateselector', function(Y) {
2
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 });
45
46 /**
47 * Override the default inDoc method as it is broken in IE
48 *
49 * YUI Bug: http://yuilibrary.com/projects/yui3/ticket/2529157
50 * Example location: lib/yui/3.1.1/build/dom/dom-debug.js
51 */
52 Y.DOM.inDoc = function(element, doc) {
53 // there may be multiple elements with the same ID
54 doc = doc || element['ownerDocument'];
55 var nodes = [],
56 ret = false,
57 i,
58 node;
59
60 if (!element.getAttribute('id')) {
61 element.setAttribute('id', Y.guid());
62 }
63 nodes = Y.DOM.allById(element.id, doc);
64 for (i = 0; node = nodes[i++];) { // check for a match
65 if (node === element) {
66 ret = true;
67 break;
68 }
69 }
70 return ret;
71 }
72
73 /**
74 * Calendar class
75 *
76 * This is our main class
77 */
78 var CALENDAR = function(config) {
79 CALENDAR.superclass.constructor.apply(this, arguments);
80 }
81 CALENDAR.prototype = {
82 panel : null,
83 yearselect : null,
84 yearselectchange : null,
85 monthselect : null,
86 monthselectchange : null,
87 dayselect : null,
88 dayselectchange : null,
89 enablecheckbox : null,
90 initializer : function(config) {
91 var controls = this.get('node').all('select');
92 controls.each(function(node){
93 if (node.get('name').match(/\[year]/)) {
94 this.yearselect = node;
95 } else if (node.get('name').match(/\[month\]/)) {
96 this.monthselect = node;
97 } else if (node.get('name').match(/\[day]/)) {
98 this.dayselect = node;
99 } else {
100 node.on('focus', this.cancel_any_timeout, this);
101 node.on('blur', this.blur_event, this);
102 return;
103 }
104 node.maskDiv = Y.Node.create('<div class="dateselector-select-mask"></div>');
105 node.maskDiv.on('click', this.focus_event, this);
106 node.ancestor().insert(
107 Y.Node.create('<div class="dateselector-select-mask-point"></div>').append(
108 node.maskDiv.setStyles({
109 width : node.get('offsetWidth')+'px',
110 height : node.get('offsetHeight')+'px',
111 opacity : 0
112 })), node);
113 }, this);
114
115 if (this.yearselect && this.monthselect && this.dayselect) {
116 this.enablecheckbox = this.get('node').one('input');
117 if (this.enablecheckbox) {
118 this.enablecheckbox.on('focus', this.focus_event, this);
119 this.enablecheckbox.on('change', this.focus_event, this);
120 this.enablecheckbox.on('blur', this.blur_event, this);
121 }
122 }
123 },
124 focus_event : function(e) {
125 M.form.dateselector.cancel_any_timeout();
126 if (this.enablecheckbox == null || this.enablecheckbox.get('checked')) {
127 this.claim_calendar();
128 } else {
129 if (M.form.dateselector.currentowner) {
130 M.form.dateselector.currentowner.release_calendar();
131 }
132 }
133 },
134 blur_event : function(e) {
135 M.form.dateselector.hidetimeout = setTimeout(M.form.dateselector.release_current, 300);
136 },
137 handle_select_change : function(e) {
138 this.set_date_from_selects();
139 },
140 claim_calendar : function() {
141 M.form.dateselector.cancel_any_timeout();
142 if (M.form.dateselector.currentowner == this) {
143 return;
144 }
145 if (M.form.dateselector.currentowner) {
146 M.form.dateselector.currentowner.release_calendar();
147 }
148
149 if (M.form.dateselector.currentowner != this) {
150 this.connect_handlers();
151 this.set_date_from_selects();
152 }
153 M.form.dateselector.currentowner = this;
154 M.form.dateselector.calendar.cfg.setProperty('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1));
155 M.form.dateselector.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31));
156 M.form.dateselector.panel.set('constrain', this.get('node').ancestor('form'));
157 M.form.dateselector.panel.show();
158 M.form.dateselector.fix_position();
159 setTimeout(function(){M.form.dateselector.cancel_any_timeout()}, 100);
160 },
161 set_date_from_selects : function() {
162 var year = parseInt(this.yearselect.get('value'));
163 var month = parseInt(this.monthselect.get('value')) - 1;
164 var day = parseInt(this.dayselect.get('value'));
165 M.form.dateselector.calendar.select(new Date(year, month, day));
166 M.form.dateselector.calendar.setMonth(month);
167 M.form.dateselector.calendar.setYear(year);
168 M.form.dateselector.calendar.render();
169 },
170 set_selects_from_date : function(eventtype, args) {
171 var date = args[0][0];
172 var newyear = date[0];
173 var newindex = newyear - this.yearselect.firstOptionValue();
174 this.yearselect.set('selectedIndex', newindex);
175 this.monthselect.set('selectedIndex', date[1] - this.monthselect.firstOptionValue());
176 this.dayselect.set('selectedIndex', date[2] - this.dayselect.firstOptionValue());
177 },
178 connect_handlers : function() {
179 M.form.dateselector.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true);
180 },
181 release_calendar : function() {
182 M.form.dateselector.panel.hide();
183 M.form.dateselector.currentowner = null;
184 M.form.dateselector.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this);
185 }
186 }
187 Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, {
188 NAME : 'Date Selector',
189 ATTRS : {
190 firstdayofweek : {
191 validator : Y.Lang.isString
192 },
193 node : {
194 setter : function(node) {
195 return Y.one(node);
196 }
197 }
198 }
199 });
200
201 M.form = M.form || {};
202 M.form.dateselector = {
203 panel : null,
204 calendar : null,
205 currentowner : null,
206 hidetimeout : null,
207 repositiontimeout : null,
208 init_date_selectors : function(config) {
209 if (this.panel === null) {
210 this.initPanel(config);
211 }
212 Y.all('fieldset.fdate_time_selector').each(function(){
213 config.node = this;
214 new CALENDAR(config);
215 });
216 Y.all('fieldset.fdate_selector').each(function(){
217 config.node = this;
218 new CALENDAR(config);
219 });
220 },
221 initPanel : function(config) {
222 this.panel = new Y.Overlay({
223 visible : false,
224 constrain : true,
225 bodyContent : Y.Node.create('<div id="dateselector-calendar-content"></div>'),
226 id : 'dateselector-calendar-panel'
227 });
228 this.panel.render(document.body);
229 this.panel.on('heightChange', this.fix_position, this);
230
231 Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();});
232 Y.one(document.body).on('click', this.document_click, this);
233
234 this.calendar = new YAHOO.widget.Calendar(document.getElementById('dateselector-calendar-content'), {
235 iframe: false,
236 hide_blank_weeks: true,
237 start_weekday: config.firstdayofweek
238 });
239 this.calendar.changePageEvent.subscribe(function(){
240 this.fix_position();
241 }, this);
242 },
243 cancel_any_timeout : function() {
244 if (this.hidetimeout) {
245 clearTimeout(this.hidetimeout);
246 this.hidetimeout = null;
247 }
248 if (this.repositiontimeout) {
249 clearTimeout(this.repositiontimeout);
250 this.repositiontimeout = null;
251 }
252 },
253 delayed_reposition : function() {
254 if (this.repositiontimeout) {
255 clearTimeout(this.repositiontimeout);
256 this.repositiontimeout = null;
257 }
258 this.repositiontimeout = setTimeout(this.fix_position, 500);
259 },
260 fix_position : function() {
261 if (this.currentowner) {
262 this.panel.set('constrain', this.currentowner.get('node').ancestor('form'));
263 this.panel.set('align', {
264 node:this.currentowner.get('node').one('select'),
265 points:[Y.WidgetPositionAlign.BL, Y.WidgetPositionAlign.TL]
266 });
267 }
268 },
269 release_current : function() {
270 if (this.currentowner) {
271 this.currentowner.release_calendar();
272 }
273 },
274 document_click : function(e) {
275 if (this.currentowner) {
276 if (this.currentowner.get('node').ancestor('div').contains(e.target)) {
277 setTimeout(function() {M.form.dateselector.cancel_any_timeout()}, 100);
278 } else {
279 this.currentowner.release_calendar();
280 }
281 }
282 }
283 }
284
285}, '@VERSION@', {requires:['base','node','overlay', 'yui2-calendar', 'moodle-form-dateselector-skin']});