From 5cba1029cae33464f4189b6eeede3262148c938d Mon Sep 17 00:00:00 2001 From: Jerome Mouneyrac Date: Fri, 28 Feb 2014 15:05:43 +0800 Subject: [PATCH] MDL-32917 port YUI2 Moodle calendar into a shifted YUI3 Calendar --- .../moodle-form-dateselector-debug.js | 360 ++++++++++++++++++ .../moodle-form-dateselector-min.js | 2 + .../moodle-form-dateselector.js | 360 ++++++++++++++++++ .../assets/skins/sam/dateselector.css | 4 - lib/form/yui/dateselector/dateselector.js | 300 --------------- lib/form/yui/src/dateselector/build.json | 12 + lib/form/yui/src/dateselector/js/calendar.js | 177 +++++++++ .../yui/src/dateselector/js/dateselector.js | 146 +++++++ .../yui/src/dateselector/js/moodlecalendar.js | 32 ++ .../src/dateselector/meta/dateselector.json | 10 + 10 files changed, 1099 insertions(+), 304 deletions(-) create mode 100644 lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js create mode 100644 lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js create mode 100644 lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js delete mode 100644 lib/form/yui/dateselector/assets/skins/sam/dateselector.css delete mode 100644 lib/form/yui/dateselector/dateselector.js create mode 100644 lib/form/yui/src/dateselector/build.json create mode 100644 lib/form/yui/src/dateselector/js/calendar.js create mode 100644 lib/form/yui/src/dateselector/js/dateselector.js create mode 100644 lib/form/yui/src/dateselector/js/moodlecalendar.js create mode 100644 lib/form/yui/src/dateselector/meta/dateselector.json diff --git a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js new file mode 100644 index 00000000000..87a9c9323de --- /dev/null +++ b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-debug.js @@ -0,0 +1,360 @@ +YUI.add('moodle-form-dateselector', function (Y, NAME) { + +/** + * Add some custom methods to the node class to make our lives a little + * easier within this module. + */ +Y.mix(Y.Node.prototype, { + /** + * Gets the value of the first option in the select box + */ + firstOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.one('option').get('value'); + }, + /** + * Gets the value of the last option in the select box + */ + lastOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.optionSize()-1).get('value'); + }, + /** + * Gets the number of options in the select box + */ + optionSize: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return parseInt(this.all('option').size(), 10); + }, + /** + * Gets the value of the selected option in the select box + */ + selectedOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.get('selectedIndex')).get('value'); + } +}); + +M.form = M.form || {}; +M.form.dateselector = { + panel: null, + calendar: null, + currentowner: null, + hidetimeout: null, + repositiontimeout: null, + init_date_selectors: function(config) { + if (this.panel === null) { + this.initPanel(config); + } + Y.all('.fdate_time_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + Y.all('.fdate_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + }, + initPanel: function(config) { + this.panel = new Y.Overlay({ + visible: false, + bodyContent: Y.Node.create('
'), + id: 'dateselector-calendar-panel' + }); + this.panel.render(document.body); + // zIndex is added by panel.render() and is set to 0. + // Remove zIndex from panel, as this should be set by CSS. This can be done by removeAttr but + // ie8 fails and there is know issue for it. + Y.one('#dateselector-calendar-panel').setStyle('zIndex', null); + this.panel.on('heightChange', this.fix_position, this); + + Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();}); + Y.one(document.body).on('click', this.document_click, this); + + this.calendar = new MOODLECALENDAR({ + contentBox: "#dateselector-calendar-content", + width: "300px", + showPrevMonth: true, + showNextMonth: true, + firstdayofweek: config.firstdayofweek, + WEEKDAYS_MEDIUM: [ + config.sun, + config.mon, + config.tue, + config.wed, + config.thu, + config.fri, + config.sat ] + }); + }, + cancel_any_timeout: function() { + if (this.hidetimeout) { + clearTimeout(this.hidetimeout); + this.hidetimeout = null; + } + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + }, + delayed_reposition: function() { + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + this.repositiontimeout = setTimeout(this.fix_position, 500); + }, + fix_position: function() { + if (this.currentowner) { + var alignpoints = [ + Y.WidgetPositionAlign.BL, + Y.WidgetPositionAlign.TL + ]; + + // Change the alignment if this is an RTL language. + if (right_to_left()) { + alignpoints = [ + Y.WidgetPositionAlign.BR, + Y.WidgetPositionAlign.TR + ]; + } + + + this.panel.set('align', { + node: this.currentowner.get('node').one('select'), + points: alignpoints + }); + } + }, + document_click: function(e) { + if (this.currentowner) { + if (this.currentowner.get('node').ancestor('div').contains(e.target)) { + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + } else { + this.currentowner.release_calendar(e); + } + } + } +}; +/** + * Provides the Moodle Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * A class to overwrite the YUI3 Calendar in order to change the strings.. + * + * @class M.form_moodlecalendar + * @constructor + * @extends Calendar + */ +var MOODLECALENDAR = function() { + MOODLECALENDAR.superclass.constructor.apply(this, arguments); +}; + +Y.extend(MOODLECALENDAR, Y.Calendar, { + initializer: function(cfg) { + this.set("strings.very_short_weekdays", cfg.WEEKDAYS_MEDIUM); + this.set("strings.first_weekday", cfg.firstdayofweek); + } + }, { + NAME: 'Calendar', + ATTRS: {} + } +); + +M.form_moodlecalendar = M.form_moodlecalendar || {}; +M.form_moodlecalendar.initializer = function(params) { + return new MOODLECALENDAR(params); +}; +/** + * Provides the Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * Calendar class + */ +var CALENDAR = function() { + CALENDAR.superclass.constructor.apply(this, arguments); +}; +CALENDAR.prototype = { + panel: null, + yearselect: null, + monthselect: null, + dayselect: null, + calendarimage: null, + enablecheckbox: null, + closepopup: true, + initializer: function() { + var controls = this.get('node').all('select'); + controls.each(function(node){ + if (node.get('name').match(/\[year\]/)) { + this.yearselect = node; + } else if (node.get('name').match(/\[month\]/)) { + this.monthselect = node; + } else if (node.get('name').match(/\[day\]/)) { + this.dayselect = node; + } + node.after('change', this.handle_select_change, this); + }, this); + + // Loop through the input fields. + var inputs = this.get('node').all('input, a'); + inputs.each(function(node) { + // Check if the current node is a calendar image field. + if (node.get('name').match(/\[calendar\]/)) { + // Set it so that when the image is clicked the pop-up displays. + node.on('click', this.focus_event, this); + // Set the node to the calendarimage variable. + this.calendarimage = node; + } else { // Must be the enabled checkbox field. + // If the enable checkbox is clicked we want to either disable/enable the calendar image. + node.on('click', this.toggle_calendar_image, this); + // Set the node to the enablecheckbox variable. + this.enablecheckbox = node; + } + // Ensure that the calendarimage and enablecheckbox values have been set. + if (this.calendarimage && this.enablecheckbox) { + // Set the calendar icon status depending on the value of the checkbox. + this.toggle_calendar_image(); + } + }, this); + }, + focus_event: function(e) { + M.form.dateselector.cancel_any_timeout(); + // If the current owner is set, then the pop-up is currently being displayed, so hide it. + if (M.form.dateselector.currentowner === this) { + this.release_calendar(); + } else if ((this.enablecheckbox === null) + || (this.enablecheckbox.get('checked'))) { // Must be hidden. If the field is enabled display the pop-up. + this.claim_calendar(); + } + // Stop the input image field from submitting the form. + e.preventDefault(); + }, + handle_select_change: function() { + // It may seem as if the following variable is not used, however any call to set_date_from_selects will trigger a + // call to set_selects_from_date if the calendar is open as the date has changed. Whenever the calendar is displayed + // the set_selects_from_date function is set to trigger on any date change (see function connect_handlers). + this.closepopup = false; + this.set_date_from_selects(); + this.closepopup = true; + }, + claim_calendar: function() { + M.form.dateselector.cancel_any_timeout(); + if (M.form.dateselector.currentowner === this) { + return; + } + if (M.form.dateselector.currentowner) { + M.form.dateselector.currentowner.release_calendar(); + } + if (M.form.dateselector.currentowner !== this) { + this.connect_handlers(); + this.set_date_from_selects(); + } + M.form.dateselector.currentowner = this; + M.form.dateselector.calendar.set('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1)); + M.form.dateselector.calendar.set('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31)); + M.form.dateselector.panel.show(); + M.form.dateselector.calendar.show(); + M.form.dateselector.fix_position(); + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + + // Focus on the calendar. + M.form.dateselector.calendar.focus(); + + // When the user tab out the calendar, close it. + Y.one(document.body).on('keyup', function(e) { + // hide the calendar if we press a key and the calendar is not focussed, or if we press ESC in the calendar. + if ((M.form.dateselector.currentowner === this && !M.form.dateselector.calendar.get('focused')) || + ((e.keyCode === 27) && M.form.dateselector.calendar.get('focused'))) { + // Focus back on the calendar button. + this.calendarimage.focus(); + this.release_calendar(); + } + }, this); + + }, + set_date_from_selects: function() { + var year = parseInt(this.yearselect.get('value'), 10); + var month = parseInt(this.monthselect.get('value'), 10) - 1; + var day = parseInt(this.dayselect.get('value'), 10); + var date = new Date(year, month, day); + M.form.dateselector.calendar.deselectDates(); + M.form.dateselector.calendar.selectDates([date]); + M.form.dateselector.calendar.set("date", date); + M.form.dateselector.calendar.render(); + if (date.getDate() !== day) { + // Must've selected the 29 to 31st of a month that doesn't have such dates. + this.dayselect.set('value', date.getDate()); + this.monthselect.set('value', date.getMonth() + 1); + } + }, + set_selects_from_date: function(ev) { + var date = ev.newSelection[0]; + var newyear = Y.DataType.Date.format(date, {format: "%Y"}); + var newindex = newyear - this.yearselect.firstOptionValue(); + this.yearselect.set('selectedIndex', newindex); + this.monthselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%m"}) - this.monthselect.firstOptionValue()); + this.dayselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%d"}) - this.dayselect.firstOptionValue()); + if (M.form.dateselector.currentowner && this.closepopup) { + this.release_calendar(); + } + }, + connect_handlers: function() { + M.form.dateselector.calendar.on('selectionChange', this.set_selects_from_date, this, true); + }, + release_calendar: function(e) { + M.form.dateselector.panel.hide(); + M.form.dateselector.calendar.detach('selectionChange', this.set_selects_from_date); + M.form.dateselector.calendar.hide(); + M.form.dateselector.currentowner = null; + + // Put the focus back to the image calendar that we clicked. + if (e === null || typeof e === "undefined" || e.type !== "click") { + this.calendarimage.focus(); + } + }, + toggle_calendar_image: function() { + // If the enable checkbox is det checked, disable the image. + if (!this.enablecheckbox.get('checked')) { + this.calendarimage.set('disabled', 'disabled'); + this.calendarimage.setStyle('cursor', 'default'); + this.release_calendar(); + } else { + this.calendarimage.set('disabled', false); + this.calendarimage.setStyle('cursor', null); + } + } +}; +Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, { + NAME: 'Date Selector', + ATTRS: { + firstdayofweek : { + validator: Y.Lang.isString + }, + node: { + setter: function(node) { + return Y.one(node); + } + } + } +}); + + +}, '@VERSION@', {"requires": ["base", "node", "overlay", "calendar"]}); diff --git a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js new file mode 100644 index 00000000000..f7aa5d63f23 --- /dev/null +++ b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector-min.js @@ -0,0 +1,2 @@ +YUI.add("moodle-form-dateselector",function(e,t){e.mix(e.Node.prototype,{firstOptionValue:function(){return this.get("nodeName").toLowerCase()!=="select"?!1:this.one("option").get("value")},lastOptionValue:function(){return this.get("nodeName").toLowerCase()!=="select"?!1:this.all("option").item(this.optionSize()-1).get("value")},optionSize:function(){return this.get("nodeName").toLowerCase()!=="select"?!1:parseInt(this.all("option").size(),10)},selectedOptionValue:function(){return this.get("nodeName").toLowerCase()!=="select"?!1:this.all("option").item(this.get("selectedIndex")).get("value")}}),M.form=M.form||{},M.form.dateselector={panel:null,calendar:null,currentowner:null,hidetimeout:null,repositiontimeout:null,init_date_selectors:function(t){this.panel===null&&this.initPanel(t),e.all(".fdate_time_selector").each(function(){t.node=this,new r(t)}),e.all(".fdate_selector").each(function(){t.node=this,new r(t)})},initPanel:function(t){this.panel=new e.Overlay({visible:!1,bodyContent:e.Node.create('
'),id:"dateselector-calendar-panel"}),this.panel.render(document.body),e.one("#dateselector-calendar-panel").setStyle("zIndex",null),this.panel.on("heightChange",this.fix_position,this),e.one("#dateselector-calendar-panel").on("click",function(e){e.halt()}),e.one(document.body).on("click",this.document_click,this),this.calendar=new n({contentBox:"#dateselector-calendar-content",width:"300px",showPrevMonth:!0,showNextMonth:!0,firstdayofweek:t.firstdayofweek,WEEKDAYS_MEDIUM:[t.sun,t.mon,t.tue,t.wed,t.thu,t.fri,t.sat]})},cancel_any_timeout:function(){this.hidetimeout&&(clearTimeout(this.hidetimeout),this.hidetimeout=null),this.repositiontimeout&&(clearTimeout(this.repositiontimeout),this.repositiontimeout=null)},delayed_reposition:function(){this.repositiontimeout&&(clearTimeout(this.repositiontimeout),this.repositiontimeout=null),this.repositiontimeout=setTimeout(this.fix_position,500)},fix_position:function(){if(this.currentowner){var t=[e.WidgetPositionAlign.BL,e.WidgetPositionAlign.TL];right_to_left()&&(t=[e.WidgetPositionAlign.BR,e.WidgetPositionAlign.TR]),this.panel.set("align",{node:this.currentowner.get("node").one("select"),points:t})}},document_click:function(e){this.currentowner&&(this.currentowner.get("node").ancestor("div").contains(e.target)?setTimeout(function(){M.form.dateselector.cancel_any_timeout()},100):this.currentowner.release_calendar(e))}};var n=function(){n.superclass.constructor.apply(this,arguments)};e.extend(n,e.Calendar,{initializer:function(e){this.set("strings.very_short_weekdays",e.WEEKDAYS_MEDIUM),this.set("strings.first_weekday",e.firstdayofweek)}},{NAME:"Calendar",ATTRS:{}}),M.form_moodlecalendar=M.form_moodlecalendar||{},M.form_moodlecalendar.initializer=function(e){return new n(e)};var r=function(){r.superclass.constructor.apply(this,arguments)};r.prototype={panel:null,yearselect:null,monthselect:null,dayselect:null,calendarimage:null,enablecheckbox:null,closepopup:!0,initializer:function(){var e=this.get("node").all("select");e.each(function(e){e.get("name").match(/\[year\]/)?this.yearselect=e:e.get("name").match(/\[month\]/)?this.monthselect=e:e.get("name").match(/\[day\]/)&&(this.dayselect=e),e.after("change",this.handle_select_change,this)},this);var t=this.get("node").all("input, a");t.each(function(e){e.get("name").match(/\[calendar\]/)?(e.on("click",this.focus_event,this),this.calendarimage=e):(e.on("click",this.toggle_calendar_image,this),this.enablecheckbox=e),this.calendarimage&&this.enablecheckbox&&this.toggle_calendar_image()},this)},focus_event:function(e){M.form.dateselector.cancel_any_timeout(),M.form.dateselector.currentowner===this?this.release_calendar():(this.enablecheckbox===null||this.enablecheckbox.get("checked"))&&this.claim_calendar(),e.preventDefault()},handle_select_change:function(){this.closepopup=!1,this.set_date_from_selects(),this.closepopup=!0},claim_calendar:function(){M.form.dateselector.cancel_any_timeout();if(M.form.dateselector.currentowner===this)return;M.form.dateselector.currentowner&&M.form.dateselector.currentowner.release_calendar(),M.form.dateselector.currentowner!==this&&(this.connect_handlers(),this.set_date_from_selects()),M.form.dateselector.currentowner=this,M.form.dateselector.calendar.set("mindate",new Date(this.yearselect.firstOptionValue(),0,1)),M.form.dateselector.calendar.set("maxdate",new Date(this.yearselect.lastOptionValue(),11,31)),M.form.dateselector.panel.show(),M.form.dateselector.calendar.show(),M.form.dateselector.fix_position(),setTimeout(function(){M.form.dateselector.cancel_any_timeout()},100),M.form.dateselector.calendar.focus(),e.one(document.body).on("keyup",function(e){if(M.form.dateselector.currentowner===this&&!M.form.dateselector.calendar.get("focused")||e.keyCode===27&&M.form.dateselector.calendar.get("focused"))this.calendarimage.focus(),this.release_calendar()},this)},set_date_from_selects:function(){var e=parseInt(this.yearselect.get("value"),10),t=parseInt(this.monthselect.get("value"),10)-1,n=parseInt(this.dayselect.get("value"),10),r=new Date(e,t,n);M.form.dateselector.calendar.deselectDates(),M.form.dateselector.calendar.selectDates([r]),M.form.dateselector.calendar.set("date",r),M.form.dateselector.calendar.render(),r.getDate()!==n&&(this.dayselect.set("value",r.getDate()),this.monthselect.set("value",r.getMonth()+1))},set_selects_from_date:function(t){var n=t.newSelection[0],r=e.DataType.Date.format(n,{format:"%Y"}),i=r-this.yearselect.firstOptionValue();this.yearselect.set("selectedIndex",i),this.monthselect.set("selectedIndex",e.DataType.Date.format(n,{format:"%m"})-this.monthselect.firstOptionValue()),this.dayselect.set("selectedIndex",e.DataType.Date.format(n,{format:"%d"})-this.dayselect.firstOptionValue()),M.form.dateselector.currentowner&&this.closepopup&&this.release_calendar()},connect_handlers:function(){M.form.dateselector.calendar.on("selectionChange",this.set_selects_from_date,this,!0)},release_calendar:function(e){M.form +.dateselector.panel.hide(),M.form.dateselector.calendar.detach("selectionChange",this.set_selects_from_date),M.form.dateselector.calendar.hide(),M.form.dateselector.currentowner=null,(e===null||typeof e=="undefined"||e.type!=="click")&&this.calendarimage.focus()},toggle_calendar_image:function(){this.enablecheckbox.get("checked")?(this.calendarimage.set("disabled",!1),this.calendarimage.setStyle("cursor",null)):(this.calendarimage.set("disabled","disabled"),this.calendarimage.setStyle("cursor","default"),this.release_calendar())}},e.extend(r,e.Base,r.prototype,{NAME:"Date Selector",ATTRS:{firstdayofweek:{validator:e.Lang.isString},node:{setter:function(t){return e.one(t)}}}})},"@VERSION@",{requires:["base","node","overlay","calendar"]}); diff --git a/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js new file mode 100644 index 00000000000..87a9c9323de --- /dev/null +++ b/lib/form/yui/build/moodle-form-dateselector/moodle-form-dateselector.js @@ -0,0 +1,360 @@ +YUI.add('moodle-form-dateselector', function (Y, NAME) { + +/** + * Add some custom methods to the node class to make our lives a little + * easier within this module. + */ +Y.mix(Y.Node.prototype, { + /** + * Gets the value of the first option in the select box + */ + firstOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.one('option').get('value'); + }, + /** + * Gets the value of the last option in the select box + */ + lastOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.optionSize()-1).get('value'); + }, + /** + * Gets the number of options in the select box + */ + optionSize: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return parseInt(this.all('option').size(), 10); + }, + /** + * Gets the value of the selected option in the select box + */ + selectedOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.get('selectedIndex')).get('value'); + } +}); + +M.form = M.form || {}; +M.form.dateselector = { + panel: null, + calendar: null, + currentowner: null, + hidetimeout: null, + repositiontimeout: null, + init_date_selectors: function(config) { + if (this.panel === null) { + this.initPanel(config); + } + Y.all('.fdate_time_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + Y.all('.fdate_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + }, + initPanel: function(config) { + this.panel = new Y.Overlay({ + visible: false, + bodyContent: Y.Node.create('
'), + id: 'dateselector-calendar-panel' + }); + this.panel.render(document.body); + // zIndex is added by panel.render() and is set to 0. + // Remove zIndex from panel, as this should be set by CSS. This can be done by removeAttr but + // ie8 fails and there is know issue for it. + Y.one('#dateselector-calendar-panel').setStyle('zIndex', null); + this.panel.on('heightChange', this.fix_position, this); + + Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();}); + Y.one(document.body).on('click', this.document_click, this); + + this.calendar = new MOODLECALENDAR({ + contentBox: "#dateselector-calendar-content", + width: "300px", + showPrevMonth: true, + showNextMonth: true, + firstdayofweek: config.firstdayofweek, + WEEKDAYS_MEDIUM: [ + config.sun, + config.mon, + config.tue, + config.wed, + config.thu, + config.fri, + config.sat ] + }); + }, + cancel_any_timeout: function() { + if (this.hidetimeout) { + clearTimeout(this.hidetimeout); + this.hidetimeout = null; + } + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + }, + delayed_reposition: function() { + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + this.repositiontimeout = setTimeout(this.fix_position, 500); + }, + fix_position: function() { + if (this.currentowner) { + var alignpoints = [ + Y.WidgetPositionAlign.BL, + Y.WidgetPositionAlign.TL + ]; + + // Change the alignment if this is an RTL language. + if (right_to_left()) { + alignpoints = [ + Y.WidgetPositionAlign.BR, + Y.WidgetPositionAlign.TR + ]; + } + + + this.panel.set('align', { + node: this.currentowner.get('node').one('select'), + points: alignpoints + }); + } + }, + document_click: function(e) { + if (this.currentowner) { + if (this.currentowner.get('node').ancestor('div').contains(e.target)) { + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + } else { + this.currentowner.release_calendar(e); + } + } + } +}; +/** + * Provides the Moodle Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * A class to overwrite the YUI3 Calendar in order to change the strings.. + * + * @class M.form_moodlecalendar + * @constructor + * @extends Calendar + */ +var MOODLECALENDAR = function() { + MOODLECALENDAR.superclass.constructor.apply(this, arguments); +}; + +Y.extend(MOODLECALENDAR, Y.Calendar, { + initializer: function(cfg) { + this.set("strings.very_short_weekdays", cfg.WEEKDAYS_MEDIUM); + this.set("strings.first_weekday", cfg.firstdayofweek); + } + }, { + NAME: 'Calendar', + ATTRS: {} + } +); + +M.form_moodlecalendar = M.form_moodlecalendar || {}; +M.form_moodlecalendar.initializer = function(params) { + return new MOODLECALENDAR(params); +}; +/** + * Provides the Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * Calendar class + */ +var CALENDAR = function() { + CALENDAR.superclass.constructor.apply(this, arguments); +}; +CALENDAR.prototype = { + panel: null, + yearselect: null, + monthselect: null, + dayselect: null, + calendarimage: null, + enablecheckbox: null, + closepopup: true, + initializer: function() { + var controls = this.get('node').all('select'); + controls.each(function(node){ + if (node.get('name').match(/\[year\]/)) { + this.yearselect = node; + } else if (node.get('name').match(/\[month\]/)) { + this.monthselect = node; + } else if (node.get('name').match(/\[day\]/)) { + this.dayselect = node; + } + node.after('change', this.handle_select_change, this); + }, this); + + // Loop through the input fields. + var inputs = this.get('node').all('input, a'); + inputs.each(function(node) { + // Check if the current node is a calendar image field. + if (node.get('name').match(/\[calendar\]/)) { + // Set it so that when the image is clicked the pop-up displays. + node.on('click', this.focus_event, this); + // Set the node to the calendarimage variable. + this.calendarimage = node; + } else { // Must be the enabled checkbox field. + // If the enable checkbox is clicked we want to either disable/enable the calendar image. + node.on('click', this.toggle_calendar_image, this); + // Set the node to the enablecheckbox variable. + this.enablecheckbox = node; + } + // Ensure that the calendarimage and enablecheckbox values have been set. + if (this.calendarimage && this.enablecheckbox) { + // Set the calendar icon status depending on the value of the checkbox. + this.toggle_calendar_image(); + } + }, this); + }, + focus_event: function(e) { + M.form.dateselector.cancel_any_timeout(); + // If the current owner is set, then the pop-up is currently being displayed, so hide it. + if (M.form.dateselector.currentowner === this) { + this.release_calendar(); + } else if ((this.enablecheckbox === null) + || (this.enablecheckbox.get('checked'))) { // Must be hidden. If the field is enabled display the pop-up. + this.claim_calendar(); + } + // Stop the input image field from submitting the form. + e.preventDefault(); + }, + handle_select_change: function() { + // It may seem as if the following variable is not used, however any call to set_date_from_selects will trigger a + // call to set_selects_from_date if the calendar is open as the date has changed. Whenever the calendar is displayed + // the set_selects_from_date function is set to trigger on any date change (see function connect_handlers). + this.closepopup = false; + this.set_date_from_selects(); + this.closepopup = true; + }, + claim_calendar: function() { + M.form.dateselector.cancel_any_timeout(); + if (M.form.dateselector.currentowner === this) { + return; + } + if (M.form.dateselector.currentowner) { + M.form.dateselector.currentowner.release_calendar(); + } + if (M.form.dateselector.currentowner !== this) { + this.connect_handlers(); + this.set_date_from_selects(); + } + M.form.dateselector.currentowner = this; + M.form.dateselector.calendar.set('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1)); + M.form.dateselector.calendar.set('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31)); + M.form.dateselector.panel.show(); + M.form.dateselector.calendar.show(); + M.form.dateselector.fix_position(); + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + + // Focus on the calendar. + M.form.dateselector.calendar.focus(); + + // When the user tab out the calendar, close it. + Y.one(document.body).on('keyup', function(e) { + // hide the calendar if we press a key and the calendar is not focussed, or if we press ESC in the calendar. + if ((M.form.dateselector.currentowner === this && !M.form.dateselector.calendar.get('focused')) || + ((e.keyCode === 27) && M.form.dateselector.calendar.get('focused'))) { + // Focus back on the calendar button. + this.calendarimage.focus(); + this.release_calendar(); + } + }, this); + + }, + set_date_from_selects: function() { + var year = parseInt(this.yearselect.get('value'), 10); + var month = parseInt(this.monthselect.get('value'), 10) - 1; + var day = parseInt(this.dayselect.get('value'), 10); + var date = new Date(year, month, day); + M.form.dateselector.calendar.deselectDates(); + M.form.dateselector.calendar.selectDates([date]); + M.form.dateselector.calendar.set("date", date); + M.form.dateselector.calendar.render(); + if (date.getDate() !== day) { + // Must've selected the 29 to 31st of a month that doesn't have such dates. + this.dayselect.set('value', date.getDate()); + this.monthselect.set('value', date.getMonth() + 1); + } + }, + set_selects_from_date: function(ev) { + var date = ev.newSelection[0]; + var newyear = Y.DataType.Date.format(date, {format: "%Y"}); + var newindex = newyear - this.yearselect.firstOptionValue(); + this.yearselect.set('selectedIndex', newindex); + this.monthselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%m"}) - this.monthselect.firstOptionValue()); + this.dayselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%d"}) - this.dayselect.firstOptionValue()); + if (M.form.dateselector.currentowner && this.closepopup) { + this.release_calendar(); + } + }, + connect_handlers: function() { + M.form.dateselector.calendar.on('selectionChange', this.set_selects_from_date, this, true); + }, + release_calendar: function(e) { + M.form.dateselector.panel.hide(); + M.form.dateselector.calendar.detach('selectionChange', this.set_selects_from_date); + M.form.dateselector.calendar.hide(); + M.form.dateselector.currentowner = null; + + // Put the focus back to the image calendar that we clicked. + if (e === null || typeof e === "undefined" || e.type !== "click") { + this.calendarimage.focus(); + } + }, + toggle_calendar_image: function() { + // If the enable checkbox is det checked, disable the image. + if (!this.enablecheckbox.get('checked')) { + this.calendarimage.set('disabled', 'disabled'); + this.calendarimage.setStyle('cursor', 'default'); + this.release_calendar(); + } else { + this.calendarimage.set('disabled', false); + this.calendarimage.setStyle('cursor', null); + } + } +}; +Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, { + NAME: 'Date Selector', + ATTRS: { + firstdayofweek : { + validator: Y.Lang.isString + }, + node: { + setter: function(node) { + return Y.one(node); + } + } + } +}); + + +}, '@VERSION@', {"requires": ["base", "node", "overlay", "calendar"]}); diff --git a/lib/form/yui/dateselector/assets/skins/sam/dateselector.css b/lib/form/yui/dateselector/assets/skins/sam/dateselector.css deleted file mode 100644 index d8abda4d431..00000000000 --- a/lib/form/yui/dateselector/assets/skins/sam/dateselector.css +++ /dev/null @@ -1,4 +0,0 @@ -#dateselector-calendar-panel {background-color:#999;border-bottom:3px solid #999;border-right:3px solid #999;} -#dateselector-calendar-content {border:1px solid #666;margin-top:-3px;margin-left:-3px;} -#dateselector-calendar-content_t th.calweekdaycell {padding-left:3px; padding-right:3px;} -body.ie #dateselector-calendar-panel.yui3-overlay-hidden table {display:none;} \ No newline at end of file diff --git a/lib/form/yui/dateselector/dateselector.js b/lib/form/yui/dateselector/dateselector.js deleted file mode 100644 index 3f4545f0855..00000000000 --- a/lib/form/yui/dateselector/dateselector.js +++ /dev/null @@ -1,300 +0,0 @@ -YUI.add('moodle-form-dateselector', function(Y) { - - /** - * Add some custom methods to the node class to make our lives a little - * easier within this module. - */ - Y.mix(Y.Node.prototype, { - /** - * Gets the value of the first option in the select box - */ - firstOptionValue : function() { - if (this.get('nodeName').toLowerCase() != 'select') { - return false; - } - return this.one('option').get('value'); - }, - /** - * Gets the value of the last option in the select box - */ - lastOptionValue : function() { - if (this.get('nodeName').toLowerCase() != 'select') { - return false; - } - return this.all('option').item(this.optionSize()-1).get('value'); - }, - /** - * Gets the number of options in the select box - */ - optionSize : function() { - if (this.get('nodeName').toLowerCase() != 'select') { - return false; - } - return parseInt(this.all('option').size()); - }, - /** - * Gets the value of the selected option in the select box - */ - selectedOptionValue : function() { - if (this.get('nodeName').toLowerCase() != 'select') { - return false; - } - return this.all('option').item(this.get('selectedIndex')).get('value'); - } - }); - - /** - * Calendar class - * - * This is our main class - */ - var CALENDAR = function(config) { - CALENDAR.superclass.constructor.apply(this, arguments); - }; - CALENDAR.prototype = { - panel : null, - yearselect : null, - monthselect : null, - dayselect : null, - calendarimage : null, - enablecheckbox : null, - closepopup : true, - initializer : function(config) { - var controls = this.get('node').all('select'); - controls.each(function(node){ - if (node.get('name').match(/\[year]/)) { - this.yearselect = node; - } else if (node.get('name').match(/\[month\]/)) { - this.monthselect = node; - } else if (node.get('name').match(/\[day]/)) { - this.dayselect = node; - } - node.after('change', this.handle_select_change, this); - }, this); - - // Loop through the input fields. - var inputs = this.get('node').all('input, a'); - inputs.each(function(node) { - // Check if the current node is a calendar image field. - if (node.get('name').match(/\[calendar]/)) { - // Set it so that when the image is clicked the pop-up displays. - node.on('click', this.focus_event, this); - // Set the node to the calendarimage variable. - this.calendarimage = node; - } else { // Must be the enabled checkbox field. - // If the enable checkbox is clicked we want to either disable/enable the calendar image. - node.on('click', this.toggle_calendar_image, this); - // Set the node to the enablecheckbox variable. - this.enablecheckbox = node; - } - // Ensure that the calendarimage and enablecheckbox values have been set. - if (this.calendarimage && this.enablecheckbox) { - // Set the calendar icon status depending on the value of the checkbox. - this.toggle_calendar_image(); - } - }, this); - }, - focus_event : function(e) { - M.form.dateselector.cancel_any_timeout(); - // If the current owner is set, then the pop-up is currently being displayed, so hide it. - if (M.form.dateselector.currentowner == this) { - this.release_calendar(); - } else if ((this.enablecheckbox == null) - || (this.enablecheckbox.get('checked'))) { // Must be hidden. If the field is enabled display the pop-up. - this.claim_calendar(); - } - // Stop the input image field from submitting the form. - e.preventDefault(); - }, - handle_select_change : function(e) { - // It may seem as if the following variable is not used, however any call to set_date_from_selects will trigger a - // call to set_selects_from_date if the calendar is open as the date has changed. Whenever the calendar is displayed - // the set_selects_from_date function is set to trigger on any date change (see function connect_handlers). - this.closepopup = false; - this.set_date_from_selects(); - this.closepopup = true; - }, - claim_calendar : function() { - M.form.dateselector.cancel_any_timeout(); - if (M.form.dateselector.currentowner == this) { - return; - } - if (M.form.dateselector.currentowner) { - M.form.dateselector.currentowner.release_calendar(); - } - if (M.form.dateselector.currentowner != this) { - this.connect_handlers(); - this.set_date_from_selects(); - } - M.form.dateselector.currentowner = this; - M.form.dateselector.calendar.cfg.setProperty('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1)); - M.form.dateselector.calendar.cfg.setProperty('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31)); - M.form.dateselector.panel.show(); - M.form.dateselector.fix_position(); - setTimeout(function(){M.form.dateselector.cancel_any_timeout()}, 100); - }, - set_date_from_selects : function() { - var year = parseInt(this.yearselect.get('value')); - var month = parseInt(this.monthselect.get('value')) - 1; - var day = parseInt(this.dayselect.get('value')); - var date = new Date(year, month, day); - M.form.dateselector.calendar.select(date); - M.form.dateselector.calendar.setMonth(month); - M.form.dateselector.calendar.setYear(year); - M.form.dateselector.calendar.render(); - if (date.getDate() != day) { - // Must've selected the 29 to 31st of a month that doesn't have such dates. - this.dayselect.set('value', date.getDate()); - this.monthselect.set('value', date.getMonth() + 1); - } - }, - set_selects_from_date : function(eventtype, args) { - var date = args[0][0]; - var newyear = date[0]; - var newindex = newyear - this.yearselect.firstOptionValue(); - this.yearselect.set('selectedIndex', newindex); - this.monthselect.set('selectedIndex', date[1] - this.monthselect.firstOptionValue()); - this.dayselect.set('selectedIndex', date[2] - this.dayselect.firstOptionValue()); - if (M.form.dateselector.currentowner && this.closepopup) { - this.release_calendar(); - } - }, - connect_handlers : function() { - M.form.dateselector.calendar.selectEvent.subscribe(this.set_selects_from_date, this, true); - }, - release_calendar : function() { - M.form.dateselector.panel.hide(); - M.form.dateselector.currentowner = null; - M.form.dateselector.calendar.selectEvent.unsubscribe(this.set_selects_from_date, this); - }, - toggle_calendar_image : function() { - // If the enable checkbox is not checked, disable the image. - if (!this.enablecheckbox.get('checked')) { - this.calendarimage.set('disabled', 'disabled'); - this.calendarimage.setStyle('cursor', 'default'); - this.release_calendar(); - } else { - this.calendarimage.set('disabled', false); - this.calendarimage.setStyle('cursor', null); - } - } - }; - Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, { - NAME : 'Date Selector', - ATTRS : { - firstdayofweek : { - validator : Y.Lang.isString - }, - node : { - setter : function(node) { - return Y.one(node); - } - } - } - }); - - M.form = M.form || {}; - M.form.dateselector = { - panel : null, - calendar : null, - currentowner : null, - hidetimeout : null, - repositiontimeout : null, - init_date_selectors : function(config) { - if (this.panel === null) { - this.initPanel(config); - } - Y.all('.fdate_time_selector').each(function() { - config.node = this; - new CALENDAR(config); - }); - Y.all('.fdate_selector').each(function() { - config.node = this; - new CALENDAR(config); - }); - }, - initPanel : function(config) { - this.panel = new Y.Overlay({ - visible : false, - bodyContent : Y.Node.create('
'), - id : 'dateselector-calendar-panel' - }); - this.panel.render(document.body); - // zIndex is added by panel.render() and is set to 0. - // Remove zIndex from panel, as this should be set by CSS. This can be done by removeAttr but - // ie8 fails and there is know issue for it. - Y.one('#dateselector-calendar-panel').setStyle('zIndex', null); - this.panel.on('heightChange', this.fix_position, this); - - Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();}); - Y.one(document.body).on('click', this.document_click, this); - - this.calendar = new Y.YUI2.widget.Calendar(document.getElementById('dateselector-calendar-content'), { - iframe: false, - hide_blank_weeks: true, - start_weekday: config.firstdayofweek, - locale_weekdays: 'medium', - locale_months: 'long', - WEEKDAYS_MEDIUM: [ - config.sun, - config.mon, - config.tue, - config.wed, - config.thu, - config.fri, - config.sat ], - MONTHS_LONG: [ - config.january, - config.february, - config.march, - config.april, - config.may, - config.june, - config.july, - config.august, - config.september, - config.october, - config.november, - config.december ] - }); - this.calendar.changePageEvent.subscribe(function(){ - this.fix_position(); - }, this); - }, - cancel_any_timeout : function() { - if (this.hidetimeout) { - clearTimeout(this.hidetimeout); - this.hidetimeout = null; - } - if (this.repositiontimeout) { - clearTimeout(this.repositiontimeout); - this.repositiontimeout = null; - } - }, - delayed_reposition : function() { - if (this.repositiontimeout) { - clearTimeout(this.repositiontimeout); - this.repositiontimeout = null; - } - this.repositiontimeout = setTimeout(this.fix_position, 500); - }, - fix_position : function() { - if (this.currentowner) { - this.panel.set('align', { - node:this.currentowner.get('node').one('select'), - points:[Y.WidgetPositionAlign.BL, Y.WidgetPositionAlign.TL] - }); - } - }, - document_click : function(e) { - if (this.currentowner) { - if (this.currentowner.get('node').ancestor('div').contains(e.target)) { - setTimeout(function() {M.form.dateselector.cancel_any_timeout()}, 100); - } else { - this.currentowner.release_calendar(); - } - } - } - } - -}, '@VERSION@', {requires:['base','node','overlay', 'yui2-calendar', 'moodle-form-dateselector-skin']}); diff --git a/lib/form/yui/src/dateselector/build.json b/lib/form/yui/src/dateselector/build.json new file mode 100644 index 00000000000..333aa1a32aa --- /dev/null +++ b/lib/form/yui/src/dateselector/build.json @@ -0,0 +1,12 @@ +{ + "name": "moodle-form-dateselector", + "builds": { + "moodle-form-dateselector": { + "jsfiles": [ + "dateselector.js", + "moodlecalendar.js", + "calendar.js" + ] + } + } +} diff --git a/lib/form/yui/src/dateselector/js/calendar.js b/lib/form/yui/src/dateselector/js/calendar.js new file mode 100644 index 00000000000..b09aec7c677 --- /dev/null +++ b/lib/form/yui/src/dateselector/js/calendar.js @@ -0,0 +1,177 @@ +/** + * Provides the Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * Calendar class + */ +var CALENDAR = function() { + CALENDAR.superclass.constructor.apply(this, arguments); +}; +CALENDAR.prototype = { + panel: null, + yearselect: null, + monthselect: null, + dayselect: null, + calendarimage: null, + enablecheckbox: null, + closepopup: true, + initializer: function() { + var controls = this.get('node').all('select'); + controls.each(function(node){ + if (node.get('name').match(/\[year\]/)) { + this.yearselect = node; + } else if (node.get('name').match(/\[month\]/)) { + this.monthselect = node; + } else if (node.get('name').match(/\[day\]/)) { + this.dayselect = node; + } + node.after('change', this.handle_select_change, this); + }, this); + + // Loop through the input fields. + var inputs = this.get('node').all('input, a'); + inputs.each(function(node) { + // Check if the current node is a calendar image field. + if (node.get('name').match(/\[calendar\]/)) { + // Set it so that when the image is clicked the pop-up displays. + node.on('click', this.focus_event, this); + // Set the node to the calendarimage variable. + this.calendarimage = node; + } else { // Must be the enabled checkbox field. + // If the enable checkbox is clicked we want to either disable/enable the calendar image. + node.on('click', this.toggle_calendar_image, this); + // Set the node to the enablecheckbox variable. + this.enablecheckbox = node; + } + // Ensure that the calendarimage and enablecheckbox values have been set. + if (this.calendarimage && this.enablecheckbox) { + // Set the calendar icon status depending on the value of the checkbox. + this.toggle_calendar_image(); + } + }, this); + }, + focus_event: function(e) { + M.form.dateselector.cancel_any_timeout(); + // If the current owner is set, then the pop-up is currently being displayed, so hide it. + if (M.form.dateselector.currentowner === this) { + this.release_calendar(); + } else if ((this.enablecheckbox === null) + || (this.enablecheckbox.get('checked'))) { // Must be hidden. If the field is enabled display the pop-up. + this.claim_calendar(); + } + // Stop the input image field from submitting the form. + e.preventDefault(); + }, + handle_select_change: function() { + // It may seem as if the following variable is not used, however any call to set_date_from_selects will trigger a + // call to set_selects_from_date if the calendar is open as the date has changed. Whenever the calendar is displayed + // the set_selects_from_date function is set to trigger on any date change (see function connect_handlers). + this.closepopup = false; + this.set_date_from_selects(); + this.closepopup = true; + }, + claim_calendar: function() { + M.form.dateselector.cancel_any_timeout(); + if (M.form.dateselector.currentowner === this) { + return; + } + if (M.form.dateselector.currentowner) { + M.form.dateselector.currentowner.release_calendar(); + } + if (M.form.dateselector.currentowner !== this) { + this.connect_handlers(); + this.set_date_from_selects(); + } + M.form.dateselector.currentowner = this; + M.form.dateselector.calendar.set('mindate', new Date(this.yearselect.firstOptionValue(), 0, 1)); + M.form.dateselector.calendar.set('maxdate', new Date(this.yearselect.lastOptionValue(), 11, 31)); + M.form.dateselector.panel.show(); + M.form.dateselector.calendar.show(); + M.form.dateselector.fix_position(); + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + + // Focus on the calendar. + M.form.dateselector.calendar.focus(); + + // When the user tab out the calendar, close it. + Y.one(document.body).on('keyup', function(e) { + // hide the calendar if we press a key and the calendar is not focussed, or if we press ESC in the calendar. + if ((M.form.dateselector.currentowner === this && !M.form.dateselector.calendar.get('focused')) || + ((e.keyCode === 27) && M.form.dateselector.calendar.get('focused'))) { + // Focus back on the calendar button. + this.calendarimage.focus(); + this.release_calendar(); + } + }, this); + + }, + set_date_from_selects: function() { + var year = parseInt(this.yearselect.get('value'), 10); + var month = parseInt(this.monthselect.get('value'), 10) - 1; + var day = parseInt(this.dayselect.get('value'), 10); + var date = new Date(year, month, day); + M.form.dateselector.calendar.deselectDates(); + M.form.dateselector.calendar.selectDates([date]); + M.form.dateselector.calendar.set("date", date); + M.form.dateselector.calendar.render(); + if (date.getDate() !== day) { + // Must've selected the 29 to 31st of a month that doesn't have such dates. + this.dayselect.set('value', date.getDate()); + this.monthselect.set('value', date.getMonth() + 1); + } + }, + set_selects_from_date: function(ev) { + var date = ev.newSelection[0]; + var newyear = Y.DataType.Date.format(date, {format: "%Y"}); + var newindex = newyear - this.yearselect.firstOptionValue(); + this.yearselect.set('selectedIndex', newindex); + this.monthselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%m"}) - this.monthselect.firstOptionValue()); + this.dayselect.set('selectedIndex', Y.DataType.Date.format(date, {format: "%d"}) - this.dayselect.firstOptionValue()); + if (M.form.dateselector.currentowner && this.closepopup) { + this.release_calendar(); + } + }, + connect_handlers: function() { + M.form.dateselector.calendar.on('selectionChange', this.set_selects_from_date, this, true); + }, + release_calendar: function(e) { + M.form.dateselector.panel.hide(); + M.form.dateselector.calendar.detach('selectionChange', this.set_selects_from_date); + M.form.dateselector.calendar.hide(); + M.form.dateselector.currentowner = null; + + // Put the focus back to the image calendar that we clicked. + if (e === null || typeof e === "undefined" || e.type !== "click") { + this.calendarimage.focus(); + } + }, + toggle_calendar_image: function() { + // If the enable checkbox is det checked, disable the image. + if (!this.enablecheckbox.get('checked')) { + this.calendarimage.set('disabled', 'disabled'); + this.calendarimage.setStyle('cursor', 'default'); + this.release_calendar(); + } else { + this.calendarimage.set('disabled', false); + this.calendarimage.setStyle('cursor', null); + } + } +}; +Y.extend(CALENDAR, Y.Base, CALENDAR.prototype, { + NAME: 'Date Selector', + ATTRS: { + firstdayofweek : { + validator: Y.Lang.isString + }, + node: { + setter: function(node) { + return Y.one(node); + } + } + } +}); diff --git a/lib/form/yui/src/dateselector/js/dateselector.js b/lib/form/yui/src/dateselector/js/dateselector.js new file mode 100644 index 00000000000..3a44e40f5a5 --- /dev/null +++ b/lib/form/yui/src/dateselector/js/dateselector.js @@ -0,0 +1,146 @@ +/** + * Add some custom methods to the node class to make our lives a little + * easier within this module. + */ +Y.mix(Y.Node.prototype, { + /** + * Gets the value of the first option in the select box + */ + firstOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.one('option').get('value'); + }, + /** + * Gets the value of the last option in the select box + */ + lastOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.optionSize()-1).get('value'); + }, + /** + * Gets the number of options in the select box + */ + optionSize: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return parseInt(this.all('option').size(), 10); + }, + /** + * Gets the value of the selected option in the select box + */ + selectedOptionValue: function() { + if (this.get('nodeName').toLowerCase() !== 'select') { + return false; + } + return this.all('option').item(this.get('selectedIndex')).get('value'); + } +}); + +M.form = M.form || {}; +M.form.dateselector = { + panel: null, + calendar: null, + currentowner: null, + hidetimeout: null, + repositiontimeout: null, + init_date_selectors: function(config) { + if (this.panel === null) { + this.initPanel(config); + } + Y.all('.fdate_time_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + Y.all('.fdate_selector').each(function() { + config.node = this; + new CALENDAR(config); + }); + }, + initPanel: function(config) { + this.panel = new Y.Overlay({ + visible: false, + bodyContent: Y.Node.create('
'), + id: 'dateselector-calendar-panel' + }); + this.panel.render(document.body); + // zIndex is added by panel.render() and is set to 0. + // Remove zIndex from panel, as this should be set by CSS. This can be done by removeAttr but + // ie8 fails and there is know issue for it. + Y.one('#dateselector-calendar-panel').setStyle('zIndex', null); + this.panel.on('heightChange', this.fix_position, this); + + Y.one('#dateselector-calendar-panel').on('click', function(e){e.halt();}); + Y.one(document.body).on('click', this.document_click, this); + + this.calendar = new MOODLECALENDAR({ + contentBox: "#dateselector-calendar-content", + width: "300px", + showPrevMonth: true, + showNextMonth: true, + firstdayofweek: config.firstdayofweek, + WEEKDAYS_MEDIUM: [ + config.sun, + config.mon, + config.tue, + config.wed, + config.thu, + config.fri, + config.sat ] + }); + }, + cancel_any_timeout: function() { + if (this.hidetimeout) { + clearTimeout(this.hidetimeout); + this.hidetimeout = null; + } + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + }, + delayed_reposition: function() { + if (this.repositiontimeout) { + clearTimeout(this.repositiontimeout); + this.repositiontimeout = null; + } + this.repositiontimeout = setTimeout(this.fix_position, 500); + }, + fix_position: function() { + if (this.currentowner) { + var alignpoints = [ + Y.WidgetPositionAlign.BL, + Y.WidgetPositionAlign.TL + ]; + + // Change the alignment if this is an RTL language. + if (right_to_left()) { + alignpoints = [ + Y.WidgetPositionAlign.BR, + Y.WidgetPositionAlign.TR + ]; + } + + + this.panel.set('align', { + node: this.currentowner.get('node').one('select'), + points: alignpoints + }); + } + }, + document_click: function(e) { + if (this.currentowner) { + if (this.currentowner.get('node').ancestor('div').contains(e.target)) { + setTimeout(function() { + M.form.dateselector.cancel_any_timeout(); + }, 100); + } else { + this.currentowner.release_calendar(e); + } + } + } +}; diff --git a/lib/form/yui/src/dateselector/js/moodlecalendar.js b/lib/form/yui/src/dateselector/js/moodlecalendar.js new file mode 100644 index 00000000000..f630c7e68a9 --- /dev/null +++ b/lib/form/yui/src/dateselector/js/moodlecalendar.js @@ -0,0 +1,32 @@ +/** + * Provides the Moodle Calendar class. + * + * @module moodle-form-dateselector + */ + +/** + * A class to overwrite the YUI3 Calendar in order to change the strings.. + * + * @class M.form_moodlecalendar + * @constructor + * @extends Calendar + */ +var MOODLECALENDAR = function() { + MOODLECALENDAR.superclass.constructor.apply(this, arguments); +}; + +Y.extend(MOODLECALENDAR, Y.Calendar, { + initializer: function(cfg) { + this.set("strings.very_short_weekdays", cfg.WEEKDAYS_MEDIUM); + this.set("strings.first_weekday", cfg.firstdayofweek); + } + }, { + NAME: 'Calendar', + ATTRS: {} + } +); + +M.form_moodlecalendar = M.form_moodlecalendar || {}; +M.form_moodlecalendar.initializer = function(params) { + return new MOODLECALENDAR(params); +}; diff --git a/lib/form/yui/src/dateselector/meta/dateselector.json b/lib/form/yui/src/dateselector/meta/dateselector.json new file mode 100644 index 00000000000..9ad3e1d034b --- /dev/null +++ b/lib/form/yui/src/dateselector/meta/dateselector.json @@ -0,0 +1,10 @@ +{ + "moodle-form-dateselector": { + "requires": [ + "base", + "node", + "overlay", + "calendar" + ] + } +}